/* 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 Cdev; static VISORCHANNEL **PControlVm_channel; static dev_t MajorDev = -1; /**< indicates major num for device */ static BOOL Registered = FALSE; static int visorchipset_open(struct inode *inode, struct file *file); static int visorchipset_release(struct inode *inode, struct file *file); static int visorchipset_mmap(struct file *file, struct vm_area_struct *vma); #ifdef HAVE_UNLOCKED_IOCTL long visorchipset_ioctl(struct file *file, unsigned int cmd, unsigned long arg); #else int visorchipset_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg); #endif static const struct file_operations visorchipset_fops = { .owner = THIS_MODULE, .open = visorchipset_open, .read = NULL, .write = NULL, #ifdef HAVE_UNLOCKED_IOCTL .unlocked_ioctl = visorchipset_ioctl, #else .ioctl = visorchipset_ioctl, #endif .release = visorchipset_release, .mmap = visorchipset_mmap, }; int visorchipset_file_init(dev_t majorDev, VISORCHANNEL **pControlVm_channel) { int rc = -1; PControlVm_channel = pControlVm_channel; MajorDev = majorDev; cdev_init(&Cdev, &visorchipset_fops); Cdev.owner = THIS_MODULE; if (MAJOR(MajorDev) == 0) { /* dynamic major device number registration required */ if (alloc_chrdev_region(&MajorDev, 0, 1, MYDRVNAME) < 0) { ERRDRV("Unable to allocate+register char device %s", MYDRVNAME); goto Away; } Registered = TRUE; INFODRV("New major number %d registered\n", MAJOR(MajorDev)); } else { /* static major device number registration required */ if (register_chrdev_region(MajorDev, 1, MYDRVNAME) < 0) { ERRDRV("Unable to register char device %s", MYDRVNAME); goto Away; } Registered = TRUE; INFODRV("Static major number %d registered\n", MAJOR(MajorDev)); } if (cdev_add(&Cdev, MKDEV(MAJOR(MajorDev), 0), 1) < 0) { ERRDRV("failed to create char device: (status=%d)\n", rc); goto Away; } INFODRV("Registered char device for %s (major=%d)", MYDRVNAME, MAJOR(MajorDev)); rc = 0; Away: return rc; } void visorchipset_file_cleanup(void) { if (Cdev.ops != NULL) cdev_del(&Cdev); Cdev.ops = NULL; if (Registered) { if (MAJOR(MajorDev) >= 0) { unregister_chrdev_region(MajorDev, 1); MajorDev = MKDEV(0, 0); } Registered = FALSE; } } static int visorchipset_open(struct inode *inode, struct file *file) { unsigned minor_number = iminor(inode); int rc = -ENODEV; DEBUGDRV("%s", __func__); if (minor_number != 0) goto Away; file->private_data = NULL; rc = 0; Away: if (rc < 0) ERRDRV("%s minor=%d failed", __func__, minor_number); return rc; } static int visorchipset_release(struct inode *inode, struct file *file) { DEBUGDRV("%s", __func__); 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(); */ DEBUGDRV("%s", __func__); if (offset & (PAGE_SIZE - 1)) { ERRDRV("%s virtual address NOT page-aligned!", __func__); return -ENXIO; /* need aligned offsets */ } switch (offset) { case VISORCHIPSET_MMAP_CONTROLCHANOFFSET: vma->vm_flags |= VM_IO; if (*PControlVm_channel == NULL) { ERRDRV("%s no controlvm channel yet", __func__); return -ENXIO; } visorchannel_read(*PControlVm_channel, offsetof(ULTRA_CONTROLVM_CHANNEL_PROTOCOL, gpControlChannel), &addr, sizeof(addr)); if (addr == 0) { ERRDRV("%s control channel address is 0", __func__); return -ENXIO; } physAddr = (ulong) (addr); DEBUGDRV("mapping physical address = 0x%lx", physAddr); if (remap_pfn_range(vma, vma->vm_start, physAddr >> PAGE_SHIFT, vma->vm_end - vma->vm_start, /*pgprot_noncached */ (vma->vm_page_prot))) { ERRDRV("%s remap_pfn_range failed", __func__); return -EAGAIN; } break; default: return -ENOSYS; } DEBUGDRV("%s success!", __func__); return 0; } #ifdef HAVE_UNLOCKED_IOCTL long visorchipset_ioctl(struct file *file, unsigned int cmd, unsigned long arg) #else int visorchipset_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) #endif { int rc = SUCCESS; s64 adjustment; s64 vrtc_offset; DBGINF("entered visorchipset_ioctl, cmd=%d", cmd); 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))) { rc = -EFAULT; goto Away; } DBGINF("insde visorchipset_ioctl, cmd=%d, vrtc_offset=%lld", cmd, vrtc_offset); break; case VMCALL_UPDATE_PHYSICAL_TIME: if (copy_from_user (&adjustment, (void __user *)arg, sizeof(adjustment))) { rc = -EFAULT; goto Away; } DBGINF("insde visorchipset_ioctl, cmd=%d, adjustment=%lld", cmd, adjustment); rc = issue_vmcall_update_physical_time(adjustment); break; default: LOGERR("visorchipset_ioctl received invalid command"); rc = -EFAULT; break; } Away: DBGINF("exiting %d!", rc); return rc; }