kernel/.wiki/Kernel-documentation.md

83 KiB

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

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

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 ]=====!
Error Message: Division Error
Interrupt ID: 0x00
Error Code: 0b00000000000000000000000000000000

Paging Info:
    Page Map: 0x000000000FAE9000

CPU Flags:
    Parity
    Sign
    Interrupt Enable

CPU Registers:
    RIP: 0xFFFFFFFF80002745   RAX: 0x0000000000000001   RBX: 0x0000000000000000
    RCX: 0x0000000000000000   RDX: 0x0000000000000000   RSI: 0x000000000001F980
    RDI: 0x00001000005DF7A0   RBP: 0xFFFF80000FAF9F40   RSP: 0xFFFF80000FAF9F30

Call Stack:
    0xFFFFFFFF80000000+033F  ->  _start
    0xFFFFFFFF8000274D+0078  ->  kmain
    0xFFFFFFFF80002732+0013  ->  test
[ 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.

Call Stack lists the current function call chain, starting from the least recent call. The big hex number is the base of the function and the small hex number is the offset, where the next function was called. After the -> follows the name of the function.

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.

Memory Layout

NoxOS uses a higher half kernel, this means, that the kernels executable is mapped above 0x800000000000. All kernel and bootloader resources are also mapped in the higher half. This leaves the lower half for process specific mappings

Process Memory Isolation

Every process has its own virtual memory space (page map). This space contains always the following mappings (except in the kernels main process):

Name Address Permissions
Process executable 0x0000010000000000 as defined in ELF
Thread data (stack, etc.) 0x0000010100000000 Read/Write
Kernel executable 0xFFFFFFFF80000000 Read/Exec
Kernel Heap 0xFFFFFFFFF0000000 Read/Write

Thread data

Every thread has a Thread Data region, that contains thread specific stuff like a stack.

The first threads Thread Data region is at 0x0000010100000000, the seconds at 0x0000010200000000 and so on.

In this setup, every Thread Data region has a virtual size of 4GB.

Context switching

Switching between threads is a bit tricky, when the threads stacks are in different page maps. When performing a context switch, the memory region 0x0000010000000000 <--> 0x0000020000000000 is dirty mapped (the PML4 entries are copied) from the next processes' into the kernels page map. This region contains all Thread Data regions and the processes' executable mappings. Due to this dirty mapping the next interrupt handler can always access the current processes' or threads' data.

Syscalls

NoxOS will use interrupt based syscalls. To perform a syscall, write its ID into the rax register and call interrupt 0x80.

Example:

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.

Executables

NoxOS uses the ELF executable format, which is the linux/unix standard. Further information can be found in the Syscalls and drivers/elf/elf.h documentation.


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.

drivers

acpi/acpi.h

OSDev Wiki: ACPI

acpi_init(boot_info) - function (void)

Initializes the ACPI (Advanced Configuration and Power Interface).

acpi/rsdp.h

OSDev Wiki: RSDP

rsdp_descriptor_v1_T - struct [packed]

The RSDP Table used in ACPI v1.0

Name Type Description
signature char[8] Needs to be "RSD PTR " (with the withespace at the end)
checksum uint8_t Used to validate the table
oem_id char[6] This string identifies the OEM
revision uint8_t Tells whether the RSDP is version 1 or 2+
rsdt_address uint32_t The Physical Address of the RSDT

rsdp_descriptor_v2_T - struct [packed]

The RSDP Table used in ACPI v2.0 or higher

Name Type Description
descriptor_v1 rsdp_descriptor_v1_T A table in the format of the ACPI 1.0 specification
length uint32_t The size of the whole table
xsdt_address uint64_t The Address of the XSDT (when available this should be used instead of the RSDT)
checksum_extended uint8_t Used to calculate the checksum of the whole table
reserved uint8_t[3] Can be ignored

elf/elf.h

elf_executable_T - struct

This struct holds the parsed data of an ELF executable.

Name Type Description
header elf_header_T The header of the elf file
num_symbols uint64_t The size of symbols
symbols symbol_T* An array containing all symbols of the elf file
num_mappings uint64_t The size of mappings
mappings elf_mapping_T* An array containing the mappings needed to load the elf file
string_table void* A copy of the elf files .strtab section, all strings are referenced here to have them available even if the elf file is closed

elf_executable_temp_T - struct

This struct is used while generating an elf_executable_T. It holds parse-time information about the elf file.

Name Type Description
executable elf_executable_T* A pointer to the final elf_executable_T
symbol_table elf_section_T* A pointer to .symtab in buffer
section_header_string_table elf_section_T* A pointer to .shstrtab in buffer
buffer uint8_t* The buffer where the executable is loaded from

elf_executable_create(buffer) - function (elf_executable_T*)

Generates an elf_executable_T from an elf file loaded to buffer and returns a pointer to it.

elf_executable_destruct(executable) - function (void)

Frees all memory allocated for executable.

elf/header.h

The enums in this header describe the possible values that a field of the elf header can have.

elf_target_architecture_E - enum

Field in header: identity[4]

elf_endianness_E - enum

Field in header: identity[5]

elf_sysabi_E - enum

Field in header: identity[7]

elf_object_type_E - enum

Field in header: type

elf_instruction_set_E - enum

Field in header: isa

elf_header_T - struct

Name Type Description
identity uint8_t[16] Information like the used endian and the SysABI is stored in here
type uint16_t The type of the elf file -> elf_object_type_E
isa uint16_t The used instruction set -> elf_instruction_set_E
version uint32_t ELF version
address_entry_point uint64_t The start point for program execution
offset_program_header uint64_t The position of the program header array in the file
offset_section_header uint64_t The position of the section header array in the file
flags uint32_t Architecture dependent, can be ignored
len_header uint16_t The size of this header
len_program_header_entry uint16_t The size of one program header
num_program_header_entries uint16_t The amount of program headers
len_section_header_entry uint16_t The size of one section header
num_section_header_entries uint16_t The amount of section headers
string_section_index uint16_t The section header index of the .shstrtab section

g_elf_target_architecture_strings - global variable

An array of strings matching elf_target_architecture_E.

g_elf_endianness_strings - global variable

An array of strings matching elf_endianess_E.

g_elf_sysabi_strings - global variable

An array of strings matching elf_sysabi_E.

g_elf_object_type_strings - global variable

An array of strings matching elf_object_type_E.

g_elf_instruction_set_strings - global variable

An array of strings matching elf_instruction_set_E.

elf_init_kernel_exec(boot_info) - function (void) [Will be replaced in near future]

Loads the kernel elf into g_kernel_executable.

g_kernel_executable - global variable [Will be replaced in near future]

Holds the parsed kernel executable.

This will be removed, when processes are implemented, because then this can be accessed via the kernel process control struct.

elf/mapping.h

elf_mapping_T - struct

A mapping describes an area of memory, that should be copied from the elf file into the RAM and how/where it should be mapped.

Name Type Description
offset_file uint64_t The mappings' start in the elf file
offset_virtual uint64_t The mappings' start in memory
length_file uint64_t The mappings' size in the elf file
length_virtual uint64_t The mappings' size in memory, if this is bigger than length_file the remaining space will be filled with zeros

elf_mappings_apply(mappings, num_mappings, buffer, base, page_map) - function (void)

Maps all mappings into page_map and copies the related data from buffer (elf file) to the mapped memory. base specifies where the mappings should start in the virtual address space.

elf/section.h

elf_section_type_E - enum

  • Null - These sections can be ignored
  • Program Data - These link to segments, if I remember right
  • Symbol Table - Here are all the executables' symbols stored
  • String Table - Here are all strings stored
  • RelocationA - Contains relocation information
  • Hash - Symbol Table hash table
  • Dynamic Link - This provides information for the dynamic linker
  • Note - notes that were created by the compiler / toolchain
  • Nobits - Nulled data like .bss

elf_section_T - struct

Name Type Description
name_offset uint32_t The offset of the sections name in .shstrtab
type uint32_t The type of the section -> elf_section_type_E
flags uint64_t Sections attribute flags
virtual_address uint64_t The address where the section should be mapped to (if it's not 0)
offset uint64_t The sections offset in the file
length uint64_t The size of the section
link uint32_t Type specific link to another section
info uint32_t Type specific information
alignment uint64_t If the section is aligned, this value specifies the alignment
entry_size uint64_t The size of the sections entries

g_elf_section_type_strings - global variable

An array of strings matching elf_section_type_E.

elf/segment.h

elf_segment_type_E - enum

  • Null - Unused segment
  • Load - Segment that should be included into mappings
  • Dynamic - Segments of this type contain dynamic linking information
  • Interpreter - This holds a path to an interpreter
  • Note - These segments hold notes by the compiler / toolchain
  • Program Header Table - This points to the table that is holding the segment headers
  • TLS - This holds a Thread Local Storage template

elf_segment_T - struct

Name Type Description
type uint32_t The segments type -> elf_segment_type_E
flags uint32_t The segments flags (Read / Write / Execute)
offset uint64_t The segments position in the elf file
address_virtual uint64_t Where the segment should be mapped in the virtual address space
address_physical uint64_t Not used in the System V ABI
length_file uint64_t The segments size in the file
length_memory uint64_t The size of the area that should be mapped for the segment
align uint64_t The segments alignment (has to be a power of 2)

g_elf_segment_type_strings - global variable

An array of strings matching elf_segment_type_E.

elf/symbol.h

ELF_SYMBOL_TYPE(info) - macro

Extracts the elf_symbol_type_E from the symbols info value.

elf_symbol_type_E - enum

  • None - Unspecified type
  • Object - Data objects like variables, arrays, etc.
  • Func - Function
  • Section - Associated section
  • File - The path to the source file associated with the object
  • Common - Uninitialized common blocks
  • TLS - Thread Local Storage

elf_symbol_T - struct

Name Type Description
name_offset The offset of the symbols name in .strtab
info Information about the symbol (type, bind)
other Information about the symbol (visibility)
related_section_index The index of the symbols related section
value Value, in most cases this is an address
length The size of the symbol (e.g. num bytes if the symbol is an array)

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/ustar.h

The USTAR 'filesystem' is probably more common known as tar-archive. It is a really simple concept, where a file consists of a header block followed by data blocks. These blocks are aligned at 512 bytes.

OSDev Wiki: USTAR

ustar_type_E - enum

The types an entry can have:

  • File
  • Hardlink
  • Symlink
  • Char Device
  • Block Device
  • Directory
  • Pipe

ustar_header_T - struct [packed / 512B aligned]

Name Type Description
name char[100] The name of the entry
mode uint64_t file mode (permissions, etc)
owner_id uint64_t The owners ID
group_id uint64_t The groups ID
size char[12] The size of the entry, represented as a string of an octal number (dafuq)
last_modification char[12] The unix-timestamp, when the entry was modified the last time
checksum uint64_t I think this is a weird checksum of the header
type uint8_t The type (ustar_type_E) of the entry, represented as ascii numbers (dafuq)
name_linked char[100] The path to the linked entry, if this is a link-entry
indicator char[6] This needs to be ustar
version uint16_t The version of the tar command, that created the archive
owner_user_name char[32] The name of the file owner
owner_group_name char[32] The name of the file group
device_major uint64_t The devices major number
device_minor uint64_t The devices minor number
name_prefix char[155] If this is not null, this acts as a prefix for name

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_directory_create(filesystem, path) - function (vfs_node_T*)

Creates a directory at path in filesystem and returns a pointer to it. The directory in path needs to exist and the name of the new directory (after the last /) needs to not exist.

vfs_directory_delete(directory) - function (void) [not implemented yet]

Deletes a directory.

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(filesystem, archive) - function (void)

This will unpack a USTAR-archive (archive) at filesystem's root.

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)
initialized bool Indicates whether the renderer is initialized or not
blocked bool Blocking variable that is used for thread safety in graphics_renderer_update

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

region.h

The first 4 digits can be ignored, they are ignored by the MMU, but for clarity / readability reasons they are FFFF in the kernel space. See General Concepts / Memory Layout and General Concepts / Process Memory Isolation for more details.

Name Start Description
MEM_REGION_PROCESS 0x0000000000000000 This is the start of the process space
MEM_REGION_PROCESS_EXEC 0x0000010000000000 Every processes' executable will be mapped here
MEM_REGION_PROCESS_THREAD_BASE 0x0000010100000000 The start of the Thread Data regions
MEM_REGION_KERNEL 0xFFFF800000000000 This is the start of the kernel space
MEM_REGION_KERNEL_STACK_DUMMY 0xFFFFF00000000000 This area is used when preparing a threads' stack
MEM_REGION_KERNEL_HEAP 0xFFFFF80000000000 The kernels' heap begins here
MEM_REGION_KERNEL_THREAD_BASE 0xFFFFFF0000000000 The kernel processes' Thread Data regions
MEM_REGION_KERNELEXEC 0xFFFFFFFF80000000 The kernel executable is mapped here

MEM_REGION_THREAD_OFFSET - macro

This time the threads id specifies its offset in its processes' Thread Data region.

KERNEL_START_ADDRESS - macro

Returns the address of _kernel_start.

KERNEL_END_ADDRESS - macro

Returns the address of _kernel_end.

_kernel_start - global variable

This symbol is inserted by the linker at the beginning of the kernel. To access its value use the KERNEL_START_ADDRESS macro.

_kernel_end - global variable

This symbol is inserted by the linker at the end of the kernel. To access its value use the KERNEL_END_ADDRESS macro.

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_dump_info(page_map) - function (void)

Logs information about page_map, including a map of all mapped regions. A region is a block of continuously mapped 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.

stack.h

stack_dump_call_info(rip, symbol) - function (void)

Logs information about a call. Give this function the rip of the call and the related symbol, to make it happy.

stack_trace_call_stack(rbp) - function (void)

Analyses the stack and recursively dumps information about all calls until it hits a call to _start.

platform

cpu.h

This header contains stuff directly related to the CPU.

OSDev Wiki: x86 CPU Registers

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

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

gdt_selector_E - enum

  • Null
  • Kernel Code - Readable
  • Kernel Data - Readable + Writable
  • User Null
  • User Code - Readable
  • User Data - Readable + Writable

gdt_descriptor_T - struct [packed]

Name Type Description
size uint16_t The tables size in bytes (-1)
offset uint64_t The virtual address, where the table lies in memory

gdt_entry_T - struct [packed]

Name Type Description
limit0 uint16_t Can be ignored in long mode
base0 uint16_t Can be ignored in long mode
base1 uint8_t Can be ignored in long mode
access uint8_t Specifies permissions (details in osdev wiki)
limit1_flags uint8_t The first 4 bits can be ignored and the last 4 specify flags
base2 uint8_t Can be ignored in long mode

gdt_T - struct [packed / page aligned]

Name Type Description
null gdt_entry_T The entry for GDT_SELECTOR_NULL
kernel_code gdt_entry_T The entry for GDT_SELECTOR_KERNEL_CODE
kernel_data gdt_entry_T The entry for GDT_SELECTOR_KERNEL_DATA
user_null gdt_entry_T The entry for GDT_SELECTOR_USER_NULL
user_code gdt_entry_T The entry for GDT_SELECTOR_USER_CODE
user_data gdt_entry_T The entry for GDT_SELECTOR_USER_DATA

g_default_gdt - global variable

The systems GDT.

gdt_init - function (void)

Populates and loads g_default_gdt. This will also set all the data segment registers to 0x10 (Kernel Data) and cs to 0x08 (Kernel Code).

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.

g_handling_interrupt - global variable

When the system isn't handling an interrupt this is set to 0. If this is greater than 0 the system is currently handling an interrupt,

idt_init() - function (void)

This function fills all the interrupt gates (handlers) into the IDT and loads it.

proc

The general processing structure is a bit more complex, so I've split the schematics into multiple parts.

Processes Schematic:

+----------------+
| Kernel Process | <----+
| [Threads]      |      |
+----------------+      |
         |           [Parent]
      [Childs]          |
         |     +--------+--------+
         v    /                   \
   +-----------+            +-----------+
   | Process 1 | --[Next]-> | Process 2 |
   | [Threads] | <-[Prev]-- | [Threads] | . . .
   +-----------+            +-----------+
         |                        |
      [Childs]                 [Childs]
         |                        |
         v                        v
       . . .                    . . .

Thread Schematics (processes view):

+---------+
| Process | <-------+
+---------+         |
     |          [Process]
 [Threads]          |
     |     +--------+--------+
     v    /                   \
+----------+                 +----------+
| Thread 1 | --[LocalNext]-> | Thread 2 |
|          | <-[LocalPrev]-- |          | . . .
+----------+                 +----------+

Thread schematics (schedulers view):

    [RunningThread]
           |
           v
      +----------+                  +----------+                  +----------+
+---> | Thread 1 | --[GlobalNext]-> | Thread 2 | --[GlobalNext]-> | Thread 3 | . . . ----+
| +-- |          | <-[GlobalPrev]-- |          | <-[GlobalPrev]-- |          | . . . <-+ |
| |   +----------+                  +----------+                  +----------+         | |
| |                                                                                    | |
| +------------------------------------[GlobalPrev]------------------------------------+ |
+--------------------------------------[GlobalNext]--------------------------------------+

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)
process process_T* The process, to which the thread belongs to
global_prev thread_T* The previous thread in the scheduling queue (should only be accessed by the scheduler!)
global_next thread_T* The next thread in the scheduling queue (should only be accessed by the scheduler!)
local_prev thread_T* The previous thread of process (should only be accessed by the scheduler!)
local_next thread_T* The next thread of process (should only be accessed by the scheduler!)
local_id uint32_t The threads id in its process

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.

process.h

MAX_THREADS_PER_PROCESS - macro

The maximum amount of threads a process can have. This limitation is just for the bitmap, the overall processing structure would be capable of processes with unlimited threads, in theory.

THREAD_ID_INVALID - macro

If process_get_thread_id returns this, the process can't spawn one more thread.

pid_t - typedef

A typedef for uint32_t, used for process identification. Such an identification number is also called pid.

processes_standard_E - enum

These are standard pids

  • None - This pid is invalid, like NULL is an invalid pointer
  • Kernel - The kernels' main process

process_T - struct

Name Type Description
name char[128] The processes' name
id pid_t The process-identification number
chunk void* A pointer to the chunk, where the process is stored in
chunk_id uint32_t The processes id inside of its chunk
page_map page_map_T* The processes page map.
executable elf_executable_T* The processes executable
num_threads uint32_t The amount of spawned threads, that belong to the process
threads void* A pointer to the processes' first thread
thread_ids bitmap_T This bitmap keeps track of the local thread ids the process has
parent process_T* The process, that spawned this process
childs process_T* A pointer to the processes' first child process
prev process_T* The previous process
next process_T* The next process

process_kernel_spawn(executable) - function (void)

Spawns the kernels' main process.

Warning: this should only be called once, when initializing the scheduler!

process_spawn(parent, name, executable, buffer) - function (pid_t)

Spawns a process named name as child of parent and returns its pid. The process gets its own page map with the mappings specified in executable. In order to apply these mappings, this function needs the buffer where the executable was loaded from.

process_get_thread_id(process) - function (int32_t)

Searches for a free thread id in the process. If it finds one it returns it, else it returns THREAD_ID_INVALID.

process_clear_thread_id(process, id) - function (void)

Frees the thread id and makes it request-able again.

process_kill_pid(pid) - function (void)

Resolves the pids process and performs process_kill on it.

process_kill(process) - function (void)

Kills process and all of its threads and child processes. This will also destruct the executable and page_map associated with process.

scheduler.h

scheduler_processes_chunk_T - struct

These chunks are a combination of static array and linked list. They store the process_T pointer for each valid pid_T.

Name Type Description
processes process_T** The array of process pointers
processes_bitmap bitmap_T If a bit in this bitmap is set, the processes entry with the same index is valid
num_free_pids uint32_t The amount of free slots in this chunk
prev scheduler_processes_chunk_T* The previous chunk
next scheduler_processes_chunk_T* The next chunk

scheduler_T - struct

Name Type Description
num_threads uint32_t Total amount of currently spawned threads
num_processes uint32_t Total amount of currently spawned processes
running_thread thread_T* A pointer to the currently running thread.
processes scheduler_processes_chunk_T* The first processes store chunk
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(boot_info) - function (void)

Initializes the scheduler and performs a scheduler_start kernel syscall. boot_info is needed in to spawn the kernels' main process. 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_dump_info(process, indent) - function (void)

This recursively lists information(pid, name, threads) for all child processes of process. indent is used intern for the recursive calls and should be set to 0 initially.

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_register_process(process) - function (pid_t)

Reqisters process and returns its pid.

scheduler_kill_process(process) - function (void)

Kills process and its threads and childs.

scheduler_get_process(pid) - function (process_T*)

Returns the process_T pointer that is associated with pid.

scheduler_get_current_thread() - function (thread_T*)

Returns a pointer to the currently running thread.

scheduler_get_current_process() - function (process_T*)

Returns a pointer to the currently running threads process.

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

Name Type Description
size uint32_t The size of buffer (in bytes)
size_bits uint32_t The amount of storable bits
buffer uint8_t* The buffer, where the bits are stored

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_INTERRUPTABLE_HALT_WHILE(a) - macro

This halts until a is true. Used when working with blocking variables in e.g. thread safe functions. To avoid deadlocks, this macro won't halt, while the system is handling an interrupt.

CORE_HALT_WHILE(a) - macro

This halts until a is true. Used when working with blocking variables in e.g. thread safe functions.

Warning: When a function containing this macro is used while handling an interrupt, this could cause deadlocks, think about using CORE_INTERRUPTABLE_HALT_WHILE instead.

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.

memory_hexdump(address, num) - function (void)

Logs num bytes from address as 8 byte rows. The data is represented in hexadecimal and ascii.

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.

string_is_char_text(chr) - function (bool)

Returns whether the char (chr) contains text(a-z, A-Z, 0-9, special chars) or not.

string_is_char_number(chr) - function (bool)

Returns whether the char (chr) is a number(0-9) or not.

string_is_char_alpha(chr) - function (bool)

Returns whether the char (chr) is alphanumeric(a-z, A-Z, 0-9) or not.

string_is_char_uppercase(chr) - function (bool)

Returns whether the char (chr) is uppercase(A-Z) or not.

string_is_char_lowercase(chr) - function (bool)

Returns whether the char (chr) is lowercase(a-z) or not.

symbol.h

symbol_type_E - enum

  • Function
  • Variable
  • Unknown

symbol_T - struct

Name Type Description
name string_t The name of the symbol (e.g. the name of the kernels entry symbol would be _start
type symbol_type_E The symbols type (elf types like File are of type Unknown)
address uint64_t The symbols address

symbol_resolve_from_name(symbols, num_symbols, name); - function (symbol_T*)

This searches symbols for a symbol with a matching name.

symbol_resolve_from_rip(symbols, num_symbols, rip); - function (symbol_T*)

Give it a list of symbols and an instruction pointer (rip) and it will return the symbol (function), where rip lays in.