aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJohannes Stoelp <johannes.stoelp@gmail.com>2024-12-21 22:01:56 +0100
committerJohannes Stoelp <johannes.stoelp@gmail.com>2024-12-21 22:01:56 +0100
commitf8221e81b9752548d26675111cffca1947c46b68 (patch)
tree79c77984c0b9ff42c8f87819cf51b64da4f8f98a
parent888faa5f4f2b89c75f2dc2610fb5253120a028ce (diff)
downloadnotes-f8221e81b9752548d26675111cffca1947c46b68.tar.gz
notes-f8221e81b9752548d26675111cffca1947c46b68.zip
x86: add small mbr exampleHEADmaster
-rw-r--r--src/arch/x86/mbr/Makefile19
-rw-r--r--src/arch/x86/mbr/mbr.S117
-rw-r--r--src/arch/x86/mbr/mbr.ld11
-rw-r--r--src/arch/x86_64.md64
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