4 .set PROT_MODE_CSEG,0x8 # code segment selector
5 .set PROT_MODE_DSEG,0x10 # data segment selector
6 .set CR0_PE_ON,0x1 # protected mode enable flag
8 ###################################################################################
10 # This code should be stored in the first sector of the hard disk.
11 # After the BIOS initializes the hardware on startup or system reset,
12 # it loads this code at physical address 0x7c00 - 0x7d00 (512 bytes).
13 # Then the BIOS jumps to the beginning of it, address 0x7c00,
14 # while running in 16-bit real-mode (8086 compatibility mode).
15 # The Code Segment register (CS) is initially zero on entry.
17 # This code switches into 32-bit protected mode so that all of
18 # memory can accessed, then calls into C.
19 ###################################################################################
21 .globl start # Entry point
22 start: .code16 # This runs in real mode
23 cli # Disable interrupts
24 cld # String operations increment
26 # Set up the important data segment registers (DS, ES, SS).
27 xorw %ax,%ax # Segment number zero
28 movw %ax,%ds # -> Data Segment
29 movw %ax,%es # -> Extra Segment
30 movw %ax,%ss # -> Stack Segment
32 # Set up the stack pointer, growing downward from 0x7c00.
33 movw $start,%sp # Stack Pointer
36 #### For fascinating historical reasons (related to the fact that
37 #### the earliest 8086-based PCs could only address 1MB of physical memory
38 #### and subsequent 80286-based PCs wanted to retain maximum compatibility),
39 #### physical address line 20 is tied to low when the machine boots.
40 #### Obviously this a bit of a drag for us, especially when trying to
41 #### address memory above 1MB. This code undoes this.
43 seta20.1: inb $0x64,%al # Get status
44 testb $0x2,%al # Busy?
46 movb $0xd1,%al # Command: Write
47 outb %al,$0x64 # output port
48 seta20.2: inb $0x64,%al # Get status
49 testb $0x2,%al # Busy?
51 movb $0xdf,%al # Enable
54 #### Switch from real to protected mode
55 #### The descriptors in our GDT allow all physical memory to be accessed.
56 #### Furthermore, the descriptors have base addresses of 0, so that the
57 #### segment translation is a NOP, ie. virtual addresses are identical to
58 #### their physical addresses. With this setup, immediately after
59 #### enabling protected mode it will still appear to this code
60 #### that it is running directly on physical memory with no translation.
61 #### This initial NOP-translation setup is required by the processor
62 #### to ensure that the transition to protected mode occurs smoothly.
64 real_to_prot: cli # Mandatory since we dont set up an IDT
65 lgdt gdtdesc # load GDT -- mandatory in protected mode
66 movl %cr0, %eax # turn on protected mode
67 orl $CR0_PE_ON, %eax #
69 ### CPU magic: jump to relocation, flush prefetch queue, and reload %cs
70 ### Has the effect of just jmp to the next instruction, but simultaneous
71 ### loads CS with $PROT_MODE_CSEG.
72 ljmp $PROT_MODE_CSEG, $protcseg
74 #### we are in 32-bit protected mode (hence the .code32)
77 # Set up the protected-mode data segment registers
78 movw $PROT_MODE_DSEG, %ax # Our data segment selector
79 movw %ax, %ds # -> DS: Data Segment
80 movw %ax, %es # -> ES: Extra Segment
83 movw %ax, %ss # -> SS: Stack Segment
85 call cmain # finish the boot load from C.
86 # cmain() should not return
87 spin: jmp spin # ..but in case it does, spin
89 .p2align 2 # force 4 byte alignment
92 SEG(STA_X|STA_R, 0x0, 0xffffffff) # code seg
93 SEG(STA_W, 0x0, 0xffffffff) # data seg
96 .word 0x17 # sizeof(gdt) - 1
97 .long gdt # address gdt