|  | /* | 
|  | * Copyright 2014, NICTA | 
|  | * | 
|  | * 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(NICTA_BSD) | 
|  | */ | 
|  |  | 
|  | #include <autoconf.h> | 
|  | #include <allocman/utspace/trickle.h> | 
|  | #include <allocman/allocman.h> | 
|  | #include <allocman/util.h> | 
|  | #include <sel4/sel4.h> | 
|  |  | 
|  | #include <vka/object.h> | 
|  |  | 
|  | #ifdef CONFIG_KERNEL_STABLE | 
|  |  | 
|  | static inline size_t _make_bitmap(size_t bits) { | 
|  | /* avoid shift by int max in BIT/MASK code */ | 
|  | if (BIT(bits - 1) == CONFIG_WORD_SIZE) { | 
|  | return -1; | 
|  | } else { | 
|  | /* shift the result up so that the high bits are set */ | 
|  | return MASK(BIT(bits - 1)) << (CONFIG_WORD_SIZE - BIT(bits - 1)); | 
|  | } | 
|  | } | 
|  |  | 
|  | static inline void _insert_node(struct utspace_trickle_node **head, struct utspace_trickle_node *node) | 
|  | { | 
|  | if (*head) { | 
|  | (*head)->prev = node; | 
|  | } | 
|  | node->next = *head; | 
|  | node->prev = NULL; | 
|  | *head = node; | 
|  | } | 
|  |  | 
|  | static inline void _remove_node(struct utspace_trickle_node **head, struct utspace_trickle_node *node) | 
|  | { | 
|  | if (node->next) { | 
|  | node->next->prev = node->prev; | 
|  | } | 
|  | if (node->prev) { | 
|  | node->prev->next = node->next; | 
|  | } else { | 
|  | *head = node->next; | 
|  | } | 
|  | } | 
|  |  | 
|  | static inline seL4_Word _make_cookie(struct utspace_trickle_node *node, size_t offset) | 
|  | { | 
|  | size_t int_node = (size_t) node; | 
|  | assert( (int_node % (CONFIG_WORD_SIZE)) == 0); | 
|  | assert(offset < CONFIG_WORD_SIZE); | 
|  | return int_node | offset; | 
|  | } | 
|  |  | 
|  | static inline struct utspace_trickle_node *_cookie_to_node(seL4_Word cookie) { | 
|  | cookie -= cookie % (CONFIG_WORD_SIZE); | 
|  | return (struct utspace_trickle_node*)cookie; | 
|  | } | 
|  |  | 
|  | static inline size_t _cookie_to_offset(seL4_Word cookie) | 
|  | { | 
|  | return cookie % (CONFIG_WORD_SIZE); | 
|  | } | 
|  |  | 
|  | static inline struct utspace_trickle_node *_make_node(struct allocman *alloc, int *error) { | 
|  | uintptr_t addr = (uintptr_t)allocman_mspace_alloc(alloc, sizeof(struct utspace_trickle_node) * 2 - sizeof(size_t), error); | 
|  | struct utspace_trickle_node *node; | 
|  | if (*error) { | 
|  | return NULL; | 
|  | } | 
|  | node = _cookie_to_node(addr + CONFIG_WORD_SIZE); | 
|  | node->padding = addr; | 
|  | return node; | 
|  | } | 
|  |  | 
|  | static inline void _free_node(struct allocman *alloc, struct utspace_trickle_node *node) | 
|  | { | 
|  | allocman_mspace_free(alloc, (void*)node->padding, sizeof(struct utspace_trickle_node) * 2 - sizeof(size_t)); | 
|  | } | 
|  |  | 
|  | int _utspace_trickle_add_uts(allocman_t *alloc, void *_trickle, size_t num, const cspacepath_t *uts, size_t *size_bits, uintptr_t *paddr) { | 
|  | utspace_trickle_t *trickle = (utspace_trickle_t*) _trickle; | 
|  | struct utspace_trickle_node *nodes[num]; | 
|  | cspacepath_t *uts_copy[num]; | 
|  | int error; | 
|  | size_t i; | 
|  | for (i = 0; i < num; i++) { | 
|  | nodes[i] = _make_node(alloc, &error); | 
|  | if (error) { | 
|  | for (i--; i >= 0; i--) { | 
|  | _free_node(alloc, nodes[i]); | 
|  | allocman_mspace_free(alloc, uts_copy[i], sizeof(cspacepath_t)); | 
|  | } | 
|  | return error; | 
|  | } | 
|  | uts_copy[i] = allocman_mspace_alloc(alloc, sizeof(cspacepath_t), &error); | 
|  | if (error) { | 
|  | _free_node(alloc, nodes[i]); | 
|  | for (i--; i >= 0; i--) { | 
|  | _free_node(alloc, nodes[i]); | 
|  | allocman_mspace_free(alloc, uts_copy[i], sizeof(cspacepath_t)); | 
|  | } | 
|  | } | 
|  | } | 
|  | for (i = 0; i < num; i++) { | 
|  | *uts_copy[i] = uts[i]; | 
|  | nodes[i]->ut = uts_copy[i]; | 
|  | nodes[i]->offset = 0; | 
|  | nodes[i]->paddr = paddr[i]; | 
|  | nodes[i]->parent_cookie = 0; | 
|  | nodes[i]->next = nodes[i]->prev = NULL; | 
|  | /* Start with only 1 thing free */ | 
|  | nodes[i]->bitmap = BIT(31); | 
|  | nodes[i]->bitmap_bits = 1; | 
|  | _insert_node(&trickle->heads[size_bits[i]], nodes[i]); | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | void utspace_trickle_create(utspace_trickle_t *trickle) | 
|  | { | 
|  | uint32_t i; | 
|  | for (i = 0; i < CONFIG_WORD_SIZE; i++) { | 
|  | trickle->heads[i] = NULL; | 
|  | } | 
|  | } | 
|  |  | 
|  | static int _refill_pool(struct allocman *alloc, utspace_trickle_t *trickle, size_t size_bits) | 
|  | { | 
|  | size_t i; | 
|  | int error; | 
|  | struct utspace_trickle_node *node; | 
|  | seL4_Word cookie; | 
|  | node = _make_node(alloc, &error); | 
|  | if (error) { | 
|  | return error; | 
|  | } | 
|  | /* Check if there are untypeds >= 5 size_bits from us */ | 
|  | for (i = size_bits + 5 > CONFIG_WORD_SIZE - 1 ? CONFIG_WORD_SIZE - 1 : size_bits + 5; i < CONFIG_WORD_SIZE; i++) { | 
|  | if (trickle->heads[i]) { | 
|  | i = size_bits + 5; | 
|  | break; | 
|  | } | 
|  | } | 
|  | if (i == CONFIG_WORD_SIZE) { | 
|  | /* Search for the biggest one near us */ | 
|  | for (i = size_bits + 5 > CONFIG_WORD_SIZE - 1 ? CONFIG_WORD_SIZE - 1 : size_bits + 5; i > size_bits; i--) { | 
|  | if (trickle->heads[i]) { | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  | if (i != size_bits) { | 
|  | cookie = _utspace_trickle_alloc(alloc, trickle, i, seL4_UntypedObject, NULL, &error); | 
|  | if (!error) { | 
|  | struct utspace_trickle_node *parent = _cookie_to_node(cookie); | 
|  | size_t offset = _cookie_to_offset(cookie); | 
|  | node->ut = parent->ut; | 
|  | node->offset = parent->offset + (offset << (i)); | 
|  | if (parent->paddr) { | 
|  | node->paddr = parent->paddr + (offset << (i)); | 
|  | } else { | 
|  | node->paddr = 0; | 
|  | } | 
|  | node->parent_cookie = cookie; | 
|  | node->bitmap_bits = i - size_bits + 1; | 
|  | node->bitmap = _make_bitmap(node->bitmap_bits); | 
|  | node->next = node->prev = NULL; | 
|  | _insert_node(&trickle->heads[size_bits], node); | 
|  | return 0; | 
|  | } | 
|  | } | 
|  | _free_node(alloc, node); | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | seL4_Word _utspace_trickle_alloc(struct allocman *alloc, void *_trickle, size_t size_bits, seL4_Word type, const cspacepath_t *slot, int *error) | 
|  | { | 
|  | size_t sel4_size_bits; | 
|  | int _error; | 
|  | utspace_trickle_t *trickle = (utspace_trickle_t*)_trickle; | 
|  | struct utspace_trickle_node *node; | 
|  | size_t offset; | 
|  | size_t mem_offset; | 
|  | /* get size of untyped call */ | 
|  | sel4_size_bits = get_sel4_object_size(type, size_bits); | 
|  | if (size_bits != vka_get_object_size(type, sel4_size_bits) || size_bits == 0) { | 
|  | SET_ERROR(error, 1); | 
|  | return 0; | 
|  | } | 
|  | assert(size_bits < CONFIG_WORD_SIZE); | 
|  | if (!trickle->heads[size_bits]) { | 
|  | _error = _refill_pool(alloc, trickle, size_bits); | 
|  | if (_error) { | 
|  | SET_ERROR(error, _error); | 
|  | return 0; | 
|  | } | 
|  | } | 
|  | node = trickle->heads[size_bits]; | 
|  | offset = CLZL(node->bitmap); | 
|  | mem_offset = node->offset + (offset << size_bits); | 
|  | if (slot) { | 
|  | _error = seL4_Untyped_RetypeAtOffset(node->ut->capPtr, type, mem_offset, sel4_size_bits, slot->root, slot->dest, slot->destDepth, slot->offset, 1); | 
|  | if (_error != seL4_NoError) { | 
|  | SET_ERROR(error, 1); | 
|  | return 0; | 
|  | } | 
|  | } | 
|  | node->bitmap &= ~BIT(CONFIG_WORD_SIZE - 1 - offset); | 
|  | if (node->bitmap == 0) { | 
|  | _remove_node(&trickle->heads[size_bits], node); | 
|  | } | 
|  | SET_ERROR(error, 0); | 
|  | return _make_cookie(node, offset); | 
|  | } | 
|  |  | 
|  | void _utspace_trickle_free(struct allocman *alloc, void *_trickle, seL4_Word cookie, size_t size_bits) | 
|  | { | 
|  | utspace_trickle_t *trickle = (utspace_trickle_t*)_trickle; | 
|  | struct utspace_trickle_node *node = _cookie_to_node(cookie); | 
|  | size_t offset = _cookie_to_offset(cookie); | 
|  | int in_list = !(node->bitmap == 0); | 
|  | node->bitmap |= BIT(CONFIG_WORD_SIZE - 1 - offset); | 
|  | if (node->bitmap == _make_bitmap(node->bitmap_bits)) { | 
|  | if (node->parent_cookie) { | 
|  | if (in_list) { | 
|  | _remove_node(&trickle->heads[size_bits], node); | 
|  | } | 
|  | _utspace_trickle_free(alloc, trickle, node->parent_cookie, size_bits + node->bitmap_bits - 1); | 
|  | _free_node(alloc, node); | 
|  | } else if (!in_list) { | 
|  | _insert_node(&trickle->heads[size_bits], node); | 
|  | } | 
|  | } else if (!in_list) { | 
|  | _insert_node(&trickle->heads[size_bits], node); | 
|  | } | 
|  | } | 
|  |  | 
|  | uintptr_t _utspace_trickle_paddr(void *_trickle, seL4_Word cookie, size_t size_bits) { | 
|  | struct utspace_trickle_node *node = _cookie_to_node(cookie); | 
|  | size_t offset = _cookie_to_offset(cookie); | 
|  | return node->paddr + (offset << size_bits); | 
|  | } | 
|  |  | 
|  | #endif |