blob: 203de0b5f6071fd9ff5d1ff86113bd869a44f1b0 [file] [log] [blame]
/* file.c
*
* Copyright (C) 2010 - 2013 UNISYS CORPORATION
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
* NON INFRINGEMENT. See the GNU General Public License for more
* details.
*/
/* This contains the implementation that allows a usermode program to
* communicate with the visorchipset driver using a device/file interface.
*/
#include "globals.h"
#include "visorchannel.h"
#include <linux/mm.h>
#include <linux/fs.h>
#include "uisutils.h"
#include "file.h"
#define CURRENT_FILE_PC VISOR_CHIPSET_PC_file_c
static struct cdev file_cdev;
static struct visorchannel **file_controlvm_channel;
void
visorchipset_file_cleanup(dev_t major_dev)
{
if (file_cdev.ops != NULL)
cdev_del(&file_cdev);
file_cdev.ops = NULL;
unregister_chrdev_region(major_dev, 1);
}
static int
visorchipset_open(struct inode *inode, struct file *file)
{
unsigned minor_number = iminor(inode);
if (minor_number != 0)
return -ENODEV;
file->private_data = NULL;
return 0;
}
static int
visorchipset_release(struct inode *inode, struct file *file)
{
return 0;
}
static int
visorchipset_mmap(struct file *file, struct vm_area_struct *vma)
{
ulong physaddr = 0;
ulong offset = vma->vm_pgoff << PAGE_SHIFT;
GUEST_PHYSICAL_ADDRESS addr = 0;
/* sv_enable_dfp(); */
if (offset & (PAGE_SIZE - 1))
return -ENXIO; /* need aligned offsets */
switch (offset) {
case VISORCHIPSET_MMAP_CONTROLCHANOFFSET:
vma->vm_flags |= VM_IO;
if (*file_controlvm_channel == NULL) {
return -ENXIO;
}
visorchannel_read(*file_controlvm_channel,
offsetof(struct spar_controlvm_channel_protocol,
gp_control_channel),
&addr, sizeof(addr));
if (addr == 0) {
return -ENXIO;
}
physaddr = (ulong)addr;
if (remap_pfn_range(vma, vma->vm_start,
physaddr >> PAGE_SHIFT,
vma->vm_end - vma->vm_start,
/*pgprot_noncached */
(vma->vm_page_prot))) {
return -EAGAIN;
}
break;
default:
return -ENOSYS;
}
return 0;
}
static long visorchipset_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
s64 adjustment;
s64 vrtc_offset;
switch (cmd) {
case VMCALL_QUERY_GUEST_VIRTUAL_TIME_OFFSET:
/* get the physical rtc offset */
vrtc_offset = issue_vmcall_query_guest_virtual_time_offset();
if (copy_to_user
((void __user *)arg, &vrtc_offset, sizeof(vrtc_offset))) {
return -EFAULT;
}
return SUCCESS;
case VMCALL_UPDATE_PHYSICAL_TIME:
if (copy_from_user
(&adjustment, (void __user *)arg, sizeof(adjustment))) {
return -EFAULT;
}
return issue_vmcall_update_physical_time(adjustment);
default:
return -EFAULT;
}
}
static const struct file_operations visorchipset_fops = {
.owner = THIS_MODULE,
.open = visorchipset_open,
.read = NULL,
.write = NULL,
.unlocked_ioctl = visorchipset_ioctl,
.release = visorchipset_release,
.mmap = visorchipset_mmap,
};
int
visorchipset_file_init(dev_t major_dev, struct visorchannel **controlvm_channel)
{
int rc = 0;
file_controlvm_channel = controlvm_channel;
cdev_init(&file_cdev, &visorchipset_fops);
file_cdev.owner = THIS_MODULE;
if (MAJOR(major_dev) == 0) {
rc = alloc_chrdev_region(&major_dev, 0, 1, MYDRVNAME);
/* dynamic major device number registration required */
if (rc < 0)
return rc;
} else {
/* static major device number registration required */
rc = register_chrdev_region(major_dev, 1, MYDRVNAME);
if (rc < 0)
return rc;
}
rc = cdev_add(&file_cdev, MKDEV(MAJOR(major_dev), 0), 1);
if (rc < 0) {
unregister_chrdev_region(major_dev, 1);
return rc;
}
return 0;
}