Commit | Line | Data |
---|---|---|
12e364b9 KC |
1 | /* file.c |
2 | * | |
f6d0c1e6 | 3 | * Copyright (C) 2010 - 2013 UNISYS CORPORATION |
12e364b9 KC |
4 | * All rights reserved. |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License as published by | |
8 | * the Free Software Foundation; either version 2 of the License, or (at | |
9 | * your option) any later version. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, but | |
12 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | |
14 | * NON INFRINGEMENT. See the GNU General Public License for more | |
15 | * details. | |
16 | */ | |
17 | ||
18 | /* This contains the implementation that allows a usermode program to | |
19 | * communicate with the visorchipset driver using a device/file interface. | |
20 | */ | |
21 | ||
22 | #include "globals.h" | |
23 | #include "visorchannel.h" | |
24 | #include <linux/mm.h> | |
25 | #include <linux/fs.h> | |
26 | #include "uisutils.h" | |
bd5b9b32 | 27 | #include "file.h" |
12e364b9 KC |
28 | |
29 | #define CURRENT_FILE_PC VISOR_CHIPSET_PC_file_c | |
30 | ||
31 | static struct cdev Cdev; | |
32 | static VISORCHANNEL **PControlVm_channel; | |
33 | static dev_t MajorDev = -1; /**< indicates major num for device */ | |
34 | static BOOL Registered = FALSE; | |
35 | ||
36 | static int visorchipset_open(struct inode *inode, struct file *file); | |
37 | static int visorchipset_release(struct inode *inode, struct file *file); | |
38 | static int visorchipset_mmap(struct file *file, struct vm_area_struct *vma); | |
39 | #ifdef HAVE_UNLOCKED_IOCTL | |
40 | long visorchipset_ioctl(struct file *file, unsigned int cmd, unsigned long arg); | |
41 | #else | |
42 | int visorchipset_ioctl(struct inode *inode, struct file *file, | |
43 | unsigned int cmd, unsigned long arg); | |
44 | #endif | |
45 | ||
46 | static const struct file_operations visorchipset_fops = { | |
47 | .owner = THIS_MODULE, | |
48 | .open = visorchipset_open, | |
49 | .read = NULL, | |
50 | .write = NULL, | |
51 | #ifdef HAVE_UNLOCKED_IOCTL | |
52 | .unlocked_ioctl = visorchipset_ioctl, | |
53 | #else | |
54 | .ioctl = visorchipset_ioctl, | |
55 | #endif | |
56 | .release = visorchipset_release, | |
57 | .mmap = visorchipset_mmap, | |
58 | }; | |
59 | ||
60 | int | |
61 | visorchipset_file_init(dev_t majorDev, VISORCHANNEL **pControlVm_channel) | |
62 | { | |
63 | int rc = -1; | |
64 | ||
65 | PControlVm_channel = pControlVm_channel; | |
66 | MajorDev = majorDev; | |
67 | cdev_init(&Cdev, &visorchipset_fops); | |
68 | Cdev.owner = THIS_MODULE; | |
69 | if (MAJOR(MajorDev) == 0) { | |
70 | /* dynamic major device number registration required */ | |
71 | if (alloc_chrdev_region(&MajorDev, 0, 1, MYDRVNAME) < 0) { | |
72 | ERRDRV("Unable to allocate+register char device %s", | |
73 | MYDRVNAME); | |
22ad57ba | 74 | goto Away; |
12e364b9 KC |
75 | } |
76 | Registered = TRUE; | |
77 | INFODRV("New major number %d registered\n", MAJOR(MajorDev)); | |
78 | } else { | |
79 | /* static major device number registration required */ | |
80 | if (register_chrdev_region(MajorDev, 1, MYDRVNAME) < 0) { | |
81 | ERRDRV("Unable to register char device %s", MYDRVNAME); | |
22ad57ba | 82 | goto Away; |
12e364b9 KC |
83 | } |
84 | Registered = TRUE; | |
85 | INFODRV("Static major number %d registered\n", MAJOR(MajorDev)); | |
86 | } | |
5e54c97d | 87 | if (cdev_add(&Cdev, MKDEV(MAJOR(MajorDev), 0), 1) < 0) { |
22ad57ba | 88 | ERRDRV("failed to create char device: (status=%d)\n", rc); |
5e54c97d KC |
89 | goto Away; |
90 | } | |
12e364b9 KC |
91 | INFODRV("Registered char device for %s (major=%d)", |
92 | MYDRVNAME, MAJOR(MajorDev)); | |
22ad57ba | 93 | rc = 0; |
12e364b9 KC |
94 | Away: |
95 | return rc; | |
96 | } | |
97 | ||
98 | void | |
99 | visorchipset_file_cleanup(void) | |
100 | { | |
101 | if (Cdev.ops != NULL) | |
102 | cdev_del(&Cdev); | |
103 | Cdev.ops = NULL; | |
104 | if (Registered) { | |
105 | if (MAJOR(MajorDev) >= 0) { | |
106 | unregister_chrdev_region(MajorDev, 1); | |
107 | MajorDev = MKDEV(0, 0); | |
108 | } | |
109 | Registered = FALSE; | |
110 | } | |
111 | } | |
112 | ||
113 | static int | |
114 | visorchipset_open(struct inode *inode, struct file *file) | |
115 | { | |
116 | unsigned minor_number = iminor(inode); | |
117 | int rc = -ENODEV; | |
118 | ||
119 | DEBUGDRV("%s", __func__); | |
120 | if (minor_number != 0) | |
22ad57ba | 121 | goto Away; |
12e364b9 | 122 | file->private_data = NULL; |
22ad57ba | 123 | rc = 0; |
12e364b9 KC |
124 | Away: |
125 | if (rc < 0) | |
126 | ERRDRV("%s minor=%d failed", __func__, minor_number); | |
127 | return rc; | |
128 | } | |
129 | ||
130 | static int | |
131 | visorchipset_release(struct inode *inode, struct file *file) | |
132 | { | |
12e364b9 | 133 | DEBUGDRV("%s", __func__); |
22ad57ba | 134 | return 0; |
12e364b9 KC |
135 | } |
136 | ||
137 | static int | |
138 | visorchipset_mmap(struct file *file, struct vm_area_struct *vma) | |
139 | { | |
140 | ulong physAddr = 0; | |
141 | ulong offset = vma->vm_pgoff << PAGE_SHIFT; | |
142 | GUEST_PHYSICAL_ADDRESS addr = 0; | |
143 | ||
144 | /* sv_enable_dfp(); */ | |
145 | DEBUGDRV("%s", __func__); | |
146 | if (offset & (PAGE_SIZE - 1)) { | |
147 | ERRDRV("%s virtual address NOT page-aligned!", __func__); | |
148 | return -ENXIO; /* need aligned offsets */ | |
149 | } | |
150 | switch (offset) { | |
151 | case VISORCHIPSET_MMAP_CONTROLCHANOFFSET: | |
152 | vma->vm_flags |= VM_IO; | |
153 | if (*PControlVm_channel == NULL) { | |
154 | ERRDRV("%s no controlvm channel yet", __func__); | |
155 | return -ENXIO; | |
156 | } | |
157 | visorchannel_read(*PControlVm_channel, | |
158 | offsetof(ULTRA_CONTROLVM_CHANNEL_PROTOCOL, | |
159 | gpControlChannel), &addr, | |
160 | sizeof(addr)); | |
161 | if (addr == 0) { | |
162 | ERRDRV("%s control channel address is 0", __func__); | |
163 | return -ENXIO; | |
164 | } | |
165 | physAddr = (ulong) (addr); | |
166 | DEBUGDRV("mapping physical address = 0x%lx", physAddr); | |
167 | if (remap_pfn_range(vma, vma->vm_start, | |
168 | physAddr >> PAGE_SHIFT, | |
169 | vma->vm_end - vma->vm_start, | |
170 | /*pgprot_noncached */ | |
171 | (vma->vm_page_prot))) { | |
172 | ERRDRV("%s remap_pfn_range failed", __func__); | |
173 | return -EAGAIN; | |
174 | } | |
175 | break; | |
176 | default: | |
177 | return -ENOSYS; | |
178 | } | |
179 | DEBUGDRV("%s success!", __func__); | |
180 | return 0; | |
181 | } | |
182 | ||
183 | #ifdef HAVE_UNLOCKED_IOCTL | |
184 | long | |
185 | visorchipset_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | |
186 | #else | |
187 | int | |
188 | visorchipset_ioctl(struct inode *inode, struct file *file, | |
189 | unsigned int cmd, unsigned long arg) | |
190 | #endif | |
191 | { | |
192 | int rc = SUCCESS; | |
ec03a7db BR |
193 | s64 adjustment; |
194 | s64 vrtc_offset; | |
5a72afb9 | 195 | |
12e364b9 KC |
196 | DBGINF("entered visorchipset_ioctl, cmd=%d", cmd); |
197 | switch (cmd) { | |
198 | case VMCALL_QUERY_GUEST_VIRTUAL_TIME_OFFSET: | |
199 | /* get the physical rtc offset */ | |
200 | vrtc_offset = Issue_VMCALL_QUERY_GUEST_VIRTUAL_TIME_OFFSET(); | |
201 | if (copy_to_user | |
22ad57ba KC |
202 | ((void __user *)arg, &vrtc_offset, sizeof(vrtc_offset))) { |
203 | rc = -EFAULT; | |
204 | goto Away; | |
205 | } | |
12e364b9 KC |
206 | DBGINF("insde visorchipset_ioctl, cmd=%d, vrtc_offset=%lld", |
207 | cmd, vrtc_offset); | |
208 | break; | |
209 | case VMCALL_UPDATE_PHYSICAL_TIME: | |
210 | if (copy_from_user | |
22ad57ba KC |
211 | (&adjustment, (void __user *)arg, sizeof(adjustment))) { |
212 | rc = -EFAULT; | |
213 | goto Away; | |
214 | } | |
12e364b9 KC |
215 | DBGINF("insde visorchipset_ioctl, cmd=%d, adjustment=%lld", cmd, |
216 | adjustment); | |
217 | rc = Issue_VMCALL_UPDATE_PHYSICAL_TIME(adjustment); | |
218 | break; | |
219 | default: | |
220 | LOGERR("visorchipset_ioctl received invalid command"); | |
22ad57ba | 221 | rc = -EFAULT; |
12e364b9 KC |
222 | break; |
223 | } | |
12e364b9 KC |
224 | Away: |
225 | DBGINF("exiting %d!", rc); | |
226 | return rc; | |
227 | } |