Skip to content

Commit

Permalink
Initial version of x86_64 long mode
Browse files Browse the repository at this point in the history
  • Loading branch information
And1sS committed Sep 10, 2023
1 parent 63c0f88 commit 77bcdc2
Show file tree
Hide file tree
Showing 7 changed files with 202 additions and 38 deletions.
9 changes: 5 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,15 @@ ISO_FILE = $(BUILD_FOLDER)/sos.iso
CROSS_COMPILE = x86_64-elf-
ASM = nasm
CC = gcc
CC_FLAGS = -c -g -O3 -m32 -mgeneral-regs-only -nostdlib -nostdinc -fno-builtin -fno-stack-protector -nostartfiles -nodefaultlibs \
CC_FLAGS = -c -g -O3 -m64 -mgeneral-regs-only -nostdlib -nostdinc -fno-builtin -fno-stack-protector -mno-red-zone -nostartfiles -nodefaultlibs \
-Wall -Wextra -Werror
QEMU_FLAGS = -D ./log.txt -d int,cpu_reset -no-reboot
LINKER = ld

all: iso

run: iso
qemu-system-x86_64 -cdrom $(ISO_FILE)
qemu-system-x86_64 -cdrom $(ISO_FILE) $(QEMU_FLAGS)

iso: $(ISO_FILE)

Expand All @@ -41,11 +42,11 @@ $(ISO_GRUB_CFG): grub.cfg
cp $< $@

$(KERNEL_ELF): $(BOOTSTRAP_ELF) $(OBJ_FILES)
$(CROSS_COMPILE)$(LINKER) -melf_i386 -Tlinker.ld $(BOOTSTRAP_ELF) $(OBJ_FILES) -o $(KERNEL_ELF)
$(CROSS_COMPILE)$(LINKER) -melf_x86_64 -Tlinker.ld $(BOOTSTRAP_ELF) $(OBJ_FILES) -o $(KERNEL_ELF)

$(BOOTSTRAP_ELF): source/bootstrap.asm
mkdir -p $(@D)
$(ASM) -f elf32 -o $@ $<
$(ASM) -f elf64 -o $@ $<

$(BUILD_FOLDER)/%.o: source/%.c $(H_FILES)
mkdir -p $(@D)
Expand Down
2 changes: 1 addition & 1 deletion run-qemu-detached.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@

make iso

qemu-system-x86_64 -s -S -cdrom build_output/sos.iso &
qemu-system-x86_64 -D ./log.txt -d int,cpu_reset -s -S -cdrom build_output/sos.iso &
./kill-qemu-after-gdb.sh &
176 changes: 159 additions & 17 deletions source/bootstrap.asm
Original file line number Diff line number Diff line change
Expand Up @@ -25,37 +25,179 @@ dd 8
; Multiboot2 header end


extern kernel_main

start:
mov esp, kernel_stack + KERNEL_STACK_SIZE
jmp kernel_main
jmp $
; call check_multiboot
call check_cpuid
call check_long_mode

call set_up_page_tables
call enable_paging

lgdt [gdt64.pointer]
jmp gdt64.code:long_mode_start


check_cpuid:
; Check if CPUID is supported by attempting to flip the ID bit (bit 21)
; in the FLAGS register. If we can flip it, CPUID is available.

; Copy FLAGS in to EAX via stack
pushfd
pop eax

; Copy to ECX as well for comparing later on
mov ecx, eax

; Flip the ID bit
xor eax, 1 << 21

; Copy EAX to FLAGS via the stack
push eax
popfd

; Copy FLAGS back to EAX (with the flipped bit if CPUID is supported)
pushfd
pop eax

; Restore FLAGS from the old version stored in ECX (i.e. flipping the
; ID bit back if it was ever flipped).
push ecx
popfd

; Compare EAX and ECX. If they are equal then that means the bit
; wasn't flipped, and CPUID isn't supported.
cmp eax, ecx
je .no_cpuid
ret
.no_cpuid:
mov al, "1"
jmp error


check_multiboot:
cmp eax, 0x36d76289
jne .no_multiboot
ret
.no_multiboot:
mov al, "0"
jmp error


check_long_mode:
; test if extended processor info in available
mov eax, 0x80000000 ; implicit argument for cpuid
cpuid ; get highest supported argument
cmp eax, 0x80000001 ; it needs to be at least 0x80000001
jb .no_long_mode ; if it's less, the CPU is too old for long mode

; use extended info to test if long mode is available
mov eax, 0x80000001 ; argument for extended processor info
cpuid ; returns various feature bits in ecx and edx
test edx, 1 << 29 ; test if the LM-bit is set in the D-register
jz .no_long_mode ; If it's not set, there is no long mode
ret
.no_long_mode:
mov al, "2"
jmp error


set_up_page_tables:
; map first P4 entry to P3 table
mov eax, p3_table
or eax, 0b11 ; present + writable
mov [p4_table], eax

global load_gdt
load_gdt:
mov eax, dword [esp + 4]
lgdt [eax]
; map first P3 entry to P2 table
mov eax, p2_table
or eax, 0b11 ; present + writable
mov [p3_table], eax

jmp 08h:temp
temp:
mov ecx, 0 ; counter variable

mov eax, 0x10
mov ds, eax
mov ss, eax
.map_p2_table:
; map ecx-th P2 entry to a huge page that starts at address 2MiB*ecx
mov eax, 0x200000 ; 2MiB
mul ecx ; start address of ecx-th page
or eax, 0b10000011 ; present + writable + huge
mov [p2_table + ecx * 8], eax ; map ecx-th entry

mov eax, 0
mov es, eax
mov fs, eax
mov gs, eax
inc ecx ; increase counter
cmp ecx, 512 ; if counter == 512, the whole P2 table is mapped
jne .map_p2_table ; else map the next entry

ret


enable_paging:
; load P4 to cr3 register (cpu uses this to access the P4 table)
mov eax, p4_table
mov cr3, eax

; enable PAE-flag in cr4 (Physical Address Extension)
mov eax, cr4
or eax, 1 << 5
mov cr4, eax

; set the long mode bit in the EFER MSR (model specific register)
mov ecx, 0xC0000080
rdmsr
or eax, 1 << 8
wrmsr

; enable paging in the cr0 register
mov eax, cr0
or eax, 1 << 31
mov cr0, eax

ret

error:
mov dword [0xb8000], 0x4f524f45
mov dword [0xb8004], 0x4f3a4f52
mov dword [0xb8008], 0x4f204f20
mov byte [0xb800a], al
hlt

section .bss

KERNEL_STACK_SIZE equ 8192

; temporary page tables mappings to get to long mode and resetup it later properly
align 4096
p4_table:
resb 4096
p3_table:
resb 4096
p2_table:
resb 4096

align 4
kernel_stack:
resb KERNEL_STACK_SIZE

section .rodata

; temporary gdt to get to long mode and resetup it later properly
gdt64:
dq 0 ; zero entry
.code: equ $ - gdt64 ; new
dq (1<<43) | (1<<44) | (1<<47) | (1<<53) ; code segment
.pointer:
dw $ - gdt64 - 1
dq gdt64


bits 64
section .text

extern kernel_main

long_mode_start:
; print `OKAY` to screen
mov rax, 0x2f592f412f4b2f4f
mov qword [0xb8000], rax
jmp kernel_main
jmp $


32 changes: 24 additions & 8 deletions source/gdt.c
Original file line number Diff line number Diff line change
Expand Up @@ -136,18 +136,34 @@ segment_descriptor gen_task_state_segment_descriptor(
return result;
}

extern void load_gdt(const gdt_descriptor* gdt);

void init_gdt(void) {
gdt_data[0] = gen_null_segment_decriptor();

// in long mode default operation size flag should be zero, base and limits
// are ignored
gdt_data[1] =
gen_code_segment_descriptor(0, 0xFFFFF, 1, 1, 0, 1, 1, 0, 0, 0, 0);
gen_code_segment_descriptor(0, 0xFFFFF, 1, 0, 1, 1, 1, 0, 0, 0, 0);
gdt_data[2] =
gen_data_segment_descriptor(0, 0xFFFFF, 1, 1, 0, 1, 1, 0, 0, 1, 0);
gen_data_segment_descriptor(0, 0xFFFFF, 1, 0, 1, 1, 1, 0, 0, 1, 0);
gdt_data[3] =
gen_code_segment_descriptor(0, 0xFFFFF, 1, 1, 0, 0, 1, 3, 0, 0, 0);
gen_code_segment_descriptor(0, 0xFFFFF, 1, 0, 1, 0, 1, 3, 0, 0, 0);
gdt_data[4] =
gen_data_segment_descriptor(0, 0xFFFFF, 1, 1, 0, 0, 1, 3, 0, 1, 0);
//gdt_data[5] = gen_task_state_segment_descriptor(0, 0xFFFFF, 1, 1, 1, 0, 0);
load_gdt(&gdt);
gen_data_segment_descriptor(0, 0xFFFFF, 1, 0, 1, 0, 1, 3, 0, 1, 0);
// gdt_data[5] = gen_task_state_segment_descriptor(0, 0xFFFFF, 1, 1, 1, 0,
// 0);

__asm__ volatile(" lgdt (%%rax)\n"
" pushq $0x8\n"
" pushq $tmp%=\n"
" retfq\n" // far ret to force cs register reloading
"tmp%=:\n"
" mov $0x10, %%rax\n"
" mov %%rax, %%ds\n"
" mov %%rax, %%ss\n"
" mov $0, %%rax\n"
" mov %%rax, %%es\n"
" mov %%rax, %%fs\n"
" mov %%rax, %%gs"
:
: "a"(&gdt));
}
16 changes: 9 additions & 7 deletions source/idt.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@
typedef struct __attribute__((__aligned__(8), __packed__)) {
u16 offset_0_15;
u16 segment_selector;
u8 reserved;
u8 ist;
u8 flags;
u16 offset_16_31;
u32 offset_32_63;
u32 zero;
} interrupt_descriptor;

typedef struct __attribute__((__packed__)) {
Expand All @@ -23,8 +25,6 @@ const int INTERRUPT_TYPE_OFFSET = 0;
interrupt_descriptor idt_data[48];
const idt_descriptor idt = {.data = idt_data, .limit = sizeof(idt_data) - 1};

extern void load_idt(const idt_descriptor* idt);

u8 gen_interrupt_descriptor_flags(bit present, u8 dpl, bit size,
bit gate_type) {

Expand All @@ -33,16 +33,18 @@ u8 gen_interrupt_descriptor_flags(bit present, u8 dpl, bit size,
| ((gate_type & 1) << INTERRUPT_TYPE_OFFSET) | 0b110;
}

interrupt_descriptor gen_interrupt_descriptor(u16 segment_selector, u32 offset,
interrupt_descriptor gen_interrupt_descriptor(u16 segment_selector, u64 offset,
bit present, u8 dpl, bit size,
bit gate_type) {

interrupt_descriptor result = {
.offset_0_15 = offset & 0xFFFF,
.segment_selector = segment_selector,
.reserved = 0,
.ist = 0,
.flags = gen_interrupt_descriptor_flags(present, dpl, size, gate_type),
.offset_16_31 = (offset >> 16) & 0xFFFF};
.offset_16_31 = (offset >> 16) & 0xFFFF,
.offset_32_63 = (offset >> 32) & 0xFFFFFFFF,
.zero = 0};

return result;
}
Expand Down Expand Up @@ -129,7 +131,7 @@ void init_pic(void) {
void init_idt(void) {
for (int i = 0; i < 48; i++) {
idt_data[i] = gen_interrupt_descriptor(
0x08, (u32) interrupt_handlers[i], 1, 0, 1, 0);
0x08, (u64) interrupt_handlers[i], 1, 0, 1, 0);
}

__asm__ volatile("lidt %0" : : "m"(idt));
Expand Down
3 changes: 3 additions & 0 deletions source/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
#define i32 signed int
#define u32 unsigned int

#define i64 long
#define u64 unsigned long

#define bit u8
#define bool u8
#define true 1
Expand Down
2 changes: 1 addition & 1 deletion source/vga_print.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
const u16 COLUMN_WIDTH = 80;
const u16 ROW_NUMBER = 25;

u16* const TEXT_BUFFER = (u16*) 0x000B8000;
volatile u16* const TEXT_BUFFER = (u16*) 0x000B8000;

u16 cur_row = ROW_NUMBER - 1;
u16 cur_col = 0;
Expand Down

0 comments on commit 77bcdc2

Please sign in to comment.