Problem with loading kernel from bootloader

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
yoav
Posts: 1
Joined: Fri May 01, 2009 11:29 am

Problem with loading kernel from bootloader

Post by yoav »

Hi

I followed some tutorials regarding writing a bootloader that will load my kernel. When I try to run it in bochs, I get the following error: LOCK prefix unallowed (op1=0x53, attr=0x0, mod=0x0, nnn=0)

In high level, I have a bootloader located at the first sector of the disk, and after that a 10 sector kernel made up of two files (start.s and kernel.c). The bootloader tries to load the kernel to memory while in real-mode, enter protected mode and jump to the loaded kernel. I am currently stuck when trying to read the sectors to 0x0:0x100000 (which is the value I got from one of the tutorials).

Note: If I change the address from 0x100000 to 0x2000 (in all places, linker script, etc) then the bootloader finishes loading the sectors but it fails to make the jump and bochs restarts and does the whole thing over again.

My entire project is listed below. I would appreciate some help :-)

Thanks,
Yoav.

makefile:

Code: Select all

clean:
    rm boot.img
    rm build/*
        
all:
    # build bootloader
    nasm -o build/bootloader.o bootloader2.s

    # build kernel
    nasm -f elf -o build/start.o start.s
    gcc -o build/kernel.o -O -c kernel.c -Wall -Werror -nostdlib -nostartfiles -nodefaultlibs    

    # link kernel
    ld -T linker.ld -o build/kernel.bin build/start.o build/kernel.o 

    # create the bootable image
    cat build/bootloader.o build/kernel.bin > boot.img
linker.ld:

Code: Select all

ENTRY (start)

SECTIONS{
    . = 0x00100000;

    .text :{
        *(.text)
    }

    .rodata ALIGN (0x1000) : {
        *(.rodata)
    }

    .data ALIGN (0x1000) : {
        *(.data)
    }

    .bss : {
        sbss = .;
        *(COMMON)
        *(.bss)
        ebss = .;
    }
}
bootloader2.s:

Code: Select all

[BITS 16]       ; We need 16-bit intructions for Real mode
[ORG 0x7C00]    ; The BIOS loads the boot sector into memory location 0x7C00
        
load_kernel:
    ; print "0" to indicate kernel is starting to load
    mov ah,0x0E
    mov al,48
    mov bl,0x07
    mov bh,0x00
    int 0x10
    ; Load the kernel to physical addresss 0x0:0x00100000
    mov bx,0x0
    mov es,bx
    mov bx,0x00100000
    ; Configure readscector BIOS function
    mov ah,0x02    ; read sector function code
    mov al,0x01    ; read 1 sector at a time
    mov ch,0x00    ; read from track 0    
    mov dl,0x00    ; read from drive 0 (floopy)
    mov dh,0x00     ; read from head 0
    mov cl,0x02    ; sector counter - start from the second sector (first sector is bootloader)
    
readsector:
    ; call the BIOS and handle errros
    int 0x13
    jc error
    ; if read enough sector, finish (10 sectors)
    cmp cl,0x0B
    je kernel_loaded
    ; if need to read more sectors, advance buffer and iterate
    add bx,WORD 0x200
    inc cl
    jmp readsector    

kernel_loaded:
    ; Print "D" to indicate that we're done with reading the kernel
    mov ah,0x0E
    mov bh,0x00
    mov bl,0x07
    mov al,68
    int 0x10
    cli                     ; Disable interrupts, we want to be alone
        xor ax, ax
        mov ds, ax              ; Set DS-register to 0 - used by lgdt
    lgdt [gdt_desc]         ; Load the GDT descriptor
        mov eax, cr0            ; Copy the contents of CR0 into EAX
        or al, 1                ; Set bit 0
        mov cr0, eax            ; Copy the contents of EAX into CR0
        jmp 08h:clear_pipe      ; Jump to code segment, offset clear_pipe

error:    
    ; Print "E" to the screen to indicate an error has occurred
    mov ah,0x0E
    mov bh,0x00
    mov bl,0x07
    mov al,69
    int 0x10
    ; Loop endlessly
    jmp $

[BITS 32]                             ; We now need 32-bit instructions
clear_pipe:
    sti
        mov ax, 10h                 ; Save data segment identifyer
        mov ds, ax                     ; Move a valid data segment into the data segment register
        mov ss, ax                  ; Move a valid data segment into the stack segment register
        mov esp, 090000h              ; Move the stack pointer to 090000h
        mov byte [ds:0B8002h], 'P'      ; Move the ASCII-code of 'P' into first video memory
        mov byte [ds:0B8003h], 1Bh      ; Assign a color code
    jmp 08h:0x00100000            ; Jump to the kernel

; Address for the GDT
gdt:
    gdt_null:               ; Null Segment
        dd 0
        dd 0
    gdt_code:               ; Code segment, read/execute, nonconforming
        dw 0FFFFh
        dw 0
        db 0
        db 10011010b
        db 11001111b
        db 0
    gdt_data:               ; Data segment, read/write, expand down
        dw 0FFFFh
        dw 0
        db 0
        db 10010010b
        db 11001111b
        db 0
gdt_end:                      ; Used to calculate the size of the GDT

gdt_desc:                       ; The GDT descriptor
        dw gdt_end - gdt - 1    ; Limit (size)
        dd gdt                   ; Address of the GDT

times 510-($-$$) db 0           ; Fill up the file with zeros
dw 0AA55h                      ; Boot sector identifyer
start.s:

Code: Select all

[BITS 32]
global start            ; making entry point visible to linker
extern kmain            ; kmain is defined elsewhere

start:
    call kmain                         ; call kernel
    hlt                                ; halt machine should kernel return
kernel.c:

Code: Select all

void kmain()
{
    // Print "K" to the screen at position 0,0
    //unsigned short *video = (unsigned short *)0XB8000;    
    //video[0] = 75;
    //video[1] = 0x07;
    for(;;);    
}
bochsrc:

Code: Select all

boot: floppy
floppya: 1_44="boot.img", status=inserted
clock: sync=realtime, time0=local
kay10
Member
Member
Posts: 30
Joined: Mon Apr 13, 2009 6:10 am
Location: Oldenburg, Germany

Re: Problem with loading kernel from bootloader

Post by kay10 »

Hi and welcome to the forum :)
yoav wrote:I get the following error: LOCK prefix unallowed (op1=0x53, attr=0x0, mod=0x0, nnn=0)
I think your bootoader executes some garbage code, caused by jumping to the wrong place in the memory.
My linkfile looks much the same as yours, the only bigger difference would be the following:

Code: Select all

OUTPUT_FORMAT("binary")
Maybe you can add this to the top of your "linker.ld" because I'm not sure whether LD will create a binary file for your, if you input ELF files.
yoav wrote:nasm -f elf -o build/start.o start.s
Binary files don't have a header, unlike ELFs, PEs etc., that are placed before the kernel in the memory.
With a binary, you don't have to calculate the "real" address of your kernel.
Or you look into this thread and try to apply it to your code.
http://forum.osdev.org/viewtopic.php?f= ... 33582ab9aa

Maybe some people in this forum know already that I don't like the ORG directive :mrgreen:
because I don't really know its behavior :oops: (which registers/memory get affected and so on). Anyway, I like to do this on my own.
So I can't tell you whether it has something to do with it or not.

By the way, you can't put numbers that are bigger than 0xFFFF in a 16 bit register.
yoav wrote:mov bx,0x00100000
I'm wondering NASM didn't complain about that.
The second thing I just saw is, you're trying to load the kernel above the 1 MB limit (0xFFFFF), that's not possible in real mode.
You could load it to an adress like 0x10000 and then copy it to a higher memory address, when you have switched into protected mode.

Sorry if you don't understand everything I'm trying to tell you, my english is not the best. :)

EDIT: Did you read tutorials about Real Mode Segmentation? It looks like that you don't really know how to access memory in real mode and the limits.
I told you to load the kernel at 0x10000, but 0x10000 don't fit into a 16 bit register (remember? 0x10000 > 0xFFFF) so real mode segmentation uses segments and offsets.
There are the following segment register: DS, ES, FS, GS (and CS, but I don't explain its special use now).
I think you can use every other register as an offset register, but I use the common ones, SI and DI.
To access the memory at 0x10000 you have to write the following:

Code: Select all

MOV AX, 0x1000
MOV DS, AX        ; DS = 0x1000
XOR AX, AX
MOV SI, AX        ; SI = 0
MOV DX, WORD [DS:SI]
Only an example.
The value in the segment register will be shifted 4 bits left (multiplication with 16) and then the offset gets added.
Example:
DS = 0x2000
SI = 0xFA0

0x2000 * 16 + 0xFA0 = 0x20FA0
or
DS = 0x20FA
SI = 0x0

0x20FA * 16 + 0x0 = 0x20FA0
You see, there are different possibilities to access the same memory.
I hope this will help you (and that I'm allowed to write that long posts :mrgreen: ).
Nombre
Posts: 1
Joined: Thu Jun 04, 2009 10:03 am

Re: Problem with loading kernel from bootloader

Post by Nombre »

My assembler knowledge is very very poor but I think this would be
lgdt [gdt_desc] ; Load the GDT descriptor
mov eax, cr0 ; Copy the contents of CR0 into EAX
or al, 1 ; Set bit 0
or eax, 1
mov cr0, eax ; Copy the contents of EAX into CR0
jmp 08h:clear_pipe ; Jump to code segment, offset clear_pipe

Sorry if I said a nonsense
User avatar
Combuster
Member
Member
Posts: 9301
Joined: Wed Oct 18, 2006 3:45 am
Libera.chat IRC: [com]buster
Location: On the balcony, where I can actually keep 1½m distance
Contact:

Re: Problem with loading kernel from bootloader

Post by Combuster »

My assembler knowledge is very very poor
Don't post if you don't know what you're talking about.
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
User avatar
kop99
Member
Member
Posts: 120
Joined: Fri May 15, 2009 2:58 am

Re: Problem with loading kernel from bootloader

Post by kop99 »

Code: Select all

    mov bx,0x0
    mov es,bx
    mov bx,0x00100000
    ; Configure readscector BIOS function
    mov ah,0x02    ; read sector function code
    mov al,0x01    ; read 1 sector at a time
    mov ch,0x00    ; read from track 0   
    mov dl,0x00    ; read from drive 0 (floopy)
    mov dh,0x00     ; read from head 0
    mov cl,0x02    ; sector counter - start from the second sector (first sector is bootloader)
   
readsector:
    ; call the BIOS and handle errros
    int 0x13
Are you gonna read sectors to address 0x100000?
Real mode can only address 20bit.
So, you couldn't access more high memory address than 1M.
Post Reply