|
| 1 | +.section .rom, "ax" |
| 2 | + |
| 3 | +# This ROM will be mapped right at the end of the 32-bit address space, but the |
| 4 | +# linker assumes all code executes in RAM, and gives symbols addresses in that |
| 5 | +# range. To get around this, we manully compute ROM addresses. |
| 6 | +gdt32_addr32 = (1 << 32) - (rom_end - gdt32_start) |
| 7 | +rom32_addr32 = (1 << 32) - (rom_end - rom32_start) |
| 8 | +gdt32_ptr_addr16 = (1 << 16) - (rom_end - gdt32_ptr) |
| 9 | + |
| 10 | +gdt32_ptr: |
| 11 | + .short gdt32_end - gdt32_start - 1 # GDT length is actually (length - 1) |
| 12 | + .long gdt32_addr32 |
| 13 | +# Note: Out GDT descriptors must be marked "accessed", or the processor will |
| 14 | +# hang when it attempts to update them (as the gdt32 is in ROM). |
| 15 | +gdt32_start: |
| 16 | + .quad 0 # First descriptor is always unused |
| 17 | +code32_desc: # base = 0x00000000, limit = 0xfffff x 4K |
| 18 | + .short 0xffff # limit[0..16] = 0xffff |
| 19 | + .short 0x0000 # base [0..16] = 0x0000 |
| 20 | + .byte 0x00 # base[16..24] = 0x00 |
| 21 | + .byte 0b10011011 # present, DPL = 0, system, code seg, grows up, readable, accessed |
| 22 | + .byte 0b11001111 # 4K gran, 32-bit, limit[16..20] = 0x1111 = 0xf |
| 23 | + .byte 0x00 # base[24..32] = 0x00 |
| 24 | +data32_desc: # base = 0x00000000, limit = 0xfffff x 4K |
| 25 | + .short 0xffff # limit 15:0 |
| 26 | + .short 0x0000 # base 15:0 |
| 27 | + .byte 0x00 # base[16..24] = 0x00 |
| 28 | + .byte 0b10010011 # present, DPL = 0, system, data seg, ring0 only, writable, accessed |
| 29 | + .byte 0b11001111 # 4K gran, 32-bit, limit[16..20] = 0x1111 = 0xf |
| 30 | + .byte 0x00 # base[24..32] = 0x00 |
| 31 | +gdt32_end: |
| 32 | + |
| 33 | +.code32 |
| 34 | +rom32_start: |
| 35 | + # Now that we are in 32-bit mode, setup all the data segments to be 32-bit. |
| 36 | + movw $(data32_desc - gdt32_start), %ax |
| 37 | + movw %ax, %ds |
| 38 | + movw %ax, %es |
| 39 | + movw %ax, %ss |
| 40 | + movw %ax, %fs |
| 41 | + movw %ax, %gs |
| 42 | + |
| 43 | + # The rest of the firmware assumes it executes from RAM in a region just |
| 44 | + # above ram_min, so we copy all of that code into RAM and jump to it. |
| 45 | + movl $ram_min, %edi |
| 46 | + # Ideally we would define: |
| 47 | + # rom_min = (1 << 32) - firmware_rom_size |
| 48 | + # above, and just do |
| 49 | + # movl $rom_min, %esi |
| 50 | + # However, firmware_rom_size is not known until link time, so the assembler |
| 51 | + # can't handle such code. Thus, the firmware has to do the addreess math. |
| 52 | + xorl %esi, %esi |
| 53 | + # For 32-bit registers: 0 - offset = (1 << 32) - offset |
| 54 | + subl $firmware_rom_size, %esi |
| 55 | + movl $firmware_ram_size, %ecx |
| 56 | + |
| 57 | + # This code is essentially: memcpy(ram_min, rom_min, firmware_ram_size) |
| 58 | + cld |
| 59 | + rep movsb (%esi), (%edi) |
| 60 | + |
| 61 | + # Jumping all that way from ROM (~4 GiB) to RAM (~1 MiB) is too far for a |
| 62 | + # relative jump, so we use an aboslute jump. |
| 63 | + movl $ram32_start, %eax |
| 64 | + jmpl *%eax |
| 65 | + |
| 66 | +.code16 |
| 67 | +rom16_protected: |
| 68 | + # We are now in 16-bit protected mode, To enter 32-bit protected mode, we |
| 69 | + # need to load 32-bit code/data segments into our GDT. The gdt32 in ROM is |
| 70 | + # at too high of an address (4 GiB - offset) for the data segment to reach. |
| 71 | + # So, we load gdt32 via the 16-bit code segement, using a 16-bit address. |
| 72 | + movw $gdt32_ptr_addr16, %bx |
| 73 | + lgdtl %cs:(%bx) |
| 74 | + |
| 75 | + # Set CS to a 32-bit segment and jump to 32-bit code. |
| 76 | + ljmpl $(code32_desc - gdt32_start), $rom32_addr32 |
| 77 | + |
| 78 | +.align 16 |
| 79 | +reset_vector: # 0xffff_fff0 |
| 80 | + # This code must be 16 bytes or less, so be careful when adding anyting. |
| 81 | + cli |
| 82 | + |
| 83 | + # Set CRO.PE (Protected Mode Enable) |
| 84 | + movl %cr0, %eax |
| 85 | + orb $0b00000001, %al # Set bit 0 |
| 86 | + movl %eax, %cr0 |
| 87 | + |
| 88 | + jmp rom16_protected |
| 89 | + |
| 90 | +.align 16 |
| 91 | +rom_end: # 0x1_0000_0000 |
0 commit comments