I'm learning x86 assembly, and I'm trying to make a toy operating system in NASM, but I don't understand some things.
I made a bootloader that is successfully boots my kernel:
kernel.feo;0x2000;jmp 0x2000:0x0000.So I have the kernel code located at 0x2000:0 in the memory. CS might be properly set because the using of a far jump. In this kernel code, I want to enter 32-bit protected mode, but I'm not sure how GDTs are working. When I run the code below on a virtual machine (QEMU), it is don't do anything.
I want to please you to help me entering 32-bit protected mode!
That said, you have the following problems:
- You assume the code is loaded at
0x7c00:0due to theorg 0, but that might not be the case. The only thing guaranteed is the physical address. You should use a far jump to your entry point so thatCSis properly set.- You are for some reason setting
DSto0x2000so your code won't find any data at all. You should setDSto matchCS, or use aCSoverride everywhere (not recommended).- The protected mode code assumes zero-based segment, which in turn means it expects
org 0x7c00which of course conflicts with your setup. You should switch toorg 0x7c00and segments0.- The VGA text mode segment is at
0xb8000not0xb80000(one less zero).- You don't have the boot signature bytes
0x55 0xaaat the end of the boot sector.
I have corrected these things in my code:
[org 0x0] is corrected to [org 0x2000] and segments are set to 0;DS is corrected to 0 instead of 0x2000, so now it matches with CS;0xb8000;But the code won't work with these corrections, it should print two strings but it don't do anything!
Note that this kernel code should not end with a boot signature 0x55 0xAA, because it isn't a boot sector.
Here is the corrected kernel code (that not working):
[bits 16]
[org 0x2000]
    jmp 0:kernel_start
gdt_start:
gdt_null:
    dd 0x0
    dd 0x0
gdt_code:
    dw 0xffff
    dw 0x0
    db 0x0
    db 10011010b
    db 11001111b
    db 0x0
gdt_data:
    dw 0xffff
    dw 0x0
    db 0x0
    db 10010010b
    db 11001111b
    db 0x0
gdt_end:
gdt_descriptor:
    dw gdt_end - gdt_start
    dd gdt_start
CODE_SEG equ gdt_code - gdt_start
DATA_SEG equ gdt_data - gdt_start
print:
    mov ah, 14
    mov bh, 0
    lodsb
    cmp al, 0
    je .done
    int 0x10
    jmp print
.done:
    ret
uzenet_real db 'uzenet16', 0
uzenet_prot db 'uzenet32', 0
kernel_start:
    mov ax, 0
    mov ss, ax
    mov sp, 0xFFFC
    mov ax, 0
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    mov si, uzenet_real
    call print
    cli
    lgdt[gdt_descriptor]
    mov eax, cr0
    or eax, 0x1
    mov cr0, eax
    jmp CODE_SEG:b32
[bits 32]
VIDEO_MEMORY equ 0xb8000
WHITE_ON_BLACK equ 0x0f
print32:
    pusha
    mov edx, VIDEO_MEMORY
.loop:
    mov al, [ebx]
    mov ah, WHITE_ON_BLACK
    cmp al, 0
    je .done
    mov [edx], ax
    add ebx, 1
    add edx, 2
    jmp .loop
.done:
    popa
    ret
b32:
    mov ax, DATA_SEG
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    mov ss, ax
    mov ebp, 0x90000
    mov esp, ebp
    mov ebx, uzenet_prot
    call print32
    jmp $
Programming an OS is an advanced task. You are at least expected to be able to use a debugger to find your own mistakes and understand basic things. You might want to reconsider whether you have all the prerequisites for this endeavour.
That said, you have the following problems:
0x7c00:0 due to the org 0, but that might not be the case. The only thing guaranteed is the physical address. You should use a far jump to your entry point so that CS is properly set.DS to 0x2000 so your code won't find any data at all. You should set DS to match CS, or use a CS override everywhere (not recommended).org 0x7c00 which of course conflicts with your setup. You should switch to org 0x7c00 and segments 0.0xb8000 not 0xb80000 (one less zero).0x55 0xaa at the end of the boot sector.The fixed code:
[bits 16]
[org 0x7c00]
    jmp 0:kernel_start
gdt_start:
gdt_null:
    dd 0x0
    dd 0x0
gdt_code:
    dw 0xffff
    dw 0x0
    db 0x0
    db 10011010b
    db 11001111b
    db 0x0
gdt_data:
    dw 0xffff
    dw 0x0
    db 0x0
    db 10010010b
    db 11001111b
    db 0x0
gdt_end:
gdt_descriptor:
    dw gdt_end - gdt_start
    dd gdt_start
CODE_SEG equ gdt_code - gdt_start
DATA_SEG equ gdt_data - gdt_start
print:
    pusha
    mov ah, 14
    mov bh, 0
.loop:
    lodsb
    cmp al, 0
    je .done
    int 0x10
    jmp .loop
.done:
    popa
    ret
uzenet16 db 'uzenet16', 0
uzenet32 db 'uzenet32', 0
kernel_start:
    mov ax, 0
    mov ss, ax
    mov sp, 0xFFFC
    mov ax, 0
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    mov si, uzenet16
    call print
    cli
    lgdt[gdt_descriptor]
    mov eax, cr0
    or eax, 0x1
    mov cr0, eax
    jmp CODE_SEG:b32
[bits 32]
VIDEO_MEMORY equ 0xb8000
WHITE_ON_BLACK equ 0x0f
print32:
    pusha
    mov edx, VIDEO_MEMORY
.loop:
    mov al, [ebx]
    mov ah, WHITE_ON_BLACK
    cmp al, 0
    je .done
    mov [edx], ax
    add ebx, 1
    add edx, 2
    jmp .loop
.done:
    popa
    ret
b32:
    mov ax, DATA_SEG
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    mov ss, ax
    mov ebp, 0x2000
    mov esp, ebp
    mov ebx, uzenet32
    call print32
    jmp $
[SECTION signature start=0x7dfe]
dw 0AA55h
Your updated question still seems to be confused about where the code is loaded: you say offset 0x2000 but then talk about Executes the kernel using a far jump jmp 0x2000:0x0000 which is of course wrong, because it has one more zero in the segment, and should be a zero-segment far jump anyway: jmp 0:0x2000. Other than that, verify that your code is indeed loaded into memory at the correct place. Learn to use a debugger.
Here is a small boot sector which loads the above code from the second sector to address 0x2000. It works fine, the problem is not with the GDT stuff, especially if you don't even get the real mode message printed (you were not clear about that either).
[bits 16]
[org 0x7c00]
mov ax, 0201h
mov cx, 0002h
mov dh, 0
mov bx, 0
mov es, bx
mov bx, 2000h
int 13h
jmp 0:2000h
[SECTION signature start=0x7dfe]
dw 0AA55h
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With