summaryrefslogtreecommitdiff
path: root/kern
diff options
context:
space:
mode:
Diffstat (limited to 'kern')
-rw-r--r--kern/Makefile1
-rw-r--r--kern/bootmem.c787
-rw-r--r--kern/bootmem.h98
-rw-r--r--kern/log2.h9
-rw-r--r--kern/thread.c2
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);
}