diff --git a/Makefile b/Makefile index 22091c6bfd9ecb5e395e2616b94a778892009fed..c29155a11a5d065d5b3c9bcea3043f55e07816ff 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ bootloader-legacy: $(MAKE) -C bootloader-legacy bootloader-uefi: - $(MAKE) -C bootloader-legacy + $(MAKE) -C bootloader-uefi kernel: $(MAKE) -C kernel diff --git a/NOTE b/NOTE deleted file mode 100644 index 84640530fed2af626a620256065015e97d76fd3c..0000000000000000000000000000000000000000 --- a/NOTE +++ /dev/null @@ -1,2 +0,0 @@ -dd if=/dev/zero of=out.img bs=512K count=16 -VBoxManage convertfromraw out.img out.vdi --format vdi diff --git a/README.md b/README.md new file mode 100644 index 0000000000000000000000000000000000000000..65d767473ab9d42adc0b18df7cabd03c93ec832e --- /dev/null +++ b/README.md @@ -0,0 +1,41 @@ +# notes + +## register usage rule + +volatile (caller saved): rax, rcx, rdx + +non-volatile: others + +arguments and return value: rax, rcx, rdx, stack + +## bootloader(legacy) memory model + +- common + +0x7c00 - 0x7e00 : bootloader.img, executes from beginning 0x7c00 +0x7e00 - 0x17c00 : kernel.img, called by bootloader, from beginning 0x7e00 + +- bios age + + + +- 32bit protected mode age + +Just used this mode to launch 64bit long mode. No paging enabled. + +- 64bit long mode age + +0x1000 - 0x5000 : 64bit long mode paging table**s** +0x00000000 - 0x00200000 : 512 * 4K pages, before the kernel initializing page table in C++. + +## bootloader(legacy) disk model + +0-511Byte : bootloader.img from boot.asm, MBR flag +512B - 64KiB : kernel.img. executes from 512Byte first instruction. + +## works and TODO + +Currently, paging for 32bit bootloader not implemented. Not planned. + +kernel currently working on both 32bit(without paging) and 64bit mode. But in the future, will work only in 64bit mode. + diff --git a/bootloader-legacy/Makefile b/bootloader-legacy/Makefile index ce6142a3fe43fb3407c00dc7d4e2c76796b12804..8fa0ceedd9f63dadef517e984786b05031cca5c4 100644 --- a/bootloader-legacy/Makefile +++ b/bootloader-legacy/Makefile @@ -1,6 +1,11 @@ build: nasm -f bin boot.asm -o boot.img +build_x86: + +build_x86_64: + + run: build qemu-system-x86_64 boot.img diff --git a/bootloader-legacy/boot.asm b/bootloader-legacy/boot.asm index 41043c6ae76886172467fcccf8a58bb74d36f625..58d580180a55f705942740cb05a38a8212d39eab 100644 --- a/bootloader-legacy/boot.asm +++ b/bootloader-legacy/boot.asm @@ -70,7 +70,7 @@ _load_kern: disk_io_error: mov bx, _motd_disk_error call println_bios - jmp _stall + jmp $ @@ -100,26 +100,56 @@ _prot_begin: mov ebx, _motd_32 call println_vga + ; Test if 64bit available + call test_support_long_mode + cmp eax, 0 + je _test_passed + mov ebx, _motd_no_long_mode + call println_vga + jmp _call_kern_32 +_test_passed: + + jmp inline_enter_long_mode +%include "./inline_x64lib.inc" + jmp _call_kern_64 + +[bits 32] +_call_kern_32: ; Enter the kernel. This should never return. call KERN_ADDR ; Kernel returns. mov ebx, _motd_endk call println_vga + jmp $ -_stall: +[bits 64] +_call_kern_64: + ; Enter the kernel. This should never return. + call KERN_ADDR + + ; Kernel returns. + ;mov ebx, _motd_endk + ;call println_vga + ; TODO: add 64bit println_vga and error msg jmp $ + _motd_disk_error: - db 'DISK_IO_ERROR', 0x0 + db 'MED', 0x0 _motd_32: - db '[LOAD KERN SUCC] [ENTER X86 MODE SUCC]', 0x0 + db 'M32', 0x0 _motd_kern_ok: - db '[LOAD KERN SUCC]', 0x0 + db 'MKN', 0x0 _motd_endk: - db '[LOAD KERN SUCC] [ENTER X86 MODE SUCC] [KERN EXITED]', 0x0 + db 'MEK', 0x0 +_motd_no_long_mode: + db 'MNL', 0x0 _boot_drive_id: db 0x0 + +_motd_debug_point: + db 'MDB', 0x0 %include "./mbr_end.inc" diff --git a/bootloader-legacy/inline_x64lib.inc b/bootloader-legacy/inline_x64lib.inc new file mode 100644 index 0000000000000000000000000000000000000000..463e4879745717bf8ccfb315b48d575bdde1d200 --- /dev/null +++ b/bootloader-legacy/inline_x64lib.inc @@ -0,0 +1,167 @@ + +; requires: str.32.inc +; runs in 32bit protection mode + +;test_cpuid_support: +; ; 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. +; sub eax, ecx +; ; eax != 0 if CPUID supported, eax==0 if not supported. +; ret + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +test_support_long_mode: + ; test if possible to enter x64 mode. + ; return value in eax. + ; return 0 = OK. 1 = CPUID_NOT_AVAIL, 2 = CPUID_EXT_NOT_AVAIL, 3 = LONGMODE_NOT_AVAIL +; call test_cpuid_support +; cmp eax, 0 +; jne _cpuid_supported +; mov eax, 1 +; ret +;_cpuid_supported: + mov eax, 0x80000000 ; Set the A-register to 0x80000000. + cpuid ; CPU identification. + cmp eax, 0x80000001 ; Compare the A-register with 0x80000001. + jb _fail_no_long_mode_2 ; It is less, there is no long mode. + mov eax, 0x80000001 ; Set the A-register to 0x80000001. + cpuid ; CPU identification. + test edx, 1 << 29 ; Test if the LM-bit, which is bit 29, is set in the D-register. + jz _fail_no_long_mode_3 ; They aren't, there is no long mode. + mov eax, 0 + ret +_fail_no_long_mode_2: + mov eax, 2 + ret +_fail_no_long_mode_3: + mov eax, 3 + ret + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +GDT64: ; Global Descriptor Table (64-bit). + .Null: equ $ - GDT64 ; The null descriptor. + dw 0xFFFF ; Limit (low). + dw 0 ; Base (low). + db 0 ; Base (middle) + db 0 ; Access. + db 1 ; Granularity. + db 0 ; Base (high). + .Code: equ $ - GDT64 ; The code descriptor. + dw 0 ; Limit (low). + dw 0 ; Base (low). + db 0 ; Base (middle) + db 10011010b ; Access (exec/read). + db 10101111b ; Granularity, 64 bits flag, limit19:16. + db 0 ; Base (high). + .Data: equ $ - GDT64 ; The data descriptor. + dw 0 ; Limit (low). + dw 0 ; Base (low). + db 0 ; Base (middle) + db 10010010b ; Access (read/write). + db 00000000b ; Granularity. + db 0 ; Base (high). + .Pointer: ; The GDT-pointer. + dw $ - GDT64 - 1 ; Limit. + dq GDT64 ; Base. + +inline_enter_long_mode: + ;;;;;;;;;;;;; section 1: paging + ; disable paging in 32bit protected mode (if any) + mov eax, cr0 + and eax, 01111111111111111111111111111111b ; Clear the PG-bit, which is bit 31. + mov cr0, eax + + ; prepare paging for long mode. Using memory 0x1000 - 0x5000, to put 4-level page tables. + mov edi, 0x1000 ; begin addr + xor eax, eax + mov ecx, 0x1000 ; size + rep stosd ; memset, 0x1000 * 4Bytes to set this memory area to ZERO. + + ; set PML4T + mov edi, 0x1000 + mov cr3, edi + + ; fill first entry of PML4T, PDPT, PDT. + mov DWORD [edi], 0x2003 ; 0x2003 IS NOT the address. it contains many flags, and another DWORD is zero. Just a trick. + mov edi, 0x2000 + mov DWORD [edi], 0x3003 + mov edi, 0x3000 + mov DWORD [edi], 0x4003 + mov edi, 0x4000 + + ; fill all entries of the PT. This is a trick, which not setting all bits in entry. + ; This page table has 512 4K page, 0x00000000 - 0x00200000. + mov ebx, 0x00000003 ; BIT M--12 contains PageFrameNumber. + mov ecx, 512 ; counter +_set_one_entry: + mov DWORD [edi], ebx + add ebx, 0x1000 ; each page is 4K + add edi, 8 ; each entry is 64bit. + loop _set_one_entry + + ; Page table prepared. Enable PAE-paging. + mov eax, cr4 + or eax, 1<<5 + mov cr4, eax + + ;;;;;;;;;;;;;;;; Section 2: enter compatibility mode + mov ecx, 0xC0000080 ; Set the C-register to 0xC0000080, which is the EFER MSR. + rdmsr ; Read from the model-specific register. + or eax, 1 << 8 ; Set the LM-bit which is the 9th bit (bit 8). + wrmsr ; Write to the model-specific register. + + mov eax, cr0 ; Set the A-register to control register 0. + or eax, 1 << 31 ; Set the PG-bit, which is the 32nd bit (bit 31). + mov cr0, eax ; Set control register 0 to the A-register. + + + ;;;;;;;;;;;;;;;;; Section 3: enter 64bit long mode + lgdt [GDT64.Pointer] ; Load the 64-bit global descriptor table. + jmp GDT64.Code:Realm64 ; Set the code segment and enter 64-bit long mode. + +[BITS 64] + +Realm64: + cli ; Clear the interrupt flag. + mov ax, GDT64.Data ; Set the A-register to the data descriptor. + mov ds, ax ; Set the data segment to the A-register. + mov es, ax ; Set the extra segment to the A-register. + mov fs, ax ; Set the F-segment to the A-register. + mov gs, ax ; Set the G-segment to the A-register. + mov ss, ax ; Set the stack segment to the A-register. + ; done + ; 32bit print_vga not working in 64bit mode + + + + + + diff --git a/kernel/Makefile b/kernel/Makefile index a766e15dec5a6c6809bab16af49c686eba673e0a..91ca42462d6043efe5769cfce8ee8fc81fa35755 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -1,17 +1,26 @@ +# BITS could be 32 or 64. +BITS ?= 64 + +ifeq ($(BITS), 64) +ARCH = x86_64 +else +ARCH = i386 +endif + assemble: kernel head - ld -o kernel.img -Ttext 0x7e00 --oformat binary image_head.o kernel.o -m elf_i386 + ld -o kernel.img -Ttext 0x7e00 --oformat binary image_head.o kernel.o -m elf_$(ARCH) # Sector 1 = bootloader, Sector 2 - (512B TO 64K) = kernel # Extend kernel.img to correct size. test $$(stat -c %s kernel.img) -le 65024 truncate --size=65024 kernel.img head: - nasm -f elf image_head.asm -o image_head.o + nasm -f elf$(BITS) -DTARGET_BITS=$(BITS) image_head.asm -o image_head.o kernel: - g++ -ffreestanding -fno-pie -c kernel.cc -o kernel.o -m32 -std=c++17 + g++ -ffreestanding -fno-pie -c kernel.cc -o kernel.o -m$(BITS) -std=c++17 clean: - rm *.o *.img + rm -f *.o *.img diff --git a/kernel/image_head.asm b/kernel/image_head.asm index f6d9c3b5503f04e8d4de215858a80b32a78bc853..92c3af292ee64f06f6742a71ca49be3bb878cb91 100644 --- a/kernel/image_head.asm +++ b/kernel/image_head.asm @@ -1,4 +1,4 @@ -[bits 32] +[bits TARGET_BITS] [extern main] call main ret diff --git a/kernel/include/vga.hpp b/kernel/include/vga.hpp index ffebbc37c2d50dad6e8cc22c4894a77c8c8efdc8..6eab67984e5dd914bd828cdf3f2a952de6b696f5 100644 --- a/kernel/include/vga.hpp +++ b/kernel/include/vga.hpp @@ -12,7 +12,6 @@ constexpr uint16_t VGA_MAKE_CHAR(char c, uint8_t color) { } constexpr uint8_t default_color = 0x0f; - inline void trigger_scroll(uint16_t *pos) { for(uint16_t row = 1; row < VGA_HEIGHT; ++row) { memcpy(VGA_BEGIN_ADDR + (row-1)*VGA_WIDTH, VGA_BEGIN_ADDR + row*VGA_WIDTH, VGA_WIDTH); diff --git a/res/1.png b/res/1.png new file mode 100644 index 0000000000000000000000000000000000000000..732f6644a26bc32a4be02483e4dca48c9ce42e79 Binary files /dev/null and b/res/1.png differ