1/* 2 * Copyright 2019, Data61 3 * Commonwealth Scientific and Industrial Research Organisation (CSIRO) 4 * ABN 41 687 119 230. 5 * 6 * This software may be distributed and modified according to the terms of 7 * the BSD 2-Clause license. Note that NO WARRANTY is provided. 8 * See "LICENSE_BSD2.txt" for details. 9 * 10 * @TAG(DATA61_BSD) 11 */ 12 13#include <camkes/io.h> 14#include <camkes/arch/io.h> 15#include <sel4/sel4.h> 16#include <utils/util.h> 17 18/* Force the _ioport_region section to be created even if no modules are defined. */ 19static USED SECTION("_ioport_regions") struct {} dummy_ioport_region; 20/* Definitions so that we can find the exposed IO port regions */ 21extern ioport_region_t *__start__ioport_regions[]; 22extern ioport_region_t *__stop__ioport_regions[]; 23 24extern const char *get_instance_name(void); 25 26/* Iterates through all the allocated IO port regions and tries to find one that fits */ 27static ioport_region_t *find_io_port_region(uint16_t port) 28{ 29 for (ioport_region_t **region = __start__ioport_regions; 30 region < __stop__ioport_regions; region++) { 31 if (port >= (*region)->start && port <= (*region)->end) { 32 return *region; 33 } 34 } 35 return NULL; 36} 37 38/* Calls the error handler if there was an error with a syscall */ 39static int syscall_error_handler(int error, int syscall_label, ioport_region_t *region) 40{ 41 ERR_IF(error != 0, region->error_handler, ((camkes_error_t) { 42 .type = CE_SYSCALL_FAILED, 43 .instance = get_instance_name(), 44 .interface = *(region->interface_name), 45 .description = "failed to read from IO port", 46 .syscall = syscall_label, 47 .error = error, 48 }), ({ 49 return -1; 50 })); 51 52 return 0; 53} 54 55static int camkes_arch_io_port_in8(ioport_region_t *region, uint16_t port, uint32_t *result) 56{ 57 seL4_X86_IOPort_In8_t reply = seL4_X86_IOPort_In8(region->cap, port); 58 59 int ret = syscall_error_handler(reply.error, X86IOPortIn8, region); 60 if (ret) { 61 return ret; 62 } 63 64 *result = reply.result; 65 66 return 0; 67} 68 69static int camkes_arch_io_port_in16(ioport_region_t *region, uint16_t port, uint32_t *result) 70{ 71 seL4_X86_IOPort_In16_t reply = seL4_X86_IOPort_In16(region->cap, port); 72 73 int ret = syscall_error_handler(reply.error, X86IOPortIn16, region); 74 if (ret) { 75 return ret; 76 } 77 78 *result = reply.result; 79 80 return 0; 81} 82 83static int camkes_arch_io_port_in32(ioport_region_t *region, uint16_t port, uint32_t *result) 84{ 85 seL4_X86_IOPort_In32_t reply = seL4_X86_IOPort_In32(region->cap, port); 86 87 int ret = syscall_error_handler(reply.error, X86IOPortIn32, region); 88 if (ret) { 89 return ret; 90 } 91 92 *result = reply.result; 93 94 return 0; 95} 96 97static int camkes_arch_io_port_out8(ioport_region_t *region, uint16_t port, uint32_t value) 98{ 99 uint8_t val = (uint8_t) value; 100 int reply = seL4_X86_IOPort_Out8(region->cap, port, val); 101 102 int ret = syscall_error_handler(reply, X86IOPortOut8, region); 103 if (ret) { 104 return ret; 105 } 106 107 return 0; 108} 109 110static int camkes_arch_io_port_out16(ioport_region_t *region, uint16_t port, uint32_t value) 111{ 112 uint16_t val = (uint16_t) value; 113 int reply = seL4_X86_IOPort_Out16(region->cap, port, val); 114 115 int ret = syscall_error_handler(reply, X86IOPortOut16, region); 116 if (ret) { 117 return ret; 118 } 119 120 return 0; 121} 122 123static int camkes_arch_io_port_out32(ioport_region_t *region, uint16_t port, uint32_t value) 124{ 125 int reply = seL4_X86_IOPort_Out32(region->cap, port, value); 126 127 int ret = syscall_error_handler(reply, X86IOPortOut32, region); 128 if (ret) { 129 return ret; 130 } 131 132 return 0; 133} 134 135int camkes_arch_io_port_in(uint32_t port, int io_size, uint32_t *result) 136{ 137 ioport_region_t *region = find_io_port_region((uint16_t) port); 138 if (!region) { 139 return -1; 140 } 141 142 switch (io_size) { 143 case IOSIZE_8: 144 return camkes_arch_io_port_in8(region, port, result); 145 case IOSIZE_16: 146 return camkes_arch_io_port_in16(region, port, result); 147 case IOSIZE_32: 148 return camkes_arch_io_port_in32(region, port, result); 149 default: 150 return -1; 151 } 152} 153 154int camkes_arch_io_port_out(uint32_t port, int io_size, uint32_t value) 155{ 156 ioport_region_t *region = find_io_port_region((uint16_t) port); 157 if (!region) { 158 return -1; 159 } 160 161 switch (io_size) { 162 case IOSIZE_8: 163 return camkes_arch_io_port_out8(region, port, value); 164 case IOSIZE_16: 165 return camkes_arch_io_port_out16(region, port, value); 166 case IOSIZE_32: 167 return camkes_arch_io_port_out32(region, port, value); 168 default: 169 return -1; 170 } 171} 172