Vystem 0.2

This commit is contained in:
2026-05-27 19:34:54 +02:00
parent a43c08b893
commit d238606b75
372 changed files with 51320 additions and 83217 deletions

116
docs/shelter/cpu/ap.md Normal file
View File

@@ -0,0 +1,116 @@
# Application Processors subsystem
## Introduction
The Application Processors (AP) subsystem is responsible for managing SMP-related CPU abstractions, including CPU descriptors and per-CPU data structures. It also handles the preparation and bootstrap sequence required to bring up secondary processors. This subsystem is defined inside `shelter/lib/include/cpu/ap.h` and implemented inside `shelter/lib/include/cpu/ap.c`.
## Overview
This subsystem is responsible for three things:
- defining the structures used to describe CPUs and storing per-CPU metadatas
- preparing all necessary structures in order to start the APs
- starting the APs
### CPU structures
The AP subsystem defines two structures related to CPU metadatas:
**`sh_ap_CPU_STRUCT`:**
``` C
#pragma pack(1)
typedef struct {
sh_page_VIRTUAL_ADDRESS c_entry_point_stack_top_va;
sh_uint32 lapic_id;
sh_uint16 tss_selector;
sh_ap_PER_CPU *per_cpu;
} sh_ap_CPU_STRUCT;
#pragma pack()
```
The CPU struct is mainly used for the AP trampoline: it contain informations specific to each AP that will allow them to reach the C entry point. It also contains a pointer to the per-CPU struct of each CPU.
**`sh_ap_PER_CPU`:**
``` C
#pragma pack(1)
typedef struct {
sh_uint64 cpu_id;
sh_uint32 bytes_outputed;
sh_bool timer_state;
char *temp_buffer;
sh_uint64 temp_buffer_size;
} sh_ap_PER_CPU;
#pragma pack()
```
The per-CPU struct is used for per-CPU metadatas like LAPIC timer state, per-CPU temporary buffer, logical CPU id and various per-CPU metadatas for differents kernel subsystems.
The logical CPU id is an id assigned to each processor. The bootstrap processor always obtain the id 0, and there is no gap in the logical CPU id allocation space. It allow for an alternative to LAPIC ids and ACPI processors id, which can contain gaps in their allocation space.
The GS base register of each AP contain a pointer to the CPU struct of each AP. The CPU and per-CPU struct of the bootstrap processor is created at the start of the kernel entry point and then elaborated during the AP bootstrap procedure. It isn't stored inside the AP's CPU struct array.
### Preparing all necessary structures for AP bootstrap
Since all APs start in 16 bits mode, Shelter uses the following **physical** address to put importants compoments there:
- `0x7000`: the physical address of the AP trampoline, shared by all APs
- `0x8000`: the physical address of the 32 bits GDT, necessary for the 32 bits far jump
- `0x9000`: the physical address of the AP bootstrap struct
All those addresses are identity mapped in the bootstrap CPU page table. Additionally, the AP trampoline loaded from the VYX binary of Shelter is mapped at the virtual address `0xA000`.
The AP bootstrap struct is a structure that contains all commons addresses used by all APs:
``` C
#pragma pack(1)
typedef struct {
sh_page_PHYSICAL_ADDRESS lapic_base_pa;
sh_page_VIRTUAL_ADDRESS c_entry_point_va;
sh_page_PHYSICAL_ADDRESS page_table_pa;
sh_page_VIRTUAL_ADDRESS cpu_struct_base_area;
sh_page_VIRTUAL_ADDRESS shared_gdt_64_va;
sh_gdt_GDTR gdt_64_gdtr;
sh_idt_IDT *idt;
} sh_ap_AP_BOOTSTRAP;
#pragma pack()
```
It contain things such as the LAPIC memory mapped area PA, the C entry point VA, the page table PA, the CPU struct array VA, the shared GDT VA for the 64 bits far jump, the GDTR for the said GDT and the IDT VA.
During the kernel boot process, all APs share the same page table as the bootstrap processor for simplicity.
First, the 32 bits GDT is generated and placed at PA `0x8000` by `sh_ap_load_gdt_32()`. Then the rest of the AP bootstrap preparation is handled by `sh_ap_prepare_for_smp_launch()`
Here is the detailled processus:
1) Identity-mapping the page at PA `0x9000` to initialize obvious values like C entry point VA, page table PA and LAPIC base PA.
2) Allocating the array of CPU struct on the heap (indexed by LAPIC id), and initializing the corresponding value in the AP bootstrap struct.
3) Allocating the array of TSS on the heap.
4) Looping on all availables LAPIC to fill all CPU struct: allocating the stack for each AP, filling the LAPIC id, filling the TSS of each AP, allocating the multiple stacks for each TSS, filling the per-CPU struct of each AP.
5) Filling the global GDT for all APs with all TSS, completing the AP bootstrap struct, initializing the AP start state.
### Starting the APs
Once everything is ready, `sh_ap_start_ap_boot_procedure()` is called once to start all APs. The procedure is as follow:
1) Copying the AP trampoline from VA `0xA000` to VA/PA `0x7000`.
2) Filling CPU struct and per-CPU struct of the bootstrap CPU.
3) Sending INIT IPI and waiting 10 millisecond.
4) Sending SIPI and waiting for all APs to start. At this point, the `sh_print` standard library feature is ready.
All APs will start in 16 bits mode and execute the following sequence of action:
1) Disabling interrupts
2) Load 32 bits GDT, enabling 32 bits mode in CR0 and executing the far jump to get to 32 bits mode
3) Reading LAPIC base, extracting LAPIC id and keeping it in a register
4) Enabling PAE, loading page table PA into CR3, enabling LME and NXE bits
5) Enabling pagging, and executing the far jump to get to 64 bits mode
6) Locating the CPU struct for this CPU using the CPU LAPIC id
7) Loading the local CPU stack
8) Loading 64 bits shared GDT, reloading CS register
9) Loading task register and GS register
10) Jumping to C entry point
The C entry point is the function named `sh_ap_entry_point()`. It does the following things:
1) Initialize the LAPIC of the AP
2) Load the shared IDT
3) Confirm to the bootstrap CPU that everything is ready for this AP by updating the corresponding AP start state and goes into an infinite loop
If the bootstrap CPU doesn't receives any confirmation from one of the AP, the boot process is halted.

View File

@@ -11,3 +11,12 @@ Here is a list of all the wrappers available. They are all defined as `static in
- `outb`: take a `sh_uint16` for specifying the port and a `sh_uint8` for specifying the byte to output
- `rdtsc`: take no arguments and return the current TSC as a `sh_uint64`
- `invlpg`: take a `void *` as an address and return nothing
- `lgdt`: take a `void *` as an address and return nothing
- `ltr`: take a `sh_uint16` as a TSS selector and return nothing
- `sidt`: take a `sh_idt_IDTR*` as a pointer and return nothing
- `sti`: take no arguments and return nothing
- `cli`: take no arguments and return nothing
- `cpuid`: take 2 `sh_uint32`: `leaf`, `subleaf`, and 2 `sh_uint32*`: `eax`, `ebx`, `ecx`, `edx`, return nothing
- `mfence`: take no arguments and return nothing
- `lfence`: take no arguments and return nothing
- `sfence`: take no arguments and return nothing

View File

@@ -9,3 +9,6 @@ This component of the Shelter kernel allow for basic CPU functions abstractions,
1) [ASM instructions](asmint.md)
2) [Serial outputing API](serial.md)
3) [TSC API](tsc.md)
4) [PIC API](pic.md)
5) [PIT API](pit.md)
6) [Application processors subsystem](ap.md)

13
docs/shelter/cpu/pic.md Normal file
View File

@@ -0,0 +1,13 @@
# PIC API
## Introduction
The PIC API is used to correctly setup the PIC, before the initialization of the IOAPIC subsystem. It provides a minimal API to manage only the part where the PIC is needed in the boot process. Otherwise, the PIC is just ignored once all legacy IRQs are managed by the IOAPIC subsystem. The PIC API is defined in `shelter/lib/include/cpu/pic.h` and implemented in `shelter/lib/include/cpu/pic.c`.
## API overview
The API provides the following functions:
- `sh_pic_remap()`: remap all legacy IRQs on IDT vectors 32 to 47, should only be called once before any use of the PIC
- `sh_pic_unmask()`: take an IRQ number and unmask this IRQ line
- `sh_pic_mask()`: take an IRQ number and mask this IRQ line
- `sh_pic_send_eoi()`: send End Of Interrupt signal to the PIC

6
docs/shelter/cpu/pit.md Normal file
View File

@@ -0,0 +1,6 @@
# PIT API
In Shelter, the PIT is only used for one purpose: estimating the CPU frequency. It's only used with the PIC, never with the IOAPIC, so the API is volontarily very simple:
- `sh_pit_set_frequency()`: expect the frequency as argument. Set the PIT to uses this frequency and return nothing
This API is defined inside `shelter/lib/include/cpu/pit.h` and implemented inside `shelter/lib/include/cpu/pic.c`.

View File

@@ -2,7 +2,7 @@
## Introduction
Shelter provide an abstraction around the serial port for outputing on it. This should act as the central point for outputing on the serial. The API is defined in `shelter/lib/include/cpu/serial.h` and implemented in `shelter/lib/src/cpu/serial.c`. This API obey the `serial_port_disabled` killswitch defined in kernel boot configuration. The subsystem prefix is `sh_serial_`.
Shelter provides an abstraction around the serial port for outputing on it. This should act as the central point for outputing on the serial. The API is defined in `shelter/lib/include/cpu/serial.h` and implemented in `shelter/lib/src/cpu/serial.c`. This API obey the `serial_port_disabled` killswitch defined in kernel boot configuration. The subsystem prefix is `sh_serial_`.
## Overview

View File

@@ -2,14 +2,16 @@
## Introduction
In order to be able to measure and approximate time as soon as the kernel start the boot process, the TSC API is implemented in a volontary minimal way. The TSC API is defined inside `shelter/lib/include/cpu/tsc.h` and implemented inside `shelter/lib/src/cpu/tsc.c`. The API prefix is `sh_tsc_`.
In order to be able to measure and approximate time as soon as the kernel start the boot process, the TSC API is implemented in a volontarily minimal way. The TSC API is defined inside `shelter/lib/include/cpu/tsc.h` and implemented inside `shelter/lib/src/cpu/tsc.c`. The API prefix is `sh_tsc_`.
## Overview
The TSC API being intented for measuring time during the boot process, the provided features are extremely basic. In order for any TSC value to start at 0, we define two concept:
One of the TSC API goal being measuring time during the boot process, the provided features regarding this goal are extremely basic. In order for any TSC value to start at 0, we define two concept:
- `kernel_init_tsc`: a TSC value initialized at the very start of the kernel boot process
- `kernel_current_tsc`: a TSC value which is the result of `kernel_init_tsc` substracted to the value returned by `sh_asm_rdtsc()`
Starting with Shelter 0.2, the TSC API also provides primitives to estimate CPU frequency using PIT and TSC, or CPU ID if available.
## API content
The API define the following elements:
@@ -18,3 +20,9 @@ The API define the following elements:
- `sh_tsc_init_tsc()`: a function initializing `kernel_init_tsc`. This function should only be called once as soon as the kernel start. Return a `SH_STATUS`
- `sh_tsc_get_kernel_init_tsc()`: return `kernel_init_tsc` under a `sh_tsc_TSC_VALUE`
- `sh_tsc_get_kernel_current_tsc()`: return the result of `sh_asm_rdtsc()` less `kernel_init_tsc`, under a `sh_tsc_TSC_VALUE`
- `sh_tsc_estimate_cpu_freq()`: estimate CPU frequency using PIT and TSC, should only be called once the IDT has been initialized and PIT remapped on the correct IDT vector, and before the start of all APs. It return the estimated frequency under a `sh_uint64`
- `sh_tsc_load_cpu_freq()`: take a `sh_uint64` and load it as CPU frequency, return nothing
- `sh_tsc_has_hypervisor()`: return `SH_TRUE` if the hypervisor bit in CPU ID is set, take no parameters
- `sh_tsc_is_tsc_constant()`: return `SH_TRUE` if the TSC is constant, according to CPU ID
- `sh_tsc_get_cpu_freq_cpuid()`: return in a variable given as a pointer the value of the CPU frequency given by CPU ID id available. Return a `SH_STATUS`
- `sh_tsc_devs_query()`: TSC DevS entry point, should only be called by `sh_devs_query`