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