Some very old changes commited now.

This commit is contained in:
Thomas Lovén 2025-03-24 22:37:27 +01:00
parent 96a3f23670
commit b79ae8996b
6 changed files with 206 additions and 10 deletions

32
TODO Normal file
View File

@ -0,0 +1,32 @@
Roadmap:
✔ Higher half booting in long mode
✔ Virtual memory management
☐ Physical memory manager
✔ Single page
☐ Multiple page blocks
☐ Multithreading
✔ Task switching
✔ Separate address spaces
☐ Forking?
☐ SSE
✔ Enable SSE instructions
✔ Save SSE registers on task switch
☐ Symmetric multiprocessing
☐ User mode code execution
☐ Syscall interface
☐ Decide kernel architecture
- Monolitic kernel?
- Microkernel?
Documentation:
✔ Toolchain
✔ Booting
✔ Memory management
✔ Libc in kernel space
✔ ACPI tables
✔ APIC, IOAPIC, APIC timer
☐ SSE
☐ Framebuffer
https://pubs.opengroup.org/onlinepubs/9699919799/

View File

@ -17,8 +17,8 @@ This contains the IDT entry number for the spurious interupt handler (`0xFF`) an
To test that the APIC works, an interrupt can be triggered by writing to the APIC Interrupt Command Registers. To test that the APIC works, an interrupt can be triggered by writing to the APIC Interrupt Command Registers.
```c ```c
*(uint32_t *)P2V(APIC_BASE + 0x310)) = 0; *(uint32_t *)P2V(APIC_BASE + 0x310) = 0;
*(uint32_t *)P2V(APIC_BASE + 0x300)) = 0x50; *(uint32_t *)P2V(APIC_BASE + 0x300) = 0x50;
``` ```
This should fire interrupt 0x50. This should fire interrupt 0x50.
@ -105,7 +105,7 @@ static void ioapic_write(int reg, uint32_t value) {
In order to know where to redirect an interrupt the IOAPIC looks into its I/O Redirection Table. This can be set up by writing to some registers. In order to know where to redirect an interrupt the IOAPIC looks into its I/O Redirection Table. This can be set up by writing to some registers.
While IOWIN is 32 bits wide, each IOREDTBL entry is 64 bits, so two read or write operations are required to register `0x10 + 2*IRQ` and `0x11 + 2*IRQ`. While IOWIN is 32 bits wide, each IOREDTBL entry is 64 bits, so two read or write operations are required to register `0x10 + 2*IRQ` and `0x11 + 2*IRQ`.
``` ```c
static uint64_t ioapic_read_redirection(int irq) { static uint64_t ioapic_read_redirection(int irq) {
union iored retval; union iored retval;
retval.l = ioapic_read(0x10 + 2*irq); retval.l = ioapic_read(0x10 + 2*irq);
@ -171,9 +171,9 @@ The APIC timer can be set up in periodic mode to fire an interrupt every set num
Then set the Timer Mode bit to Periodic and write an interrupt vector to the Local Vector Table entry for the APIC Timer: Then set the Timer Mode bit to Periodic and write an interrupt vector to the Local Vector Table entry for the APIC Timer:
```c ```c
*(uint32_t *)P2V(APIC_BASE + 0x3E0)) = 0xB; *(uint32_t *)P2V(APIC_BASE + 0x3E0) = 0xB;
*(uint32_t *)P2V(APIC_BASE + 0x380)) = microseconds * calibration_factor; *(uint32_t *)P2V(APIC_BASE + 0x380) = microseconds * calibration_factor;
*(uint32_t *)P2V(APIC_BASE + 0x320)) = 1<<17 | timer_interrupt; *(uint32_t *)P2V(APIC_BASE + 0x320) = 1<<17 | timer_interrupt;
``` ```
Since the cpu bus clock varies from machine to machine, the `calibration_factor` needs to be calculated or measured. A simple measurement method is as follows: Since the cpu bus clock varies from machine to machine, the `calibration_factor` needs to be calculated or measured. A simple measurement method is as follows:
@ -189,9 +189,9 @@ The APIC timer is monotonically decreasing and triggers when it reaches zero, so
Setting the divisor, masking its interrupt and setting the initual count will start it counting down immediately: Setting the divisor, masking its interrupt and setting the initual count will start it counting down immediately:
```c ```c
*(uint32_t *)P2V(APIC_BASE + 0x3E0)) = 0xB; *(uint32_t *)P2V(APIC_BASE + 0x3E0) = 0xB;
*(uint32_t *)P2V(APIC_BASE + 0x380)) = 0xFFFFFFFF; *(uint32_t *)P2V(APIC_BASE + 0x380) = 0xFFFFFFFF;
*(uint32_t *)P2V(APIC_BASE + 0x320)) = 1<<16; *(uint32_t *)P2V(APIC_BASE + 0x320) = 1<<16;
``` ```
For the known interval, I'll use the legacy Programmable Interval Timer - PIT. For the known interval, I'll use the legacy Programmable Interval Timer - PIT.
@ -228,7 +228,7 @@ while(!(inb(0x61) & 0x20));
Then finally, the remaining count is read from the APIC timer and the calibration factor is calculated. Note that this is not read from the same register as the initial count is written to: Then finally, the remaining count is read from the APIC timer and the calibration factor is calculated. Note that this is not read from the same register as the initial count is written to:
```c ```c
uint32_t remaining = *(uint32_t *)P2V(APIC_BASE + 0x390)); uint32_t remaining = *(uint32_t *)P2V(APIC_BASE + 0x390);
``` ```
## A note about the Red Zone ## A note about the Red Zone

87
documentation/7 - sse.md Normal file
View File

@ -0,0 +1,87 @@
# Using SSE
Stream SIMD Extensions (SSD) is a set of processor instructions that help with floating point operations. They have been around for over 20 years, but are not enabled by default.
The reason for this is that the SSE instructions make use of some special CPU registers, and the OS needs to be aware of this so they can be saved and restored at context switching.
TODO: Three versions of SSE instructions and registers?
## Preserving registers
Saving and restoring the SSE registers is actually very easy, and there's even special instructions that does it:
```asm
.global sse_save
sse_save:
fxsave [rdi]
ret
.global sse_restore
sse_restore:
fxrstor [rdi]
ret
```
Those functions will save or restore the SSE registers to or from the 512 byte buffer used as the first argument (passed in `rdi`).
A good time to do this is to restore the registers right before switching to a new thread, and saving them right after:
```c
process *next = scheduler_next();
...
sse_restore(next->sse);
switch_stack(scheduler->stack_ptr, next->stack_ptr);
sse_save(next->sse);
...
```
TODO: Can SSE usage be checked through exceptions?
Now, before moving on to how to actually enable SSE, there's one pitfall to look out for.
The memory location for storing the SSE registers MUST BE 16 BYTE ALLIGNED.
This is easiest done by allocation 16 bytes extra, and adding an offset to the pointer:
TODO: Make sure this is freed correctly!
```c
struct process *new_process() {
...
void *sse = malloc(512+16);
new->sse = (void *) (((uintptr_t)sse + 0xF) & ~0xF);
```
## Enabling SSE
Ok, so finally - enabling SSE.
This is simply done by setting or unsetting four controll register bits.
```asm
sse_init:
mov rax, cr4
or rax, 1<<9 //; Enable Operating System FXSAVE/FXRSTOR Support bit (OSFXSR)
or rax, 1<<10 //; Enable Operating System Unmasked Exception Support bit (OSXMMEXCPT)
mov cr4, rax
mov rax, cr0
or rax, 1<<1 //; Enable Monitor Coprocessor bit (MP)
and rax, ~(1<<2) //; Disable Emulate Coprocessor bit (EM)
mov cr0, rax
```
And that's it.
We can now use floating point math!
TODO: Find out and explain what the flags actually do and why they are all needed.
OSFXSR: Enable use of legacy SSE instructions and indicate that OS saves 64 and 128 bit registers on task switching
OSXMMEXCPT: Indicate that floating point excetion (#XF) is handled by the OS
MP: CHECK IF THIS IS REQUIRED HERE OR ALREADY SET
EM: Should be set to 1 if processor supports x86 - check if this is set already
CR0= 0x80000011
EM already unset, MP not set
CR4= 0x00000020
CR0= 0x80000013
CR4= 0x00000620

View File

@ -0,0 +1,27 @@
#!/bin/sh -e
TARGET=x86_64-elf-mittos
SYSROOT=/opt/sysroot
binutils_version=binutils-2.38
mkdir -p /opt/external && cd /opt/external
[ -f "${binutils_version}.tar.gz" ] || wget "http://ftp.gnu.org/gnu/binutils/${binutils_version}.tar.gz"
[ -d "${binutils_version}" ] || tar -xf ${binutils_version}.tar.gz
mkdir -p build-binutils && cd build-binutils
if ! grep -q mittos ../${binutils_version}/config.sub; then
sed -i '/^case $os in$/a \\tmittos*) ;;' ../${binutils_version}/config.sub
fi
../${binutils_version}/configure \
--target=${TARGET} \
--with-sysroot=${SYSROOT} \
--disable-nls \
--disable-werror \
CC="x86_64-elf-gcc"
# make
# make install

View File

@ -0,0 +1,30 @@
#!/bin/sh -e
TARGET=x86_64-elf
SYSROOT=/opt/sysroot
cairo_version=cairo-1.16.0
mkdir -p /opt/external && cd /opt/external
[ -f "${cairo_version}.tar.xz" ] || wget "https://www.cairographics.org/releases/${cairo_version}.tar.xz"
[ -d "${cairo_version}" ] || tar -xf ${cairo_version}.tar.xz
mkdir -p build-cairo && cd build-cairo
rm -rf *
../${cairo_version}/configure \
--target=${TARGET} \
--prefix=${SYSROOT}/usr \
--enable-xlib=no \
--enable-png=no \
--enable-script=no \
--enable-ps=no \
--enable-pdf=no \
--enable-svg=no \
--enable-interpreter=no \
--enable-shared=no \
--enable-gtk-doc-html=no \
CFLAGS="-DCAIRO_NO_MUTEX=1 -mcmodel=large -mno-red-zone -O0 -ggdb" \
PKG_CONFIG_PATH="${SYSROOT}/usr/lib/pkgconfig"
make
make install

View File

@ -0,0 +1,20 @@
#!/bin/sh -e
TARGET=x86_64-elf
SYSROOT=/opt/sysroot
pixman_version=pixman-0.40.0
mkdir -p /opt/external && cd /opt/external
[ -f "${pixman_version}.tar.gz" ] || wget "https://www.cairographics.org/releases/${pixman_version}.tar.gz"
[ -d "${pixman_version}" ] || tar -xf ${pixman_version}.tar.gz
mkdir -p build-pixman && cd build-pixman
../${pixman_version}/configure \
--target=${TARGET} \
--prefix=${SYSROOT}/usr \
--with-sysroot=${SYSROOT}\
CFLAGS="-mcmodel=large -mno-red-zone -O0 -ggdb"
make
make install