Skip to content
Snippets Groups Projects
Verified Commit 010569fb authored by Jeremy Soller's avatar Jeremy Soller
Browse files

Add some long mode support

parent fb29a362
No related branches found
No related tags found
No related merge requests found
TARGET?=x86-unknown-none
BUILD=build/$(TARGET)
export RUST_TARGET_PATH=$(CURDIR)/targets
ifeq ($(TARGET),x86-unknown-none)
export LD=ld -m elf_i386
export OBJCOPY=objcopy
export PARTED=parted
export QEMU=qemu-system-x86_64
else
$(error target $(TARGET) not supported by bootloader yet)
endif
BUILD=build/$(TARGET)
export RUST_TARGET_PATH=$(CURDIR)/targets
export LD=ld -m elf_i386
export OBJCOPY=objcopy
export PARTED=parted
export QEMU=qemu-system-x86_64
all: $(BUILD)/bootloader.bin
else
all:
$(error target $(TARGET) not supported by bootloader yet)
endif
clean:
rm -rf build
......@@ -61,6 +65,7 @@ qemu: $(BUILD)/harddrive.bin
$(QEMU) \
-d cpu_reset \
-d guest_errors \
-no-reboot \
-smp 4 -m 2048 \
-chardev stdio,id=debug,signal=off,mux=on \
-serial chardev:debug \
......
......@@ -8,7 +8,7 @@ stage2:
stage2.end:
stage3:
%defstr STAGE3_STR %[STAGE3]
incbin STAGE3_STR
align 512, db 0
%defstr STAGE3_STR %[STAGE3]
incbin STAGE3_STR
align 512, db 0
.end:
attrib:
SECTION .text ; cannot use .data
struc GDTEntry
.limitl resw 1
.basel resw 1
.basem resb 1
.attribute resb 1
.flags__limith resb 1
.baseh resb 1
endstruc
gdt_attr:
.present equ 1 << 7
.ring1 equ 1 << 5
.ring2 equ 1 << 6
......@@ -35,7 +46,7 @@ attrib:
.interrupt64 equ 0xE
.trap64 equ 0xF
flags:
gdt_flag:
.granularity equ 1 << 7
.available equ 1 << 4
;user
......@@ -44,3 +55,74 @@ flags:
.long_mode equ 1 << 5
; data
.reserved equ 1 << 5
gdtr:
dw gdt.end + 1 ; size
dd gdt ; offset
gdt:
.null equ $ - gdt
dq 0
.lm64_code equ $ - gdt
istruc GDTEntry
at GDTEntry.limitl, dw 0
at GDTEntry.basel, dw 0
at GDTEntry.basem, db 0
at GDTEntry.attribute, db gdt_attr.present | gdt_attr.user | gdt_attr.code
at GDTEntry.flags__limith, db gdt_flag.long_mode
at GDTEntry.baseh, db 0
iend
.lm64_data equ $ - gdt
istruc GDTEntry
at GDTEntry.limitl, dw 0
at GDTEntry.basel, dw 0
at GDTEntry.basem, db 0
; AMD System Programming Manual states that the writeable bit is ignored in long mode, but ss can not be set to this descriptor without it
at GDTEntry.attribute, db gdt_attr.present | gdt_attr.user | gdt_attr.writable
at GDTEntry.flags__limith, db 0
at GDTEntry.baseh, db 0
iend
.pm32_code equ $ - gdt
istruc GDTEntry
at GDTEntry.limitl, dw 0xFFFF
at GDTEntry.basel, dw 0
at GDTEntry.basem, db 0
at GDTEntry.attribute, db gdt_attr.present | gdt_attr.user | gdt_attr.code | gdt_attr.readable
at GDTEntry.flags__limith, db 0xF | gdt_flag.granularity | gdt_flag.default_operand_size
at GDTEntry.baseh, db 0
iend
.pm32_data equ $ - gdt
istruc GDTEntry
at GDTEntry.limitl, dw 0xFFFF
at GDTEntry.basel, dw 0
at GDTEntry.basem, db 0
at GDTEntry.attribute, db gdt_attr.present | gdt_attr.user | gdt_attr.writable
at GDTEntry.flags__limith, db 0xF | gdt_flag.granularity | gdt_flag.default_operand_size
at GDTEntry.baseh, db 0
iend
.pm16_code equ $ - gdt
istruc GDTEntry
at GDTEntry.limitl, dw 0xFFFF
at GDTEntry.basel, dw 0
at GDTEntry.basem, db 0
at GDTEntry.attribute, db gdt_attr.present | gdt_attr.user | gdt_attr.code | gdt_attr.readable
at GDTEntry.flags__limith, db 0xF
at GDTEntry.baseh, db 0
iend
.pm16_data equ $ - gdt
istruc GDTEntry
at GDTEntry.limitl, dw 0xFFFF
at GDTEntry.basel, dw 0
at GDTEntry.basem, db 0
at GDTEntry.attribute, db gdt_attr.present | gdt_attr.user | gdt_attr.writable
at GDTEntry.flags__limith, db 0xF
at GDTEntry.baseh, db 0
iend
.end equ $ - gdt
struc GDTEntry
.limitl resw 1
.basel resw 1
.basem resb 1
.attribute resb 1
.flags__limith resb 1
.baseh resb 1
endstruc
SECTION .text
USE32
long_mode:
.func: dq 0
.page_table: dd 0
.entry:
; disable interrupts
cli
;disable paging
mov eax, cr0
and eax, 0x7FFFFFFF
mov cr0, eax
;cr3 holds pointer to PML4
mov eax, [.page_table]
mov cr3, eax
;enable OSXSAVE, FXSAVE/FXRSTOR, Page Global, Page Address Extension, and Page Size Extension
mov eax, cr4
or eax, 1 << 18 | 1 << 9 | 1 << 7 | 1 << 5 | 1 << 4
mov cr4, eax
; load long mode GDT
lgdt [gdtr]
; enable long mode
mov ecx, 0xC0000080 ; Read from the EFER MSR.
rdmsr
or eax, 1 << 11 | 1 << 8 ; Set the Long-Mode-Enable and NXE bit.
wrmsr
; enabling paging and protection simultaneously
mov eax, cr0
or eax, 1 << 31 | 1 << 16 | 1 ;Bit 31: Paging, Bit 16: write protect kernel, Bit 0: Protected Mode
mov cr0, eax
; far jump to enable Long Mode and load CS with 64 bit segment
jmp gdt.lm64_code:.inner
USE64
.inner:
; load all the other segments with 64 bit data segments
mov rax, gdt.lm64_data
mov ds, rax
mov es, rax
mov fs, rax
mov gs, rax
mov ss, rax
; jump to specified function
mov rax, [.func]
jmp rax
SECTION .text
USE16
;Generate a memory map at 0x500 to 0x5000 (available memory not used for kernel or bootloader)
memory_map:
.start equ 0x0500
......
SECTION .text
USE16
; provide function for printing in x86 real mode
; print a string and a newline
......
SECTION .text
USE16
protected_mode:
.func: dd 0
.entry:
; disable interrupts
cli
; load protected mode GDT
lgdt [gdtr]
; set protected mode bit of cr0
mov eax, cr0
or eax, 1
mov cr0, eax
; far jump to load CS with 32 bit segment
jmp gdt.pm32_code:.inner
USE32
.inner:
; load all the other segments with 32 bit data segments
mov eax, gdt.pm32_data
mov ds, eax
mov es, eax
mov fs, eax
mov gs, eax
mov ss, eax
; jump to specified function
mov eax, [.func]
jmp eax
......@@ -20,16 +20,8 @@ stage2.entry:
shr ecx, 9
call load_extent
; load protected mode GDT and IDT
cli
lgdt [gdtr]
; set protected mode bit of cr0
mov eax, cr0
or eax, 1
mov cr0, eax
; far jump to load CS with 32 bit segment
jmp gdt.pm32_code:protected_mode
mov dword [protected_mode.func], stage3.entry
jmp protected_mode.entry
args:
.stage3_base dq 0x100000
......@@ -108,22 +100,15 @@ load_extent:
call print_line
ret
%include "descriptor_flags.inc"
%include "gdt_entry.inc"
%include "gdt.asm"
%include "memory_map.asm"
%include "thunk.asm"
%include "unreal.asm"
%include "protected_mode.asm"
%include "long_mode.asm"
USE32
protected_mode:
; load all the other segments with 32 bit data segments
mov eax, gdt.pm32_data
mov ds, eax
mov es, eax
mov fs, eax
mov gs, eax
mov ss, eax
stage3.entry:
mov esp, 0x800000 - 128
; entry point
......@@ -144,53 +129,3 @@ protected_mode:
cli
hlt
jmp .halt
gdtr:
dw gdt.end + 1 ; size
dd gdt ; offset
gdt:
.null equ $ - gdt
dq 0
.pm32_code equ $ - gdt
istruc GDTEntry
at GDTEntry.limitl, dw 0xFFFF
at GDTEntry.basel, dw 0
at GDTEntry.basem, db 0
at GDTEntry.attribute, db attrib.present | attrib.user | attrib.code | attrib.readable
at GDTEntry.flags__limith, db 0xF | flags.granularity | flags.default_operand_size
at GDTEntry.baseh, db 0
iend
.pm32_data equ $ - gdt
istruc GDTEntry
at GDTEntry.limitl, dw 0xFFFF
at GDTEntry.basel, dw 0
at GDTEntry.basem, db 0
at GDTEntry.attribute, db attrib.present | attrib.user | attrib.writable
at GDTEntry.flags__limith, db 0xF | flags.granularity | flags.default_operand_size
at GDTEntry.baseh, db 0
iend
.pm16_code equ $ - gdt
istruc GDTEntry
at GDTEntry.limitl, dw 0xFFFF
at GDTEntry.basel, dw 0
at GDTEntry.basem, db 0
at GDTEntry.attribute, db attrib.present | attrib.user | attrib.code | attrib.readable
at GDTEntry.flags__limith, db 0xF
at GDTEntry.baseh, db 0
iend
.pm16_data equ $ - gdt
istruc GDTEntry
at GDTEntry.limitl, dw 0xFFFF
at GDTEntry.basel, dw 0
at GDTEntry.basem, db 0
at GDTEntry.attribute, db attrib.present | attrib.user | attrib.writable
at GDTEntry.flags__limith, db 0xF
at GDTEntry.baseh, db 0
iend
.end equ $ - gdt
SECTION .text
USE32
thunk:
.int10:
mov dword [.func], .int10_real
......@@ -55,6 +57,7 @@ thunk:
ret
USE16
.int10_real:
int 0x10
ret
......
......@@ -5,7 +5,7 @@ USE16
unreal:
cli
lgdt [unreal_gdtr]
lgdt [gdtr]
push es
push ds
......@@ -22,7 +22,7 @@ unreal:
; the switch back to real mode, these values are not modified, regardless of
; what value is in the 16-bit segment register. So the 64k limit is no longer
; valid and 32-bit offsets can be used with the real-mode addressing rules
mov bx, unreal_gdt.data
mov bx, gdt.pm32_data
mov es, bx
mov ds, bx
......@@ -33,22 +33,3 @@ unreal:
pop es
sti
ret
unreal_gdtr:
dw unreal_gdt.end + 1 ; size
dd unreal_gdt ; offset
unreal_gdt:
.null equ $ - unreal_gdt
dq 0
.data equ $ - unreal_gdt
istruc GDTEntry
at GDTEntry.limitl, dw 0xFFFF
at GDTEntry.basel, dw 0x0
at GDTEntry.basem, db 0x0
at GDTEntry.attribute, db attrib.present | attrib.user | attrib.writable
at GDTEntry.flags__limith, db 0xF | flags.granularity | flags.default_operand_size
at GDTEntry.baseh, db 0x0
iend
.end equ $ - unreal_gdt
......@@ -24,6 +24,7 @@ use self::vga::{VgaTextBlock, VgaTextColor, Vga};
mod disk;
mod logger;
mod paging;
mod panic;
mod thunk;
mod vbe;
......@@ -43,6 +44,8 @@ static ALLOCATOR: LockedHeap = LockedHeap::empty();
static mut VGA: Vga = unsafe { Vga::new(VGA_ADDR as *mut VgaTextBlock, 80, 25) };
static mut KERNEL_PHYS: u64 = 0;
#[no_mangle]
pub unsafe extern "C" fn kstart(
boot_disk: usize,
......@@ -77,7 +80,8 @@ pub unsafe extern "C" fn kstart(
}
}
// Initialize allocator at the end of stage 3 with a meager 1 MiB
// Initialize allocator at the end of stage 3 with a meager 1 MiB, which we can be pretty
// sure will be available.
extern "C" {
static mut __end: u8;
}
......@@ -119,6 +123,8 @@ pub unsafe extern "C" fn kstart(
}
}
//let page_phys = paging::paging_create(KERNEL_PHYS);
let mut modes = Vec::new();
{
// Get card info
......
use core::alloc::{GlobalAlloc, Layout};
use core::slice;
unsafe fn paging_allocate() -> Option<&'static mut [u64]> {
let ptr = crate::ALLOCATOR.alloc_zeroed(
Layout::from_size_align(4096, 4096).unwrap()
);
if ! ptr.is_null() {
Some(slice::from_raw_parts_mut(
ptr as *mut u64,
512 // page size divided by u64 size
))
} else {
None
}
}
pub unsafe fn paging_create(kernel_phys: u64) -> Option<u64> {
// Create PML4
let pml4 = paging_allocate()?;
// Recursive mapping for compatibility
pml4[511] = pml4.as_ptr() as u64 | 1 << 1 | 1;
{
// Create PDP for identity mapping
let pdp = paging_allocate()?;
// Link first user and first kernel PML4 entry to PDP
pml4[0] = pdp.as_ptr() as u64 | 1 << 1 | 1;
pml4[256] = pdp.as_ptr() as u64 | 1 << 1 | 1;
// Identity map 8 GiB pages
for pdp_i in 0..8 {
let pd = paging_allocate()?;
pdp[pdp_i] = pd.as_ptr() as u64 | 1 << 1 | 1;
for pd_i in 0..pd.len() {
let pt = paging_allocate()?;
pd[pd_i] = pt.as_ptr() as u64 | 1 << 1 | 1;
for pt_i in 0..pt.len() {
let addr =
pdp_i as u64 * 0x4000_0000 +
pd_i as u64 * 0x20_0000 +
pt_i as u64 * 0x1000;
pt[pt_i] = addr | 1 << 1 | 1;
}
}
}
}
{
// Create PDP for kernel mapping
let pdp = paging_allocate()?;
// Link second to last PML4 entry to PDP
pml4[510] = pdp.as_ptr() as u64 | 1 << 1 | 1;
// Map 1 GiB at kernel offset
for pdp_i in 0..1 {
let pd = paging_allocate()?;
pdp[pdp_i] = pd.as_ptr() as u64 | 1 << 1 | 1;
for pd_i in 0..pd.len() {
let pt = paging_allocate()?;
pd[pd_i] = pt.as_ptr() as u64 | 1 << 1 | 1;
for pt_i in 0..pt.len() {
let addr =
pdp_i as u64 * 0x4000_0000 +
pd_i as u64 * 0x20_0000 +
pt_i as u64 * 0x1000 +
kernel_phys;
pt[pt_i] = addr | 1 << 1 | 1;
}
}
}
}
Some(pml4.as_ptr() as u64)
}
......@@ -11,7 +11,7 @@ pub extern "C" fn rust_eh_personality() {}
#[panic_handler]
#[no_mangle]
pub extern "C" fn rust_begin_unwind(info: &PanicInfo) -> ! {
//println!("BOOTLOADER PANIC: {}", info);
log::error!("BOOTLOADER PANIC: {}", info);
loop {
unsafe {
llvm_asm!("hlt" : : : : "intel", "volatile");
......@@ -23,7 +23,7 @@ pub extern "C" fn rust_begin_unwind(info: &PanicInfo) -> ! {
#[no_mangle]
#[allow(improper_ctypes_definitions)] // Layout is not repr(C)
pub extern fn rust_oom(_layout: Layout) -> ! {
panic!("kernel memory allocation failed");
panic!("memory allocation failed");
}
#[allow(non_snake_case)]
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment