|  | /* | 
|  | * Copyright 2017, Data61 | 
|  | * Commonwealth Scientific and Industrial Research Organisation (CSIRO) | 
|  | * ABN 41 687 119 230. | 
|  | * | 
|  | * This software may be distributed and modified according to the terms of | 
|  | * the BSD 2-Clause license. Note that NO WARRANTY is provided. | 
|  | * See "LICENSE_BSD2.txt" for details. | 
|  | * | 
|  | * @TAG(DATA61_BSD) | 
|  | */ | 
|  |  | 
|  | /* | 
|  | * An implementation of the vspace virtual memory allocation interface, using a two | 
|  | * level page table. | 
|  | * | 
|  | * This implementation expects malloc to work, although it only uses it for small amounts of | 
|  | * memory. | 
|  | * | 
|  | * Stack size is constant and be configured in menuconfig. | 
|  | * | 
|  | * Stacks are allocated with 1 guard page between them, but they are contiguous in the vmem | 
|  | * region. | 
|  | * | 
|  | * Allocation starts at 0x00001000. | 
|  | * | 
|  | * This library will allow you to map over anything that it doesn't know about, so | 
|  | * make sure you tell it if you do any external mapping. | 
|  | * | 
|  | */ | 
|  | #pragma once | 
|  |  | 
|  | #include <autoconf.h> | 
|  | #include <sel4utils/gen_config.h> | 
|  |  | 
|  | #include <vspace/vspace.h> | 
|  | #include <vka/vka.h> | 
|  | #include <sel4utils/util.h> | 
|  | #include <sel4utils/arch/vspace.h> | 
|  |  | 
|  | /* These definitions are only here so that you can take the size of them. | 
|  | * TOUCHING THESE DATA STRUCTURES IN ANY WAY WILL BREAK THE WORLD | 
|  | */ | 
|  | #define KERNEL_RESERVED_START (ALIGN_DOWN(seL4_UserTop, PAGE_SIZE_4K)) | 
|  | #define VSPACE_LEVEL_SIZE BIT(VSPACE_LEVEL_BITS) | 
|  |  | 
|  | typedef struct vspace_mid_level { | 
|  | /* there is a clear optimization that could be done where instead of always pointing to a | 
|  | * sub table, there is the option of pointing directly to a page. This allows more | 
|  | * efficient memory usage for book keeping large pages */ | 
|  | uintptr_t table[VSPACE_LEVEL_SIZE]; | 
|  | } vspace_mid_level_t; | 
|  |  | 
|  | typedef struct vspace_bottom_level { | 
|  | seL4_CPtr cap[VSPACE_LEVEL_SIZE]; | 
|  | uintptr_t cookie[VSPACE_LEVEL_SIZE]; | 
|  | } vspace_bottom_level_t; | 
|  |  | 
|  | typedef int(*sel4utils_map_page_fn)(vspace_t *vspace, seL4_CPtr cap, void *vaddr, seL4_CapRights_t rights, | 
|  | int cacheable, size_t size_bits); | 
|  |  | 
|  | struct sel4utils_res { | 
|  | uintptr_t start; | 
|  | uintptr_t end; | 
|  | seL4_CapRights_t rights; | 
|  | int cacheable; | 
|  | int malloced; | 
|  | bool rights_deferred; | 
|  | struct sel4utils_res *next; | 
|  | }; | 
|  |  | 
|  | typedef struct sel4utils_res sel4utils_res_t; | 
|  |  | 
|  | typedef struct sel4utils_alloc_data { | 
|  | seL4_CPtr vspace_root; | 
|  | vka_t *vka; | 
|  | vspace_mid_level_t *top_level; | 
|  | uintptr_t next_bootstrap_vaddr; | 
|  | uintptr_t last_allocated; | 
|  | vspace_t *bootstrap; | 
|  | sel4utils_map_page_fn map_page; | 
|  | sel4utils_res_t *reservation_head; | 
|  | bool is_empty; | 
|  | } sel4utils_alloc_data_t; | 
|  |  | 
|  | static inline sel4utils_res_t *reservation_to_res(reservation_t res) | 
|  | { | 
|  | return (sel4utils_res_t *) res.res; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * This is a mostly internal function for constructing a vspace. Allows a vspace to be created | 
|  | * with an arbitrary function to invoke for the mapping of pages. This is useful if you want | 
|  | * a vspace manager, but you do not want to use seL4 page directories | 
|  | * | 
|  | * @param loader                  vspace of the current process, used to allocate | 
|  | *                                virtual memory book keeping. | 
|  | * @param new_vspace              uninitialised vspace struct to populate. | 
|  | * @param data                    uninitialised vspace data struct to populate. | 
|  | * @param vka                     initialised vka that this virtual memory allocator will use to | 
|  | *                                allocate pages and pagetables. This allocator will never invoke free. | 
|  | * @param vspace_root             root object for the new vspace. | 
|  | * @param allocated_object_fn     function to call when objects are allocated. Can be null. | 
|  | * @param allocated_object_cookie cookie passed when the above function is called. Can be null. | 
|  | * @param map_page                Function that will be called to map seL4 pages | 
|  | * | 
|  | * @return 0 on success. | 
|  | */ | 
|  | int | 
|  | sel4utils_get_vspace_with_map(vspace_t *loader, vspace_t *new_vspace, sel4utils_alloc_data_t *data, | 
|  | vka_t *vka, seL4_CPtr vspace_root, | 
|  | vspace_allocated_object_fn allocated_object_fn, void *allocated_object_cookie, sel4utils_map_page_fn map_page); | 
|  | /** | 
|  | * Allows for an empty vspace to be created with an arbitrary function to invoke for the mapping of pages. | 
|  | * This is useful if you want a vspace manager, but you do not want parts of the virtual address space to be | 
|  | * pre-reserved e.g the kernel region. The vspace is useful for guest virtual machine-based applications. | 
|  | * | 
|  | * @param loader                  vspace of the current process, used to allocate | 
|  | *                                virtual memory book keeping. | 
|  | * @param new_vspace              uninitialised vspace struct to populate. | 
|  | * @param data                    uninitialised vspace data struct to populate. | 
|  | * @param vka                     initialised vka that this virtual memory allocator will use to | 
|  | *                                allocate pages and pagetables. This allocator will never invoke free. | 
|  | * @param vspace_root             root object for the new vspace. | 
|  | * @param allocated_object_fn     function to call when objects are allocated. Can be null. | 
|  | * @param allocated_object_cookie cookie passed when the above function is called. Can be null. | 
|  | * @param map_page                Function that will be called to map seL4 pages | 
|  | * | 
|  | * @return 0 on success. | 
|  | */ | 
|  | int | 
|  | sel4utils_get_empty_vspace_with_map(vspace_t *loader, vspace_t *new_vspace, sel4utils_alloc_data_t *data, | 
|  | vka_t *vka, seL4_CPtr vspace_root, | 
|  | vspace_allocated_object_fn allocated_object_fn, void *allocated_object_cookie, sel4utils_map_page_fn map_page); | 
|  | /** | 
|  | * Initialise a vspace allocator for a new address space (not the current one). | 
|  | * | 
|  | * @param loader                  vspace of the current process, used to allocate | 
|  | *                                virtual memory book keeping. | 
|  | * @param new_vspace              uninitialised vspace struct to populate. | 
|  | * @param data                    uninitialised vspace data struct to populate. | 
|  | * @param vka                     initialised vka that this virtual memory allocator will use to | 
|  | *                                allocate pages and pagetables. This allocator will never invoke free. | 
|  | * @param vspace_root             root object for the new vspace. | 
|  | * @param allocated_object_fn     function to call when objects are allocated. Can be null. | 
|  | * @param allocated_object_cookie cookie passed when the above function is called. Can be null. | 
|  | * | 
|  | * @return 0 on success. | 
|  | */ | 
|  | int sel4utils_get_vspace(vspace_t *loader, vspace_t *new_vspace, sel4utils_alloc_data_t *data, | 
|  | vka_t *vka, seL4_CPtr vspace_root, vspace_allocated_object_fn allocated_object_fn, | 
|  | void *allocated_object_cookie); | 
|  |  | 
|  | /** | 
|  | * Allows for an empty vspace to be created. | 
|  | * This is useful if you want a vspace manager, but you do not want parts of the virtual address space to be | 
|  | * pre-reserved e.g the kernel region. The vspace is useful for guest virtual machine-based applications. | 
|  | * | 
|  | * @param loader                  vspace of the current process, used to allocate | 
|  | *                                virtual memory book keeping. | 
|  | * @param new_vspace              uninitialised vspace struct to populate. | 
|  | * @param data                    uninitialised vspace data struct to populate. | 
|  | * @param vka                     initialised vka that this virtual memory allocator will use to | 
|  | *                                allocate pages and pagetables. This allocator will never invoke free. | 
|  | * @param vspace_root             root object for the new vspace. | 
|  | * @param allocated_object_fn     function to call when objects are allocated. Can be null. | 
|  | * @param allocated_object_cookie cookie passed when the above function is called. Can be null. | 
|  | * | 
|  | * @return 0 on success. | 
|  | */ | 
|  | int sel4utils_get_empty_vspace(vspace_t *loader, vspace_t *new_vspace, sel4utils_alloc_data_t *data, | 
|  | vka_t *vka, seL4_CPtr vspace_root, vspace_allocated_object_fn allocated_object_fn, | 
|  | void *allocated_object_cookie); | 
|  |  | 
|  |  | 
|  | #ifdef CONFIG_VTX | 
|  | /** | 
|  | * Initialise a vspace allocator for an EPT address space | 
|  | * | 
|  | * @param loader                  vspace of the current process, used to allocate | 
|  | *                                virtual memory book keeping. | 
|  | * @param new_vspace              uninitialised vspace struct to populate. | 
|  | * @param vka                     initialised vka that this virtual memory allocator will use to | 
|  | *                                allocate pages and pagetables. This allocator will never invoke free. | 
|  | * @param ept                     EPT page directory for the new vspace. | 
|  | * @param allocated_object_fn     function to call when objects are allocated. Can be null. | 
|  | * @param allocated_object_cookie cookie passed when the above function is called. Can be null. | 
|  | * | 
|  | * @return 0 on success. | 
|  | */ | 
|  | int sel4utils_get_vspace_ept(vspace_t *loader, vspace_t *new_vspace, vka_t *vka, | 
|  | seL4_CPtr ept, vspace_allocated_object_fn allocated_object_fn, void *allocated_object_cookie); | 
|  | #endif /* CONFIG_VTX */ | 
|  |  | 
|  | /** | 
|  | * Initialise a vspace allocator for the current address space (this is intended | 
|  | * for use a task that is not the root task but has no vspace, ie one loaded by the capDL loader). | 
|  | * | 
|  | * @param vspace                  uninitialised vspace struct to populate. | 
|  | * @param data                    uninitialised vspace data struct to populate. | 
|  | * @param vka                     initialised vka that this virtual memory allocator will use to | 
|  | *                                allocate pages and pagetables. This allocator will never invoke free. | 
|  | * @param vspace_root             root object for the new vspace. | 
|  | * @param allocated_object_fn     function to call when objects are allocated. Can be null. | 
|  | * @param allocated_object_cookie cookie passed when the above function is called. Can be null. | 
|  | * @param existing_frames         a NULL terminated list of virtual addresses for 4K frames that are | 
|  | *                                already allocated. For larger frames, just pass in the virtual | 
|  | *                                address range in 4K addresses. This will prevent the allocator | 
|  | *                                from overriding these frames. | 
|  | * | 
|  | * @return 0 on succes. | 
|  | * | 
|  | */ | 
|  | int sel4utils_bootstrap_vspace(vspace_t *vspace, sel4utils_alloc_data_t *data, | 
|  | seL4_CPtr vspace_root, vka_t *vka, | 
|  | vspace_allocated_object_fn allocated_object_fn, void *allocated_object_cookie, | 
|  | void *existing_frames[]); | 
|  |  | 
|  | /** | 
|  | * Initialise a vspace allocator for the current address space (this is intended | 
|  | * for use by the root task). Take details of existing frames from bootinfo. | 
|  | * | 
|  | * @param vspace                  uninitialised vspace struct to populate. | 
|  | * @param data                    uninitialised vspace data struct to populate. | 
|  | * @param vka                     initialised vka that this virtual memory allocator will use to | 
|  | *                                allocate pages and pagetables. This allocator will never invoke free. | 
|  | * @param info                    seL4 boot info | 
|  | * @param vspace_root             root object for the new vspace. | 
|  | * @param allocated_object_fn     function to call when objects are allocated. Can be null. | 
|  | * @param allocated_object_cookie cookie passed when the above function is called. Can be null. | 
|  | * | 
|  | * @return 0 on succes. | 
|  | * | 
|  | */ | 
|  | int sel4utils_bootstrap_vspace_with_bootinfo(vspace_t *vspace, sel4utils_alloc_data_t *data, | 
|  | seL4_CPtr vspace_root, | 
|  | vka_t *vka, seL4_BootInfo *info, vspace_allocated_object_fn allocated_object_fn, | 
|  | void *allocated_object_cookie); | 
|  |  | 
|  | /* Wrapper function that configures a vspaceator such that all allocated objects are not | 
|  | * tracked. | 
|  | */ | 
|  | static inline int sel4utils_get_vspace_leaky(vspace_t *loader, vspace_t *new_vspace, sel4utils_alloc_data_t *data, | 
|  | vka_t *vka, seL4_CPtr vspace_root) | 
|  | { | 
|  | return sel4utils_get_vspace(loader, new_vspace, data, vka, vspace_root, | 
|  | (vspace_allocated_object_fn) NULL, NULL); | 
|  | } | 
|  |  | 
|  | #ifdef CONFIG_VTX | 
|  | static inline int sel4utils_get_vspace_ept_leaky(vspace_t *loader, vspace_t *new_vspace, | 
|  | vka_t *vka, seL4_CPtr vspace_root) | 
|  | { | 
|  | return sel4utils_get_vspace_ept(loader, new_vspace, vka, vspace_root, | 
|  | (vspace_allocated_object_fn) NULL, NULL); | 
|  | } | 
|  | #endif /* CONFIG_VTX */ | 
|  |  | 
|  | static inline int sel4utils_bootstrap_vspace_with_bootinfo_leaky(vspace_t *vspace, sel4utils_alloc_data_t *data, | 
|  | seL4_CPtr vspace_root, | 
|  | vka_t *vka, seL4_BootInfo *info) | 
|  | { | 
|  | return sel4utils_bootstrap_vspace_with_bootinfo(vspace, data, vspace_root, vka, info, NULL, NULL); | 
|  | } | 
|  |  | 
|  | static inline int sel4utils_bootstrap_vspace_leaky(vspace_t *vspace, sel4utils_alloc_data_t *data, | 
|  | seL4_CPtr vspace_root, vka_t *vka, | 
|  | void *existing_frames[]) | 
|  | { | 
|  | return sel4utils_bootstrap_vspace(vspace, data, vspace_root, vka, NULL, NULL, existing_frames); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Attempts to create a new vspace reservation. Function behaves similarly to vspace_reserve_range | 
|  | * except a reservation struct is passed in, instead of being malloc'ed. This is intended to be | 
|  | * used during bootstrapping where malloc has not yet been setup. | 
|  | * Reservations created with this function should *only* be freed with sel4utils_reserve_range_at_no_alloc | 
|  | * | 
|  | * Result will be aligned to 4K. | 
|  | * | 
|  | * @param vspace the virtual memory allocator to use. | 
|  | * @param reservation Allocated reservation struct to fill out | 
|  | * @param bytes the size in bytes to map. | 
|  | * @param rights the rights to map the pages in with in this reservation | 
|  | * @param cacheable 1 if the pages should be mapped with cacheable attributes. 0 for DMA. | 
|  | * @param vaddr the virtual address of the reserved range will be returned here. | 
|  | * | 
|  | * @return 0 on success | 
|  | */ | 
|  | int sel4utils_reserve_range_no_alloc(vspace_t *vspace, sel4utils_res_t *reservation, size_t size, | 
|  | seL4_CapRights_t rights, int cacheable, void **result); | 
|  |  | 
|  | /* | 
|  | * @see sel4utils_reserve_range_no_alloc, however result is aligned to size_bits. | 
|  | */ | 
|  | int sel4utils_reserve_range_no_alloc_aligned(vspace_t *vspace, sel4utils_res_t *reservation, | 
|  | size_t size, size_t size_bits, seL4_CapRights_t rights, int cacheable, void **result); | 
|  |  | 
|  | /** | 
|  | * Attempts to create a new vspace reservation. Function behaves similarly to vspace_reserve_range_at | 
|  | * except a reservation struct is passed in, instead of being malloc'ed. This is intended to be | 
|  | * used during bootstrapping where malloc has not yet been setup. | 
|  | * Reservations created with this function should *only* be freed with sel4utils_reserve_range_at_no_alloc | 
|  | * | 
|  | * @param vspace the virtual memory allocator to use. | 
|  | * @param reservation Allocated reservation struct to fill out | 
|  | * @param vaddr the virtual address to start the range at. | 
|  | * @param bytes the size in bytes to map. | 
|  | * @param rights the rights to map the pages in with in this reservatio | 
|  | * @param cacheable 1 if the pages should be mapped with cacheable attributes. 0 for DMA. | 
|  | * | 
|  | * @return 0 on success | 
|  | */ | 
|  | int sel4utils_reserve_range_at_no_alloc(vspace_t *vspace, sel4utils_res_t *reservation, void *vaddr, | 
|  | size_t size, seL4_CapRights_t rights, int cacheable); | 
|  |  | 
|  | /** | 
|  | * Move and/or resizes a reservation in any direction, allowing for both start and/or end address to | 
|  | * be changed. Any overlapping region will keep their reservation, any non-overlapping regions will | 
|  | * be unreserved. Does not make any mapping changes. | 
|  | * @param vspace the virtual memory allocator to use. | 
|  | * @param reservation the reservation to move. | 
|  | * @param vaddr the start virtual address to move the reservation to. | 
|  | * @param bytes the size in bytes of new reservation. | 
|  | * @return 0 on success, -1 if region start could not be moved, -2 if end could not be moved. | 
|  | */ | 
|  | int sel4utils_move_resize_reservation(vspace_t *vspace, reservation_t reservation, void *vaddr, | 
|  | size_t bytes); | 
|  |  | 
|  | /* | 
|  | * Copy the code and data segment (the image effectively) from current vspace | 
|  | * into clone vspace. The clone vspace should be initialised. | 
|  | * | 
|  | * @param current the vspace to copy from. | 
|  | * @param clone the vspace to copy to. | 
|  | * @param reservation the previously established reservation in clone to copy. | 
|  | * @return 0 on success. | 
|  | */ | 
|  | int sel4utils_bootstrap_clone_into_vspace(vspace_t *current, vspace_t *clone, reservation_t reserve); | 
|  |  | 
|  | /** | 
|  | * Get the bounds of _executable_start and _end. | 
|  | * | 
|  | * @param va_start return va_start. | 
|  | * @param va_end return va_end. | 
|  | */ | 
|  | void sel4utils_get_image_region(uintptr_t *va_start, uintptr_t *va_end); | 
|  |  | 
|  | /** | 
|  | * | 
|  | * @return the physical address that vaddr is mapped to. | 
|  | *         VKA_NO_PADDR if there is no mapping | 
|  | */ | 
|  | uintptr_t sel4utils_get_paddr(vspace_t *vspace, void *vaddr, seL4_Word type, seL4_Word size_bits); | 
|  |  |