diff options
Diffstat (limited to 'src/arch')
-rw-r--r-- | src/arch/x86/mbr/Makefile | 19 | ||||
-rw-r--r-- | src/arch/x86/mbr/mbr.S | 117 | ||||
-rw-r--r-- | src/arch/x86/mbr/mbr.ld | 11 | ||||
-rw-r--r-- | src/arch/x86_64.md | 64 |
4 files changed, 210 insertions, 1 deletions
diff --git a/src/arch/x86/mbr/Makefile b/src/arch/x86/mbr/Makefile new file mode 100644 index 0000000..bcecb76 --- /dev/null +++ b/src/arch/x86/mbr/Makefile @@ -0,0 +1,19 @@ +mbr: mbr.ld mbr.o + ld -o $@.elf -nostdlib -T $^ + objcopy -O binary $@.elf $@ + +mbr.o: mbr.S + gcc -c -o $@ -m32 -ffreestanding $^ + +clean: + $(RM) mbr.o mbr.elf mbr + +run: mbr + qemu-system-i386 -hda mbr $(QEMU_ARGS) + +debug: + make run QEMU_ARGS="-s -S" + +gdb: + gdb -ex 'target remote :1234' mbr.elf -ex 'layout asm' -ex 'b entry_pm32' -ex 'c' + diff --git a/src/arch/x86/mbr/mbr.S b/src/arch/x86/mbr/mbr.S new file mode 100644 index 0000000..b74bae5 --- /dev/null +++ b/src/arch/x86/mbr/mbr.S @@ -0,0 +1,117 @@ +.code16 +.intel_syntax noprefix + +.section .boot, "ax", @progbits + // Disable interrupts. + cli + + // Clear segment selectors. + xor ax, ax + mov ds, ax + mov es, ax + mov ss, ax + mov fs, ax + mov gs, ax + + // Set cs to 0x0000, as some BIOSes load the MBR to either 07c0:0000 or 0000:7c000. + jmp 0x0000:entry_rm16 + +entry_rm16: + // Set video mode 3, see [1]. + // * 80x25 text mode + // * 640x200 pixel resolution (8x8 pixel per char) + // * 16 colors (4bit) + // * 4 pages + // * 0xB800 screen address + // + // [1] http://www.ctyme.com/intr/rb-0069.htm + mov ax, 0x3 + int 0x10 + + // Move cursor to second row. + // http://www.ctyme.com/intr/rb-0087.htm + mov ah, 0x02 + mov bh, 0 // page + mov dh, 1 // row + mov dl, 0 // col + int 0x10 + + // Clear direction flag for lodsb below. + cld + + // Load pointer to msg_rm string (null terminated). + lea si, [msg_rm] + + // Teletype output char at current cursor position. + // http://www.ctyme.com/intr/rb-0106.htm + mov ah, 0x0e +1: + lodsb // al <- ds:si ; si+=1 ; (al char to write) + test al,al // test for null terminator + jz 2f + int 0x10 + jmp 1b +2: + + // Enable A20 address line. + in al, 0x92 + or al, 2 + out 0x92, al + + // Load GDT descriptor. + lgdt [gdt_desc] + + // Enable protected mode (set CR0.PE bit). + mov eax, cr0 + or eax, (1 << 0) + mov cr0, eax + + // Far jump which loads segment selector (0x0008) into cs. + // 0x0008 -> RPL=0, TI=0(GDT), I=1 + jmp 0x0008:entry_pm32 + +.code32 +entry_pm32: + // Select data segment selector (0x0010) for ds. + mov ax, gdt_data - gdt + mov ds, ax + + // Write through VGA interface (video memory). + // Each character is represented by 2 bytes. + // 4 bit bg | 4 bit fg | 8 bit ascii char + // + // Start writing at third line. + mov edi, 0xb8000 + (80 * 2 * 2) // + + lea esi, [msg_pm] +1: + lodsb // al <- ds:esi ; esi+=1 + test al, al // test for null terminator + jz 2f + or eax, 0x1f00 // blue bg, white fg + stosw // ds:[edi] <- ax; edi+=2 + jmp 1b +2: + hlt + jmp 2b + +// For simplicity keep data used by boot sector in the same section. +.balign 8 +msg_rm: + .asciz "Hello from Real Mode!" +msg_pm: + .asciz "Hello from Protected Mode!" + +.balign 8 +gdt: + .8byte 0x0000000000000000 // 0x00 | null descriptor + .8byte 0x00cf9a000000ffff // 0x08 | 32 bit, code (rx), present, dpl=0, g=4K, base=0, limit=fffff +gdt_data: + .8byte 0x00cf92000000ffff // 0x10 | 32 bit, data (rw), present, dpl=0, g=4K, base=0, limit=fffff +gdt_desc: + .2byte .-gdt-1 // size + .4byte gdt // address + +// Write MBR boot magic value. +.fill 510 - (. - .boot), 1, 0x00 +.2byte 0xaa55 diff --git a/src/arch/x86/mbr/mbr.ld b/src/arch/x86/mbr/mbr.ld new file mode 100644 index 0000000..46d8a14 --- /dev/null +++ b/src/arch/x86/mbr/mbr.ld @@ -0,0 +1,11 @@ +OUTPUT_FORMAT(elf32-i386) +OUTPUT_ARCH(i386) + +SECTIONS { + . = 0x7c00; + .boot : { *(.boot) } + _boot_end = .; + /DISCARD/ : { *(.*) } + + ASSERT(_boot_end - 0x7c00 == 512, "boot sector must be exact 512 bytes") +} diff --git a/src/arch/x86_64.md b/src/arch/x86_64.md index 1f7ffdd..8bcc308 100644 --- a/src/arch/x86_64.md +++ b/src/arch/x86_64.md @@ -312,7 +312,7 @@ must must save these registers in case they are used. - `r12` - `r15` - `xmm6` - `xmm15` -## ASM skeleton +## ASM skeleton - linux userspace Small assembler skeleton, ready to use with following properties: - use raw Linux syscalls (`man 2 syscall` for ABI) - no `C runtime (crt)` @@ -350,6 +350,67 @@ To compile and run: Hi ASM-World! ``` +## MBR boot sectors example +The following shows a non-minimal [MBR][mbr] boot sector, which transitions from +16-bit _real mode_ to 32-bit _protected mode_ by setting up a small _global +descriptor table (GDT)_. A string is printed in each mode. + +```x86asm +{{ #include x86/mbr/mbr.S }} +``` + +The linker script. +```x86asm +{{ #include x86/mbr/mbr.ld }} +``` + +The build instructions. +```make +{{ #include x86/mbr/Makefile:1:6 }} +``` + +One can boot into the bootsector from legacy BIOS, either with qemu or by +writing the mbr boot sector as first sector onto a usb stick. + +```sh +qemu-system-i386 -hda mbr +``` + +The following gives some more detailed description for the _segment selector_ +registers, the _segment descriptors_ in the GDT, and the _GDT descriptor_ +itself. +``` +# Segment Selector (cs, ds, es, ss, fs, gs). + +[15:3] I Descriptor Index + [2:1] TI Table Indicator (0=GTD | 1=LDT) + [0] RPL Requested Privilege Level + + +# Segment Descriptor (2 x 4 byte words). + +0x4 [31:24] Base[31:24] +0x4 [23] G Granularity, scaling of limit (0=1B | 1=4K) +0x4 [22] D/B (0=16bit | 1=32bit) +0x4 [21] L (0=compatibility mode | 1=64bit code) if 1 -> D/B = 0 +0x4 [20] AVL Free use for system sw +0x4 [19:16] Limit[19:16] +0x4 [15] P Present +0x4 [14:13] DPL Descriptor privilege level +0x4 [12] S (0=system segment | 1=code/data) +0x4 [11:0] Type Code or data and access information. +0x4 [7:0] Base[23:16] + +0x0 [31:16] Base[15:0] +0x0 [15:0] Limit[15:0] + + +# GDT descriptor (32bit mode) + +[47:16] Base address of GDT table. + [15:0] Length of GDT table. +``` + ## References - [SystemV AMD64 ABI][sysvabi] - [AMD64 Vol1: Application Programming][amd64_vol1] @@ -379,3 +440,4 @@ Hi ASM-World! [gas_directives]: https://sourceware.org/binutils/docs/as/Pseudo-Ops.html#Pseudo-Ops [gas_x86_64]: https://sourceware.org/binutils/docs/as/i386_002dDependent.html [juicebox]: https://github.com/johannst/juicebox-asm +[mbr]: https://en.wikipedia.org/wiki/Master_boot_record |