kernel/.wiki/Kernel-documentation.md

1179 lines
50 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:
- **Page Frame Manager**
- **Interrupts**
- **[IDT] Interrupt Descriptor Table**
- **[PIC] Programmable Interrupt Controller**
- **Paging**
- **Kernel Heap**
- **Graphics Renderer**
- **Scheduler**
## 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.
## Panic screen
When a fatal / not recoverable error occurs, the kernel panics. It logs panic information and then halts forever.
Such a panic log can look like the following one:
```
[ Error ] !=====[ KERNEL PANIC ]=====!
Interrupt ID: 0x0E
Error Code: 0b00000000000000000000000000000010
Error Message: Page Fault
Paging Info:
Page Map: 0x000000000FB0A000
CPU Flags:
Parity
Sign
CPU Registers:
RIP: 0xFFFFFFFF8000027A RAX: 0x0000100000079838 RBX: 0x0000000000000000
RCX: 0xFFFFFFFFFFFFFFD8 RDX: 0x0000000000000000 RSI: 0x0000000000000000
RDI: 0x0000100000079838 RBP: 0xFFFF80000FB1AF10 RSP: 0xFFFF80000FB1AEF0
[ Warning ] !=====[ HALTING SYSTEM ]=====!
```
but what does it say?
In most cases, a panic occurs while handling an interrupt.
If this is the case, we will have the state of the cpu while it was interrupted.
This cpu state provides us very much information.
`Interrup ID` tells us, which interrupt caused the panic.
In this case the ID is `0x0E`, a `Page Fault Exception`.
`Error Code` is a binary representation of the 32 least significant bits of the error code pushed by some interrupts.
If an interrupt pushes no error code, this will be just zeros.
In our example the code tells us, that the error happened because of a write attempt to a not present page.
`Error Message` tells us, what happened.
`Paging Info` contains all information about paging.
At the moment, this is just the physical address of the loaded page map.
`CPU Flags` contains information about which bits are set in the CPU status register.
If this block doesn't appear, there are no bits set.
`CPU Registers` contains the data, in the main cpu registers.
This is probably the most interesting block, because you get very detailed information out of here,
if you know what each of these registers does in the cpu.
### Panic without interrupt
If the panic wasn't caused by an interrupt, it has no cpu_state, and because of that it has no detailed info about the execution state.
In this rare case, you will get the following message:
```
No detailed Information available (cpu_state null reference)
```
The `Error Message` could still be helpful, but good luck finding that bug.
## Syscalls
NoxOS will use interrupt based syscalls.
To perform a syscall, write its ID into the `rax` register and call interrupt 0x80.
**Example:**
```nasm
mov rax, 0x0000
int 0x80
```
The syscalls are grouped into groups and their ID consists of a _group-ID_ (first two digits) and a _syscall-ID_ (last two digits).
### Syscall groups
- **Misc** - 0x00
- **File** - 0x01
- **Proc** - 0x02
- **Kernel** - 0xFF
### Misc Syscalls - `0x00--`
### File Syscalls - `0x01--`
### Proc Syscalls - `0x02--`
### Kernel Syscalls - `0xFF--`
The kernel syscalls can only be called by the kernel process and its childs. All other processes, won't be able to use this functions.
| ID | Name | Description |
|--------|-----------------|------------------------------------------------------------------------------------------------------|
| 0xFF00 | scheduler_start | Initializes the Kernels main thread from the current cpu_state. This is used to start multithreading |
## Format strings
Format strings are strings that are formatted at runtime.
They are created by defining a pattern, like the following one:
`"Name: %s / ID: %d"`
And giving it arguments at runtime, let's use the following ones for our example:
`"Main Process", 42`
This would format to that:
`Name: Main Process / ID: 42`
As you see, `%s` and `%d` are placeholders.
Placeholders consist of a `%` sign followed by one or two letters.
When formatting the string, the placeholders are replaced with the arguments.
The first placeholder is replaced with the first argument, the second with the second and so on.
### Numeric specifier
If you put a `.` followed by a number right after the percentage sign of a placeholder,
you will set the `Numeric specifier`.
If the `.` is followed by an astrix, the numeric specifier is passed as its own argument.
Some placeholders use this numeric specifier to configure their output.
If you don't set a numeric specifier, the placeholders, that would use it will use a default value instead.
### Arguments
Make sure, that the arguments you pass, are really of the right type.
If you e.g. pass a negative value of type `int32_t` like `-1312`,
the formatter will have problems with that, because the `int32_t` representation of that number is as an `int64_t` a positive number.
### Placeholders
#### `%s` - string
| **Argument Type** | `string_t` |
|-------------------------------|--------------------------------------------------|
| **Numeric Specifier Use** | Specifies the maximum length the string can have |
| **Numeric Specifier Default** | String Length |
| **Description** | Inserts a string |
#### `%c` - char
| **Argument Type** | `char` |
|-------------------------------|---------------------|
| **Numeric Specifier Use** | None |
| **Numeric Specifier Default** | None |
| **Description** | Inserts a character |
#### `%u` - unsigned decimal
| **Argument Type** | `uint64_t` |
|-------------------------------|-----------------------------|
| **Numeric Specifier Use** | None |
| **Numeric Specifier Default** | None |
| **Description** | Inserts an unsigned integer |
#### `%d` - signed decimal
| **Argument Type** | `int64_t` |
|-------------------------------|--------------------------|
| **Numeric Specifier Use** | None |
| **Numeric Specifier Default** | None |
| **Description** | Inserts a signed integer |
#### `%x` - hexadecimal
| **Argument Type** | `uint64_t` |
|-------------------------------|------------------------------|
| **Numeric Specifier Use** | None |
| **Numeric Specifier Default** | None |
| **Description** | Inserts a 64 bit hex integer |
##### variants
###### `%xb` - byte hexadecimal
| **Argument Type** | `uint8_t` |
|-------------------------------|-----------------------------|
| **Numeric Specifier Use** | None |
| **Numeric Specifier Default** | None |
| **Description** | Inserts a 8 bit hex integer |
###### `%xw` - word hexadecimal
| **Argument Type** | `uint16_t` |
|-------------------------------|------------------------------|
| **Numeric Specifier Use** | None |
| **Numeric Specifier Default** | None |
| **Description** | Inserts a 16 bit hex integer |
###### `%xd` - dword hexadecimal
| **Argument Type** | `uint32_t` |
|-------------------------------|------------------------------|
| **Numeric Specifier Use** | None |
| **Numeric Specifier Default** | None |
| **Description** | Inserts a 32 bit hex integer |
###### `%xq` - qword hexadecimal
This variant is the `%x` standard.
| **Argument Type** | `uint64_t` |
|-------------------------------|------------------------------|
| **Numeric Specifier Use** | None |
| **Numeric Specifier Default** | None |
| **Description** | Inserts a 64 bit hex integer |
#### `%?` - boolean
| **Argument Type** | `bool` |
|-------------------------------|---------------------------|
| **Numeric Specifier Use** | None |
| **Numeric Specifier Default** | None |
| **Description** | Inserts `true` or `false` |
#### `%b` - binary
| **Argument Type** | `uint64_t` |
|-------------------------------|-----------------------------------------------|
| **Numeric Specifier Use** | The amount of bits that are shown |
| **Numeric Specifier Default** | 64 |
| **Description** | Inserts the binary string of the given number |
#### `%%` - mask
This is not a really a placeholder, but you can use this to mask the % sign,
so it will be interpreted as just a `%` instead of a placeholder.
##
---
**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
| Name | Description |
|-------------|---------------------------------------------------|
| framebuffer | struct with information about the graphics buffer |
| terminal | bootloader terminal / graphical log |
| memory_map | information about the memory layout / regions |
| kernel_file | The unparsed kernel ELF file |
| rsdp | _Root System Description Pointer_ |
### 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).
## drivers
### fs/ramfs.h
**Warning:** This is a filesystem specific driver, by this it should only be accessed by the vfs.
The ramfs (ram-filesystem) is not a filesystem in the common sense.
All data that is stored in ramfs is stored in the virtual file systems cache.
This means that all data in ramfs is temporary and erased after a reboot.
#### `ramfs_file_delete(node)` - function (void)
Frees the files cache space. This won't delete the node in the vfs.
#### `ramfs_file_write(node, size, buffer_in)` - function (void)
If this file has some cache space allocated, it will be freed.
Then there will be some cache space (**_size_** bytes) allocated and **_size_** bytes from **_buffer_in_** copied into the cache.
This use of the vfs cache isn't a great solution and should be reworked.
#### `ramfs_file_read(node, size, buffer_out)` - function (void)
Copies **_size_** bytes from the files' cache to **_buffer_out_**.
This won't copy more bytes than the allocated cache space is big.
### fs/vfs.h
**VFS** stands for _Virtual File System_ and is an abstraction,
that kinda merges all mounted filesystems into one.
It provides a general API for dealing with files, and handles all fs specific stuff in the background.
This VFS is node based, meaning, that every file, directory, mount point, etc is represented as a node in memory.
Nodes have a type and a fs.
Nodes can have children and next and previous nodes.
Example node relations:
```
+-----------+
| Root Node | <---------+
+-----------+ |
| |
[Childs] [Parent]
| +-------------+---------------+
v / | \
+-------+ +-------+ +-------+
| Child | --[Next]-> | Child | --[Next]-> | Child |
| 1 | <-[Prev]-- | 2 | <-[Prev]-- | 3 | ...
+-------+ +-------+ +-------+
|
[Childs]
|
v
...
```
If a node is accessed it is linked as the first node in the childs order, to make name resolving process faster.
#### `VFS_MAX_NAME_LENGTH` - macro
The maximum length of a nodes name.
Bigger names will be cut of at the end.
#### `fs_type_E` - enum
This enum specifies all supported filesystems:
- **RAMFS** - A filesystem, that is bound very tight to the vfs and is completely in the RAM.
#### `vfs_node_type_E` - enum
This enum specifies all types a node can have:
- **Directory** - A directory can contain other nodes
- **File** - A file can hold data
- **Mount Point** - A mount point is like a directory, but in the vfs backend this resolves to the root of a filesystem
- **Block Device** - Neither used nor implemented yet
#### `fs_T` - struct
This struct specifies a filesystem instance.
| Name | Type | Description |
|-----------|-------------|-------------------------------------------------------------|
| type | fs_type_E | The type of the filesystem |
| root_node | vfs_node_T* | A pointer to the vfs node of the filesystems root directory |
#### `vfs_node_cache_T` - struct
The current node caching system is just a small placeholder that will be reworked soon.
| Name | Type | Description |
|-------------|-------------|-----------------------------------------------------|
| buffer | void* | The actual buffer, where data is cached |
| buffer_size | uint64_t | The size of _buffer_ |
| reclaimable | bool | Not used atm, but could be important after refactor |
| node | vfs_node_T* | The node, that the cache belongs to |
#### `vfs_node_T` - struct
| Name | Type | Description |
|------------|------------------|------------------------------------------------|
| name | char[] | The name of the node |
| type | vfs_node_type_E | The type of the node |
| cache | vfs_node_cache_T | The nodes cache segment |
| size | uint64_t | The nodes size |
| specific | void* | General purpose pointer (details below) |
| filesystem | fs_T* | The filesystem this node actually lies in |
| prev | vfs_node_T* | The previous node |
| next | vfs_node_T* | The next node |
| parent | vfs_node_T* | The parent node (has to be dir or mount point) |
| childs | vfs_node_T* | The first child node |
##### specific
The usage of this value is specific to th nodes type:
- **Directories:** NULL
- **Files:** NULL
- **Mount points:** a pointer to the mounted filesystem.
- **Block devices:** NULL
#### `g_root_fs` - global variable
The systems root filesystem.
Every node resolve will start at this filesystem.
#### `vfs_node_cache_create(node, size)` - function (vfs_node_cache_T*)
Allocates a **_size_** bytes big cache segment for **_node_**.
#### `vfs_node_cache_destruct(node_cache)` - function (void)
Frees **_node_cache_** and its buffer.
#### `vfs_node_create(parent, name, type, specific)` - function (vfs_node_T*)
Allocates a node with the given parameters.
The nodes _fs_ value is inherited from **_parent_**,
or from **_parent_**'s specific value if **_parent_** is a mount point.
#### `vfs_node_destruct(node)` - function (void)
Recursively destructs **_node_** and all it's children.
#### `vfs_node_dump_info(node, indent)` - function (void)
Prints the complete directory structure starting at **_node_**.
**_indent_** is used for the recursive calls and should be set to 0.
#### `vfs_node_resolve_child(node, child_name)` - function (vfs_node_T*)
Searches **_node_** for a child named **_child_name_**.
Returns the first matching child or NULL if no matching child was found.
#### `vfs_file_create(filesystem, path)` - function (vfs_node_T*)
Creates a file at **_path_** in **_filesystem_** and returns a pointer to it.
The directory in **_path_** needs to exist and the filename needs to not exist.
#### `vfs_file_delete(file)` - function (void)
Deletes **_file_**.
#### `vfs_file_write(file, position, size, buffer_in)` - function (void)
Writes **_size_** bytes from **_buffer_in_** at **_position_** into **_file_**.
**Warning:** the current ramfs implementation will ignore **_position_**!
#### `vfs_file_read(file, position, size, buffer_out)` - function (void)
Reads **_size_** bytes from **_file_** at **_position_** into **_buffer_out_**.
**Warning:** the current ramfs implementation will ignore **_position_**!
#### `vfs_init(boot_info)` - function (void)
Initializes the VFS.
In future this will also unpack the initial ramdisk into the _temp_ directory.
#### `vfs_resolve_path(filesystem, path)` - function (vfs_node_T*)
Returns the node at **_path_** or NULL if **_path_** is invalid.
#### `vfs_unpack_archive_ustar(node, archive)` - function (void) [Not Implemented yet]
This will unpack a USTAR-archive (**_archive_**) at **_node_**.
### graphics/color.h
#### `color_palette_E` - enum
Indexes for `g_color_palette`
- **Grey Dark**
- **Pink**
- **Signal Green**
- **Orange**
- **Blue**
- **Purple**
- **Green**
- **Grey Light**
- **Red**
#### `color_argb_T` - struct
| Name | Type | Description |
|-------|---------|---------------------------------|
| alpha | uint8_t | Transparency value of the color |
| red | uint8_t | Red value of the color |
| green | uint8_t | Green value of the color |
| blue | uint8_t | Blue value of the color |
#### `color_argb_blend_alpha(background, foreground)` - function (color_argb_T)
Blends **_background_** and **_foreground_** with the _alpha_ value of **_foreground_**.
#### `g_color_palette` - global variable
An array of standard colors.
This array is indexed using `color_palette_E`.
### graphics/font.h
#### `font_T` - struct
| Name | Type | Description |
|------------|----------|------------------------------------------------|
| width | uint8_t | The width of each char (in pixels) |
| height | uint8_t | The height of each char (in pixels) |
| glyph_size | uint8_t | The amount of bytes a char takes in the buffer |
| buffer | uint8_t* | The buffer, where the char bitmaps lay |
#### `g_font` - global variable
A global usable 8x8 font.
### graphics/framebuffer.h
#### `framebuffer_T` - struct
| Name | Type | Description |
|-----------------|----------|-----------------------------------------------------|
| address | void* | The address of the framebuffer |
| width | uint64_t | The pixel width of the framebuffer |
| height | uint64_t | The pixel height of the framebuffer |
| pitch | uint64_t | The number of bytes in each row |
| bits_per_pixel | uint16_t | The amount of bits a pixel consumes in the buffer |
| bytes_per_pixel | uint8_t | The amount of bytes a pixel consumes in the buffer |
| shift_red | uint8_t | How many bits the red value is shifted in a pixel |
| shift_green | uint8_t | How many bits the green value is shifted in a pixel |
| shift_blue | uint8_t | How many bits the blue value is shifted in a pixel |
### graphics/renderer.h
#### `graphics_buffer_layer_E` - enum
- **Standard** - The layer, where almost everything should be on
- **Overlay** - This layer should be used for stuff like a mouse cursor, that should always be visible
#### `graphics_buffer_T` - struct
| Name | Type | Description |
|---------|-------------------------|------------------------------------------------------------------------------|
| buffer | color_argb_T* | The buffer, where all the pixels are stored |
| width | uint32_t | The width of the buffer |
| height | uint32_t | The height of the buffer |
| pos_x | uint32_t | The buffers x offset (from the top-left corner) in the renderers main buffer |
| pos_y | uint32_t | The buffers y offset (from the top-left corner) in the renderers main buffer |
| blocked | bool | Thread safety block variable |
| render | bool | Controls, if the buffer will be rendered or not |
| layer | graphics_buffer_layer_E | The layer, on which the buffer will be rendered |
| prev | graphics_buffer_T* | The previous buffer in the rendering queue |
| next | graphics_buffer_T* | The next buffer in the rendering queue |
#### `graphics_renderer_T` - struct
| Name | Type | Description |
|------------------------|---------------------|------------------------------------------------------------------------------------------|
| framebuffer | framebuffer_T | The systems framebuffer (requested from bootloader) |
| back_buffer | uint32_t* | The buffer, where the final image is calculated, before sending it to the framebuffer |
| buffer_size | uint64_t | The size of `back_buffer` (in bytes) |
| graphics_buffer_layers | graphics_buffer_T** | List of pointers to the first graphics_buffer of every layer |
| font | font_T | The font, all graphics buffers use to draw chars (could be moved to `graphics_buffer_T`) |
#### `graphics_buffer_request(pos_x, pos_y, width, height, layer)` - function (graphics_buffer_T*)
Allocates a graphics buffer and pushes it on top of the rendering queue of **_layer_**.
#### `graphics_buffer_show(graphics_buffer)` - function (void)
Enables rendering for this buffer.
Every created buffer will be rendered by default.
#### `graphics_buffer_hide(graphics_buffer)` - function (void)
Disables rendering for this buffer.
#### `graphics_buffer_destruct(graphics_buffer)` - function (void)
Removes **_graphics_buffer_** from the rendering queue and frees its memory allocations.
#### `graphics_buffer_shift_up(graphics_buffer, shift)` - function (void)
Shifts **_graphics_buffer_**'s content **_shift_** rows up.
#### `graphics_buffer_set_pixel(graphics_buffer, x, y, color)` - function (void)
Sets a pixel with the given **_color_** at position(**_x_** | **_y_**) in **_graphics_buffer_**.
**_x_** and **_y_** are graphics buffer relative.
#### `graphics_buffer_get_pixel(graphics_buffer, x, y)` - function (color_argb_T)
Returns the color of the pixel at position(**_x_** | **_y_**) in **_graphics_buffer_**.
#### `graphics_buffer_draw_char(graphics_buffer, x, y, color, chr)` - function (void)
Draws a character (**_chr_**) at position(**_x_** | **_y_**) in **_graphics_buffer_**.
The position is the top-left corner of the char.
#### `graphics_buffer_draw_string(graphics_buffer, x, y, color, string)` - function (position_T)
Draws **_string_** at position(**_x_** | **_y_**) in **_graphics_buffer_**.
The position is the top-left corner of the string.
Returns the position after the last char of the string.
#### `graphics_renderer_init(boot_info)` - function (void)
Initializes the global graphics renderer.
Needs a pointer to `boot_info` to extract information about the framebuffer.
#### `graphics_renderer_update()` - function (void)
Updates the renderers back_buffer and swaps it into the framebuffer.
To update the back_buffer, it iterates over the rendering queue and copies every buffer to the back_buffer.
If there are overlapping graphics_buffers, it alpha-blends them.
#### `graphics_renderer_get_top_buffer(layer)` - function (graphics_buffer_T*)
Returns a pointer to the graphics_buffer, that is on top of the rendering queue of **_layer_**.
#### `graphics_renderer_get_width()` - function (uint32_t)
Returns the width of the framebuffer.
#### `graphics_renderer_get_height()` - function (uint32_t)
Returns the height of the framebuffer.
### time/pit.h
#### `PIT_CHANNEL_0_PORT` - macro
The IO port, where channel 0 of the PIT (which is capable of firing IRQs) can be configured.
#### `PIT_DIVISOR` - macro
The standard divisor nox_os loads into channel 0 of the PIT.
`32768` fires an interrupt every `~27ms`, what is perfect for preemptive multithreading.
#### `pit_set_divisor(divisor)` - function (void)
Loads **_divisor_** into channel 0 of the PIT.
If **_divisor_** is smaller than 100, it will be set to 100.
## mm
### heap.h
#### `heap_segment_T` - struct
This is the header for each heap segment.
It holds its status information and a pointer to the next and previous segments.
It lies in memory, directly before the accessible buffer of the segment.
#### `heap_T` - struct
This struct describes a heap.
The area between **_start_** and **_end_** is filled with heap segments.
#### `heap_init(heap*, base)` - function (void)
Initializes **_heap_** at **_base_** (virtual address).
It will automatically map some page frames to that address.
#### `heap_memory_allocate(heap*, size)` - function (void)
Returns a pointer to a free usable memory location, that has at least the given **_size_**.
It will return `NULL` and log an error, if the heap is corrupted.
Because this function iterates over the complete heap to find a free segment, it is slow.
#### `heap_memory_free(heap*, address)` - function (void)
Frees a with `heap_memory_allocate` created heap segment, and makes it usable again.
Does nothing, if the address doesn't point to a valid heap segment.
#### `heap_dump_segments(heap*)` - function (void)
Logs a complete list, of all heap segments.
Useful, when debugging / testing the heap.
#### `heap_destruct(heap*)` - function (void)
Invalidates all segments of a heap, frees all used page frames and unmaps them.
### memory_map.h
#### `memory_map_get_total_memory_size(boot_info*)` - function (uint64_t)
Calculates the total amount of memory available, by iterating over the memory map.
The size is stored in a static variable, so no matter how often you call this function, the size will only be calculated once.
It returns the total amount of memory in bytes.
### 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.
### page_map.h
#### `VIRTUAL_ADDRESS_MAX` - macro
The highest mappable virtual address.
4 level page maps have a maximum address space of 256TB.
#### `page_map_flag_E` - enum
- **Present** - This indicates if the entry is used or should be ignored. Automatically set when mapping a page.
- **Read & Write** - A mapped Page is always readable. This flag allows writing to that page.
- **User Super** - If set, user mode access to the page is allowed.
- **Write Through** - Enables _Write Through Caching_ for this page.
- **Cache Disabled** - If this bit is set, the page won't be cached.
- **Accessed** - Set by the CPU, when this PDE or PTE was read. Won't be reset by the CPU.
- **Dirty** - Set when the page has been modified.
- **Larger Pages** - When this bit is set in a PDE or PTE, the entry points to a 1GB or 2MB page.
- **Custom 1 - 3** - Not used in NoxOS.
- **No Execute** - When this bit is set, the CPU won't execute code that lies in that page.
#### `page_map_T` - struct [page aligned]
This struct contains 512 entries.
These entries contain an address and flags.
The addresses link like this:
- **PML4** --> **Page Directory** or _1GB Page_
- **Page Directory** --> **Page Table** or _2MB Page_
- **Page Table** --> _4KB Page_
A pointer to a `page_map_T` can be loaded into `cr3` to load this pagemap.
#### `page_map_create()` - function (page_map_T*)
Allocates a `page_map_T` and returns a pointer to it.
#### `page_map_fetch_current()` - function (page_map_T*) [ASM implementation]
This function will return the page map, that is currently loaded.
To achieve this, it just reads the `cr3` value.
#### `page_map_load(page_map*)` - function (void) [ASM implementation]
Loads the given page map.
To achieve this, it writes the `cr3` value.
#### `page_map_map_memory(page_map*, virtual_address, physical_address, flags)` - function (void)
This maps **_physical_address_** to **_virtual_address_** in **_page_map_**.
The **_flags_** will be applied to the page mapping / page table entry.
It always applies the _Present_ flag.
#### `page_map_unmap_memory(page_map*, virtual_address)` - function (void)
Removes a page mapping from the **_page_map_**.
Page map structure intern pages won't be checked if they're still needed or not.
#### `page_map_get_physical_address(page_map*, virtual_address)` - function (void*)
Returns the physical address of the page, that is mapped to **_virtual_address_**.
#### `page_map_destruct(page_map*)` - function (void)
Clears a page map and frees all page map structure intern pages.
#### `page_map_entry_set_flags(entry, uint64_t flags)` - function (void)
This will set the provided flags to a page map entry.
#### `page_map_entry_get_flag(entry, page_map_flag_E flag)` - function (bool)
Returns if the given flag is set in the page map entry, or not.
#### `page_map_entry_set_address(entry, void* address)` - function (void)
This will set the provided address to a page map entry.
#### `page_map_entry_get_address(entry)` - function (void*)
This will read and return the address set in the page map entry.
#### `paging_init()` - function (void)
Initializes paging.
This reads the current page map set by the kernel and writes it to `g_kernel_page_map`.
#### `g_kernel_page_map` - global variable
The kernels page map. This page map is provided by the bootloader and read from `cr3` at `paging_init`.
## 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** - Readable
- **Kernel Data** - Readable + Writable
NoxOS uses the GDT loaded by limine, because in 64 bit mode a GDT is only needed for backwards compatability.
### 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.
## proc
### thread.h
#### `thread_T` - struct
| Name | Type | Description |
|------------|-------------|--------------------------------------------------------------------------------------------------|
| state | cpu_state_T | The last saved state of the thread. ( -> _context switching_) |
| cpu_time | uint64_t | The amount of cpu time the thread had. (currently the amount of context switches the thread had) |
| stack | void* | The bottom of the threads stack |
| stack_size | uint32_t | The size of the threads stack (in bytes) |
| prev | thread_T* | The previous thread in the scheduling queue (**should only be accessed by the scheduler!**) |
| next | thread_T* | The next thread in the scheduling queue (**should only be accessed by the scheduler!**) |
#### `thread_spawn(function)` - function (thread_T*)
Allocates a `thread_T` and registers it in the scheduler.
The thread starts execution at **_function_**.
The for the thread allocated stack has a size of 16 KB (4 Pages).
The thread still needs to be started with a `thread_start` call.
Returns a pointer to the created thread.
#### `thread_spawn_from_state(state)` - function (thread_T*)
Allocates a `thread_T` and registers it in the scheduler.
The threads' _cpu_state_ is copied from **_state_**.
This won't allocate a stack for the stack.
The thread still needs to be started with a `thread_start` call.
Returns a pointer to the created thread.
This function should be avoided.
#### `thread_start(thread)` - function (void)
Starts/unpauses **_thread_**.
#### `thread_pause(thread)` - function (void)
Pauses **_thread_**.
#### `thread_kill(thread)` - function (void)
Kills **_thread_**.
The threads stack and `thread_T` structure will be freed.
### scheduler.h
#### `scheduler_T` - struct
| Name | Type | Description |
|----------------|-----------|--------------------------------------------------------------------|
| num_threads | uint32_t | Total amount of currently spawned threads |
| running_thread | thread_T* | A pointer to the currently running thread. |
| blocked | bool | Set to true, while switching the context. Thread safety mechanism. |
| initialized | bool | Set to true, if the scheduler is initialized and started. |
#### `scheduler_init()` - function (void)
Initializes the scheduler and performs a `scheduler_start` kernel syscall.
After this function, the whole kernel is in scheduling mode.
#### `scheduler_start(state)` - function (cpu_state_T*)
Creates and starts a thread from **_state_**.
It returns the result of a context switch, I forgot, why I did it like that.
This is basically the backend for the `scheduler_start` kernel syscall.
#### `scheduler_is_initialized()` - function (bool)
Returns if the scheduler is initialized (and running) or not.
#### `scheduler_register_thread(thread)` - function (thread_T*)
Registers **_thread_** in the scheduler.
#### `scheduler_pause_thread(thread)` - function (void)
Pauses **_thread_**, by removing it from the scheduling queue.
**Potential Bug:** if **_thread_** was the currently running thread,
this could cause issues, because it's _prev_ and _next_ values are nulled.
#### `scheduler_start_thread(thread)` - function (void)
Starts **_thread_**, by linking it into the scheduling queue.
#### `scheduler_kill_thread(thread)` - function (void)
Pauses and unregisters **_thread_**.
#### `scheduler_get_current_thread()` - function (thread_T*)
Returns a pointer to the currently running thread.
#### `scheduler_switch_context(state)` - function (cpu_state_T*)
Saves **_state_** in the running threads _state_ value and increments their _cpu_time_ value.
Then it sets the next thread as the running thread and returns its _state_.
This needs to be called from an interrupt handler, for the returned state to be loaded.
## 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)
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)
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.
#### `io_wait()` - function (void)
Waits one IO cycle.
Should be used to give the devices enough time to respond.
### logger.h
Functionalities to write logs to QEMU's serial port.
#### `log_level_E` - enum
- **None** - Logs just the message without a prefix
- **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.
Format strings are supported.
### 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.
#### `position_T` - struct
| Name | Description |
|------|------------------------------|
| x | X coordinate of the position |
| y | Y coordinate of the position |
#### `pow(base, exponent)` - function (uint64_t)
Returns the power of `base ^ exponent`.
#### `abs(number)` - function (uint64_t)
Returns the absolute value of **_number_**.
#### `octal_string_to_int(string, size)` - function (uint64_t)
Converts a base-8 **_string_** with length **_size_** into an integer and returns it.
### 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_.
#### `memory_allocate(size)` - function (void*)
Returns the address to a buffer, that is at least **_size_** bytes big.
On linux this function is called _malloc_.
#### `memory_free(address)` - function (void)
Free the buffer at address and make it reallocatable , this buffer needs to be a buffer, that was created with `memory_allocate`.
On linux this function is called _free_.
#### `memory_allocator_init(base)` - function (void)
This initializes the heap, where `memory_allocate` allocates memory.
### 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 signed 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.
#### `string_find_next(string, chr)` - function (uint32_t)
Returns the index of the next character that matches **_chr_** in **_string_**.
#### `string_find_last(string, chr)` - function (uint32_t)
Returns the index of the last character that matches **_chr_** in **_string_**.
#### `variadic_format_size(string, args)` - function (uint64_t)
Returns how long a format string with the given pattern (**_string_**) and **_args_** would be.
Useful to create a big enough buffer before formatting a string.
#### `format_size(string, ...)` - function (uint64_t)
This calls `variadic_format_size`, but instead of giving it a `va_list` you can give this function the actual arguments.
#### `variadic_format(output, string, args)` - function (void)
Formats **_string_** with **_args_** and writes the product to **_output_**.
The rules for format strings are specified on top of this document in the _General concepts_ block.
#### `format(output, string, ...)` - function (void)
This calls `variadic_format`, but instead of giving it a `va_list` you can give this function the actual arguments.
#### `string_unsigned_dec_to_alpha(string, value)` - function (void)
Converts the unsigned integer in **_value_** to an alphanumeric string.
The representation is decimal.
This string will be written into **_string_**.
#### `string_dec_to_alpha(string, value)` - function (void)
Converts the signed integer in **_value_** to an alphanumeric string.
If it is negative it will be prefixed with a hyphen.
The representation is decimal.
This string will be written into **_string_**.
#### `string_hex_8bit_to_alpha(string, value)` - function (void)
Converts the byte in **_value_** to an alphanumeric string.
The representation is hexadecimal.
This string will be written into **_string_**.
#### `string_hex_16bit_to_alpha(string, value)` - function (void)
Converts the word(16-bits) in **_value_** to an alphanumeric string.
The representation is hexadecimal.
This string will be written into **_string_**.
#### `string_hex_32bit_to_alpha(string, value)` - function (void)
Converts the dword(32-bits) in **_value_** to an alphanumeric string.
The representation is hexadecimal.
This string will be written into **_string_**.
#### `string_hex_64bit_to_alpha(string, value)` - function (void)
Converts the qword(64-bits) in **_value_** to an alphanumeric string.
The representation is hexadecimal.
This string will be written into **_string_**.
#### `string_bin_to_alpha(string, num_bits, value)` - function (void)
Converts the data in **_value_** to an alphanumeric string.
The representation is binary.
**_num_bits_** specifies how many bits, starting at the least significant bit, will be converted.
This string will be written into **_string_**.
#### `string_bool_to_alpha(string, value`) - function (void)
Converts the boolean in **_value_** to an alphanumeric string.
The representation is `true` or `false`.
This string will be written into **_string_**.