use long mode, x86_64-unknown-none works now

This commit is contained in:
andromeda
2026-03-04 22:45:35 +01:00
parent 1b6f76488d
commit 094e8c288a
6 changed files with 211 additions and 133 deletions

View File

@@ -1,2 +1,5 @@
[build]
target = "x86_64-unknown-none"
rustflags = [
"-Crelocation-model=static"
]

View File

@@ -16,7 +16,7 @@ run with `nix run git+https://git.mtgmonkey.net/andromeda/bootler#bootler`
+------ 0x00100000 ------+
| hardware, bios stuff |
+------ 0x00080000 ------+
| pm stack (esp) | from boot.asm
| free |
+------ 0x00010200 ------+
| kernel (kernel.asm) |
+------ 0x00010000 ------+
@@ -24,7 +24,17 @@ run with `nix run git+https://git.mtgmonkey.net/andromeda/bootler#bootler`
+------ 0x00007E00 ------+
| bootloader (boot.asm) |
+------ 0x00007C00 ------+
| real mode stack (sp) | from boot.asm
| stack | TODO get real stack
+------ 0x00005000 ------+
| PT |
+------ 0x00004000 ------+
| PDT |
+------ 0x00003000 ------+
| PDPT |
+------ 0x00002000 ------+
| PML4T |
+------ 0x00001000 ------+
| free |
+------ 0x00000500 ------+
| bios stuff |
+------ 0x00000000 ------+

View File

@@ -3,155 +3,209 @@
; 2026 Andromeda
; GPL licensed
LOAD_ADDR equ 0x7C00
KERNEL_START equ 2 ; first sector on disk to load kernel from; 1 indexed
KERNEL_SIZE equ 1 ; length of kernel in sectors
KERNEL_LOAD_ADDR_ES equ 0x1000 ; kernel to be loaded at es * 0x10 + 0x0000
PAGE_TABLE_LOAD_ADDR equ 0x1000 ; start of page table; 4 * pt size
PAGE_TABLE_SIZE equ 0x1000 ; size of page table in bytes
A20_LINE_ENABLE equ 0x2401 ; magic number in ax to enable a20 line with 0x15
BIOS_INT_DISK_OP equ 0x13
BIOS_INT_MEMORY_OP equ 0x15
CR0_PE equ 1 << 0 ; Protected Mode Enabled
CR0_PG equ 1 << 31 ; Paging
CR4_PAE equ 1 << 5 ; Physical Address Extension
CR4_PGE equ 1 << 7 ; Page Global Enabled
MSR_IA32_EFER equ 0xC0000080 ; Extended Feature Enable model-specific Register
[bits 16]
[org 0x7C00] ; address where bootloader is loaded
[org LOAD_ADDR]
jmp 0x0000:.cs_reset ; in case it loads us at 0x7C00:0x0000
.cs_reset:
xor ax, ax
mov ss, ax
mov sp, LOAD_ADDR ; stack builds down, loader faces up
mov ds, ax
mov es, ax
mov ss, ax
mov sp, 0x7C00
mov ah, 0x0E
mov al, 'R'
int 0x10
mov al, 'e'
int 0x10
mov al, 'a'
int 0x10
mov al, 'l'
int 0x10
mov al, 0x0D
int 0x10
mov al, 0x0A
int 0x10
mov al, 0x00 ; stops serial and bios output from overlapping in qemu -nographic
int 0x10 ; output
mov fs, ax
mov gs, ax
cld
; activate a20
mov ax, 0x2401
int 0x15
jc error
mov ax, A20_LINE_ENABLE
int BIOS_INT_MEMORY_OP
jc error_a20_line
mov si, msg_a20_line
call print
; load kernel
; 'docs' from https://www.ctyme.com/intr/rb-0607.htm
mov ah, 0x02
mov al, 1 ; number of sectors to read (must be nonzero)
mov ch, 0 ; low eight bits of cylinder number
mov cl, 2 ; sector number 1-63 (bits 0-5); high two bits of cylinder (bits 6-7,
; hard disk only)
mov dh, 0 ; head number
; dl is already set by bios
mov bx, 0x1000 ; data buffer
; prep to read disk
mov al, KERNEL_SIZE ; size in sectors, min. 1
mov ch, 0 ; cylinder
mov cl, KERNEL_START ; start sector, 1-indexed
mov dh, 0 ; head
; dl set by bios
mov bx, KERNEL_LOAD_ADDR_ES ; load addr es:bx
mov es, bx
xor bx, bx
int 0x13 ; read disk
jc error
; load gdt
; read disk
mov ah, 0x02
int BIOS_INT_DISK_OP
jc error_disk_read
mov si, msg_disk_read
call print
; zero es
push ax
xor ax, ax
mov es, ax
pop ax
mov edi, PAGE_TABLE_LOAD_ADDR
jmp lm_load
; expects:
; es:edi -> 16kb for page table
; ss:esp -> stack
; heavily based on https://wiki.osdev.org/Entering_Long_Mode_Directly
lm_load:
; clear page table
push di
mov ecx, PAGE_TABLE_SIZE
xor eax, eax
cld
rep stosd ; clear space for all 4 tables
pop di
; PML4TE0
lea eax, [es:di + PAGE_TABLE_SIZE]
or eax, 11b
mov [es:di], eax
; PDPT0E0
lea eax, [es:di + 2 * PAGE_TABLE_SIZE]
or eax, 11b
mov [es:di + PAGE_TABLE_SIZE], eax
; PDT0E0
lea eax, [es:di + 3 * PAGE_TABLE_SIZE]
or eax, 11b
mov [es:di + 2 * PAGE_TABLE_SIZE], eax
; PT0
push di
lea di, [di + 3 * PAGE_TABLE_SIZE]
mov eax, 0x11
.set_pt_entry
mov [es:di], eax
add eax, 0x1000
add di, 8
cmp eax, 0x200000 ; size of page
jb .set_pt_entry
pop di
; disable interrupts from PIC by masking all interrupts
mov al, 0xFF ; mask
out 0xA1, al
out 0x21, al
; PAE, PGE
mov eax, CR4_PAE | CR4_PGE
mov cr4, eax
; cr3 -> PML4TE0
mov edx, edi
mov cr3, edx
; highs LME bit in EFER
mov ecx, MSR_IA32_EFER
rdmsr
or eax, 0x00000100
wrmsr
; activate long mode
mov ebx, cr0
or ebx, CR0_PG | CR0_PE
mov cr0, ebx
cli
lgdt [gdt_descriptor]
; jump to pm
cli
mov eax, cr0
or eax, 1
mov cr0, eax
jmp 0x08:pm_start
jmp 0x08:lm_start
error:
mov al, 'e'
int 0x10
mov al, 'r'
int 0x10
mov al, 'r'
int 0x10
mov al, 'o'
int 0x10
mov al, 'r'
int 0x10
jmp $
error_a20_line:
mov si, .msg
call print
jmp done
.msg db "err a20 line", 0x00
error_disk_read:
mov si, .msg
call print
jmp done
.msg db "err disk read", 0x00
done:
hlt
jmp done
print:
pushad
.loop
lodsb
test al, al
je .done
mov ah, 0x0E
int 0x10
jmp .loop
.done
popad
ret
; TODO make real idt
empty_idt:
dw 0 ; length
dd 0 ; base
; TODO make readable (yoinked from https://wiki.osdev.org/Entering_Long_Mode_Directly)
gdt_data:
; null segment
dq 0x0000000000000000
; code segment
dw 0xFFFF
dw 0x0000
db 0x00
db 10011010b ; 32-bit code segment, present, ring 0
db 11001111b ; limit bits 16-19
db 0x00
; data segment
dw 0xFFFF
dw 0x0000
db 0x00
db 10010010b ; 32-bit data segment, present, ring 0
db 11001111b ; limit bits 16-19
db 0x00
dq 0x00209A0000000000
dq 0x0000920000000000
gdt_descriptor:
dw $ - gdt_data - 1 ; size
dd gdt_data ; start
[bits 32]
[bits 64]
; entry point
pm_start:
; data segment registers
; long mode entry
lm_start:
mov ax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
mov esp, 0x80000
; TODO actually configure COM1
mov dx, 0x3F8 ; COM1
mov al, 'P'
out dx, al
mov al, 'r'
out dx, al
mov al, 'o'
out dx, al
mov al, 't'
out dx, al
mov al, 'e'
out dx, al
mov al, 'c'
out dx, al
mov al, 't'
out dx, al
mov al, 'e'
out dx, al
mov al, 'd'
out dx, al
mov al, 0x0D
out dx, al
mov al, 0x0A
out dx, al
; set up serial device
; TODO poll and whatnot (in rust??)
mov dx, 0x3F8
jmp 0x08:0x10000
jmp KERNEL_LOAD_ADDR_ES * 0x10
pm_error:
mov al, 'p'
out dx, al
mov al, 'm'
out dx, al
mov al, 'e'
out dx, al
mov al, 'r'
out dx, al
mov al, 'r'
out dx, al
mov al, 'o'
out dx, al
mov al, 'r'
out dx, al
jmp $
msg_a20_line db "a20 line", 0x0D, 0x0A, 0x00
msg_disk_read db "disk read", 0x0D, 0x0A, 0x00
times 510-($-$$) db 0 ; 2 bytes less now
db 0x55

View File

@@ -1,4 +1,4 @@
[bits 32]
[bits 64]
mov al, 'K'
out dx, al
@@ -18,6 +18,6 @@ mov al, 0x0A
out dx, al
mov al, 0x00
out dx, al
jmp $
times 512-($-$$) db 0
.done
hlt
jmp .done

View File

@@ -23,7 +23,11 @@ in (naersk'.buildPackage {
release = true;
# build std
cargoBuildOptions = x: x ++ ["-Zbuild-std=core,compiler_builtins,alloc" "-Zbuild-std-features=compiler-builtins-mem"];
cargoBuildOptions = x:
x
++ [
"-Zbuild-std=core,compiler_builtins"
];
postInstall = ''
ld --oformat binary \

View File

@@ -6,15 +6,22 @@ use core::panic::PanicInfo;
#[unsafe(no_mangle)]
pub extern "C" fn _start() -> ! {
// 'k'
loop{unsafe {core::arch::asm!(
"out dx, al", in("al") 0x6Bu8
)}}
print("kernel\n");
loop {}
}
fn print(s: &str) {
let mut bytes = s.bytes();
while let Some(b) = bytes.next() {
unsafe {core::arch::asm!(
"out dx, al"
, in("al") b
)};
};
}
#[panic_handler]
fn panic(_: &PanicInfo) -> ! {
// 'p'
loop{unsafe {core::arch::asm!(
"out dx, al", in("al") 0x70u8
)}}
print("panicked");
loop {}
}