377 lines
13 KiB
Markdown
377 lines
13 KiB
Markdown
# Kernel Documentation
|
|
The kernel is booted using the limine boot protocol.
|
|
|
|
## Directory structure
|
|
- **boot** - all stuff related to booting / jumping into the kernel
|
|
- **drivers** - everything from the graphics driver, to the FS drivers
|
|
- **mm** - memory management stuff like page frames and page maps
|
|
- **platform** - universal API to the platform specific code in the subdirs
|
|
- **proc** - all the process/thread related stuff like the scheduler
|
|
- **utils** - utilities like type definitions, math functions, high-level memory management
|
|
|
|
---
|
|
|
|
# General concepts
|
|
## kernel initialization
|
|
The single parts of the kernel are initialized in the following order:
|
|
- **Global Descriptor Table**
|
|
- **Page Frame Manager**
|
|
- **Interrupt Descriptor Table**
|
|
|
|
## interrupt handling
|
|
OSDev Wiki: [Interrupts](https://wiki.osdev.org/Interrupts)
|
|
|
|
Unfortunatly the x86 architecture doesn't provide a method to get the ID of the current interrupt.
|
|
To solve this problem, there is a simple assembly function for every interrupt used by NoxOS.
|
|
This function pushes its ID on the stack.
|
|
After that it calls a common Interrupt handler, this handler will generate the current `cpu_state_T` and call the C interrupt handler implementation.
|
|
The C implementation returns a `cpu_state_T` that will then be loaded.
|
|
|
|
## paging
|
|
OSDev Wiki: [Paging](https://wiki.osdev.org/Paging)
|
|
|
|
There is a difference between `Virtual Memory Spaces` and the `Physical Memory Space`.
|
|
The Physical memory space is how the data lies directly in the RAM.
|
|
|
|
Virtual memory spaces are a bit more tricky.
|
|
To understand them, we have to understand first, that the physical memory space is divided into so-called **pages** / **page frames**.
|
|
These pages have a size of 4KB.
|
|
|
|
A virtual memory space is a table of page mappings.
|
|
Per default there are no pages mapped to such a table.
|
|
When the OS maps a page to a **page table**, it says:
|
|
"This page is now accessible from this virtual space, at this address".
|
|
When the Computer is in paging mode, only mapped pages are accessible.
|
|
Now every Process gets its own page table and tada: we have successfully isolated the processes from each other,
|
|
because every process can only access the data that it needs to access.
|
|
|
|
---
|
|
|
|
**DISCLAIMER:** Only the headers are documented, because documenting the whole code itself would be very time intensive and the headers as 'public' API are the most important to document.
|
|
|
|
## boot
|
|
|
|
### boot_info.h
|
|
The goal of this file is to provide a universal struct of information needed by the kernel at start time.
|
|
At the moment this information is very limine specific, but the goal is to make it easy to add support for other boot protocols.
|
|
|
|
#### `boot_info_T` - struct
|
|
- **framebuffer** - struct with information about the graphics buffer
|
|
- **terminal** - bootloader terminal / log
|
|
- **memory_map** - information about the memory layout / regions
|
|
- **rsdp** - pointer to RSDP
|
|
|
|
### limine.h
|
|
This header provides the API to "communicate" with the limine bootloader.
|
|
More information can be found on the limine project's [GitHub](https://github.com/limine-bootloader/limine/blob/trunk/PROTOCOL.md).
|
|
|
|
|
|
## mm
|
|
|
|
### page_frame.h
|
|
This header provides the functions for basic interactions with pages (in the physical memory space).
|
|
|
|
#### `pframe_manager_init()` - function (void)
|
|
Initializes the page frame manager, needs to be called once at kernel init.
|
|
|
|
#### `pframe_reserve(address)` - function (void) [Thread Safe]
|
|
Blocks a page, so it can't be requested or anything else.
|
|
If the page is already blocked by anything else, e.g. by a request, it won't be reserved.
|
|
|
|
#### `pframe_reserve_multi(address, n)` - function (void) [Thread Safe]
|
|
Reserves the page at the given address, plus *n* pages after that page.
|
|
|
|
#### `pframe_unreserve(address)` - function (void) [Thread Safe]
|
|
Unreserves a reserved page and makes it accessible again.
|
|
|
|
#### `pframe_unreserve_multi(address, n)` - function (void) [Thread Safe]
|
|
Unreserves the page at the given address, plus *n* pages after that page.
|
|
|
|
#### `pframe_request()` - function (void*) [Thread Safe]
|
|
Returns the physical address of a page.
|
|
This is kind of the low level version of malloc.
|
|
|
|
#### `pframe_free(address)` - function (void) [Thread Safe]
|
|
Needs a valid page address produced by `pframe_request()` as argument.
|
|
Invalidates the address and frees it, so it can be requested again.
|
|
This is kind of the low level version of free.
|
|
|
|
#### `pframe_free_multi(address, n)` - function (void) [Thread Safe]
|
|
Frees the page at the given address, plus *n* pages after that page.
|
|
|
|
## platform
|
|
|
|
### cpu.h
|
|
This header contains stuff directly related to the CPU.
|
|
|
|
OSDev Wiki: [x86 CPU Registers](https://wiki.osdev.org/CPU_Registers_x86)
|
|
|
|
#### `cpu_state_T` - struct
|
|
- **cr3** - Control register 3, holds the current page table
|
|
- **rax** - General purpose register
|
|
- **rbx** - General purpose register
|
|
- **rcx** - General purpose register
|
|
- **rdx** - General purpose register
|
|
- **rsi** - General purpose register
|
|
- **rdi** - General purpose register
|
|
- **rbp** - The Bottom of the current stack frame
|
|
- **interrupt_id** - The ID of the interrupt, that captured the cpu state
|
|
- **error_code** - Some exceptions such as the Page fault push more detailed information into here
|
|
- **rip** - The current instruction address
|
|
- **crs** - Segment selector of the associated IDT descriptor
|
|
- **flags** - The CPU's FLAGS register, a status bitmap
|
|
- **rsp** - The Top of the current stack frame
|
|
- **ss** - Not totally sure, what this does, but it has to do with security rings
|
|
|
|
This struct defines a *complete* CPU state, that can be saved and restored.
|
|
It is saved when the CPU fires an interrupt and restored by the interrupt handler when it's finished.
|
|
This allows multithreading and exception analysis.
|
|
|
|
#### `cpu_flags_E` - enum
|
|
- **CPU_FLAG_CARRY**
|
|
- **CPU_FLAG_PARITY**
|
|
- **CPU_FLAG_AUXILIARY**
|
|
- **CPU_FLAG_ZERO**
|
|
- **CPU_FLAG_SIGN**
|
|
- **CPU_FLAG_TRAP**
|
|
- **CPU_FLAG_INTERRUPT_ENABLE**
|
|
- **CPU_FLAG_DIRECTION**
|
|
- **CPU_FLAG_OVERFLOW**
|
|
- **CPU_FLAG_IO_PRIVILEGE_0**
|
|
- **CPU_FLAG_IO_PRIVILEGE_1**
|
|
- **CPU_FLAG_NESTED_TASK**
|
|
- **CPU_FLAG_RESUME**
|
|
- **CPU_FLAG_VIRTUAL_8086**
|
|
- **CPU_FLAG_ALIGNMENT_CHECK**
|
|
- **CPU_FLAG_VIRTUAL_INTERRUPT**
|
|
- **CPU_FLAG_VIRTUAL_INTERRUPT_PENDING**
|
|
- **CPU_FLAG_CPUID**
|
|
|
|
### exceptions.h
|
|
OSDev Wiki: [Exceptions](https://wiki.osdev.org/Exceptions)
|
|
|
|
#### `exception_type_E` - enum
|
|
These are just the definitions of the CPU-exception interrupt IDs.
|
|
|
|
#### `g_exception_type_strings` - global variable
|
|
This array of strings defines the names of the Exceptions.
|
|
|
|
#### `exception_handle(cpu_state)` - function (cpu_state_T*)
|
|
If an interrupt is an exception, the interrupt handler will call this function to handle the exception.
|
|
At the moment it will just panic, but in far future this could get expanded for page swapping, etc.
|
|
|
|
### gdt.h
|
|
OSDev Wiki: [Global Descriptor Table](https://wiki.osdev.org/GDT)
|
|
|
|
#### `gdt_selector_E` - enum
|
|
- **Null**
|
|
- **Kernel Code**
|
|
- **Kernel Data**
|
|
- **User Null**
|
|
- **User Code**
|
|
- **User Data**
|
|
|
|
#### `gdt_descriptor_T` - struct [packed]
|
|
**Well documented on the osdev wiki.**
|
|
|
|
#### `gdt_entry_T` - struct [packed]
|
|
**Well documented on the osdev wiki.**
|
|
|
|
#### `gdt_T` - struct [packed / page aligned]
|
|
A template that holds a `gdt_entry_T` for every selector
|
|
|
|
#### `g_default_gdt` - global variable
|
|
The default GDT configuration loaded when the GDT gets initialized.
|
|
|
|
#### `gdt_init()` - function (void)
|
|
Loads a descriptor of `g_default_gdt` as the system GDT.
|
|
|
|
### interrupts.h
|
|
This header contains all the stuff, needed to init and handle Interrupts.
|
|
|
|
#### `idt_register_T` - struct [packed]
|
|
This struct is very similar to the GDT descriptor.
|
|
It holds the size and address of the Table, where the interrupt handlers are looked up.
|
|
|
|
#### `idt_descriptor_entry_T` - struct
|
|
This struct stores information about one interrupt handler.
|
|
The osdev wiki explains this more detailed.
|
|
|
|
#### `g_idt_register` - global variable
|
|
The default IDT configuration loaded when the IDT gets initialized.
|
|
|
|
#### `idt_init()` - function (void)
|
|
This function fills all the interrupt gates (handlers) into the IDT and loads it.
|
|
|
|
|
|
## utils
|
|
|
|
### bitmap.h
|
|
Provides functionalities to create, destruct and work with bitmaps.
|
|
|
|
#### `bitmap_T` - struct
|
|
This struct holds a buffer for a bitmap and its size.
|
|
The size is the size of the buffer in bytes, to get the amount of storable bits multiply size with 8.
|
|
|
|
#### `bitmap_init_from_buffer(buffer, size)` - function (bitmap_T)
|
|
Creates a bitmap object from a given buffer and size
|
|
|
|
#### `bitmap_init(size)` - function (bitmap_T) [NOT IMPLEMENTED YET]
|
|
Allocates memory to hold a bitmap in the given size and returns a `bitmap_T` with that buffer and size.
|
|
|
|
#### `bitmap_destruct(bitmap*)` - function (void) [NOT IMPLEMENTED YET]
|
|
Frees the memory of the given bitmap created with `bitmap_init`.
|
|
|
|
#### `bitmap_set(bitmap*, index, value)` - function (bool)
|
|
Sets the bit at the given index in the given bitmap to the given boolean value.
|
|
Returns **false**, if the index is out of the bitmaps size bounds.
|
|
Returns **true**, if the operation was successful.
|
|
|
|
#### `bitmap_get(bitmap*, index)` - function (bool)
|
|
Returns the boolean value stored at the given index in the given bitmap.
|
|
Always returns **false**, if the index is out of the bitmaps size bounds.
|
|
|
|
### core.h
|
|
All the utils, which I didn't know how to name.
|
|
|
|
#### `CORE_HALT_WHILE(a)` - macro
|
|
This halts until **_a_** is true.
|
|
Used when working with blocking variables in e.g. thread safe functions.
|
|
|
|
#### `CORE_HALT_FOREVER` - macro
|
|
This halts forever and warns about this in the log.
|
|
|
|
### io.h
|
|
Provides basic Input/Output functionalities.
|
|
|
|
#### `io_out_byte(port, data)` - function (void)
|
|
Writes one byte of **_data_** to **_port_**.
|
|
This is a wrapper around the assembly `outb` instruction.
|
|
|
|
#### `io_in_byte(port)` - function (uint8_t)
|
|
Reads one byte from **_port_** and returns it.
|
|
This is a wrapper around the assembly `inb` instruction.
|
|
|
|
### logger.h
|
|
Functionalities to write logs to QEMU's serial port.
|
|
|
|
#### `log_level_E` - enum
|
|
- **Info** - General information, that could be useful
|
|
- **Debug** - Should only be used to find bugs and removed (or commented out) after the bug is found
|
|
- **Warning** - Used for warnings and not important errors
|
|
- **Error** - Used for Fatal Errors / Will be printed to the screen (graphics driver is not Implemented yet)
|
|
|
|
#### `log(log_level, string)` - function (void)
|
|
Logs the given string to QEMU's log port, the string is prefixed with the log type.
|
|
|
|
### math.h
|
|
Mathematical functions, definitions, etc.
|
|
|
|
#### `MAX(a, b)` - macro
|
|
Returns the bigger one of the given values.
|
|
|
|
#### `MIN(a, b)` - macro
|
|
Returns the smaller one of the given values.
|
|
|
|
#### `CEIL_TO(a, b)` - macro
|
|
Aligns **_a_** upwards to **_b_**.
|
|
Example: `CEIL_TO(13, 8)` would return 16, because 16 is the next higher multiple of 8 after 13.
|
|
|
|
#### `FLOOR_TO(a, b)` - macro
|
|
Aligns **_a_** downwards to **_b_**.
|
|
Example: `FLOOR_TO(13, 8)` would return 8, because 8 is the next smaller multiple of 8 before 13.
|
|
|
|
#### `pow(base, exponent)` - function (uint64_t)
|
|
Returns the power of `base ^ exponent`.
|
|
|
|
### memory.h
|
|
Basic memory functionalities.
|
|
|
|
#### `memory_copy(source, destination, num)` - function (void)
|
|
Copies **_num_** bytes from **_source_** to **_destination_**.
|
|
On linux this function is called _memcpy_.
|
|
|
|
#### `memory_set(destination, data, num)` - function (void)
|
|
Sets **_num_** bytes at **_destination_** to **_data_**.
|
|
On linux this function is called _memset_.
|
|
|
|
#### `memory_compare(a, b, num)` - function (bool)
|
|
Compares the first **_num_** bytes at **_a_** and **_b_**.
|
|
Returns **false** if there is a different byte.
|
|
Returns **true** if the data is the same.
|
|
There is a similar function on linux called _memcmp_.
|
|
|
|
### panic.h
|
|
Ahhhhh - the kernel is burning!
|
|
|
|
#### `panic(state, message)` - function (void)
|
|
This prints out the error message, a stack backtrace (planned) and a register dump (planned).
|
|
After that, the kernel halts forever.
|
|
This function is called, when a fatal error occurs
|
|
|
|
### stdtypes.h
|
|
Standard type definitions, that are used almost everywhere.
|
|
|
|
#### `uint8_t` - typedef
|
|
8-bit wide unsigned int.
|
|
|
|
Range: `0` - `255`
|
|
|
|
#### `int8_t` - typedef
|
|
8-bit wide signed int.
|
|
|
|
Range: `-128` - `127`
|
|
|
|
#### `uint16_t` - typedef
|
|
16-bit wide unsigned int.
|
|
|
|
Range: `0` - `65536`
|
|
|
|
#### `int16_t` - typedef
|
|
16-bit wide signed int.
|
|
|
|
Range: `-32768` - `32767`
|
|
|
|
#### `uint32_t` - typedef
|
|
32-bit wide unsigned int.
|
|
|
|
Range: `0` - `4294967296`
|
|
|
|
#### `int32_t` - typedef
|
|
32-bit wide signed int.
|
|
|
|
Range: `-2147483648` - `2147483647`
|
|
|
|
#### `uint64_t` - typedef
|
|
64-bit wide unsigned int.
|
|
|
|
Range: `0` - `18446744073709551616`
|
|
|
|
#### `int64_t` - typedef
|
|
64-bit wide unsigned int.
|
|
|
|
Range: `-9223372036854775808` - `9223372036854775807`
|
|
|
|
#### `bool` - typedef
|
|
Boolean type, can hold a logical value **true** or **false**.
|
|
|
|
#### `true` - macro
|
|
Logical **true** value.
|
|
|
|
#### `false` - macro
|
|
Logical **false** value
|
|
|
|
#### `NULL` - macro
|
|
A pointer to nowhere.
|
|
|
|
### string.h
|
|
|
|
#### `string_t` - typedef
|
|
A null-terminated array of chars.
|
|
|
|
#### `string_length(string)` - function (uint32_t)
|
|
Returns the amount of chars a string has before it's null-terminator.
|
|
|
|
#### `string_compare(a, b)` - function (bool)
|
|
Returns **true** when the strings **_a_** and **_b_** are equal.
|
|
Returns **false** if they aren't equal. |