/* 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 #include #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; }