Search code examples
assemblyx86nasmosdevprotected-mode

OS development : switching to 32bit mode breaks my code


EDIT: My problem solved when I move everything after BITS 32 to disk_double_error but now it is still broken yesy it displays the message but then when i hit Y it just restarts/reboots the system What may be the reason for that ?

(sry for bad english) Hello I am trying to write a simple os just for fun I made my bootloader and I displayed some messages and read the disk but when it comes to switching to 32 bit protected mode all of the things I did doesnt work It normally displayed

`> `
`>succesfully loaded  = if loaded succesfully > failed = if not `
`>Do you want to boot kernel mode Y/N       then gets input`
`>Booting in kernel mode = if Y   null = if no`

But after i added lgdt and other switches to 32 bit it just doesnt display anything at all so I thought that switching is somehow clearing the screen so i tried to print things in pm with print_string_pm function but still nothing I am using

nasm -fbin bootload.asm -o bootload.bin

to compile the bootloader

What may be wrong with my code please help ?

here is the code:

BITS 16
[ORG 0x7c00]
global _boot_start

_boot_start:

    cli
    mov bx,0x9000
    mov bp,bx
    mov sp,bx
    sti 

    pusha
    mov ax, 0x0000     
    mov ds, ax         
    mov es, ax
    popa

    mov [bootdev], dl


    mov bx, MSG_START       
    call print_string


    mov dl, [bootdev]             
    mov dh, 1             
    mov bx, 0x1000         
    call disk_load
    mov bx, MSG_YESNO
    call print_string

    call get_yes_no

    mov bx, MSG_BOOT_KERNEL
    call print_string



    lgdt [gdt_descriptor]

    mov eax , cr0 
    or eax , 0x1 
    mov cr0 , eax

    jmp CODE_SEG:pm

    BITS 32

    pm:


    mov ax , DATA_SEG
    mov ds , ax 
    mov ss , ax 
    mov es , ax
    mov fs , ax
    mov gs , ax

    mov ebp , 0x90000 
    mov esp , ebp

    call beg_pm

    beg_pm:

    mov ebx,MSG_BOOT_32
    call print_string_pm

    call KERNEL_OFFSET

    jmp $

;******************************************************************
print_string:       

.loop:
    mov al,[bx]
    cmp al,0
    je return
    push bx
    mov ah,0Eh

    int 10h
    pop bx
    inc bx
    jmp .loop

return:
    ret 

;******************************************************************
get_yes_no:
    pusha 
.loop:
    mov ah, 00h
    int 16h
    cmp ah, 15h
    je .yes
    cmp ah, 31h
    je .no
    jmp .loop
    ret
.no:
    mov bx, No
    call print_string
    mov ah, 00h
    int 13h 
    jmp $
.yes:   
    mov bx, Yes
    call print_string
    jmp .done
.done:
    popa
    ret 
;******************************************************************
disk_load:
    push dx
    mov ah, 02h    
    mov al, dh    
    mov ch, 0x00   
    mov dh, 0x00  
    mov cl, 0x02   
    int 0x13       
    pop dx
    jc disk_error 
    cmp dh, al 
    jne disk_error_double
    mov bx,MSG_LOAD_SUCC
    call print_string
    ret 

disk_error:
    mov bx,MSG_LOAD_FAIL 
    call print_string
    jmp $
disk_error_double:
    mov bx,MSG_LOAD_FAIL_C
    call print_string
    jmp $

;******************************************************************
print_string_pm :
    pusha
    mov edx , VIDEO_MEMORY ; Set edx to the start of vid mem.
    .print_string_pm_loop :
    mov al , [ ebx ] ; Store the char at EBX in AL
    mov ah , WHITE_ON_BLACK ; Store the attributes in AH
    cmp al , 0 ; if (al == 0), at end of string , so
    je .print_string_pm_done ; jump to done
    mov [edx], ax ; Store char and attributes at current
    ; character cell.
    add ebx , 1 ; Increment EBX to the next char in string.
    add edx , 2 ; Move to next character cell in vid mem.
    jmp .print_string_pm_loop ; loop around to print the next char.
    .print_string_pm_done :
    popa
    ret 

; Global Descriptor table
; GDT
gdt_start :
gdt_null : ; the mandatory null descriptor
dd 0x0 ; 'dd ' means define double word ( i.e. 4 bytes )
dd 0x0
gdt_code : 
dw 0xffff ; Limit ( bits 0 -15)
dw 0x0 ; Base ( bits 0 -15)
db 0x0 ; Base ( bits 16 -23)
db 10011010b ; 1st flags , type flags
db 11001111b ; 2nd flags , Limit ( bits 16 -19)
db 0x0 ; Base ( bits 24 -31)
gdt_data : 
dw 0xffff ; Limit ( bits 0 -15)
dw 0x0 ; Base ( bits 0 -15)
db 0x0 ; Base ( bits 16 -23)
db 10010010b ; 1st flags , type flags
db 11001111b ; 2nd flags , Limit ( bits 16 -19)
db 0x0 ; Base ( bits 24 -31)
gdt_end : 

gdt_descriptor :
dw gdt_end - gdt_start - 1 
dd gdt_start 

CODE_SEG equ gdt_code - gdt_start
DATA_SEG equ gdt_data - gdt_start   




MSG_START DB ">",13,10,0
MSG_LOAD_SUCC DB "> Succesfully loaded",13,10,0
MSG_LOAD_FAIL DB "> Failed to load Please try to restart the system",13,10,0
MSG_LOAD_FAIL_C DB "> Error while loading",13,10,0
MSG_YESNO DB "> Do you want to boot up in kernel mode Y/N :",0
MSG_BOOT_KERNEL DB 13,10,"> Booting in kernel mode",0
MSG_BOOT_32 DB "32 bit pm",13,10,0
Yes db "Y",0
No db "N",0
bootdev: db 0
KERNEL_OFFSET equ 0x1000 
VIDEO_MEMORY equ 0xb8000
WHITE_ON_BLACK equ 0x0f

times 510-($-$$) db 0
dw 0xaa55

And here is my kernel code:

int kmain()
{


    char* vm = 0xb8000;
    *vm = 'X';


}

and my kernel entry

[BITS 32]
[extern _kmain]

call _kmain 
jmp $

I use follwing lines to build my disk image:

nasm -fbin bootload.asm -o bootload.bin
gcc -ffreestanding -c kernel.c -o kernel.o
nasm kernel_entry.asm -f elf -o k_entry.o
ld -T NUL -o kernel.tmp -Ttext 0x1000 k_entry.o kernel.o
objcopy -O binary -j .text  kernel.tmp kernel.bin
copy /b bootload.bin+kernel.bin os-image.bin 

Solution

  • I'm going to ignore issues with setting SP without SS and other issues. See the comments under the question for things you can do to clean up the code.

    Your primary problem is in switching to 32-bit instruction encoding with bits 32. Once you set encoding to 32-bit all the instructions after it will be encoded as 32-bit until you change (with bits 16). The easiest thing you can do is move the code for label pm to a point past all your 16-bit code. Move all these lines (deleting the original):

    BITS 32
    
    pm:
    
    mov ax , DATA_SEG
    mov ds , ax 
    mov ss , ax 
    mov es , ax
    mov fs , ax
    mov gs , ax
    
    mov ebp , 0x90000 
    mov esp , ebp
    
    call beg_pm
    
    beg_pm:
    
    mov ebx,MSG_BOOT_32
    call print_string_pm
    
    call KERNEL_OFFSET
    
    jmp $
    

    to the beginning of the 32-bit code you have. That appears to be the print_string_pm label. So when moved it would look like:

        [snip]
    disk_error_double:
        mov bx,MSG_LOAD_FAIL_C
        call print_string
        jmp $
    
        BITS 32
    
        pm:
    
        mov ax , DATA_SEG
        mov ds , ax 
        mov ss , ax 
        mov es , ax
        mov fs , ax
        mov gs , ax
    
        mov ebp , 0x90000 
        mov esp , ebp
    
        call beg_pm
    
        beg_pm:
    
        mov ebx,MSG_BOOT_32
        call print_string_pm
    
        call KERNEL_OFFSET
    
        jmp $
    
    ;******************************************************************
    print_string_pm :
        pusha
    
        [snip]
    

    The code for disk_error_double was the last of your 16-bit code, print_string_pm was the beginning of the 32-bit code. We simply place the pm code after disk_error_double and before print_string_pm.


    Before entering protected mode you will need to turn off interrupts until you set up an Interrupt Descriptor Table (IDT). Place a cli instruction before mov cr0 , eax. Failure to do this will cause a triple fault and reboot when the first interrupt occurs while in protected mode.