diff options
Diffstat (limited to 'kern')
-rw-r--r-- | kern/Makefile | 1 | ||||
-rw-r--r-- | kern/bootmem.c | 787 | ||||
-rw-r--r-- | kern/bootmem.h | 98 | ||||
-rw-r--r-- | kern/log2.h | 9 | ||||
-rw-r--r-- | kern/thread.c | 2 |
5 files changed, 895 insertions, 2 deletions
diff --git a/kern/Makefile b/kern/Makefile index 0aa96fc3..dfd1cbd8 100644 --- a/kern/Makefile +++ b/kern/Makefile @@ -1,6 +1,7 @@ x15_SOURCES-y += \ kern/arg.c \ kern/bitmap.c \ + kern/bootmem.c \ kern/cbuf.c \ kern/clock.c \ kern/condition.c \ diff --git a/kern/bootmem.c b/kern/bootmem.c new file mode 100644 index 00000000..0bf92e19 --- /dev/null +++ b/kern/bootmem.c @@ -0,0 +1,787 @@ +/* + * Copyright (c) 2017 Richard Braun. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * + * The purpose of this module is to provide all memory-related services + * required during bootstrap, before paging is enabled. + * + * In order to meet the requirements of the various supported page table + * formats, where some page tables may be smaller than a page while others + * may be bigger, but always aligned on the page table size, this module + * implements a buddy memory allocator similar to the vm_page module. + */ + +#include <assert.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> +#include <string.h> + +#include <kern/bootmem.h> +#include <kern/error.h> +#include <kern/macros.h> +#include <machine/boot.h> +#include <machine/page.h> +#include <machine/pmem.h> +#include <machine/types.h> +#include <vm/vm_kmem.h> +#include <vm/vm_page.h> + +#define BOOTMEM_MAX_RESERVED_RANGES 64 + +/* + * Contiguous block of physical memory. + * + * These are semantically the same as those used by the VM system, and are + * actually loaded into the VM system when it's enabled. + * + * The boundaries of a zone must be page-aligned and must not overlap. + */ +struct bootmem_zone { + phys_addr_t start; + phys_addr_t end; + bool registered; + bool direct_mapped; +}; + +static struct bootmem_zone bootmem_zones[PMEM_MAX_ZONES] __bootdata; + +/* + * Physical memory range descriptor. + * + * Such ranges are used to describe memory containing data that must be + * preserved during bootstrap. If temporary, a range is released to the + * VM system when it's enabled. + * + * The boundary addresses must not be fixed up (e.g. page-aligned), since + * ranges may overlap the same pages. + */ +struct bootmem_range { + phys_addr_t start; + phys_addr_t end; + bool temporary; +}; + +/* + * Sorted array of reserved range descriptors. + */ +static struct bootmem_range bootmem_reserved_ranges[BOOTMEM_MAX_RESERVED_RANGES] + __bootdata; +static unsigned int bootmem_nr_reserved_ranges __bootdata; + +#if BOOT_MEM_BLOCK_BITS > PAGE_BITS +#error "block size too large" +#endif + +/* + * Basic block size. + * + * A block descriptor of order 0 describes a block of this size, also aligned + * to this value. + */ +#define BOOTMEM_BLOCK_SIZE (1 << BOOT_MEM_BLOCK_BITS) + +/* + * Special order value for blocks that aren't in a free list. Such blocks are + * either allocated, or part of a free block of pages but not the head page. + */ +#define BOOTMEM_ORDER_UNLISTED ((unsigned short)-1) + +/* + * Descriptor for a block in the buddy allocator heap. + * + * The physical address member doesn't have phys_addr_t type because the + * heap is used to return directly accessible blocks, and has uintptr_t + * type because that's the proper type for integers that may safely be + * converted to pointers. + * + * The size of a block is BOOTMEM_BLOCK_SIZE * 2^block->order. + */ +struct bootmem_block { + uintptr_t phys_addr; + struct bootmem_block *next; + struct bootmem_block **pprev; + unsigned short order; + bool allocated; +}; + +struct bootmem_free_list { + struct bootmem_block *blocks; +}; + +struct bootmem_heap { + uintptr_t start; + uintptr_t end; + struct bootmem_block *blocks; + struct bootmem_block *blocks_end; + struct bootmem_free_list free_lists[BOOT_MEM_NR_FREE_LISTS]; +}; + +static struct bootmem_heap bootmem_heap __bootdata; + +static char bootmem_panic_msg_zone_overlapping[] __bootdata + = "bootmem: zone overlapping"; +static char bootmem_panic_msg_invalid_zone_index_msg[] __bootdata + = "bootmem: invalid zone index"; +static char bootmem_panic_msg_zone_already_registered[] __bootdata + = "bootmem: zone already registered"; +static char bootmem_panic_msg_invalid_reserved_range[] __bootdata + = "bootmem: invalid reserved range"; +static char bootmem_panic_msg_too_many_reserved_ranges[] __bootdata + = "bootmem: too many reserved ranges"; +static char bootmem_panic_msg_setup[] __bootdata + = "bootmem: unable to set up the early memory allocator"; +static char bootmem_panic_msg_nomem[] __bootdata + = "bootmem: unable to allocate memory"; +static char bootmem_panic_msg_invalid_argument[] __bootdata + = "bootmem: invalid argument"; + +void * __boot +bootmem_memcpy(void *dest, const void *src, size_t n) +{ + const char *src_ptr; + char *dest_ptr; + + dest_ptr = dest; + src_ptr = src; + + for (size_t i = 0; i < n; i++) { + *dest_ptr = *src_ptr; + dest_ptr++; + src_ptr++; + } + + return dest; +} + +void * __boot +bootmem_memmove(void *dest, const void *src, size_t n) +{ + const char *src_ptr; + char *dest_ptr; + + if (dest <= src) { + dest_ptr = dest; + src_ptr = src; + + for (size_t i = 0; i < n; i++) { + *dest_ptr = *src_ptr; + dest_ptr++; + src_ptr++; + } + } else { + dest_ptr = dest + n - 1; + src_ptr = src + n - 1; + + for (size_t i = 0; i < n; i++) { + *dest_ptr = *src_ptr; + dest_ptr--; + src_ptr--; + } + } + + return dest; +} + +void * __boot +bootmem_memset(void *s, int c, size_t n) +{ + char *buffer; + + buffer = s; + + for (size_t i = 0; i < n; i++) { + buffer[i] = c; + } + + return s; +} + +size_t __boot +bootmem_strlen(const char *s) +{ + const char *start; + + start = s; + + while (*s != '\0') { + s++; + } + + return (s - start); +} + +static bool __boot +bootmem_overlaps(phys_addr_t start1, phys_addr_t end1, + phys_addr_t start2, phys_addr_t end2) +{ + return ((end2 > start1) && (start2 < end1)); +} + +static bool __boot +bootmem_included(phys_addr_t start1, phys_addr_t end1, + phys_addr_t start2, phys_addr_t end2) +{ + return ((start2 >= start1) && (end2 <= end1)); +} + +static void __boot +bootmem_zone_init(struct bootmem_zone *zone, phys_addr_t start, + phys_addr_t end, bool direct_mapped) +{ + zone->start = start; + zone->end = end; + zone->registered = true; + zone->direct_mapped = direct_mapped; +} + +static phys_addr_t __boot +bootmem_zone_end(const struct bootmem_zone *zone) +{ + return zone->end; +} + +static phys_addr_t __boot +bootmem_zone_size(const struct bootmem_zone *zone) +{ + return zone->end - zone->start; +} + +static bool __boot +bootmem_zone_registered(const struct bootmem_zone *zone) +{ + return zone->registered; +} + +static bool __boot +bootmem_zone_overlaps(const struct bootmem_zone *zone, + phys_addr_t start, phys_addr_t end) +{ + return bootmem_overlaps(zone->start, zone->end, start, end); +} + +static struct bootmem_zone * __boot +bootmem_get_zone(unsigned int index) +{ + assert(index < ARRAY_SIZE(bootmem_zones)); + return &bootmem_zones[index]; +} + +void __boot +bootmem_register_zone(unsigned int zone_index, bool direct_mapped, + phys_addr_t start, phys_addr_t end) +{ + struct bootmem_zone *zone, *tmp; + + for (size_t i = 0; i < ARRAY_SIZE(bootmem_zones); i++) { + tmp = bootmem_get_zone(i); + + if (!bootmem_zone_registered(tmp)) { + continue; + } + + if (bootmem_zone_overlaps(tmp, start, end)) { + boot_panic(bootmem_panic_msg_zone_overlapping); + } + } + + zone = bootmem_get_zone(zone_index); + + if (zone == NULL) { + boot_panic(bootmem_panic_msg_invalid_zone_index_msg); + } + + if (bootmem_zone_registered(zone)) { + boot_panic(bootmem_panic_msg_zone_already_registered); + } + + bootmem_zone_init(zone, start, end, direct_mapped); +} + +static void __boot +bootmem_range_init(struct bootmem_range *range, phys_addr_t start, + phys_addr_t end, bool temporary) +{ + range->start = start; + range->end = end; + range->temporary = temporary; +} + +static phys_addr_t __boot +bootmem_range_start(const struct bootmem_range *range) +{ + return range->start; +} + +static bool __boot +bootmem_range_temporary(const struct bootmem_range *range) +{ + return range->temporary; +} + +static void __boot +bootmem_range_clear_temporary(struct bootmem_range *range) +{ + range->temporary = false; +} + +static bool __boot +bootmem_range_overlaps(const struct bootmem_range *range, + phys_addr_t start, phys_addr_t end) +{ + return bootmem_overlaps(range->start, range->end, start, end); +} + +static bool __boot +bootmem_range_included(const struct bootmem_range *range, + phys_addr_t start, phys_addr_t end) +{ + return bootmem_included(range->start, range->end, start, end); +} + +static int __boot +bootmem_range_clip_region(const struct bootmem_range *range, + phys_addr_t *region_start, phys_addr_t *region_end) +{ + phys_addr_t range_start, range_end; + + range_start = vm_page_trunc(range->start); + range_end = vm_page_round(range->end); + + if (range_end < range->end) { + boot_panic(bootmem_panic_msg_invalid_reserved_range); + } + + if ((range_end <= *region_start) || (range_start >= *region_end)) { + return 0; + } + + if (range_start > *region_start) { + *region_end = range_start; + } else { + if (range_end >= *region_end) { + return ERROR_NOMEM; + } + + *region_start = range_end; + } + + return 0; +} + +static struct bootmem_range * __boot +bootmem_get_reserved_range(unsigned int index) +{ + assert(index < ARRAY_SIZE(bootmem_reserved_ranges)); + return &bootmem_reserved_ranges[index]; +} + +static void __boot +bootmem_shift_ranges_up(struct bootmem_range *range) +{ + struct bootmem_range *end; + size_t size; + + end = bootmem_reserved_ranges + ARRAY_SIZE(bootmem_reserved_ranges); + size = (end - range - 1) * sizeof(*range); + bootmem_memmove(range + 1, range, size); +} + +void __boot +bootmem_reserve_range(phys_addr_t start, phys_addr_t end, bool temporary) +{ + struct bootmem_range *range; + + if (start >= end) { + boot_panic(bootmem_panic_msg_invalid_reserved_range); + } + + if (bootmem_nr_reserved_ranges >= ARRAY_SIZE(bootmem_reserved_ranges)) { + boot_panic(bootmem_panic_msg_too_many_reserved_ranges); + } + + range = NULL; + + for (unsigned int i = 0; i < bootmem_nr_reserved_ranges; i++) { + range = bootmem_get_reserved_range(i); + + if (bootmem_range_overlaps(range, start, end)) { + /* + * If the range overlaps, check whether it's part of another + * range. For example, this applies to debugging symbols directly + * taken from the kernel image. + */ + if (bootmem_range_included(range, start, end)) { + /* + * If it's completely included, make sure that a permanent + * range remains permanent. + * + * XXX This means that if one big range is first registered + * as temporary, and a smaller range inside of it is + * registered as permanent, the bigger range becomes + * permanent. It's not easy nor useful in practice to do + * better than that. + */ + if (bootmem_range_temporary(range) != temporary) { + bootmem_range_clear_temporary(range); + } + + return; + } + + boot_panic(bootmem_panic_msg_invalid_reserved_range); + } + + if (end <= bootmem_range_start(range)) { + break; + } + } + + if (range == NULL) { + range = bootmem_reserved_ranges; + } + + bootmem_shift_ranges_up(range); + bootmem_range_init(range, start, end, temporary); + bootmem_nr_reserved_ranges++; +} + +static uintptr_t __boot +bootmem_block_round(uintptr_t size) +{ + return P2ROUND(size, BOOTMEM_BLOCK_SIZE); +} + +static uintptr_t __boot +bootmem_byte2block(uintptr_t byte) +{ + return byte >> BOOT_MEM_BLOCK_BITS; +} + +static uintptr_t __boot +bootmem_block2byte(uintptr_t block) +{ + return block << BOOT_MEM_BLOCK_BITS; +} + +static uintptr_t __boot +bootmem_compute_blocks(uintptr_t start, uintptr_t end) +{ + return bootmem_byte2block(end - start); +} + +static uintptr_t __boot +bootmem_compute_table_size(uintptr_t nr_blocks) +{ + return bootmem_block_round(nr_blocks * sizeof(struct bootmem_block)); +} + +static void __boot +bootmem_block_init(struct bootmem_block *block, uintptr_t pa) +{ + block->phys_addr = pa; + block->order = BOOTMEM_ORDER_UNLISTED; + block->allocated = true; +} + +static void __boot +bootmem_free_list_init(struct bootmem_free_list *list) +{ + list->blocks = NULL; +} + +static bool __boot +bootmem_free_list_empty(const struct bootmem_free_list *list) +{ + return list->blocks == NULL; +} + +static void __boot +bootmem_free_list_insert(struct bootmem_free_list *list, + struct bootmem_block *block) +{ + struct bootmem_block *blocks; + + blocks = list->blocks; + block->next = blocks; + block->pprev = &list->blocks; + + if (blocks != NULL) { + blocks->pprev = &block->next; + } + + list->blocks = block; +} + +static void __boot +bootmem_free_list_remove(struct bootmem_block *block) +{ + if (block->next != NULL) { + block->next->pprev = block->pprev; + } + + *block->pprev = block->next; +} + +static struct bootmem_block * __boot +bootmem_free_list_pop(struct bootmem_free_list *list) +{ + struct bootmem_block *block; + + block = list->blocks; + bootmem_free_list_remove(block); + return block; +} + +static struct bootmem_free_list * __boot +bootmem_heap_get_free_list(struct bootmem_heap *heap, unsigned int index) +{ + assert(index < ARRAY_SIZE(heap->free_lists)); + return &heap->free_lists[index]; +} + +static struct bootmem_block * __boot +bootmem_heap_get_block(struct bootmem_heap *heap, uintptr_t pa) +{ + return &heap->blocks[bootmem_byte2block(pa - heap->start)]; +} + +static void __boot +bootmem_heap_free(struct bootmem_heap *heap, struct bootmem_block *block, + unsigned short order) +{ + struct bootmem_block *buddy; + uintptr_t pa, buddy_pa; + + assert(block >= heap->blocks); + assert(block < heap->blocks_end); + assert(block->order == BOOTMEM_ORDER_UNLISTED); + assert(order < BOOTMEM_ORDER_UNLISTED); + assert(block->allocated); + + block->allocated = false; + pa = block->phys_addr; + + while (order < (BOOT_MEM_NR_FREE_LISTS - 1)) { + buddy_pa = pa ^ bootmem_block2byte(1 << order); + + if ((buddy_pa < heap->start) || (buddy_pa >= heap->end)) { + break; + } + + buddy = &heap->blocks[bootmem_byte2block(buddy_pa - heap->start)]; + + if (buddy->order != order) { + break; + } + + bootmem_free_list_remove(buddy); + buddy->order = BOOTMEM_ORDER_UNLISTED; + order++; + pa &= -bootmem_block2byte(1 << order); + block = &heap->blocks[bootmem_byte2block(pa - heap->start)]; + } + + bootmem_free_list_insert(&heap->free_lists[order], block); + block->order = order; +} + +static struct bootmem_block * __boot +bootmem_heap_alloc(struct bootmem_heap *heap, unsigned short order) +{ + struct bootmem_free_list *free_list; + struct bootmem_block *block, *buddy; + unsigned int i; + + assert(order < BOOT_MEM_NR_FREE_LISTS); + + for (i = order; i < BOOT_MEM_NR_FREE_LISTS; i++) { + free_list = &heap->free_lists[i]; + + if (!bootmem_free_list_empty(free_list)) { + break; + } + } + + if (i == BOOT_MEM_NR_FREE_LISTS) { + return NULL; + } + + block = bootmem_free_list_pop(free_list); + block->order = BOOTMEM_ORDER_UNLISTED; + + while (i > order) { + i--; + buddy = &block[1 << i]; + bootmem_free_list_insert(bootmem_heap_get_free_list(heap, i), buddy); + buddy->order = i; + } + + return block; +} + +static void __boot +bootmem_heap_init(struct bootmem_heap *heap, uintptr_t start, uintptr_t end) +{ + uintptr_t heap_blocks, table_size, table_blocks; + + bootmem_reserve_range(start, end, false); + + heap->start = start; + heap->end = end; + heap_blocks = bootmem_compute_blocks(start, end); + table_size = bootmem_compute_table_size(heap_blocks); + assert((end - table_size) > start); + heap->blocks = (struct bootmem_block *)(end - table_size); + heap->blocks_end = &heap->blocks[heap_blocks]; + + for (size_t i = 0; i < ARRAY_SIZE(heap->free_lists); i++) { + bootmem_free_list_init(&heap->free_lists[i]); + } + + for (phys_addr_t pa = start; pa < end; pa += BOOTMEM_BLOCK_SIZE) { + bootmem_block_init(bootmem_heap_get_block(heap, pa), pa); + } + + table_blocks = bootmem_byte2block(table_size); + heap_blocks -= table_blocks; + + for (size_t i = 0; i < heap_blocks; i++) { + bootmem_heap_free(heap, &heap->blocks[i], 0); + } +} + +static struct bootmem_heap * __boot +bootmem_get_heap(void) +{ + return &bootmem_heap; +} + +/* + * Find available memory. + * + * The search starts at the given start address, up to the given end address. + * If a range is found, it is stored through the region_startp and region_endp + * pointers. + * + * The range boundaries are page-aligned on return. + */ +static int __boot +bootmem_find_avail(phys_addr_t start, phys_addr_t end, + phys_addr_t *region_start, phys_addr_t *region_end) +{ + phys_addr_t orig_start; + int error; + + assert(start <= end); + + orig_start = start; + start = vm_page_round(start); + end = vm_page_trunc(end); + + if ((start < orig_start) || (start >= end)) { + return ERROR_INVAL; + } + + *region_start = start; + *region_end = end; + + for (unsigned int i = 0; i < bootmem_nr_reserved_ranges; i++) { + error = bootmem_range_clip_region(bootmem_get_reserved_range(i), + region_start, region_end); + + if (error) { + return error; + } + } + + return 0; +} + +void __boot +bootmem_setup(void) +{ + phys_addr_t heap_start, heap_end, max_heap_start, max_heap_end; + phys_addr_t start, end; + int error; + + bootmem_reserve_range((uintptr_t)&_boot, BOOT_VTOP((uintptr_t)&_end), false); + + /* + * Find some memory for the heap. Look for the largest unused area in + * upper memory, carefully avoiding all boot data. + */ + end = bootmem_directmap_end(); + + max_heap_start = 0; + max_heap_end = 0; + start = PMEM_RAM_START; + + for (;;) { + error = bootmem_find_avail(start, end, &heap_start, &heap_end); + + if (error) { + break; + } + + if ((heap_end - heap_start) > (max_heap_end - max_heap_start)) { + max_heap_start = heap_start; + max_heap_end = heap_end; + } + + start = heap_end; + } + + if (max_heap_start >= max_heap_end) { + boot_panic(bootmem_panic_msg_setup); + } + + assert(max_heap_start == (uintptr_t)max_heap_start); + assert(max_heap_end == (uintptr_t)max_heap_end); + bootmem_heap_init(bootmem_get_heap(), max_heap_start, max_heap_end); +} + +static unsigned short __boot +bootmem_alloc_order(size_t size) +{ + return iorder2(bootmem_byte2block(bootmem_block_round(size))); +} + +void * __boot +bootmem_alloc(size_t size) +{ + struct bootmem_block *block; + + block = bootmem_heap_alloc(bootmem_get_heap(), bootmem_alloc_order(size)); + + if (block == NULL) { + boot_panic(bootmem_panic_msg_nomem); + } + + return (void *)block->phys_addr; +} + +size_t __boot +bootmem_directmap_end(void) +{ + if (bootmem_zone_size(bootmem_get_zone(PMEM_ZONE_DIRECTMAP)) != 0) { + return bootmem_zone_end(bootmem_get_zone(PMEM_ZONE_DIRECTMAP)); + } else if (bootmem_zone_size(bootmem_get_zone(PMEM_ZONE_DMA32)) != 0) { + return bootmem_zone_end(bootmem_get_zone(PMEM_ZONE_DMA32)); + } else { + return bootmem_zone_end(bootmem_get_zone(PMEM_ZONE_DMA)); + } +} diff --git a/kern/bootmem.h b/kern/bootmem.h new file mode 100644 index 00000000..69a62115 --- /dev/null +++ b/kern/bootmem.h @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2017 Richard Braun. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * + * This module provides memory-related services at bootstrap, before + * paging is enabled. In particular : + * - zone registration + * - memory allocation (mainly for page tables) + * - boot data reservation + */ + +#ifndef _KERN_BOOTMEM_H +#define _KERN_BOOTMEM_H + +#include <stdbool.h> +#include <stddef.h> + +#include <kern/init.h> +#include <machine/types.h> + +/* + * Helper functions available before paging is enabled. + * + * Any memory passed to these must also be accessible without paging. + */ +void * bootmem_memcpy(void *dest, const void *src, size_t n); +void * bootmem_memmove(void *dest, const void *src, size_t n); +void * bootmem_memset(void *s, int c, size_t n); +size_t bootmem_strlen(const char *s); + +/* + * Register a physical memory zone. + * + * Zones are expected to be sorted in ascending order of addresses and + * not overlap. They are later loaded to the VM system. Set direct_mapped + * to true if the zone is part of the direct mapping of physical memory. + * + * This function is called before paging is enabled. + */ +void bootmem_register_zone(unsigned int zone_index, bool direct_mapped, + phys_addr_t start, phys_addr_t end); + +/* + * Report reserved addresses to the bootmem module. + * + * The kernel is automatically reserved. + * + * Once all reserved ranges have been registered, the user can initialize + * the module. + * + * If the range is marked temporary, it will be unregistered once + * the boot data have been saved/consumed so that their backing + * pages are loaded into the VM system. + * + * This function is called before paging is enabled. + */ +void bootmem_reserve_range(phys_addr_t start, phys_addr_t end, bool temporary); + +/* + * Initialize the early page allocator. + * + * This function builds a heap based on the registered zones while carefully + * avoiding reserved data. + * + * This function is called before paging is enabled. + */ +void bootmem_setup(void); + +/* + * Allocate memory. + * + * The size of the returned region is a power-of-two, and its address is + * aligned on that size. The region is guaranteed to be part of the direct + * physical mapping when paging is enabled. + * + * This function is called before paging is enabled. + */ +void * bootmem_alloc(size_t size); + +/* + * Return the end address of the direct physical mapping. + */ +size_t bootmem_directmap_end(void); + +#endif /* _KERN_BOOTMEM_H */ diff --git a/kern/log2.h b/kern/log2.h index ed12b441..a4e57bf4 100644 --- a/kern/log2.h +++ b/kern/log2.h @@ -16,6 +16,9 @@ * * * Integer base 2 logarithm operations. + * + * The functions provided by this module are always inlined so they may + * safely be used before paging is enabled. */ #ifndef _KERN_LOG2_H @@ -24,14 +27,16 @@ #include <assert.h> #include <limits.h> -static inline unsigned int +#include <kern/macros.h> + +static __always_inline unsigned int ilog2(unsigned long x) { assert(x != 0); return LONG_BIT - __builtin_clzl(x) - 1; } -static inline unsigned int +static __always_inline unsigned int iorder2(unsigned long size) { assert(size != 0); diff --git a/kern/thread.c b/kern/thread.c index 09e15aa0..f5b0727e 100644 --- a/kern/thread.c +++ b/kern/thread.c @@ -2641,6 +2641,8 @@ thread_schedule_intr(void) assert(thread_check_intr_context()); + /* TODO Explain */ + runq = thread_runq_local(); syscnt_inc(&runq->sc_schedule_intrs); } |