ported data from nerdcult.net wiki

This commit is contained in:
antifallobst 2023-05-28 19:16:43 +02:00
parent 95f5b9156e
commit a6f4c7457d
66 changed files with 2161 additions and 0 deletions

48
build-instructions.md Normal file
View File

@ -0,0 +1,48 @@
# Build Instructions
**DISCLAIMER:** To build this project you need a linux system (WSL could work for windows users, but I haven't tested it)
First you need to clone the repository:
`git clone https://git.nerdcult.net/noxos/kernel`
then there are a few tools required, that need to be installed:
* **gcc** - compiler
* **ld** - linker
* **nasm** - assembler
* **cmake** - build system
* **xorriso** - ISO creation tools
* **qemu** - emulator
* **ovmf** - UEFI firmware
you can check if you have all tools installed, with `./build.sh check`.
There is a shell script to setup the workspace and compile.
Just run it using the following commands:
```bash
cd noxos
./build.sh
```
If the Bootloader ([limine](https://github.com/limine-bootloader/limine)) isn't downloaded yet, the script will download it.
The final ISO file will be written to the *build* directory.
To launch the ISO, run the `run.sh` script.
# BIOS Emulation
To emulate a BIOS system, you can run `./run.sh bios`.
# Debugging
## Logs
When running through the script, qemu is configured to write the kernels logs to *STDOUT* and a file called *noxos.log*.
## GDB
You can connect GDB to qemu.
To achieve this, run `./run.sh debug`.
QEMU will wait until you connect GDB to it as a 'remote' target on `localhost:1234`.
```bash
gdb build/cmake/kernel/kernel
target remote localhost:1234
c
```
(note, that the last to commands have to be 'executed' in gdb not in your shell)

66
codestyle.md Normal file
View File

@ -0,0 +1,66 @@
# Code Style
**Read this before contributing**
The following code style should be applied to the whole noxos codebase.
Pull Requests that don't follow this code style will be rejected.
# Naming conventions
- **structs** are suffixed with **_T**
- **typedefs** are suffixed with **_t**
- **enums** are suffixed with **_E**
- **global** variables are prefixed with **g_**
Everything is **snake_case**.
# Code readability
To make the code as readable and self explaining as possible, there are a few patterns that should be used.
## Avoid abbreviations
Give your variables, functions, etc. meaningfully names.
The times of 80-char wide screens are over, feel free to use that space :D
Example: `sym_at_idx` -> `get_symbol_at_index`
But you're not forced to (anything) write out everything.
For example the **Interrupt Descriptor Table** is simply referred to as **idt** (this abbreviation is btw standard).
## Avoid indentation nesting
In a perfect codebase, the maximum indention level would be 3.
The goal of NoxOS is not to be a perfect codebase, but please avoid huge indention nesting.
To achieve this, you can break parts into extra functions and/or use the early return pattern.
Example (definitely written 100% sober):
```c
bool likes_pigs (void* a) {
if (a != NULL) {
if (a == 0xACAB) {
return false;
}
}
return true;
}
```
->
```c
bool likes_pigs (void* a) {
if (a == NULL) { return true; }
if (a != 0xACAB) { return true; }
return false;
}
```
## align declarations
Please align declarations, definitions, etc like in the example:
```c
char am_i_a_variable = '?';
uint64_t number = 0;
bool i_waste_bits = true;
```
## namespaces
Unlike C++, C has no namespaces.
To achieve the goal of namespaces, please prefix your functions, structs, etc. with the "namespace" they are in.
Example: `out_byte` -> `io_out_byte`

4
kernel/README.md Normal file
View File

@ -0,0 +1,4 @@
# Codebase
Every header in the codebase has its own page.
The structure is identically to the codebase directory structure.

3
kernel/boot/README.md Normal file
View File

@ -0,0 +1,3 @@
# boot
boot process control and bootloader communication

View File

@ -0,0 +1,13 @@
# 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_ |

4
kernel/boot/limine.h.md Normal file
View File

@ -0,0 +1,4 @@
# 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).

3
kernel/drivers/README.md Normal file
View File

@ -0,0 +1,3 @@
# drivers
everything from the graphics driver, to the FS drivers

View File

@ -0,0 +1,5 @@
# acpi
This directory holds everything, that is needed to talk to the ACPI (_Advanced Configuration and Power Interface_).
OSDev Wiki: [ACPI](https://wiki.osdev.org/ACPI)

View File

@ -0,0 +1,5 @@
# acpi.h
# `acpi_init(boot_info)` - function (void)
Initializes the ACPI implementation.
This function needs to be called once during the kernel initialization.

View File

@ -0,0 +1,33 @@
# rsdp.h
The infrastructure to parse the _Root System Description Pointer_ Table.
OSDev Wiki: [RSDP](https://wiki.osdev.org/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](https://nerdcult.net/projects/noxos/docs/codebase/drivers/acpi/rsdp.h/#rsdp_descriptor_v1_t---struct-packed) | 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 |
# `rsdp_descriptor_v1_verify(descriptor)` - function (bool)
Verifies the integrity of **_descriptor_**.
# `rsdp_descriptor_v2_verify(descriptor)` - function (bool)
Verifies the integrity of **_descriptor_**.

View File

@ -0,0 +1,3 @@
# elf
ELF executable parser.

View File

@ -0,0 +1,32 @@
# elf.h
The ELF-parsers' heart.
#### `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_**.

View File

@ -0,0 +1,53 @@
# header.h
Definitions needed to parse the ELF Header.
# `elf_target_architecture_E` - enum
Field in elf_header_T: **identity[4]**
# `elf_endianness_E` - enum
Field in elf_header_T: **identity[5]**
# `elf_sysabi_E` - enum
Field in elf_header_T: **identity[7]**
# `elf_object_type_E` - enum
Field in elf_header_T: **type**
# `elf_instruction_set_E` - enum
Field in elf_header_T: **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.

View File

@ -0,0 +1,18 @@
# mapping.h
Handles the memory mappings that an ELF file can request.
# `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.

View File

@ -0,0 +1,34 @@
# section.h
Definitions needed to parse the elf section headers.
# `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.

View File

@ -0,0 +1,28 @@
# segment.h
Definitions needed to parse the elf program/segment headers.
# `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.

View File

@ -0,0 +1,26 @@
# symbol.h
Stuff needed to extract symbols from an ELFs `.symtab` section.
# `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) | |

View File

@ -0,0 +1,3 @@
# fs
Filesystem drivers.

View File

@ -0,0 +1,22 @@
# ramfs.h
Specific driver for RAMFS.
**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.

View File

@ -0,0 +1,42 @@
# ustar.h
Specific driver for USTAR.
**Warning:** This is a filesystem specific driver, by this it should only be accessed by the VFS.
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](https://wiki.osdev.org/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 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_ |

146
kernel/drivers/fs/vfs.h.md Normal file
View File

@ -0,0 +1,146 @@
# vfs.h
**VFS** stands for _Virtual File System_ and is an abstraction layer,
that merges all mounted filesystems into one big virtual filesystem.
It provides a general API for dealing with files, and handles all filesystem 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] [Childs]
| |
v v
. . . . . .
```
If a node is accessed it is linked as the first node in the childs order, to make the 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 stores data 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.
The initialization process includes, that the kernel unpacks the initial ramdisk to `/initrd/`
# `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.

View File

@ -0,0 +1,4 @@
# graphics
The graphics renderer is just for the development time.
As soon as the kernel supports modules, the renderer will be a module, or even an userspace program.

View File

@ -0,0 +1,30 @@
# color.h
Provides basic color definitions and operations.
# `color_palette_E` - enum
Indexes for g_color_palette. The color palette was designed by [gogh](https://gogh-co.github.io/Gogh/).
- **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.

View File

@ -0,0 +1,15 @@
# font.h
Basic bitmap font definitions.
# `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 globally usable 8x8 font.

View File

@ -0,0 +1,18 @@
# framebuffer.h
Framebuffer definitions.
#### `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 |

View File

@ -0,0 +1,88 @@
# renderer.h
The renderer is maybe a bit over-engineered and slow.
The renderer has a stack of buffers, which act like canvases.
You can create, move, hide and show buffers.
#### `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 | g_color_palette* | 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_layer_E* | 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_T 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.

79
kernel/drivers/pci.h.md Normal file
View File

@ -0,0 +1,79 @@
# pci.h
PCI bus stuff.
# Class and subclasses enums
All these enums are not documented in here, because they are basically just the definitions matching the PCI device main and sub classes.
A [list of these (sub)classes](https://wiki.osdev.org/PCI#Class_Codes) can be found in the osdev wiki.
# `pci_device_header_T` - struct
| Name | Type | Description |
|-----------------|----------|------------------------------------------------------------------------------------------------------------------------------|
| vendor_id | uint16_t | The Device manufacturers ID number |
| device_id | uint16_t | The devices ID number |
| command_reg | uint16_t | Provides control over a device's ability to generate and respond to PCI cycles. |
| status | uint16_t | The status register PCI bus related events |
| revision_id | uint8_t | The devices revision ID number |
| progif | uint8_t | Programming Interface Byte. This can specify special programming interfaces. |
| subclass | uint8_t | The special function that the device performs. |
| main_class | uint8_t | The general function class, which the device operates in. |
| cache_line_size | uint8_t | Specifies the system cache line size in 32-bit units. |
| latency_timer | uint8_t | Specifies the latency timer in units of PCI bus clocks. |
| header_type | uint8_t | Specifies whether the device is a general device or a bridge. This affects the data representation that follows this header. |
| bist | uint8_t | Built-In-Self-Test status and control. |
# `pci_header_0_T` - struct
| Name | Type | Description |
|----------------------------|---------------------|-------------------------------------------------------------------------------------------------|
| header | pci_device_header_T | The devices base header |
| bar0 | uint32_t | Base address 0 |
| bar1 | uint32_t | Base address 1 |
| bar2 | uint32_t | Base address 2 |
| bar3 | uint32_t | Base address 3 |
| bar4 | uint32_t | Base address 4 |
| bar5 | uint32_t | Base address 5 |
| cardbus_cis_pointer | uint32_t | Used by devices that share silicon between CardBus and PCI. |
| subsystem_vendor_id | uint16_t | idk, but probably not that important |
| subsystem_id | uint16_t | idk, but probably not that important |
| expansion_rom_base_address | uint32_t | An optional max 16MB ROM BAR. |
| capabilities | uint8_t | An offset in the devices config space that points to a linked list of implemented capabilities. |
| reserved | uint8_t[7] | Reserved. |
| interrupt_line | uint8_t | The devices interrupt IRQ. |
| interrupt_pin | uint8_t | The devices interrupt pin. |
| min_grant | uint8_t | Specifies the burst period length, in 1/4 microsecond units, that the device needs. |
| max_latency | uint8_t | Specifies how often the device needs access to the PCI bus (in 1/4 microsecond units). |
# `pci_device_T` - struct
| Name | Type | Description |
|--------|----------------------|-------------------------|
| header | pci_device_header_T* | The devices base header |
| prev | pci_device_T* | The previous device. |
| next | pci_device_T* | The next device. |
# `pci_manager_T` - struct
| Name | Type | Description |
|---------|---------------|-----------------------------------------------|
| devices | pci_device_T* | The linked list of devices connected via PCI. |
# `pci_init()` - function (void)
Initializes the global PCI manager and enumerates the PCI bus.
This needs to be called after `acpi_init`, because it uses the _MCFG_ table.
# `pci_manager_add_device(header*)` - function (void)
Adds a device to the global PCI manager.
This should only be called by the PCI enumerator.
# `pci_manager_remove_device(device*)` - function (void)
Removes a device from the global PCI manager, and frees it resources in the manager.
# `pci_manager_find_device(class, subclass, progif)` - function (pci_device_T*)
Returns the first device with **_class_**, **_subclass_** and **_progif_** that is defined in the global PCI manager.
# `pci_manager_dump_devices()` - function (void)
Logs a list of all registered PCI devices.
# `pci_get_subclass_string(class, subclass)` - function (string_t)
Returns the Name of **_class_** + **_subclass_**.
# `pci_get_vendor_string(vendor_id)` - function (string_t)
Returns the name of the vendor.

View File

@ -0,0 +1,4 @@
# PS/2
The PS/2 standard for communicating with keyboards and mice.

View File

@ -0,0 +1,38 @@
# controller.h
Communicates with the PS/2 controller chip.
**Warning:** This whole code needs to be reworked!
# `PS2_CONTROLLER_DATA_PORT` - macro
The data transmission IO port.
# `PS2_CONTROLLER_STATUS_PORT` - macro
The Status register IO port.
# `PS2_CONTROLLER_COMMAND_PORT` - macro
The command transmission IO port.
# `PS2_CONTROLLER_TIMEOUT` - macro
The amount of cycles the system waits for the controller before cancelling an operation.
# `ps2_controller_init()` - function (void)
Initializes the PS/2 controller and devices.
At the moment it trusts very much stuff blindly.
This needs to be changed.
The osdev wiki has a list of stuff to do while initializing PS/2.
# `ps2_controller_command(command)` - function (uint8_t)
Sends the PS/2 controller a command.
# `ps2_controller_command_with_data(command, data)` - function (uint8_t)
Sends the PS/2 controller a command that needs additional data on the data port.
# `ps2_controller_wait_until_ready_for_input()` - function (bool)
Halts until the status register says that there can be data read, or the timeout is reached.
# `ps2_controller_wait_until_ready_for_output()` - function (bool)
Halts until the status register says that there can be data written, or the timeout is reached.
# `ps2_controller_read_data()` - function (uint8_t)
Reads data from the PS/2 controller.

View File

@ -0,0 +1,24 @@
# keyboard.h
The driver for PS/2 keyboards.
# `ps2_keyboard_command_E` - enum
Commands that can be issued to the keyboard.
# `ps2_keyboard_response_E` - enum
Responses that a keyboard can reply on a command.
# `ps2_keyboard_command(command, data)` - function (uint8_t)
Sends a command to the keyboard.
# `ps2_keyboard_init()` - function (void)
Initializes the keyboard driver.
This should only be called by the PS/2 controller init function.
# `ps2_keyboard_read()` - function (void)
Reads a key from the keyboard and writes it to the tty.
This should be called when IRQ1 is fired.
# `ps2_keyboard_command_to_string(command)` - function (string_t)
Returns a matching string for **_command_** in `ps2_keyboard_command_E`.

View File

@ -0,0 +1,4 @@
# scancodes.h
This holds definitions on which key is mapped to which scancode.
Currently only scancode set 1 is supported.

View File

@ -0,0 +1,5 @@
# time
Drivers for time related hardware.
Tick. Tack. Tick. Tack.

View File

@ -0,0 +1,14 @@
# pit.h
Communicates with the _Programmable Interval Timer_.
#### `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, that noxos 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.

45
kernel/drivers/tty.h.md Normal file
View File

@ -0,0 +1,45 @@
# tty.h
The TTY is a virtual device, that acts as a text IO layer.
It prints keyboard input and process output to the screen.
The keyboard input can be directed to a processes stdin and the processes stdout can be printed to the screen.
Schematic:
```
+-------------------+
| Graphics Renderer |
+-------------------+
^ ^
| | +----------+ +-----------------+
| | | TTY | | Process (shell) |
| | | | | |
| |--|-[Input]--|-<----|-[stdout] |
| | | | |
-[Keyboard]----->-|-[Output]-|->----| [stdin] |
+----------+ +-----------------+
```
# `tty_T` - struct
| Name | Type | Description |
|-----------------|--------------------|---------------------------------------------------------------------------------------------------|
| graphics_buffer | graphics_buffer_T* | The graphics buffer where all output will be rendered to. |
| output | pipe_T* | Points to the linked processes stdin pipe. |
| input | pipe_T | This pipes content will be rendered to the screen. The linked processes stdout should point here. |
| cursor | position_T | The cursors position. |
| color | color_argb_T | The current drawing color. |
# `tty_init()` - function (void)
Initializes `g_tty`.
# `tty_update()` - function (void)
Renders the text from the ttys _input pipe_ onto the screen.
This will be automatically called when writing into the ttys _input pipe_.
# `tty_write(string)` - function (uint32_t)
Renders the text from **_string_** onto the screen.
This also writes **_string_** into the ttys _output pipe_.
# `g_tty` - global variable
This is the systems TTY.
Currently, there is no support planned for multiple ttys.

3
kernel/mm/README.md Normal file
View File

@ -0,0 +1,3 @@
# mm
Memory management.

44
kernel/mm/heap.h.md Normal file
View File

@ -0,0 +1,44 @@
# heap.h
Dynamic memory allocator.
# `heap_segment_T` - struct
This header lies in memory, directly before the accessible buffer of the related segment.
| Name | Type | Description |
|-------|-----------------|------------------------------------------------------------------------------------------|
| magic | uint32_t | An out of bounds memory write would override this, what would indicate a heap corruption |
| size | uint64_t | The size of the segment |
| free | bool | If this is set, the segment is reclaimable |
| next | heap_segment_T* | The next segment in the heap |
| prev | heap_segment_T* | The previous segment in the heap |
# `heap_T` - struct
| Name | Type | Description |
|--------------|-----------------|-------------------------------------------------------------|
| start | void* | The start of the heaps segment pool |
| end | void* | The current end of the heaps segment pool (growing upwards) |
| last_segment | heap_segment_T* | The current last segment in the heaps segment pool |
# `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 entire heap to find a free segment, it is kinda 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.

View File

@ -0,0 +1,9 @@
# memory_map.h
Functions to parse the memory map provided by the bootloader.
# `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.
The total amount of memory is returned in bytes.

46
kernel/mm/page_frame.h.md Normal file
View File

@ -0,0 +1,46 @@
# page_frame.h
Physical memory management.
# `PFRAME_SIZE` - macro
The size of one page (4KB).
# `page_frame_manager_T` - struct
| Name | Type | Description |
|-------------------|----------|------------------------------------------------------------------------------------|
| free_memory | uint64_t | The amount of free/usable memory in bytes |
| reserved_memory | uint64_t | The amount of reserved memory in bytes |
| used_memory | uint64_t | The amount of memory used by noxos in bytes |
| page_bitmap_index | uint64_t | The index to the first free page |
| page_bitmap | bitmap_T | A huge bitmap, that stores, which pages are claimable and which are already in use |
| blocked | bool | Thread safety guard |
#### `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 `memory_allocate`.
#### `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 `memory_free`.
#### `pframe_free_multi(address, n)` - function (void) [Thread Safe]
Frees the page at the given address, plus *n* pages after that page.

95
kernel/mm/page_map.h.md Normal file
View File

@ -0,0 +1,95 @@
# page_map.h
Virtual memory management.
Virtual memory spaces are a bit more tricky than the physical memory space.
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.
# `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.
The page map is populated with the mappings for the kernel executable and the loaded IDT.
# `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` register.
# `page_map_load(page_map*)` - function (void) [ASM implementation]
Loads the given page map.
To achieve this, it writes the `cr3` register.
# `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 automatically.
# `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_check_memory(page_map*, virtual_address)` - function (bool)
Check if there is a page mapped at **_virtual_address_** in **_page_map_**.
Returns _true_ if there is a page mapped and _false_ if 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.

35
kernel/mm/region.h.md Normal file
View File

@ -0,0 +1,35 @@
# region.h
Virtual memory space layout.
The first 4 digits of an address can be ignored, they are also ignored by the MMU,
but for clarity / readability reasons they are `FFFF` in the kernel space.
See _General Concepts / Memory Layout_(todo: add reference) and _General Concepts / Process Memory Isolation_(todo: add reference) 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 KERNEL_START_ADDRESS.
# `_kernel_end` - global variable
This symbol is inserted by the linker at the end of the kernel.
To access its value use KERNEL_END_ADDRESS.

10
kernel/mm/stack.h.md Normal file
View File

@ -0,0 +1,10 @@
# stack.h
Functions to extract data from the call stack.
# `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` or fails to resolve the calls symbol name.

View File

@ -0,0 +1,3 @@
# platform
Platform specific code.

45
kernel/platform/cpu.h.md Normal file
View File

@ -0,0 +1,45 @@
# cpu.h
Stuff related directly with 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
- **cs** - Segment selector of the associated IDT descriptor
- **flags** - The CPU's FLAGS register, a status bitmap -> cpu_flags_E
- **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.
Such a state 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**

View File

@ -0,0 +1,15 @@
# exceptions.h
CPU-exception handling.
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 CPU-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.

49
kernel/platform/gdt.h.md Normal file
View File

@ -0,0 +1,49 @@
# gdt.h
Definitions to handle segments (_Global Descriptor Table_).
Segmentation is deprecated and is only needed for backwards compatibility.
OSDev Wiki: [Global Descriptor Table](https://wiki.osdev.org/GDT)
# `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).

View File

@ -0,0 +1,21 @@
# interrupts.h
The infrastructure to handle x86 interrupts.
# `idt_register_T` - struct [packed]
This struct is very similar to gdt_descriptor_T.
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.

54
kernel/proc/README.md Normal file
View File

@ -0,0 +1,54 @@
# 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]--------------------------------------+
```

View File

@ -0,0 +1,55 @@
# file_descriptor.h
Processes have no direct access to the VFS, instead they have file descriptors.
File descriptors are numbers that represent files.
They are mostly used in file related syscalls.
# `FILE_DESCRIPTOR_ARRAY_CHUNK_SIZE` - macro
The amount of file descriptors a `file_descriptor_array_chunk_T` (reference needed) can hold.
# `file_descriptor_t` - typedef
This is a typedef of `ìnt32_t` and represents a file descriptor.
# `std_file_descriptors_E` - enum
These are reserved file descriptors.
* `FILE_DESCRIPTOR_INVALID` - This represents an invalid file descriptor, just like NULL is an invalid pointer.
* `FILE_DESCRIPTOR_STDOUT` - This represents the file descriptor that is used, when writing to the Standard-Output stream.
* `FILE_DESCRIPTOR_STDIN` - This represents the file descriptor that is used, when reading from the Standard-Input stream.
* `FILE_DESCRIPTOR_STDERR` - This represents the file descriptor that is used, when writing to the Standard-Error stream.
# `file_descriptor_array_chunk_T` - struct
| Name | Type | Description |
|--------|------------------------------------------------|----------------------------------------------------------------------------------------|
| prev | file_descriptor_array_chunk_T* | The previous chunk |
| next | file_descriptor_array_chunk_T* | The next chunk |
| lookup | vfs_node_T* [FILE_DESCRIPTOR_ARRAY_CHUNK_SIZE] | The array of file pointers, that the file descriptors reference |
| bitmap | bitmap_T | This bitmap indicates which file descriptors of this chunk are free nad which are used |
| amount | uint32_t | The amount of file descriptors which the chunk actually holds |
# `file_descriptor_array_T` - struct
| Name | Type | Description |
|------------|--------------------------------|------------------------------|
| base_chunk | file_descriptor_array_chunk_T* | The first chunk in the array |
# `file_descriptor_request(fd_array, node)` - function (file_descriptor_t)
Requests and returns new file descriptor, which will be linked with _**node**_.
# `file_descriptor_resolve(fd_array, fd)` - function (vfs_node_T*)
Returns the VFS node, with which _**fd**_ is linked.
# `file_descriptor_free(fd_array, fd)` - function (void)
Frees _**fd**_ and marks it as reclaimable.
# `file_descriptor_array_alloc()` - function (file_descriptor_array_T*)
Allocates a new chunked file descriptor array.
# `file_descriptor_array_destruct(fd_array)` - function (void)
Destructs _**fd_array**_ and all its resources, like e.g. chunks.
# `file_descriptor_array_chunk_alloc(prev)` - function (file_descriptor_array_chunk_T*)
Allocates a new chunk for a file descriptor array.
_**prev**_ has to be the last chunk of an array or NULL when the chunk is the base chunk for a fresh file descriptor array.
# `file_descriptor_array_chunk_destruct(chunk)` - function (void)
Destructs _**chunk**_ and all its resources.

44
kernel/proc/pipe.h.md Normal file
View File

@ -0,0 +1,44 @@
# pipe.h
Pipes are data streams between processes.
# `PIPE_MAX_SENDERS` - macro
The maximum amount of processes, which can write data to one pipe.
# `PIPE_STD_STREAM_SIZE` - macro
The size of a pipes stream buffer, in bytes.
# `pipe_T` - struct
| Name | Type | Description |
|----------------|-------------------------------|-----------------------------------------------------------------------------------------|
| stream | stream_T* | The stream, where the pipes data is saved. |
| receiver | process_T* | The process which is allowed to read from the pipe. |
| senders | process_T* [PIPE_MAX_SENDERS] | The processes which are allowed to write to the pipe. |
| senders_bitmap | bitmap_T | Indicates which fields of _senders_ are currently used. |
| on_write | void function pointer | If this is not NULL, this function will be called when data is written into the stream. |
# `pipe_init(pipe*, receiver*, (*on_write)())` - function (void)
Initializes **_pipe_** and allocates a stream for it.
# `pipe_destruct(pipe*)` - function (void)
Destructs **_pipe_** and frees all of its resources.
# `pipe_add_sender(pipe*, sender*)` - function (bool)
Adds **_sender_** to the sender list of **_pipe_**.
Returns if the operation was successful.
# `pipe_remove_sender(pipe*, sender*)` - function (void)
Removes **_sender_** from the sender list of **_pipe_**.
# `pipe_write(pipe*, buffer_in*, n)` - function (uint64_t)
Writes at most **_n_** bytes from **_buffer_in_** into **_pipe_**.
Returns the amount of bytes that were actually written.
If the amount of actually written bytes is smaller than **_n_** all sender processes of **_pipe_** will be _PIPE_OUT blocked_.
If the receiver is _PIPE_IN blocked_ the blocker will be removed.
# `pipe_read(pipe*, buffer_out*, n)` - function (uint64_t)
Reads at most **_n_** bytes from **_buffer_in_** into **_pipe_**.
Returns the amount of bytes that were actually read.
If the amount of actually read bytes is smaller than **_n_** the receiver process of **_pipe_** will be _PIPE_IN blocked_.
If the senders are _PIPE_OUT blocked_ the blockers will be removed.

84
kernel/proc/process.h.md Normal file
View File

@ -0,0 +1,84 @@
# process.h
Processes are the containers, in which threads are organized.
# `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_blockers_E` - enum
- **PIPE_OUT** - This blocker becomes activated, when the process tries to write to a pipe, but the pipe has not enough space for the data. It becomes deactivated, when the pipe has space to fit the rest of the data.
- **PIPE_IN** - This blocker becomes activated, when the process tries to read from a pipe, but the pipe has not the requested amount of data. It becomes deactivated, when the pipe contains data again.
# `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 |
| fd_array | file_descriptor_array_T* | Contains the open file descriptors of the process. |
| stdout | pipe_T* | Pointer to the pipe, where **stdout** writes to. |
| stdin | pipe_T | The pipe, where **stdin** reads from. |
| stderr | pipe_T* | Pointer to the pipe, where **stderr** writes to. |
| waiting | bitmap_T | A bitmap of process blockers. If this is greater 0 the process is blocked by something. |
| 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(reference needed) | 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_pause_pid(pid)` - function (void)
Resolves the pids process and performs `process_pause` on it.
# `process_pause(process)` - function (void)
Pauses **_process_** and all of its threads.
# `process_start_pid(pid)` - function (void)
Resolves the pids process and performs `process_start` on it.
# `process_start(process)` - function (void)
Starts **_process_** and all of its threads.
# `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_**.

View File

@ -0,0 +1,84 @@
# scheduler.h
The scheduler is responsible for switching between threads and controlling processes.
#### `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(reference needed) | 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 `nx_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 `nx_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_pause_process(process)` - function (void)
Pauses **_process_** and its threads.
#### `scheduler_start_process(process)` - function (void)
Starts **_process_** and its threads.
#### `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.

43
kernel/proc/thread.h.md Normal file
View File

@ -0,0 +1,43 @@
# thread.h
Threading infrastructure.
# `thread_T` - struct
| Name | Type | Description |
|-------------|--------------|--------------------------------------------------------------------------------------------------|
| state | cpu_state_T* | The last saved state of the thread ( -> _context switching_(reference needed)) |
| 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 thread_start().
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 thread_start().
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.

3
kernel/utils/README.md Normal file
View File

@ -0,0 +1,3 @@
# utils
Basic utilities that are used almost everywhere.

29
kernel/utils/bitmap.h.md Normal file
View File

@ -0,0 +1,29 @@
# bitmap.h
Utils to handle 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.

19
kernel/utils/core.h.md Normal file
View File

@ -0,0 +1,19 @@
# 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, consider using CORE_INTERRUPTABLE_HALT_WHILE instead.
# `CORE_HALT_FOREVER` - macro
This halts forever and warns about this in the log.

15
kernel/utils/io.h.md Normal file
View File

@ -0,0 +1,15 @@
# 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.

18
kernel/utils/logger.h.md Normal file
View File

@ -0,0 +1,18 @@
# logger.h
Functionalities to write logs to QEMU's serial port.
# `DEBUG(string, ...)` - macro
calls `log` with log_level _Debug_.
Puts filename and line number in front of the format text.
# `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.

35
kernel/utils/math.h.md Normal file
View File

@ -0,0 +1,35 @@
# 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
This describes a position in 2D space.
| 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.

32
kernel/utils/memory.h.md Normal file
View File

@ -0,0 +1,32 @@
# 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 marks it claimable again , 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.

8
kernel/utils/panic.h.md Normal file
View File

@ -0,0 +1,8 @@
# panic.h
ahhhh - the kernel is burning!
# `panic(state, message)` - function (void)
This prints out the error message, a stack backtrace and a register dump.
After that, the kernel halts forever.
This function is called, when a fatal error occurs

View File

@ -0,0 +1,55 @@
# 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.

27
kernel/utils/stream.h.md Normal file
View File

@ -0,0 +1,27 @@
# stream.h
Streams are cycled buffers, that have __one__ input and __one__ output.
# `stream_T` - struct
| Name | Type | Description |
|--------------|----------|----------------------------------------------------------------------------------|
| buffer | uint8_t* | The buffer where all the streams data is stored in. |
| size | uint32_t | The size of _buffer_ in bytes. |
| pos_sender | uint32_t | The senders (input) position in the buffer. |
| pos_receiver | uint32_t | The receivers (output) position in the buffer. This is always behind the sender. |
# `stream_alloc(size)` - function (stream_T*)
Allocates and returns a new stream, with a buffer of size **_size_**.
# `stream_destruct(stream)` - function (void)
Frees the resources of a stream.
# `stream_write(stream, buffer_in, n)` - function (uint32_t)
Writes at most **_n_** bytes from **_buffer_in_** into **_stream_**.
Returns the amount of bytes, that where actually written into the stream.
# `stream_read(stream, buffer_out, n)` - function (uint32_t)
Reads at most **_n_** bytes from **_stream_** into **_buffer_out_**.
Returns the amount of bytes, that where actually read from the stream.

90
kernel/utils/string.h.md Normal file
View File

@ -0,0 +1,90 @@
# string.h
Basic functions for string manipulation.
#### `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.

22
kernel/utils/symbol.h.md Normal file
View File

@ -0,0 +1,22 @@
# symbol.h
Basic functions to define and resolve symbols.
#### `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.

30
roadmap.md Normal file
View File

@ -0,0 +1,30 @@
# Roadmap
- [x] Bootable system
- [x] Logger
- [x] Page frame manager
- [x] Interrupts
- [x] Page maps
- [x] Heap
- [x] Format strings
- [x] Graphics Renderer
- [x] Text
- [x] Double buffering
- [x] Buffer requests
- [x] Panic Screen
- [x] Register dump
- [x] Stack tracing
- [x] Scheduler
- [x] (Kernel) Threads
- [x] Ramdisk
- [x] USTAR
- [x] RAMFS
- [x] VFS
- [x] ELF loading
- [x] Processes
- [X] Keyboard input (ps/2 int)
- [ ] Shell
- [X] JSON parser for system config
- [ ] FAT32
- [ ] Text Editor
- [ ] TCC

80
sysabi.md Normal file
View File

@ -0,0 +1,80 @@
# SysABI
Syscalls are a way for programms to communicate with the kernel.
To perform a syscall, you need to populate the following registers:
| Register | Value |
|----------|-----------------|
| rax | The syscalls ID |
| rdi | Argument 1 |
| rsi | Argument 2 |
| rdx | Argument 3 |
| rcx | Argument 4 |
---
The following calls should be implemented to some degree in noxos 1.0.
# Categories
## Files
| ID | Name | Description | Arg1 | Arg2 | Arg3 | Arg4 | Result | Status |
|--------|--------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------|--------|------|-------------|--------|-------------|
| 0x0101 | `nx_fopen` | Opens the file at the _len_ bytes long path _path_ and writes a file descriptor to the referenced file into _fd_ (which needs to be a pointer). | path | len | *fd | | status | Implemented |
| 0x0102 | `nx_fclose` | Closes the file which is indicated by descriptor _fd_. This should always be called, otherwise the OS will have to unload files without knowledge of usage. | fd | | | | status | Implemented |
| 0x0103 | `nx_fread` | Reads at most _n_ bytes from a file given as descriptor _fd_ and at a specific position _offset_ into a given memory region _mem_. | fd | offset | mem | n | status | Implemented |
| 0x0104 | `nx_fwrite` | Writes _n_ bytes from a given memory region _mem_ into the file referenced by _fd_ at the given position _offset_, not inserting but either overwriting existing content or appending to the file. | fd | offset | mem | n | status | Implemented |
| 0x0105 | `nx_fdelete` | Completely delete the file or folder at a given path _path_, which is _len_ bytes long. | path | len | | | status | Implemented |
| 0x0106 | `nx_flist` | List all files and/or directories at the _len_ bytes long path _path_ and write their NULL-separated names into _mem_. When _mem_ is NULL, _needed_mem_ (which needs to be a pointer to an 32-bit integer) is filled with the appropriate value. | path | len | mem | *needed_mem | status | Implemented |
| 0x0107 | `nx_finfo` | Writes the information about the files attribute _attr_ into mem. Types of attributes are described below. | fd | attr | mem | | status | N/A |
**Note:** The _len_ argument of paths isn't used at the moment, rather than using _len_, _path_ needs to be Null-Terminated.
## Memory
| ID | Name | Description | Arg1 | Arg2 | Arg3 | Arg4 | Result | Status |
|--------|-------------|-------------------------------------------------------------------------------------------------------------------------------------------------|------|------|-------|------|--------|-------------|
| 0x0201 | `nx_mmap` | Maps _n_ 4KB pages to address _addr_. You can provide a bitmap of flags with the _flags_ argument. Which flags can be used, is described below. | addr | n | flags | | status | Implemented |
| 0x0202 | `nx_munmap` | Unmaps _n_ 4KB pages at address _addr_. | addr | n | | | status | Implemented |
## Processes
| ID | Name | Description | Arg1 | Arg2 | Arg3 | Arg4 | Result | Status |
|-----|------|-------------|------|------|------|------|--------|--------|
| | | | | | | | | |
## Runtime / Dynamic Linker
| ID | Name | Description | Arg1 | Arg2 | Arg3 | Arg4 | Result | Status |
|-----|------|-------------|------|------|------|------|--------|--------|
| | | | | | | | | |
## Compatibility & Safety
| ID | Name | Description | Arg1 | Arg2 | Arg3 | Arg4 | Result | Status |
|-----|------|-------------|------|------|------|------|--------|--------|
| | | | | | | | | |
## Kernel
The kernel syscalls can only be called from the kernels main process [link to processing article needed].
If another process calls them, they will just return without doing anything.
| ID | Name | Description | Arg1 | Arg2 | Arg3 | Arg4 | Result | Status |
|-----|------|-------------|------|------|------|------|--------|--------|
| | | | | | | | | |
---
## Appendixes
### Types of file attributes
| ID | Name | Description | Data Type | Status |
|-----|---------------|--------------------------------------------|-----------|--------|
| 0 | `permissions` | Who has what permission to access the file | uint16_t | N/A |
| 1 | `size` | The files size in bytes | uint64_t | N/A |
### Memory mapping flags
| Bit | Name | Description |
|-----|-----------|-------------------------------------------|
| 0 | `write` | Enables write access to the mapped pages. |
| 1 | `no_exec` | Prevents execution of the mapped pages. |