| /* |
| * Copyright 2017, Data61, CSIRO (ABN 41 687 119 230) |
| * |
| * SPDX-License-Identifier: BSD-2-Clause |
| */ |
| |
| /* |
| * 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 vspace_root root object for the new vspace. |
| * @param vka initialised vka that this virtual memory allocator will use to |
| * allocate pages and pagetables. This allocator will never invoke free. |
| * @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 vspace_root root object for the new vspace. |
| * @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 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); |
| |