diff --git a/README.md b/README.md index 14bfe35..d0097b1 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ Documents in this repo assumes MacOS, but one should be able to follow the steps # Sections 1. [Real Mode Development](./doc/real_mode_development.md) +2. [Protected Mode Development](./doc/protected_mode_development.md) # Resources diff --git a/boot.asm b/boot.asm index 6ae77a2..2032bcc 100644 --- a/boot.asm +++ b/boot.asm @@ -1,6 +1,9 @@ -ORG 0 +ORG 0x7c00 BITS 16 ; 16-bit (real mode) +CODE_SEG equ gdt_code - gdt_start +DATA_SEG equ gdt_data - gdt_start + ; BIOS Parameter Block ; https://wiki.osdev.org/FAT#BPB_.28BIOS_Parameter_Block.29 _start: @@ -9,67 +12,67 @@ _start: times 33 db 0 init: - jmp 0x7c0:start + jmp 0:start start: ; setup the data segment cli ; Disable interrupts. We don't want interrupts messing with registers - mov ax, 0x7c0 + mov ax, 0x00 mov ds, ax mov es, ax - mov ax, 0x00 mov ss, ax mov sp, 0x7c00 sti ; Enables interrups - ; Disk interrupt preparation - mov ah, 0x02 ; READ SECTOR command - mov al, 0x01 ; Read 1 sector - mov ch, 0x00 ; Cylinder number 0 - mov cl, 0x02 ; Sector number 2 - mov dh, 0x00 ; Head number 0 - ; We don't set DL. BIOS sets it to the booted disk - mov bx, buffer ; Data read will be buffered at ES:BX - int 0x13 - - jc error ; if the carry flag is set (error), jump - - mov si, buffer - call print - +.load_protected: + cli + lgdt[gdt_descriptor] + mov eax, cr0 + or al, 1 + mov cr0, eax + jmp CODE_SEG:load32 + +; Global Descriptor Table +gdt_start: +gdt_null: ; 64 bits of zeros + dd 0 + dd 0 +; Offset 0x8 +gdt_code: ;; CS should point to this + dw 0xffff ; Segment Limit 0-15 bits + dw 0 ; Base first 0-15 bits + db 0 ; Base 16-23 bits + db 0x9a ; Access Byte + db 11001111b ; High and Low 4-bit flags + db 0 ; Base 24-31 bits +; Offset 0x10 +gdt_data: ;; DS, SS, ES, FS, GS + dw 0xffff ; Segment Limit 0-15 bits + dw 0 ; Base first 0-15 bits + db 0 ; Base 16-23 bits + db 0x92 ; Access Byte + db 11001111b ; High and Low 4-bit flags + db 0 ; Base 24-31 bits +gdt_end: +gdt_descriptor: + dw gdt_end - gdt_start - 1 + dd gdt_start + +[BITS 32] +load32: + mov ax, DATA_SEG + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + mov ss, ax + mov ebp, 0x00200000 + mov esp, ebp cld ; Clears direction flag cli ; Disables interrupts hlt ; This hangs the computer - -error: - mov si, error_message - call print - -print: - ; setup INT10h params - mov ah, 0eh ; Teletype output - mov bx, 0 ; bh = page number, bl = color - -.loop: - lodsb ; Load a byte from DS:SI into AL, then increase SI (see "Notes" in ./doc/real_mode_development.md) - - cmp al, 0 ; if AL contains a null-byte, stop - je .done - - int 0x10 ; BIOS interrupt call 0x10. https://en.wikipedia.org/wiki/INT_10H - - jmp .loop - -.done: - ret - -error_message: - db 'Failed to load sector', 0 - times 510 - ($ - $$) db 0 ; Pad the boot sector to 510 bytes dw 0xAA55 ; Boot signature. 55AA (2 bytes) in the little-endian ;; Everything under here is at the second sector from 0x7c00 (0x7e00) - -buffer: \ No newline at end of file diff --git a/doc/protected_mode_development.md b/doc/protected_mode_development.md new file mode 100644 index 0000000..cb8d66b --- /dev/null +++ b/doc/protected_mode_development.md @@ -0,0 +1,72 @@ +# Protected Mode Development + +Protected mode is an operation mode of x86 architectures. It gives access to 4GB of address space, memory protection, and much more. ([osdev wiki](https://wiki.osdev.org/Protected_mode)) + +## Memory and Hardware Protection + +There are different protection levels in the processor. Levels are called _Rings_. The kernel runs in _Ring 0_. This is the most privileged mode that can talk with hardware and write data to any memory address. _Ring 1_ and _2_ are generally not used, but can be used for device drivers. _Ring 3_ is the least privileged level which is used to run user code. This prevents user applications from overwriting kernel memory, talking with hardware directly, accessing other processes' memory, and using privileged instructions (i.e., `sti`, `cli`). + +## Memory access + +### Selector Memory Scheme + +What we know as _segmentation_ registers in Real Mode become _selector_ registers in Protected Mode. _Selectors_ point to data structures that describe memory ranges and the permissions (ring level) require to access a given range. + +### Paging Memory Scheme + +Paging memory scheme maps virtual memory addresses to physical memory addresses somewhere entirely different in memory. This allows user processes to believe that they are loaded into the same memory address, and makes it impossible for a user program to see the address space of other running programs. As far as a program is concerned, it is the only process running. Note that all virtual and physical addresses need to be divisible by 4096. + +### 32-bit instructions + +In Protected Mode, we gain access to 32-bit instructions, which enable easy access to 32-bit registers, thus 4GB memory. + +# Development + +## 1. Entering Protected Mode + +We create entries for Global Descriptor Table (GDT) and load its address into GDT register by `lgdt` instruction, with additional parameters to enter Protected Mode ([osdev wiki](https://wiki.osdev.org/GDT)). We will use the GDT default values since we'll be using the paging memory scheme. + +## 2. Verify using LLDB + +Since this repo assumes you are using MacOS, we need to use LLDB instead of GDB. Here's how: + +``` +# Launch QEMU with gdb server enabled +# '-s' shorthand for -gdb tcp::1234 +# '-S' freeze CPU at startup (use 'c' to start execution) +> qemu-x86-64 -hda ./boot.bin -s -S & +> lldb +(lldb) gdb-remote 1234 # connects to gdb server on localhost:1234 +Process 1 stopped +* thread #1, stop reason = signal SIGTRAP + frame #0: 0x000000000000fff0 +-> 0xfff0: addb %al, (%rax) + 0xfff2: addb %al, (%rax) + 0xfff4: addb %al, (%rax) + 0xfff6: addb %al, (%rax) +Target 0: (No executable module.) stopped. +(lldb) c # continue +Process 1 resuming +(lldb) process interrupt # now the bootloader should come to a halt +Process 1 stopped +* thread #1, stop reason = signal SIGINT + frame #0: 0x0000000000007c68 +-> 0x7c68: jmp 0x7c68 + 0x7c6a: addb %al, (%rax) + 0x7c6c: addb %al, (%rax) + 0x7c6e: addb %al, (%rax) +Target 0: (No executable module.) stopped. +(lldb) register read +general: + rax = 0x0000000000000011 + ... + rbp = 0x0000000000200000 + rsp = 0x0000000000200000 + ... + cs = 0x00000008 # Code Segment = 8 means Protected Mode (?) + ss = 0x00000010 + ds = 0x00000010 + es = 0x00000010 + fs = 0x00000010 + gs = 0x00000010 +``` diff --git a/doc/real_mode_development.md b/doc/real_mode_development.md index 8e1b3d4..c6d36bc 100644 --- a/doc/real_mode_development.md +++ b/doc/real_mode_development.md @@ -4,7 +4,13 @@ Bootloader is a set of CPU instructions (usually written in assembly) that is loaded by the BIOS when a PC is booted. Bootloader's code sits at 0x7c00 when loaded into the memory, and must be 1-sector (= 512 bytes) long. The end of the bootloader code is marked by 2-byte signature `0x55AA`. -BIOS operates in _Real Mode_. Real Mode (aka. read address mode) is an operating mode available to all x86-compatible CPUs. All code in real mode is required to be 16 bits. Addresses in real mode correspond to real locations in memory. It uses a 20-bit _segmented memory_ address space (= 1MB of addressable memory) and unlimited direct software access to all addressable memory, I/O addresses and peripheral hardware. It does not provide memory protection, multitasking, or code privilege levels. [(wiki)](https://en.wikipedia.org/wiki/Real_mode) +## Real Mode + +BIOS operates in _Real Mode_. Real Mode (aka. read address mode) is an operating mode available to all x86-compatible CPUs. All code in real mode is required to be 16 bits. Addresses in real mode correspond to real locations in memory. + +Real Mode uses a 20-bit _segmented memory_ address space (= 1MB of addressable memory) and unlimited direct software access to all addressable memory, I/O addresses and peripheral hardware. It does not provide memory protection, multitasking, or code privilege levels. [(wiki)](https://en.wikipedia.org/wiki/Real_mode) + +# Development ## 1. Printing a string to the screen