TrustworthySystems | cedc056 | 2014-07-22 14:11:16 +1000 | [diff] [blame] | 1 | /* |
Anna Lyons | 9214341 | 2017-06-05 08:29:55 +1000 | [diff] [blame^] | 2 | * Copyright 2017, Data61 |
| 3 | * Commonwealth Scientific and Industrial Research Organisation (CSIRO) |
| 4 | * ABN 41 687 119 230. |
TrustworthySystems | cedc056 | 2014-07-22 14:11:16 +1000 | [diff] [blame] | 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 | * |
Anna Lyons | 9214341 | 2017-06-05 08:29:55 +1000 | [diff] [blame^] | 10 | * @TAG(DATA61_BSD) |
TrustworthySystems | cedc056 | 2014-07-22 14:11:16 +1000 | [diff] [blame] | 11 | */ |
| 12 | |
| 13 | #include <autoconf.h> |
| 14 | |
TrustworthySystems | cedc056 | 2014-07-22 14:11:16 +1000 | [diff] [blame] | 15 | #include <sel4platsupport/io.h> |
Adrian Danis | 3209509 | 2017-05-25 17:00:35 +1000 | [diff] [blame] | 16 | #ifdef CONFIG_ARCH_ARM |
Alexander Kroh | 637e0d4 | 2015-06-26 13:44:02 +1000 | [diff] [blame] | 17 | #include <platsupport/clock.h> |
| 18 | #include <platsupport/mux.h> |
| 19 | #endif |
TrustworthySystems | cedc056 | 2014-07-22 14:11:16 +1000 | [diff] [blame] | 20 | #include <utils/util.h> |
Anna Lyons | 3a9da54 | 2016-02-29 14:07:08 +1100 | [diff] [blame] | 21 | #include <vspace/page.h> |
TrustworthySystems | cedc056 | 2014-07-22 14:11:16 +1000 | [diff] [blame] | 22 | |
| 23 | #include <vspace/vspace.h> |
| 24 | #include <vka/capops.h> |
| 25 | |
Matthew Fernandez | 9270216 | 2016-06-10 15:35:43 +1000 | [diff] [blame] | 26 | #include <stdint.h> |
TrustworthySystems | cedc056 | 2014-07-22 14:11:16 +1000 | [diff] [blame] | 27 | #include <stdlib.h> |
| 28 | |
| 29 | typedef struct io_mapping { |
TrustworthySystems | cedc056 | 2014-07-22 14:11:16 +1000 | [diff] [blame] | 30 | /* address we returned to the user */ |
| 31 | void *returned_addr; |
| 32 | /* base address of the mapping with respect to the vspace */ |
| 33 | void *mapped_addr; |
Anna Lyons | f2887a5 | 2016-09-30 16:06:15 +1000 | [diff] [blame] | 34 | size_t num_pages; |
| 35 | size_t page_size; |
| 36 | size_t page_size_bits; |
| 37 | /* caps for the mappings (s) */ |
TrustworthySystems | cedc056 | 2014-07-22 14:11:16 +1000 | [diff] [blame] | 38 | seL4_CPtr *caps; |
Anna Lyons | f2887a5 | 2016-09-30 16:06:15 +1000 | [diff] [blame] | 39 | /* allocation cookie for allocation(s) */ |
| 40 | seL4_Word *alloc_cookies; |
TrustworthySystems | cedc056 | 2014-07-22 14:11:16 +1000 | [diff] [blame] | 41 | struct io_mapping *next, *prev; |
| 42 | } io_mapping_t; |
| 43 | |
| 44 | typedef struct sel4platsupport_io_mapper_cookie { |
| 45 | vspace_t vspace; |
TrustworthySystems | cedc056 | 2014-07-22 14:11:16 +1000 | [diff] [blame] | 46 | vka_t vka; |
| 47 | io_mapping_t *head; |
| 48 | } sel4platsupport_io_mapper_cookie_t; |
| 49 | |
Anna Lyons | f2887a5 | 2016-09-30 16:06:15 +1000 | [diff] [blame] | 50 | static void |
| 51 | free_node(io_mapping_t *node) |
Alexander Kroh | 637e0d4 | 2015-06-26 13:44:02 +1000 | [diff] [blame] | 52 | { |
Anna Lyons | f2887a5 | 2016-09-30 16:06:15 +1000 | [diff] [blame] | 53 | assert(node); |
| 54 | if (node->caps) { |
| 55 | free(node->caps); |
| 56 | } |
| 57 | if (node->alloc_cookies) { |
| 58 | free(node->alloc_cookies); |
| 59 | } |
| 60 | free(node); |
| 61 | } |
| 62 | |
| 63 | static io_mapping_t * |
| 64 | new_node(size_t num_pages) |
| 65 | { |
| 66 | io_mapping_t *ret = calloc(1, sizeof(io_mapping_t)); |
| 67 | |
TrustworthySystems | cedc056 | 2014-07-22 14:11:16 +1000 | [diff] [blame] | 68 | if (!ret) { |
| 69 | return NULL; |
| 70 | } |
Anna Lyons | f2887a5 | 2016-09-30 16:06:15 +1000 | [diff] [blame] | 71 | |
| 72 | ret->caps = calloc(num_pages, sizeof(seL4_CPtr)); |
| 73 | if (!ret->caps) { |
| 74 | free_node(ret); |
| 75 | return NULL; |
| 76 | } |
| 77 | |
| 78 | ret->alloc_cookies = calloc(num_pages, sizeof(seL4_Word)); |
| 79 | if (!ret->alloc_cookies) { |
| 80 | free_node(ret); |
| 81 | return NULL; |
| 82 | } |
| 83 | |
| 84 | ret->num_pages = num_pages; |
TrustworthySystems | cedc056 | 2014-07-22 14:11:16 +1000 | [diff] [blame] | 85 | return ret; |
| 86 | } |
| 87 | |
| 88 | static void |
Anna Lyons | f2887a5 | 2016-09-30 16:06:15 +1000 | [diff] [blame] | 89 | destroy_node(vka_t *vka, io_mapping_t *mapping) |
| 90 | { |
| 91 | cspacepath_t path; |
| 92 | for (size_t i = 0; i < mapping->num_pages; i++) { |
| 93 | /* free the allocation */ |
| 94 | vka_utspace_free(vka, kobject_get_type(KOBJECT_FRAME, mapping->page_size_bits), |
| 95 | mapping->page_size_bits, mapping->alloc_cookies[i]); |
| 96 | /* free the caps */ |
Anna Lyons | 6899ede | 2016-10-04 10:05:57 +1100 | [diff] [blame] | 97 | vka_cspace_make_path(vka, mapping->caps[i], &path); |
| 98 | vka_cnode_delete(&path); |
| 99 | vka_cspace_free(vka, mapping->caps[i]); |
Anna Lyons | f2887a5 | 2016-09-30 16:06:15 +1000 | [diff] [blame] | 100 | } |
| 101 | free_node(mapping); |
| 102 | } |
| 103 | |
| 104 | static void |
| 105 | insert_node(sel4platsupport_io_mapper_cookie_t *io_mapper, io_mapping_t *node) |
TrustworthySystems | cedc056 | 2014-07-22 14:11:16 +1000 | [diff] [blame] | 106 | { |
| 107 | node->prev = NULL; |
| 108 | node->next = io_mapper->head; |
| 109 | if (io_mapper->head) { |
| 110 | io_mapper->head->prev = node; |
| 111 | } |
| 112 | io_mapper->head = node; |
| 113 | } |
| 114 | |
| 115 | static io_mapping_t * |
Anna Lyons | f2887a5 | 2016-09-30 16:06:15 +1000 | [diff] [blame] | 116 | find_node(sel4platsupport_io_mapper_cookie_t *io_mapper, void *returned_addr) |
TrustworthySystems | cedc056 | 2014-07-22 14:11:16 +1000 | [diff] [blame] | 117 | { |
| 118 | io_mapping_t *current; |
| 119 | for (current = io_mapper->head; current; current = current->next) { |
| 120 | if (current->returned_addr == returned_addr) { |
| 121 | return current; |
| 122 | } |
| 123 | } |
| 124 | return NULL; |
| 125 | } |
| 126 | |
| 127 | static void |
Anna Lyons | f2887a5 | 2016-09-30 16:06:15 +1000 | [diff] [blame] | 128 | remove_node(sel4platsupport_io_mapper_cookie_t *io_mapper, io_mapping_t *node) |
TrustworthySystems | cedc056 | 2014-07-22 14:11:16 +1000 | [diff] [blame] | 129 | { |
| 130 | if (node->prev) { |
| 131 | node->prev->next = node->next; |
| 132 | } else { |
| 133 | assert(io_mapper->head == node); |
| 134 | io_mapper->head = node->next; |
| 135 | } |
| 136 | if (node->next) { |
| 137 | node->next->prev = node->prev; |
| 138 | } |
| 139 | } |
| 140 | |
TrustworthySystems | cedc056 | 2014-07-22 14:11:16 +1000 | [diff] [blame] | 141 | static void * |
Anna Lyons | f2887a5 | 2016-09-30 16:06:15 +1000 | [diff] [blame] | 142 | sel4platsupport_map_paddr_with_page_size(sel4platsupport_io_mapper_cookie_t *io_mapper, uintptr_t paddr, size_t size, size_t page_size_bits, bool cached) |
TrustworthySystems | cedc056 | 2014-07-22 14:11:16 +1000 | [diff] [blame] | 143 | { |
| 144 | |
| 145 | vka_t *vka = &io_mapper->vka; |
| 146 | vspace_t *vspace = &io_mapper->vspace; |
TrustworthySystems | cedc056 | 2014-07-22 14:11:16 +1000 | [diff] [blame] | 147 | |
| 148 | /* search at start of page */ |
| 149 | int page_size = BIT(page_size_bits); |
| 150 | uintptr_t start = ROUND_DOWN(paddr, page_size); |
Adrian Danis | 5f4e7d1 | 2016-03-03 15:27:15 +1100 | [diff] [blame] | 151 | uintptr_t offset = paddr - start; |
TrustworthySystems | cedc056 | 2014-07-22 14:11:16 +1000 | [diff] [blame] | 152 | size += offset; |
| 153 | |
Anna Lyons | f2887a5 | 2016-09-30 16:06:15 +1000 | [diff] [blame] | 154 | io_mapping_t *mapping = new_node(BYTES_TO_SIZE_BITS_PAGES(size, page_size_bits)); |
| 155 | assert(mapping->num_pages << page_size_bits >= size); |
| 156 | if (!mapping) { |
| 157 | ZF_LOGE("Failed to allocate node for %zu pages", mapping->num_pages); |
TrustworthySystems | cedc056 | 2014-07-22 14:11:16 +1000 | [diff] [blame] | 158 | return NULL; |
| 159 | } |
Anna Lyons | f2887a5 | 2016-09-30 16:06:15 +1000 | [diff] [blame] | 160 | mapping->page_size_bits = page_size_bits; |
TrustworthySystems | cedc056 | 2014-07-22 14:11:16 +1000 | [diff] [blame] | 161 | |
Anna Lyons | f2887a5 | 2016-09-30 16:06:15 +1000 | [diff] [blame] | 162 | seL4_Word type = kobject_get_type(KOBJECT_FRAME, mapping->page_size_bits); |
| 163 | /* allocate all of the physical frame caps */ |
| 164 | for (unsigned int i = 0; i < mapping->num_pages; i++) { |
TrustworthySystems | cedc056 | 2014-07-22 14:11:16 +1000 | [diff] [blame] | 165 | /* allocate a cslot */ |
Anna Lyons | f2887a5 | 2016-09-30 16:06:15 +1000 | [diff] [blame] | 166 | int error = vka_cspace_alloc(vka, &mapping->caps[i]); |
TrustworthySystems | cedc056 | 2014-07-22 14:11:16 +1000 | [diff] [blame] | 167 | if (error) { |
Anna Lyons | 67c869c | 2016-03-30 11:16:41 +1100 | [diff] [blame] | 168 | ZF_LOGE("cspace alloc failed"); |
TrustworthySystems | cedc056 | 2014-07-22 14:11:16 +1000 | [diff] [blame] | 169 | assert(error == 0); |
| 170 | /* we don't clean up as everything has gone to hell */ |
| 171 | return NULL; |
| 172 | } |
| 173 | |
| 174 | /* create a path */ |
| 175 | cspacepath_t path; |
Anna Lyons | f2887a5 | 2016-09-30 16:06:15 +1000 | [diff] [blame] | 176 | vka_cspace_make_path(vka, mapping->caps[i], &path); |
TrustworthySystems | cedc056 | 2014-07-22 14:11:16 +1000 | [diff] [blame] | 177 | |
Anna Lyons | f2887a5 | 2016-09-30 16:06:15 +1000 | [diff] [blame] | 178 | /* allocate the frame */ |
| 179 | error = vka_utspace_alloc_at(vka, &path, type, page_size_bits, start + (i * page_size), |
Anna Lyons | 6899ede | 2016-10-04 10:05:57 +1100 | [diff] [blame] | 180 | &mapping->alloc_cookies[i]); |
TrustworthySystems | cedc056 | 2014-07-22 14:11:16 +1000 | [diff] [blame] | 181 | if (error) { |
| 182 | /* free this slot, and then do general cleanup of the rest of the slots. |
| 183 | * this avoids a needless seL4_CNode_Delete of this slot, as there is no |
| 184 | * cap in it */ |
Anna Lyons | f2887a5 | 2016-09-30 16:06:15 +1000 | [diff] [blame] | 185 | vka_cspace_free(vka, mapping->caps[i]); |
| 186 | mapping->num_pages = i; |
TrustworthySystems | cedc056 | 2014-07-22 14:11:16 +1000 | [diff] [blame] | 187 | goto error; |
| 188 | } |
TrustworthySystems | cedc056 | 2014-07-22 14:11:16 +1000 | [diff] [blame] | 189 | } |
| 190 | |
| 191 | /* Now map the frames in */ |
Anna Lyons | f2887a5 | 2016-09-30 16:06:15 +1000 | [diff] [blame] | 192 | mapping->mapped_addr = vspace_map_pages(vspace, mapping->caps, mapping->alloc_cookies, seL4_AllRights, mapping->num_pages, |
Anna Lyons | 6899ede | 2016-10-04 10:05:57 +1100 | [diff] [blame] | 193 | mapping->page_size_bits, cached); |
Anna Lyons | f2887a5 | 2016-09-30 16:06:15 +1000 | [diff] [blame] | 194 | if (mapping->mapped_addr != NULL) { |
| 195 | /* fill out and insert node */ |
| 196 | mapping->returned_addr = mapping->mapped_addr + offset; |
| 197 | insert_node(io_mapper, mapping); |
| 198 | return mapping->returned_addr; |
TrustworthySystems | cedc056 | 2014-07-22 14:11:16 +1000 | [diff] [blame] | 199 | } |
| 200 | error: |
Anna Lyons | f2887a5 | 2016-09-30 16:06:15 +1000 | [diff] [blame] | 201 | destroy_node(vka, mapping); |
TrustworthySystems | cedc056 | 2014-07-22 14:11:16 +1000 | [diff] [blame] | 202 | return NULL; |
| 203 | } |
| 204 | |
| 205 | static void * |
Anna Lyons | f2887a5 | 2016-09-30 16:06:15 +1000 | [diff] [blame] | 206 | sel4platsupport_map_paddr(void *cookie, uintptr_t paddr, size_t size, int cached, UNUSED ps_mem_flags_t flags) |
TrustworthySystems | cedc056 | 2014-07-22 14:11:16 +1000 | [diff] [blame] | 207 | { |
TrustworthySystems | cedc056 | 2014-07-22 14:11:16 +1000 | [diff] [blame] | 208 | sel4platsupport_io_mapper_cookie_t* io_mapper = (sel4platsupport_io_mapper_cookie_t*)cookie; |
TrustworthySystems | cedc056 | 2014-07-22 14:11:16 +1000 | [diff] [blame] | 209 | int frame_size_index = 0; |
| 210 | /* find the largest reasonable frame size */ |
Anna Lyons | 3a9da54 | 2016-02-29 14:07:08 +1100 | [diff] [blame] | 211 | while (frame_size_index + 1 < SEL4_NUM_PAGE_SIZES) { |
| 212 | if (size >> sel4_page_sizes[frame_size_index + 1] == 0) { |
TrustworthySystems | cedc056 | 2014-07-22 14:11:16 +1000 | [diff] [blame] | 213 | break; |
| 214 | } |
| 215 | frame_size_index++; |
| 216 | } |
Alexander Kroh | 637e0d4 | 2015-06-26 13:44:02 +1000 | [diff] [blame] | 217 | |
TrustworthySystems | cedc056 | 2014-07-22 14:11:16 +1000 | [diff] [blame] | 218 | /* try mapping in this and all smaller frame sizes until something works */ |
| 219 | for (int i = frame_size_index; i >= 0; i--) { |
Anna Lyons | 3a9da54 | 2016-02-29 14:07:08 +1100 | [diff] [blame] | 220 | void *result = sel4platsupport_map_paddr_with_page_size(io_mapper, paddr, size, sel4_page_sizes[i], cached); |
TrustworthySystems | cedc056 | 2014-07-22 14:11:16 +1000 | [diff] [blame] | 221 | if (result) { |
| 222 | return result; |
| 223 | } |
| 224 | } |
| 225 | |
TrustworthySystems | cedc056 | 2014-07-22 14:11:16 +1000 | [diff] [blame] | 226 | /* shit out of luck */ |
Anna Lyons | 67c869c | 2016-03-30 11:16:41 +1100 | [diff] [blame] | 227 | ZF_LOGE("Failed to find a way to map address %p", (void *)paddr); |
TrustworthySystems | cedc056 | 2014-07-22 14:11:16 +1000 | [diff] [blame] | 228 | return NULL; |
| 229 | } |
| 230 | |
| 231 | static void |
Anna Lyons | f2887a5 | 2016-09-30 16:06:15 +1000 | [diff] [blame] | 232 | sel4platsupport_unmap_vaddr(void * cookie, void *vaddr, UNUSED size_t size) |
TrustworthySystems | cedc056 | 2014-07-22 14:11:16 +1000 | [diff] [blame] | 233 | { |
Anna Lyons | f2887a5 | 2016-09-30 16:06:15 +1000 | [diff] [blame] | 234 | sel4platsupport_io_mapper_cookie_t* io_mapper = cookie; |
TrustworthySystems | cedc056 | 2014-07-22 14:11:16 +1000 | [diff] [blame] | 235 | |
Anna Lyons | f2887a5 | 2016-09-30 16:06:15 +1000 | [diff] [blame] | 236 | vspace_t *vspace = &io_mapper->vspace; |
| 237 | vka_t *vka = &io_mapper->vka; |
| 238 | io_mapping_t *mapping = find_node(io_mapper, vaddr); |
TrustworthySystems | cedc056 | 2014-07-22 14:11:16 +1000 | [diff] [blame] | 239 | |
TrustworthySystems | cedc056 | 2014-07-22 14:11:16 +1000 | [diff] [blame] | 240 | if (!mapping) { |
Anna Lyons | 67c869c | 2016-03-30 11:16:41 +1100 | [diff] [blame] | 241 | ZF_LOGF("Tried to unmap vaddr %p, which was never mapped in", vaddr); |
TrustworthySystems | cedc056 | 2014-07-22 14:11:16 +1000 | [diff] [blame] | 242 | return; |
| 243 | } |
TrustworthySystems | cedc056 | 2014-07-22 14:11:16 +1000 | [diff] [blame] | 244 | |
Anna Lyons | f2887a5 | 2016-09-30 16:06:15 +1000 | [diff] [blame] | 245 | /* unmap the pages */ |
| 246 | vspace_unmap_pages(vspace, mapping->mapped_addr, mapping->num_pages, mapping->page_size_bits, |
Alexander Kroh | 637e0d4 | 2015-06-26 13:44:02 +1000 | [diff] [blame] | 247 | VSPACE_PRESERVE); |
TrustworthySystems | cedc056 | 2014-07-22 14:11:16 +1000 | [diff] [blame] | 248 | |
Anna Lyons | f2887a5 | 2016-09-30 16:06:15 +1000 | [diff] [blame] | 249 | /* clean up the node */ |
| 250 | remove_node(io_mapper, mapping); |
| 251 | destroy_node(vka, mapping); |
TrustworthySystems | cedc056 | 2014-07-22 14:11:16 +1000 | [diff] [blame] | 252 | } |
| 253 | |
| 254 | int |
Anna Lyons | 76fcd2c | 2016-10-05 12:21:33 +1100 | [diff] [blame] | 255 | sel4platsupport_new_io_mapper(vspace_t vspace, vka_t vka, ps_io_mapper_t *io_mapper) |
TrustworthySystems | cedc056 | 2014-07-22 14:11:16 +1000 | [diff] [blame] | 256 | { |
Anna Lyons | f2887a5 | 2016-09-30 16:06:15 +1000 | [diff] [blame] | 257 | sel4platsupport_io_mapper_cookie_t *cookie = malloc(sizeof(sel4platsupport_io_mapper_cookie_t)); |
TrustworthySystems | cedc056 | 2014-07-22 14:11:16 +1000 | [diff] [blame] | 258 | if (!cookie) { |
Anna Lyons | f2887a5 | 2016-09-30 16:06:15 +1000 | [diff] [blame] | 259 | ZF_LOGE("Failed to allocate %zu bytes", sizeof(sel4platsupport_io_mapper_cookie_t)); |
TrustworthySystems | cedc056 | 2014-07-22 14:11:16 +1000 | [diff] [blame] | 260 | return -1; |
| 261 | } |
Anna Lyons | f2887a5 | 2016-09-30 16:06:15 +1000 | [diff] [blame] | 262 | |
| 263 | cookie->vspace = vspace; |
| 264 | cookie->vka = vka; |
| 265 | io_mapper->cookie = cookie; |
| 266 | io_mapper->io_map_fn = sel4platsupport_map_paddr; |
| 267 | io_mapper->io_unmap_fn = sel4platsupport_unmap_vaddr; |
| 268 | |
TrustworthySystems | cedc056 | 2014-07-22 14:11:16 +1000 | [diff] [blame] | 269 | return 0; |
| 270 | } |
Alexander Kroh | 637e0d4 | 2015-06-26 13:44:02 +1000 | [diff] [blame] | 271 | |
| 272 | int |
Anna Lyons | 76fcd2c | 2016-10-05 12:21:33 +1100 | [diff] [blame] | 273 | sel4platsupport_new_io_ops(vspace_t vspace, vka_t vka, ps_io_ops_t *io_ops) |
Alexander Kroh | 637e0d4 | 2015-06-26 13:44:02 +1000 | [diff] [blame] | 274 | { |
Anna Lyons | 76fcd2c | 2016-10-05 12:21:33 +1100 | [diff] [blame] | 275 | int err = sel4platsupport_new_io_mapper(vspace, vka, &io_ops->io_mapper); |
Alexander Kroh | 637e0d4 | 2015-06-26 13:44:02 +1000 | [diff] [blame] | 276 | if (err) { |
| 277 | return err; |
| 278 | } |
Adrian Danis | 3209509 | 2017-05-25 17:00:35 +1000 | [diff] [blame] | 279 | #ifdef CONFIG_ARCH_ARM |
Anna Lyons | f2887a5 | 2016-09-30 16:06:15 +1000 | [diff] [blame] | 280 | clock_sys_init(io_ops, &io_ops->clock_sys); |
| 281 | mux_sys_init(io_ops, &io_ops->mux_sys); |
Alexander Kroh | 637e0d4 | 2015-06-26 13:44:02 +1000 | [diff] [blame] | 282 | #endif |
Anna Lyons | f2887a5 | 2016-09-30 16:06:15 +1000 | [diff] [blame] | 283 | |
| 284 | return err; |
Alexander Kroh | 637e0d4 | 2015-06-26 13:44:02 +1000 | [diff] [blame] | 285 | } |
| 286 | |
| 287 | |