GDT_MEMORY_AREA equ 0x7e00 GDT_ENTRY_COUNT equ 3 ; Arguments (note: all arguments are 4-byte padded): ; [Furthest from EBP] ; ; 4. uint32 privilege_level ; - Only 0 to 3; 0 being kernel and 3 being userland. ; Everything above 3 is an error. ; ; 3 uint32 num_pages ; - Only 20 bits are usable. Everything above will ; be cut off to be written to the segment descriptor. ; ; 2. ptr32 base_address ; ; 1. uint32 segment_index ; Note: ; - Zero is NOT usable and will throw an error. ; the highest theoretical value for this is 8191, ; but generally, one should stick to GDT_ENTRY_COUNT. ; ; [Nearest to EBP] make_regular_data_segment_in_gdt: .prolog: push esi add esp, 64 mov esi, esp mov [esi + (64 - 4)], eax mov [esi + (64 - 8)], ebx mov [esi + (64 - 12)], ecx mov [esi + (64 - 16)], edx mov [esi + (64 - 20)], edi .get_arguments: mov eax, [ebp - 4] ; segment_index mov ebx, [ebp - 8] ; base_address mov ecx, [ebp - 12] ; num_pages mov edx, [ebp - 16] ; privilege_level mov [esi + 16], eax ; segment_index mov [esi + 20], ebx ; base_address mov [esi + 24], ecx ; num_pages mov [esi + 28], edx ; privilege_level .validate_arguments: cmp eax, 8191 ja .segment_index_too_tall cmp eax, 0 je .segment_index_is_zero cmp edx, 3 ja .privilege_level_too_large .calculate_work_address: shl eax, 3 ; Entry offset from table start add eax, GDT_MEMORY_AREA ; Entry address mov [esi + 12], eax .write_segment_limit: mov [eax], cx shr ecx, 16 and ecx, 0x0f mov [eax + 6], cl .write_base_address: mov [eax + 2], bx shr bx, 16 mov [eax + 4], bl mov [eax + 7], bh .set_default_flags: or [eax + 6], byte (0b1100 << 4) .set_data_segment_specific_access_byte: mov al, 0b1001 ; OR the privilege level into the access byte mov dl, [ebp - 16] shl dl, 1 and dl, 0b0110 or al, dl shl al, 4 ; Move the 4 bits of general flags to their place ; Data-segment specific part or al, 0b0010 ; DATA | HEAP | READ_WRITE | NOT_ACCESSED mov ebx, [esi + 12] mov [ebx + 5], al ; Write to the final destination .epilog: mov edi, [esi + (64 - 20)] mov edx, [esi + (64 - 16)] mov ecx, [esi + (64 - 12)] mov ebx, [esi + (64 - 8)] mov eax, [esi + (64 - 4)] sub esp, 64 pop esi ret .segment_index_too_tall: .segment_index_is_zero: .privilege_level_too_large: ; todo(logs): Write debug message. cli hlt ; Arguments (note: all arguments are 4-byte padded): ; [Furthest from EBP] ; ; 4. uint32 privilege_level ; - Only 0 to 3; 0 being kernel and 3 being userland. ; Everything above 3 is an error. ; ; 3 uint32 num_pages ; - Only 20 bits are usable. Everything above will ; be cut off to be written to the segment descriptor. ; ; 2. ptr32 base_address ; ; 1. uint32 segment_index ; Note: ; - Zero is NOT usable and will throw an error. ; the highest theoretical value for this is 8191, ; but generally, one should stick to GDT_ENTRY_COUNT. ; ; [Nearest to EBP] ; COMMENTED OUT DUE TO BEING BUGGY! ; make_stack_segment_in_gdt: ; .prolog: ; push esi ; add esp, 64 ; mov esi, esp ; mov [esi + (64 - 4)], eax ; mov [esi + (64 - 8)], ebx ; mov [esi + (64 - 12)], ecx ; mov [esi + (64 - 16)], edx ; mov [esi + (64 - 20)], edi ; .get_arguments: ; mov eax, [ebp - 4] ; segment_index ; mov ebx, [ebp - 8] ; base_address ; mov ecx, [ebp - 12] ; num_pages ; mov edx, [ebp - 16] ; privilege_level ; mov [esi + 16], eax ; segment_index ; mov [esi + 20], ebx ; base_address ; mov [esi + 24], ecx ; num_pages ; mov [esi + 28], edx ; privilege_level ; .validate_arguments: ; cmp eax, 8191 ; ja .segment_index_too_tall ; cmp eax, 0 ; je .segment_index_is_zero ; cmp edx, 3 ; ja .privilege_level_too_large ; .calculate_work_address: ; shl eax, 3 ; Entry offset from table start ; add eax, GDT_MEMORY_AREA ; Entry address ; mov [esi + 12], eax ; .write_segment_limit: ; mov [eax], cx ; shr ecx, 16 ; and ecx, 0x0f ; mov [eax + 6], cl ; .write_base_address: ; mov [eax + 2], bx ; shr bx, 16 ; mov [eax + 4], bl ; mov [eax + 7], bh ; .set_default_flags: ; or [eax + 6], word (0b1100 << 4) ; .set_stack_specific_access_byte: ; mov al, 0b1001 ; ; OR the privilege level into the access byte ; mov dl, [ebp - 16] ; shl dl, 1 ; and dl, 0b0110 ; or al, dl ; shl al, 4 ; Move the 4 bits of general flags to their place ; ; Stack specific part ; or al, 0b0110 ; DATA | STACK | READ_WRITE | NOT_ACCESSED ; mov ebx, [esi + 12] ; mov [ebx + 5], al ; Write to the final destination ; .epilog: ; mov edi, [esi + (64 - 20)] ; mov edx, [esi + (64 - 16)] ; mov ecx, [esi + (64 - 12)] ; mov ebx, [esi + (64 - 8)] ; mov eax, [esi + (64 - 4)] ; sub esp, 64 ; pop esi ; ret ; .segment_index_too_tall: ; .segment_index_is_zero: ; .privilege_level_too_large: ; ; todo(logs): Write debug message. ; cli ; hlt ; Arguments (note: all arguments are 4-byte padded): ; [Furthest from EBP] ; ; 4. uint32 privilege_level ; - Only 0 to 3; 0 being kernel and 3 being userland. ; Everything above 3 is an error. ; ; 3 uint32 num_pages ; - Only 20 bits are usable. Everything above will ; be cut off to be written to the segment descriptor. ; ; 2. ptr32 base_address ; ; 1. uint32 segment_index ; Note: ; - Zero is NOT usable and will throw an error. ; the highest theoretical value for this is 8191, ; but generally, one should stick to GDT_ENTRY_COUNT. ; ; [Nearest to EBP] make_code_segment_in_gdt: .prolog: push esi add esp, 64 mov esi, esp mov [esi + (64 - 4)], eax mov [esi + (64 - 8)], ebx mov [esi + (64 - 12)], ecx mov [esi + (64 - 16)], edx mov [esi + (64 - 20)], edi .get_arguments: mov eax, [ebp - 4] ; segment_index mov ebx, [ebp - 8] ; base_address mov ecx, [ebp - 12] ; num_pages mov edx, [ebp - 16] ; privilege_level mov [esi + 16], eax ; segment_index mov [esi + 20], ebx ; base_address mov [esi + 24], ecx ; num_pages mov [esi + 28], edx ; privilege_level .validate_arguments: cmp eax, 8191 ja .segment_index_too_tall cmp eax, 0 je .segment_index_is_zero cmp edx, 3 ja .privilege_level_too_large .calculate_work_address: shl eax, 3 ; Entry offset from table start add eax, GDT_MEMORY_AREA ; Entry address mov [esi + 12], eax .write_segment_limit: mov [eax], cx shr ecx, 16 and ecx, 0x0f mov [eax + 6], cl .write_base_address: mov [eax + 2], bx shr bx, 16 mov [eax + 4], bl mov [eax + 7], bh .set_default_flags: or [eax + 6], word (0b1100 << 4) .set_code_segment_specific_access_byte: mov al, 0b1001 ; OR the privilege level into the access byte mov dl, [ebp - 16] shl dl, 1 and dl, 0b0110 or al, dl shl al, 4 ; Move the 4 bits of general flags to their place ; Code-segment specific part or al, 0b1010 ; CODE | NON-CONFORMING | READABLE | NOT_ACCESSED mov ebx, [esi + 12] mov [ebx + 5], al ; Write to the final destination .epilog: mov edi, [esi + (64 - 20)] mov edx, [esi + (64 - 16)] mov ecx, [esi + (64 - 12)] mov ebx, [esi + (64 - 8)] mov eax, [esi + (64 - 4)] sub esp, 64 pop esi ret .segment_index_too_tall: .segment_index_is_zero: .privilege_level_too_large: ; todo(logs): Write debug message. cli hlt ; [Furthest to EBP] ; 1) Return Address ; [Nearest to EBP] load_flat_gdt: .prolog: push esi add esp, 64 mov esi, esp mov [esi + (64 - 4)], eax mov [esi + (64 - 8)], ebx mov [esi + (64 - 12)], ecx mov [esi + (64 - 16)], edx mov [esi + (64 - 20)], edi ; mov eax, [ebp - 4] ; mov [esi], eax .set_segments: push ebp mov ebp, esp push dword 2 ; Segment Index push dword 0 ; Start Offset push dword 0xfffff ; Page Count push dword 0 ; Required privilege level call make_regular_data_segment_in_gdt mov esp, ebp pop ebp ; COMMENTED OUT DUE TO BEING BUGGY ; push ebp ; mov ebp, esp ; push dword 3 ; Segment Index ; push dword LOWER_STACK_POINTER ; Start Offset ; push dword 0xfffff ; Page Count ; push dword 0 ; Required privilege level ; call make_stack_segment_in_gdt ; mov esp, ebp ; pop ebp push ebp mov ebp, esp push dword 1 ; Segment Index push dword 0x0000 ; Start Offset push dword 0xfffff ; Page Count push dword 0 ; Required privilege level call make_code_segment_in_gdt mov esp, ebp pop ebp mov bx, 0 mov ds, bx lgdt [.gdtr] mov bx, (2 << 3) mov ds, bx mov es, bx mov fs, bx mov gs, bx ; mov bx, (3 << 3) mov ss, bx mov ebx, cr0 or ebx, 1 mov cr0, ebx jmp dword 0x08:.epilog bits 32 .epilog: mov edi, [esi + (64 - 20)] mov edx, [esi + (64 - 16)] mov ecx, [esi + (64 - 12)] mov ebx, [esi + (64 - 8)] mov eax, [esi + (64 - 4)] add esp, 64 pop esi jmp stage2_true_entry ; jmp after_gdt_loaded align 16 .gdtr: .gdtr.length: dw 23 .gdtr.pointer: dd GDT_MEMORY_AREA align 1