1 /*
2 * Copyright 2014, General Dynamics C4 Systems
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 */
6
7 #include <kernel/thread.h>
8 #include <api/failures.h>
9 #include <api/syscall.h>
10 #include <machine/io.h>
11 #include <arch/object/ioport.h>
12 #include <arch/api/invocation.h>
13 #include <plat/machine/io.h>
14
apply_pattern(word_t_may_alias * w,word_t pattern,bool_t set)15 static inline void apply_pattern(word_t_may_alias *w, word_t pattern, bool_t set)
16 {
17 if (set) {
18 *w |= pattern;
19 } else {
20 *w &= ~pattern;
21 }
22 }
23
make_pattern(int start,int end)24 static inline word_t make_pattern(int start, int end)
25 {
26 // number of bits we want to have set
27 int num_bits = end - start;
28 // shift down to cut off the bits we don't want, then shift up to put the
29 // bits into position
30 return (~(word_t)0) >> (CONFIG_WORD_SIZE - num_bits) << start;
31 }
32
ensurePortOperationAllowed(cap_t cap,uint32_t start_port,uint32_t size)33 static exception_t ensurePortOperationAllowed(cap_t cap, uint32_t start_port, uint32_t size)
34 {
35 uint32_t first_allowed = cap_io_port_cap_get_capIOPortFirstPort(cap);
36 uint32_t last_allowed = cap_io_port_cap_get_capIOPortLastPort(cap);
37 uint32_t end_port = start_port + size - 1;
38 assert(first_allowed <= last_allowed);
39 assert(start_port <= end_port);
40
41 if ((start_port < first_allowed) || (end_port > last_allowed)) {
42 userError("IOPort: Ports %d--%d fall outside permitted range %d--%d.",
43 (int)start_port, (int)end_port,
44 (int)first_allowed, (int)last_allowed);
45 current_syscall_error.type = seL4_IllegalOperation;
46 return EXCEPTION_SYSCALL_ERROR;
47 }
48
49 return EXCEPTION_NONE;
50 }
51
freeIOPortRange(uint16_t first_port,uint16_t last_port)52 void freeIOPortRange(uint16_t first_port, uint16_t last_port)
53 {
54 setIOPortMask(x86KSAllocatedIOPorts, first_port, last_port, false);
55 }
56
isIOPortRangeFree(uint16_t first_port,uint16_t last_port)57 static bool_t isIOPortRangeFree(uint16_t first_port, uint16_t last_port)
58 {
59 int low_word = first_port >> wordRadix;
60 int high_word = last_port >> wordRadix;
61 int low_index = first_port & MASK(wordRadix);
62 int high_index = last_port & MASK(wordRadix);
63
64 // check if we are operating on a partial word
65 if (low_word == high_word) {
66 if ((x86KSAllocatedIOPorts[low_word] & make_pattern(low_index, high_index + 1)) != 0) {
67 return false;
68 }
69 return true;
70 }
71 // check the starting word
72 if ((x86KSAllocatedIOPorts[low_word] & make_pattern(low_index, CONFIG_WORD_SIZE)) != 0) {
73 return false;
74 }
75 low_word++;
76 // check the rest of the whole words
77 while (low_word < high_word) {
78 if (x86KSAllocatedIOPorts[low_word] != 0) {
79 return false;
80 }
81 low_word++;
82 }
83 // check any trailing bits
84 if ((x86KSAllocatedIOPorts[low_word] & make_pattern(0, high_index + 1)) != 0) {
85 return false;
86 }
87 return true;
88 }
89
invokeX86PortControl(uint16_t first_port,uint16_t last_port,cte_t * ioportSlot,cte_t * controlSlot)90 static exception_t invokeX86PortControl(uint16_t first_port, uint16_t last_port, cte_t *ioportSlot, cte_t *controlSlot)
91 {
92 setIOPortMask(x86KSAllocatedIOPorts, first_port, last_port, true);
93 cteInsert(cap_io_port_cap_new(first_port, last_port
94 #ifdef CONFIG_VTX
95 , VPID_INVALID
96 #endif
97 ),
98 controlSlot, ioportSlot);
99
100 return EXCEPTION_NONE;
101 }
102
decodeX86PortControlInvocation(word_t invLabel,word_t length,cptr_t cptr,cte_t * slot,cap_t cap,word_t * buffer)103 exception_t decodeX86PortControlInvocation(
104 word_t invLabel,
105 word_t length,
106 cptr_t cptr,
107 cte_t *slot,
108 cap_t cap,
109 word_t *buffer
110 )
111 {
112 uint16_t first_port;
113 uint16_t last_port;
114 word_t index, depth;
115 cap_t cnodeCap;
116 cte_t *destSlot;
117 lookupSlot_ret_t lu_ret;
118 exception_t status;
119
120 if (invLabel != X86IOPortControlIssue) {
121 userError("IOPortControl: Unknown operation.");
122 current_syscall_error.type = seL4_IllegalOperation;
123 return EXCEPTION_SYSCALL_ERROR;
124 }
125
126 if (length < 4 || current_extra_caps.excaprefs[0] == NULL) {
127 userError("IOPortControl: Truncated message.");
128 current_syscall_error.type = seL4_TruncatedMessage;
129 return EXCEPTION_SYSCALL_ERROR;
130 }
131
132 first_port = getSyscallArg(0, buffer) & 0xffff;
133 last_port = getSyscallArg(1, buffer) & 0xffff;
134 index = getSyscallArg(2, buffer);
135 depth = getSyscallArg(3, buffer);
136
137 cnodeCap = current_extra_caps.excaprefs[0]->cap;
138
139 if (last_port < first_port) {
140 userError("IOPortControl: Last port must be > first port.");
141 current_syscall_error.type = seL4_InvalidArgument;
142 current_syscall_error.invalidArgumentNumber = 1;
143 return EXCEPTION_SYSCALL_ERROR;
144 }
145
146 if (!isIOPortRangeFree(first_port, last_port)) {
147 userError("IOPortControl: Some ports in range already in use.");
148 current_syscall_error.type = seL4_RevokeFirst;
149 return EXCEPTION_SYSCALL_ERROR;
150 }
151
152 lu_ret = lookupTargetSlot(cnodeCap, index, depth);
153 if (lu_ret.status != EXCEPTION_NONE) {
154 userError("Target slot for new IO Port cap invalid: cap %lu.", getExtraCPtr(buffer, 0));
155 return lu_ret.status;
156 }
157 destSlot = lu_ret.slot;
158
159 status = ensureEmptySlot(destSlot);
160 if (status != EXCEPTION_NONE) {
161 userError("Target slot for new IO Port cap not empty: cap %lu.", getExtraCPtr(buffer, 0));
162 return status;
163 }
164
165 setThreadState(NODE_STATE(ksCurThread), ThreadState_Restart);
166 return invokeX86PortControl(first_port, last_port, destSlot, slot);
167 }
168
invokeX86PortIn(word_t invLabel,uint16_t port,bool_t call)169 static exception_t invokeX86PortIn(word_t invLabel, uint16_t port, bool_t call)
170 {
171 uint32_t res;
172 word_t len;
173
174 switch (invLabel) {
175 case X86IOPortIn8:
176 res = in8(port);
177 break;
178 case X86IOPortIn16:
179 res = in16(port);
180 break;
181 case X86IOPortIn32:
182 res = in32(port);
183 break;
184 }
185
186 if (call) {
187 setRegister(NODE_STATE(ksCurThread), badgeRegister, 0);
188
189 if (n_msgRegisters < 1) {
190 word_t *ipcBuffer;
191 ipcBuffer = lookupIPCBuffer(true, NODE_STATE(ksCurThread));
192 if (ipcBuffer != NULL) {
193 ipcBuffer[1] = res;
194 len = 1;
195 } else {
196 len = 0;
197 }
198 } else {
199 setRegister(NODE_STATE(ksCurThread), msgRegisters[0], res);
200 len = 1;
201 }
202
203 setRegister(NODE_STATE(ksCurThread), msgInfoRegister,
204 wordFromMessageInfo(seL4_MessageInfo_new(0, 0, 0, len)));
205 }
206 // Prevent handleInvocation from attempting to complete the 'call' with an empty
207 // message (via replyFromKernel_success_empty) by forcing the thread state to
208 // be running. This prevents our stored message we just created from being
209 // overwritten.
210 setThreadState(NODE_STATE(ksCurThread), ThreadState_Running);
211
212 return EXCEPTION_NONE;
213 }
214
invokeX86PortOut(word_t invLabel,uint16_t port,uint32_t data)215 static exception_t invokeX86PortOut(word_t invLabel, uint16_t port, uint32_t data)
216 {
217 switch (invLabel) {
218 case X86IOPortOut8:
219 out8(port, data);
220 break;
221 case X86IOPortOut16:
222 out16(port, data);
223 break;
224 case X86IOPortOut32:
225 out32(port, data);
226 break;
227 }
228
229 return EXCEPTION_NONE;
230 }
231
decodeX86PortInvocation(word_t invLabel,word_t length,cptr_t cptr,cte_t * slot,cap_t cap,bool_t call,word_t * buffer)232 exception_t decodeX86PortInvocation(
233 word_t invLabel,
234 word_t length,
235 cptr_t cptr,
236 cte_t *slot,
237 cap_t cap,
238 bool_t call,
239 word_t *buffer
240 )
241 {
242 exception_t ret;
243
244 if (invLabel == X86IOPortIn8 || invLabel == X86IOPortIn16 || invLabel == X86IOPortIn32) {
245 if (length < 1) {
246 userError("IOPort: Truncated message.");
247 current_syscall_error.type = seL4_TruncatedMessage;
248 return EXCEPTION_SYSCALL_ERROR;
249 }
250 /* Get the port the user is trying to read from. */
251 uint16_t port = getSyscallArg(0, buffer) & 0xffff;
252 switch (invLabel) {
253 case X86IOPortIn8:
254 ret = ensurePortOperationAllowed(cap, port, 1);
255 break;
256 case X86IOPortIn16:
257 ret = ensurePortOperationAllowed(cap, port, 2);
258 break;
259 case X86IOPortIn32:
260 ret = ensurePortOperationAllowed(cap, port, 4);
261 break;
262 }
263 if (ret != EXCEPTION_NONE) {
264 return ret;
265 }
266 setThreadState(NODE_STATE(ksCurThread), ThreadState_Restart);
267 return invokeX86PortIn(invLabel, port, call);
268 } else if (invLabel == X86IOPortOut8 || invLabel == X86IOPortOut16 || invLabel == X86IOPortOut32) {
269 /* Ensure the incoming message is long enough for the write. */
270 if (length < 2) {
271 userError("IOPort Out: Truncated message.");
272 current_syscall_error.type = seL4_TruncatedMessage;
273 return EXCEPTION_SYSCALL_ERROR;
274 }
275 /* Get the port the user is trying to write to. */
276 uint16_t port = getSyscallArg(0, buffer) & 0xffff;
277 seL4_Word raw_data = getSyscallArg(1, buffer);
278 /* We construct the value for data from raw_data based on the actual size of the port
279 operation. This ensures that there is no 'random' user data left over in the value
280 passed to invokeX86PortOut. Whilst invokeX86PortOut will ignore any extra data and
281 cast down to the correct word size removing the extra here is currently relied upon
282 for verification */
283 uint32_t data;
284
285 switch (invLabel) {
286 case X86IOPortOut8:
287 ret = ensurePortOperationAllowed(cap, port, 1);
288 data = raw_data & 0xff;
289 break;
290 case X86IOPortOut16:
291 ret = ensurePortOperationAllowed(cap, port, 2);
292 data = raw_data & 0xffff;
293 break;
294 case X86IOPortOut32:
295 ret = ensurePortOperationAllowed(cap, port, 4);
296 data = raw_data & 0xffffffff;
297 break;
298 }
299 if (ret != EXCEPTION_NONE) {
300 return ret;
301 }
302 setThreadState(NODE_STATE(ksCurThread), ThreadState_Restart);
303 return invokeX86PortOut(invLabel, port, data);
304 } else {
305 userError("IOPort: Unknown operation.");
306 current_syscall_error.type = seL4_IllegalOperation;
307 return EXCEPTION_SYSCALL_ERROR;
308 }
309 }
310
setIOPortMask(void * ioport_bitmap,uint16_t low,uint16_t high,bool_t set)311 void setIOPortMask(void *ioport_bitmap, uint16_t low, uint16_t high, bool_t set)
312 {
313 //get an aliasing pointer
314 word_t_may_alias *bitmap = ioport_bitmap;
315
316 int low_word = low >> wordRadix;
317 int high_word = high >> wordRadix;
318 int low_index = low & MASK(wordRadix);
319 int high_index = high & MASK(wordRadix);
320
321 // see if we are just manipulating bits inside a single word. handling this
322 // specially makes reasoning easier
323 if (low_word == high_word) {
324 apply_pattern(bitmap + low_word, make_pattern(low_index, high_index + 1), set);
325 } else {
326 // operate on the potentially partial first word
327 apply_pattern(bitmap + low_word, make_pattern(low_index, CONFIG_WORD_SIZE), set);
328 low_word++;
329 // iterate over the whole words
330 while (low_word < high_word) {
331 apply_pattern(bitmap + low_word, ~(word_t)0, set);
332 low_word++;
333 }
334 // apply to any remaining bits
335 apply_pattern(bitmap + low_word, make_pattern(0, high_index + 1), set);
336 }
337 }
338