Skip to content

Commit

Permalink
Reorder boot initialization (#109)
Browse files Browse the repository at this point in the history
Cleanup and reorder machine-specific initialization code.
  • Loading branch information
phaubertin authored Jan 13, 2025
1 parent 95f1aba commit f7746e4
Show file tree
Hide file tree
Showing 6 changed files with 131 additions and 125 deletions.
9 changes: 3 additions & 6 deletions include/kernel/infrastructure/i686/boot_alloc.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2019 Philippe Aubertin.
* Copyright (C) 2019-2025 Philippe Aubertin.
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
Expand Down Expand Up @@ -55,14 +55,11 @@ void boot_alloc_reinit_at_klimit(boot_alloc_t *boot_alloc);
* @return the allocated object
*
* */
#define boot_heap_alloc(boot_alloc, t, align) ((t *)boot_heap_alloc_size(boot_alloc, sizeof(t), align))
#define boot_heap_alloc(boot_alloc, T, align) \
((T *)boot_heap_alloc_size(boot_alloc, sizeof(T), align))

void *boot_heap_alloc_size(boot_alloc_t *boot_alloc, size_t size, size_t align);

void boot_heap_push(boot_alloc_t *boot_alloc);

void boot_heap_pop(boot_alloc_t *boot_alloc);

void *boot_page_alloc(boot_alloc_t *boot_alloc);

void *boot_page_alloc_n(boot_alloc_t *boot_alloc, int num_pages);
Expand Down
9 changes: 7 additions & 2 deletions include/kernel/infrastructure/i686/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ typedef uint64_t seg_descriptor_t;

typedef uint32_t seg_selector_t;


/* There are alignment constraints that apply to this structure if it is used
* with the sgdt/sidt instructions but, currently, the kernel only uses lgdt
* and lidt for which these aligment constraints do not apply. */
typedef struct {
uint16_t padding;
uint16_t limit;
Expand Down Expand Up @@ -104,11 +108,12 @@ typedef struct {
uint16_t iomap;
} tss_t;

/* Assembly language code accesses members in this structure. Make sure to
* update the PERCPU_OFFSET_... definitions when you change its layout. */
struct percpu_t {
/* Assembly language code accesses members in this structure. Make sure to
* update the PERCPU_OFFSET_... definitions when you change its layout. */
struct percpu_t *self;
addr_space_t *current_addr_space;
/* should be aligned on an 8-byte boundary for performance. */
seg_descriptor_t gdt[GDT_NUM_ENTRIES];
tss_t tss;
};
Expand Down
39 changes: 1 addition & 38 deletions kernel/infrastructure/i686/boot_alloc.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2019 Philippe Aubertin.
* Copyright (C) 2019-2025 Philippe Aubertin.
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
Expand Down Expand Up @@ -124,43 +124,6 @@ void *boot_heap_alloc_size(boot_alloc_t *boot_alloc, size_t size, size_t align)
return object;
}

/**
* Push the current state of the boot allocator heap.
*
* All heap allocations performed after calling this function are freed by the
* matching call to boot_heap_pop(). This function can be called multiple times
* before calling boot_heap_pop(). Heap states pushed by this function are
* popped by boot_heap_pop()() in the reverse order they were pushed.
*
* @param boot_alloc the boot allocator state
*
* */
void boot_heap_push(boot_alloc_t *boot_alloc) {
struct boot_heap_pushed_state *pushed_state =
boot_heap_alloc(boot_alloc, struct boot_heap_pushed_state, 0);

pushed_state->next = boot_alloc->heap_pushed_state;
boot_alloc->heap_pushed_state = pushed_state;
}

/**
* Pop the last pushed boot allocator heap.
*
* This function frees all heap allocations performed since the matching call to
* boot_heap_push().
*
* @param boot_alloc the boot allocator state
*
* */
void boot_heap_pop(boot_alloc_t *boot_alloc) {
if(boot_alloc->heap_pushed_state == NULL) {
panic("No more boot heap pushed state to pop.");
}

boot_alloc->heap_ptr = boot_alloc->heap_pushed_state;
boot_alloc->heap_pushed_state = boot_alloc->heap_pushed_state->next;
}

/**
* Early page allocation.
*
Expand Down
3 changes: 3 additions & 0 deletions kernel/infrastructure/i686/debug.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@
#include <stddef.h>

void machine_dump_call_stack(void) {
/* This function is called by the panic handler and one potential reason
* for a kernel panic is an early boot check that the boot information
* structure is valid. We can't assume that it is valid here. */
if(!check_bootinfo(false)) {
warning("warning: cannot dump call stack because boot information structure is invalid.");
return;
Expand Down
109 changes: 42 additions & 67 deletions kernel/infrastructure/i686/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,18 @@
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
*
* 3. Neither the name of the author nor the names of other contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
Expand Down Expand Up @@ -71,24 +71,6 @@
/** Specifies the entry point to use for system calls */
int syscall_implementation;

static void check_data_segment(const bootinfo_t *bootinfo) {
if( bootinfo->data_start == 0 ||
bootinfo->data_size == 0 ||
bootinfo->data_physaddr == 0) {
panic("Setup code wasn't able to load kernel data segment");
}
}

static void check_alignment(const bootinfo_t *bootinfo) {
if(page_offset_of(bootinfo->image_start) != 0) {
panic("Kernel image start is not aligned on a page boundary");
}

if(page_offset_of(bootinfo->image_top) != 0) {
panic("Top of kernel image is not aligned on a page boundary");
}
}

static void move_kernel_at_16mb(const bootinfo_t *bootinfo) {
move_and_remap_kernel(
(addr_t)bootinfo->page_table_1mb,
Expand Down Expand Up @@ -135,11 +117,11 @@ static void init_idt(void) {
addr_t addr = (addr_t)(uintptr_t)idt[idx];

/* Set interrupt gate flags.
*
*
* Because we are using an interrupt gate, the IF flag is cleared when
* the interrupt routine is entered, which means interrupts are
* disabled.
*
*
* See Intel 64 and IA-32 Architectures Software Developer’s Manual
* Volume 3 section 7.12.1.3 "Flag Usage By Exception- or Interrupt-
* Handler Procedure".
Expand All @@ -162,25 +144,20 @@ static void init_idt(void) {
}
}

static void load_selectors(percpu_t *cpu_data, boot_alloc_t *boot_alloc) {
/* Pseudo-descriptor allocation is temporary for the duration of this
* function only. Remember heap pointer on entry so we can free the
* pseudo-allocator when we are done. */
boot_heap_push(boot_alloc);

pseudo_descriptor_t *pseudo =
boot_heap_alloc(boot_alloc, pseudo_descriptor_t, sizeof(pseudo_descriptor_t));
static void load_selectors(percpu_t *cpu_data) {
pseudo_descriptor_t pseudo;

/* load interrupt descriptor table */
pseudo->addr = (addr_t)idt;
pseudo->limit = IDT_VECTOR_COUNT * sizeof(seg_descriptor_t) - 1;
lidt(pseudo);
pseudo.addr = (addr_t)idt;
pseudo.limit = IDT_VECTOR_COUNT * sizeof(seg_descriptor_t) - 1;

/* load new GDT and TSS */
pseudo->addr = (addr_t)&cpu_data->gdt;
pseudo->limit = GDT_NUM_ENTRIES * 8 - 1;
lidt(&pseudo);

lgdt(pseudo);
/* load new GDT */
pseudo.addr = (addr_t)&cpu_data->gdt;
pseudo.limit = GDT_NUM_ENTRIES * 8 - 1;

lgdt(&pseudo);

/* load new segment descriptors */
uint32_t code_selector = SEG_SELECTOR(GDT_KERNEL_CODE, RPL_KERNEL);
Expand All @@ -196,9 +173,6 @@ static void load_selectors(percpu_t *cpu_data, boot_alloc_t *boot_alloc) {

/* load TSS segment into task register */
ltr( SEG_SELECTOR(GDT_TSS, RPL_KERNEL) );

/* free pseudo-descriptor. */
boot_heap_pop(boot_alloc);
}

static void enable_global_pages(void) {
Expand All @@ -216,7 +190,7 @@ static void remap_text_video_memory(void) {

vga_set_base_addr(mapped);

info("Remapped text video memory at %#p", mapped);
info("Remapped text video memory at %#p", mapped);
}

static void initialize_page_allocator(boot_alloc_t *boot_alloc) {
Expand Down Expand Up @@ -293,7 +267,7 @@ static void get_loader_elf(exec_file_t *loader, const bootinfo_t *bootinfo) {
static void get_ramdisk(kern_mem_block_t *ramdisk, const bootinfo_t *bootinfo) {
ramdisk->start = bootinfo->ramdisk_start;
ramdisk->size = bootinfo->ramdisk_size;

if(ramdisk->start == 0 || ramdisk->size == 0) {
panic("No initial RAM disk loaded.");
}
Expand All @@ -318,13 +292,9 @@ void machine_init(const config_t *config) {
/* Validate the boot information structure before using it. */
(void)check_bootinfo(true);

const bootinfo_t *bootinfo = get_bootinfo();

detect_cpu_features();

check_data_segment(bootinfo);

check_alignment(bootinfo);
const bootinfo_t *bootinfo = get_bootinfo();

check_memory(bootinfo);

Expand All @@ -345,45 +315,39 @@ void machine_init(const config_t *config) {
boot_alloc_reinit_at_16mb(&boot_alloc);

/* allocate per-CPU data
*
*
* We need to ensure that the Task State Segment (TSS) contained in this
* memory block does not cross a page boundary. */
assert(sizeof(percpu_t) < PERCPU_DATA_ALIGNMENT);
percpu_t *cpu_data = boot_heap_alloc(&boot_alloc, percpu_t, PERCPU_DATA_ALIGNMENT);

/* initialize per-CPU data */
init_percpu_data(cpu_data);

/* Initialize interrupt descriptor table (IDT) */
init_idt();

/* load segment selectors */
load_selectors(cpu_data, &boot_alloc);

/* Initialize programmable interrupt_controller. */
pic8259_init();

/* Initialize programmable interval timer and enable timer interrupt.
*
* Interrupts are disabled during initialization so the CPU won't actually
* be interrupted until the first user space thread starts. */
pit8253_init();
pic8259_unmask(IRQ_TIMER);
load_selectors(cpu_data);

/* We must look for the ACPI RDSP while the relevant memory is still
* identity mapped before we swith to the initial address space. */
acpi_init();

/* This must be done before we switch to the new address space because only
* the boot allocator can allocate multiple consecutive pages. */
memory_initialize_array(&boot_alloc, bootinfo);

exec_file_t kernel;
get_kernel_exec_file(&kernel, bootinfo);

addr_space_t *addr_space = pmap_create_initial_addr_space(&kernel, &boot_alloc, bootinfo);

memory_initialize_array(&boot_alloc, bootinfo);

/* switch to new address space */
pmap_switch_addr_space(addr_space);

enable_global_pages();

/* This should be done early after calling pmap_switch_addr_space()
* because, until it's done, any attempt to log anything to VGA will
* result in a kernel panic caused by a kernel page fault (and the
Expand All @@ -400,11 +364,22 @@ void machine_init(const config_t *config) {
* because the slab allocator needs to allocate a slab to allocate the new
* slab cache on the slab cache cache.
*
* This must be done before the first time pmap_create_addr_space() is called. */
* This must be done before the first time pmap_create_addr_space() is
* called, which happens when the first process is created. */
if(pae_enabled) {
pae_create_pdpt_cache();
}

/* Initialize programmable interrupt_controller. */
pic8259_init();

/* Initialize programmable interval timer and enable timer interrupt.
*
* Interrupts are disabled during initialization so the CPU won't actually
* be interrupted until the first user space thread starts. */
pit8253_init();
pic8259_unmask(IRQ_TIMER);

/* choose a system call implementation */
select_syscall_implementation();
}
Loading

0 comments on commit f7746e4

Please sign in to comment.