From 1ce7df083a8b1b6d3ed63e529d128e399f5ee032 Mon Sep 17 00:00:00 2001
From: nakst <>
Date: Fri, 29 Oct 2021 14:42:47 +0100
Subject: [PATCH] booting into 32 bit kernels

---
 arch/x86_64/kernel.cpp |   1 +
 boot/x86/loader.s      | 179 ++++++++++++++++++++++++++++++++++++++++-
 util/build.c           |  10 ++-
 3 files changed, 186 insertions(+), 4 deletions(-)

diff --git a/arch/x86_64/kernel.cpp b/arch/x86_64/kernel.cpp
index 98eabeb..73448e5 100644
--- a/arch/x86_64/kernel.cpp
+++ b/arch/x86_64/kernel.cpp
@@ -303,6 +303,7 @@ bool MMArchMapPage(MMSpace *space, uintptr_t physicalAddress, uintptr_t virtualA
 	// That said, a CPU won't overwrite and clear a dirty bit when writing out its accessed flag (tested on Qemu);
 	// see here https://stackoverflow.com/questions/69024372/.
 	// Tl;dr: if a CPU ever sees an entry without these bits set, it can overwrite the entry with junk whenever it feels like it.
+	// TODO Should we be marking page tables as dirty/accessed? (Including those made by the 32-bit AND 64-bit bootloader and MMArchInitialise).
 	value |= (1 << 5) | (1 << 6);
 
 	if ((oldValue & 1) && !(flags & MM_MAP_PAGE_OVERWRITE)) {
diff --git a/boot/x86/loader.s b/boot/x86/loader.s
index bf4314d..b5c95f2 100644
--- a/boot/x86/loader.s
+++ b/boot/x86/loader.s
@@ -1,7 +1,7 @@
 [bits 16]
 [org 0x1000]
 
-; This is missing any fileSystem specific macros.
+; This is missing any file system specific macros.
 %define vesa_info 0x7000
 %define os_installation_identifier 0x7FF0
 %define temporary_load_buffer 0x9000
@@ -363,7 +363,184 @@ setup_elf:
 	jne	error_32
 	cmp	byte [ebx + 4],2
 	je	setup_elf_64
+	cmp	byte [ebx + 4],1
 	jne	error_32
+	jmp	setup_elf_32
+
+setup_elf_32:
+	mov	dword [page_table_allocation_location],0x2000
+
+	; Check the ELF data is correct
+	mov	ebx,[kernel_buffer]
+	mov	esi,error_bad_kernel
+	cmp	byte [ebx + 5],1
+	jne	error_32
+	cmp	byte [ebx + 7],0
+	jne	error_32
+	cmp	byte [ebx + 16],2
+	jne	error_32
+	cmp	byte [ebx + 18],0x03
+	jne	error_32
+
+	; Find the program headers
+	; EAX = ELF header, EBX = program headers
+	mov	eax,ebx
+	mov	ebx,[eax + 28]
+	add	ebx,eax
+
+	; ECX = entries, EDX = size of entry
+	movzx	ecx,word [eax + 44]
+	movzx	edx,word [eax + 42]
+
+	; Loop through each program header
+	.loop_program_headers:
+	push	eax
+	push	ecx
+	push	edx
+	push	ebx
+
+	; Only deal with load segments
+	mov	eax,[ebx]
+	cmp	eax,1
+	jne	.next_entry
+
+	; Work out how many physical pages we need to allocate
+	mov	ecx,[ebx + 20]
+	shr	ecx,12
+	inc	ecx
+
+	; Get the starting virtual address
+	mov	eax,[ebx + 8]
+	mov	[.target_page],eax
+
+	; For every frame in the segment
+	.frame_loop:
+	xor	ebx,ebx
+
+	; For every memory region
+	.memory_region_loop:
+
+	; Check the region has enough pages remaining
+	mov	eax,[ebx + memory_map + 8]
+	or	eax,eax
+	jz	.try_next_memory_region
+
+	; Remove one page from the region
+	mov	eax,[ebx + memory_map + 4]
+	or	eax,eax
+	jnz	.try_next_memory_region ; Reject 64-bit pages.
+	mov	eax,[ebx + memory_map + 0]
+	mov	[.physical_page],eax
+	add	eax,0x1000
+	mov	[ebx + memory_map + 0],eax
+	sub	dword [ebx + memory_map + 8],1
+
+	jmp	.found_physical_page
+
+	; Go to the next memory region
+	.try_next_memory_region:
+	add	ebx,16
+	mov	eax,[load_memory_map.pointer]
+	cmp	ebx,eax
+	jne	.memory_region_loop
+	mov	esi,error_no_memory
+	jmp	error_32
+
+	; Map the page into virtual memory
+	.found_physical_page:
+
+	; Make sure we have a page table
+	mov	eax,[.target_page]
+	shr	eax,22
+	shl	eax,2
+	add	eax,0xFFFFF000
+	mov	ebx,[eax]
+	cmp	ebx,0
+	jne	.has_pt
+	mov	ebx,[page_table_allocation_location]
+	add	ebx,page_directory
+	or	ebx,7
+	mov	[eax],ebx
+	add	dword [page_table_allocation_location],0x1000
+	mov	eax,cr3
+	mov	cr3,eax
+	.has_pt:
+
+	; Map the page!
+	mov	eax,[.target_page]
+	shr	eax,12
+	mov	ebx,[.physical_page]
+	or	ebx,0x103
+	shl	eax,2
+	add	eax,0xFFC00000
+	mov	[eax],ebx
+	mov	ebx,[.target_page]
+	invlpg	[ebx]
+
+	; Go to the next frame
+	add	dword [.target_page],0x1000
+	dec	ecx
+	or	ecx,ecx
+	jnz	.frame_loop
+
+	; Restore the pointer to the segment
+	pop	ebx
+	push	ebx
+
+	; Clear the memory
+	mov	ecx,[ebx + 20]
+	xor	eax,eax
+	mov	edi,[ebx + 8]
+	rep	stosb
+
+	; Copy the memory
+	mov	ecx,[ebx + 16]
+	mov	esi,[ebx + 4]
+	add	esi,[kernel_buffer]
+	mov	edi,[ebx + 8]
+	rep	movsb
+
+	; Go to the next entry
+	.next_entry:
+	pop	ebx
+	pop	edx
+	pop	ecx
+	pop	eax
+
+	add	ebx,edx
+	dec	ecx
+	or	ecx,ecx
+	jnz	.loop_program_headers
+
+	jmp	run_kernel32
+
+	.target_page: dd 0
+	.physical_page: dd 0
+
+run_kernel32:
+	; Let the kernel use the memory that was used to store the executable
+	xor	eax,eax
+	mov	ebx,[load_memory_map.pointer]
+	mov	[memory_map + ebx + 4],eax
+	mov	[memory_map + ebx + 12],eax
+	mov	[memory_map + ebx + 16],eax
+	mov	[memory_map + ebx + 20],eax
+	mov	eax,[memory_map + ebx + 8]
+	mov	[memory_map + ebx + 24],eax
+	mov	eax,[memory_map + ebx + 12]
+	mov	[memory_map + ebx + 28],eax
+	mov	eax,[kernel_buffer]
+	mov	[memory_map + ebx],eax
+	mov	eax,[kernel_size]
+	shr	eax,12
+	mov	[memory_map + ebx + 8],eax
+
+	; Execute the kernel's _start function
+	mov	ebx,[kernel_buffer]
+	mov	ecx,[ebx + 24]
+	xor	edi,edi
+	mov	esi,1
+	jmp	ecx
 
 setup_elf_64:
 	; Check that the processor is 64-bit
diff --git a/util/build.c b/util/build.c
index 7a486d1..791106a 100644
--- a/util/build.c
+++ b/util/build.c
@@ -2,14 +2,16 @@
 #define _GNU_SOURCE
 #endif
 
-#if 1
+#if 0
 #define TOOLCHAIN_PREFIX "x86_64-essence"
 #define TARGET_NAME "x86_64"
 #define TOOLCHAIN_HAS_RED_ZONE
 #define TOOLCHAIN_HAS_CSTDLIB
+#define QEMU_EXECUTABLE "qemu-system-x86_64"
 #else
 #define TOOLCHAIN_PREFIX "i686-elf"
 #define TARGET_NAME "x86_32"
+#define QEMU_EXECUTABLE "qemu-system-i386"
 #endif
 
 #define WARNING_FLAGS \
@@ -519,7 +521,7 @@ void Run(int emulator, int log, int debug) {
 				serialFlags[0] = 0;
 			}
 
-			CallSystemF("%s %s qemu-system-x86_64 %s%s %s -m %d %s -smp cores=%d -cpu Haswell "
+			CallSystemF("%s %s " QEMU_EXECUTABLE " %s%s %s -m %d %s -smp cores=%d -cpu Haswell "
 					" -device qemu-xhci,id=xhci -device usb-kbd,bus=xhci.0,id=mykeyboard -device usb-mouse,bus=xhci.0,id=mymouse "
 					" -netdev user,id=u1 -device e1000,netdev=u1 -object filter-dump,id=f1,netdev=u1,file=bin/net.dat "
 					" %s %s %s %s %s %s %s ", 
@@ -1397,7 +1399,9 @@ void DoCommand(const char *l) {
 		LineCountFile("", "start.sh");
 
 		const char *folders[] = {
-			"desktop/", "boot/x86/", "drivers/", "kernel/", "apps/", "apps/file_manager/", "shared/", "util/"
+			"desktop/", "boot/x86/", "drivers/", "kernel/", 
+			"apps/", "apps/file_manager/", "shared/", "util/",
+			"arch/", "arch/x86_32/", "arch/x86_64/",
 		};
 
 		for (uintptr_t i = 0; i < sizeof(folders) / sizeof(folders[0]); i++) {