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