From b79ae8996b8abb4b07d5a6f11f6ff79ebd3aa86e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Lov=C3=A9n?= Date: Mon, 24 Mar 2025 22:37:27 +0100 Subject: [PATCH] Some very old changes commited now. --- TODO | 32 ++++++++ documentation/6 - apic.md | 20 ++--- documentation/7 - sse.md | 87 ++++++++++++++++++++++ toolchain/cross-compiler/binutils/build.sh | 27 +++++++ toolchain/ports/cairo/build.sh | 30 ++++++++ toolchain/ports/pixman/build.sh | 20 +++++ 6 files changed, 206 insertions(+), 10 deletions(-) create mode 100644 TODO create mode 100644 documentation/7 - sse.md create mode 100644 toolchain/cross-compiler/binutils/build.sh create mode 100644 toolchain/ports/cairo/build.sh create mode 100644 toolchain/ports/pixman/build.sh diff --git a/TODO b/TODO new file mode 100644 index 0000000..e33037f --- /dev/null +++ b/TODO @@ -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/ \ No newline at end of file diff --git a/documentation/6 - apic.md b/documentation/6 - apic.md index 2f9a205..6f462d8 100644 --- a/documentation/6 - apic.md +++ b/documentation/6 - apic.md @@ -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. ```c -*(uint32_t *)P2V(APIC_BASE + 0x310)) = 0; -*(uint32_t *)P2V(APIC_BASE + 0x300)) = 0x50; +*(uint32_t *)P2V(APIC_BASE + 0x310) = 0; +*(uint32_t *)P2V(APIC_BASE + 0x300) = 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. 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) { union iored retval; 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: ```c -*(uint32_t *)P2V(APIC_BASE + 0x3E0)) = 0xB; -*(uint32_t *)P2V(APIC_BASE + 0x380)) = microseconds * calibration_factor; -*(uint32_t *)P2V(APIC_BASE + 0x320)) = 1<<17 | timer_interrupt; +*(uint32_t *)P2V(APIC_BASE + 0x3E0) = 0xB; +*(uint32_t *)P2V(APIC_BASE + 0x380) = microseconds * calibration_factor; +*(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: @@ -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: ```c -*(uint32_t *)P2V(APIC_BASE + 0x3E0)) = 0xB; -*(uint32_t *)P2V(APIC_BASE + 0x380)) = 0xFFFFFFFF; -*(uint32_t *)P2V(APIC_BASE + 0x320)) = 1<<16; +*(uint32_t *)P2V(APIC_BASE + 0x3E0) = 0xB; +*(uint32_t *)P2V(APIC_BASE + 0x380) = 0xFFFFFFFF; +*(uint32_t *)P2V(APIC_BASE + 0x320) = 1<<16; ``` 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: ```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 diff --git a/documentation/7 - sse.md b/documentation/7 - sse.md new file mode 100644 index 0000000..1a6e705 --- /dev/null +++ b/documentation/7 - sse.md @@ -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 \ No newline at end of file diff --git a/toolchain/cross-compiler/binutils/build.sh b/toolchain/cross-compiler/binutils/build.sh new file mode 100644 index 0000000..e50f3f5 --- /dev/null +++ b/toolchain/cross-compiler/binutils/build.sh @@ -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 \ No newline at end of file diff --git a/toolchain/ports/cairo/build.sh b/toolchain/ports/cairo/build.sh new file mode 100644 index 0000000..ef02a6c --- /dev/null +++ b/toolchain/ports/cairo/build.sh @@ -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 diff --git a/toolchain/ports/pixman/build.sh b/toolchain/ports/pixman/build.sh new file mode 100644 index 0000000..3f61b18 --- /dev/null +++ b/toolchain/ports/pixman/build.sh @@ -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 \ No newline at end of file