[bits 16] %include "fn.s" %include "layout.s" %include "s2_fns.s" extern test_a20 extern enable_a20_intel_8042 extern load_s3 s2_main: call test_a20 test al, al jnz .a20_enabled ; Try to enable A20 using the Intel 8042 PS/2 keyboard controller. call enable_a20_intel_8042 call test_a20 test al, al jnz .a20_enabled ; TODO: try other methods first before we panic: ; - [ ] BIOS interrupt ; - [ ] Fast A20 enable jmp panic_simple .a20_enabled: call load_s3 jc panic_simple mov ax, 0x0003 int 0x10 ; Disable cursor mov ax, 0x0100 mov cx, 0x3f00 int 0x10 ; Copy the GDT mov cx, GDT_FLAT_LEN mov si, gdt_flat mov di, GDT_FLAT_ADDR rep movsb ; Ensure interrupts are definitely disabled. cli ; Load our flat-address-space GDT. lgdt [gdt_flat_slice] ; Set the protected-mode bit in cr0. mov eax, cr0 or al, 0x01 mov cr0, eax ; Long jump to set the code segment to gdt_flat.segment_code, and to clear the instruction ; pipeline. jmp GDT_FLAT_IDX_CODE_32:.protected_mode_32 [bits 32] .protected_mode_32: ; Set the data segments to gdt_flat.segment_data. mov eax, GDT_FLAT_IDX_DATA mov ds, eax mov es, eax mov fs, eax mov gs, eax mov ss, eax ; Reset the stack. ; TODO: put the 32-bit stack somewhere else. mov ebp, REAL_STACK_BASE mov esp, ebp jmp S3_LOAD_ADDR .halt: hlt jmp .halt global s2_main section .s3_data gdt_flat_slice: dw GDT_FLAT_LEN dd GDT_FLAT_ADDR ; Segment descriptor layout ; | Range (bits) | Field | ; |--------------|---------------| ; | 0-16 | limit | ; | 16-32 | base | ; | 32-40 | base cont. | ; | 40-48 | access | ; | 48-52 | limit cont. | ; | 52-56 | flags | ; | 56-64 | base cont. | ; ; Flags ; - 0: reserved ; - 1: long-mode code segment ; - 2: size ; - unset: 16-bit ; - set: 32-bit ; - 3: granularity ; - unset: limit is measured in bytes ; - set: limit is measured in 4KiB pages ; ; Access ; - 0: accessed ; - unset: CPU will set it when the segment is accessed ; - 1: readable / writable ; - data segments: is segment writable (data segments are always readable) ; - code segments: is segment readable (code segments are never writable) ; - 2: direction / conforming ; - data segments: whether segment grows down ; - code segments: whether this can be executed from a lower-privilege ring ; - 3: executable ; - unset: this is a data segment ; - set: this is a code segment ; - 4: descriptor type ; - unset: this is a task state segment ; - set: this is a data or code segment ; - 5-6: privilege level (ring number) ; - 7: present (must be set) ; align 8 gdt_flat: ; First GDT entry must be 0. dq 0 ; 32-bit code segment. ; Pages 0x0000 - 0xffff. Needs to contain 0x10000, where the stage 3 text is loaded. .segment_code_32: db 0xff, 0xff, \ 0x00, 0x00, \ 0x00, \ 10011011b, \ 11001111b, \ 0x00 ; 16-bit code segment, to use if we want to switch back to real mode. ; Bytes 0x0000 - 0xffff. .segment_code_16: db 0xff, 0xff, \ 0x00, 0x00, \ 0x00, \ 10011011b, \ 00000000b, \ 0x00 ; Data segment. ; Pages 0x000000 - 0x0fffff, which covers the entire 32-bit address space (start of 0xfffff-th page ; is 0xfffff * 4096 = 0xfffff000, end of page exclusive is 0xfffff000 + 4096 = 0x100000000). .segment_data: db 0xff, 0xff, \ 0x00, 0x00, \ 0x00, \ 10010011b, \ 11001111b, \ 0x00 GDT_FLAT_LEN equ ($ - gdt_flat) GDT_FLAT_IDX_CODE_32 equ (gdt_flat.segment_code_32 - gdt_flat) global GDT_FLAT_IDX_CODE_32 GDT_FLAT_IDX_CODE_16 equ (gdt_flat.segment_code_16 - gdt_flat) global GDT_FLAT_IDX_CODE_16 GDT_FLAT_IDX_DATA equ (gdt_flat.segment_data - gdt_flat) global GDT_FLAT_IDX_DATA