small question on rudimentary kernel
It is a really small experiment i am doing and learning. I recently went through a website called Xosdev and downloaded files for a bootsector and kernel. After exploring a little bit i wanted to compile the boot sector and initial kernel given there(it is in no way a kernel, just something to print that i have gone to protected mode). If some kernel developer out there can have a look and atleast point me towards where the mistake is, i would be ever thankful to him for giving me knowledge. I have literally broken my head on this
There are 3 files boot.asm, k_init.asm and kernel.c k_init.asm is just having an entry point to k_main() function in kernel.c k_main() just does the job of assigning some value to some variable, for now Here is the code : <snip boot.asm> [bits 16] [org 0x7c00] jmp boot ; jump over the data to our code ;-----------------------Data-----------------------; ;------------GDT Table---------------; GDTR: GDTsize DW GDT_END-GDT-1 GDTbase DD GDT GDT: NULL_SEL EQU $-GDT ; null descriptor is required (64bit per entry) DD 0x0 DD 0x0 CODESEL EQU $-GDT ; 4GB Flat Code at 0x0 with max 0xFFFFF limit DW 0xFFFF ; Limit(2):0xFFFF DW 0x0 ; Base(3) DB 0x0 ; Base(2) DB 0x9A ; Type: present,ring0,code,exec/read/accessed (10011000) DB 0xCF ; Limit(1):0xF | Flags:4Kb inc,32bit (11001111) DB 0x0 ; Base(1) DATASEL EQU $-GDT ; 4GB Flat Data at 0x0 with max 0xFFFFF limit DW 0xFFFF ; Limit(2):0xFFFF DW 0x0 ; Base(3) DB 0x0 ; Base(2) DB 0x92 ; Type: present,ring0,data/stack,read/write (10010010) DB 0xCF ; Limit(1):0xF | Flags:4Kb inc,32bit (11001111) DB 0x0 ; Base(1) GDT_END: ;----------End GDT Table-------------; ;------------Variables---------------; start_s DB 0 ; starting sector [0x1-0x12] total DB 0 ; number of sector [max 2880] track DB 0 ; track number [max 160] head DB 0 ; head number [max 2] drive DB 0 ; boot drive number [usually 0] bseg DB 0 ; memory address segment boff DW 0 ; and offset to load into ;----------End Variables-------------; ;------------Functions---------------; ;; 'wait keyboard to clear' function ;; wkc: xor al,al in al, 0x64 ; get kbd status test al, 2 ; is bit 1 clear? jnz wkc ; if not wait some more ret ;; 'wait keyboard to be full' function ;; wkf: xor cx,cx in al, 0x64 ; get kbd status test al, 1 ; is bit 0 clear? jz wkf ; if not wait some more ret ;; 'halt on error' function ;; halt: mov byte [gs:0],al mov byte [gs:1],0x04 cli hlt ;----------End Functions-------------; ;---------------------End Data---------------------; boot: mov [drive],dl ; save boot drive number(0x00=floppy 0x80=hard drive) mov ax,cs ; setup ds segment mov ds,ax mov es,ax mov fs,ax mov ss,ax ; align stack also mov sp,0x200 ; 512 byte stack mov ax,0xb800 ; setup video segment mov gs,ax jmp init ; Some BIOSes jump to 0x7c0:0x0 rather than 0x0:0x7c0 init: ;enable a20 cli call wkc ; wait for kbd buffer to clear mov al,0xd1 ; tell it we want to write to output port out 0x64,al call wkc ; wait again for kbd to clear mov al,0xdf ; set desired settings (A20 gate) out 0x60,al ; send value to data reg call wkc ; wait for kbd to clear mov cx,0x10 kbdwait: xor ax,ax ; do anything out 0xe0,ax ; some mor nonsense loop kbdwait ; loop to waste time ;check if a20 was enabled mov al,0xd0 out 0x64,al ; tell kbdc we want to read output port call wkf ; wait for data to get in it in al,0x60 ; get it test al,2 ; test if A20 is on jnz a20_on ; if it is clear, then it is off mov al,'A' ; Error: A20 gate not enabled, halt pc call halt a20_on: sti ; move GDT to 0x500 xor ax,ax mov ds,ax mov es,ax mov si,GDT ; Move From [DS:SI] mov di,[GDTbase] ; Move to [ES:DI] mov cx,[GDTsize] ; size of GDT cld ; Clear the Direction Flag rep movsb ; Move it cli ;enter pmode mov eax,cr0 or al,1 mov cr0,eax ;load gdt lgdt[GDTR] read: xor ax,ax ; Floppy Reset BIOS Function mov dl,[drive] ; Select floppy that was booted from int 0x13 jc read mov ax,0xffff ; Doubtful, it says 0x100000, and it is 0xffff here mov es,ax ; Data buffer for file mov bx,0x10 ; Start of segment mov ah,2 ; Function to read disk mov al,17 ; Total sectors to read mov ch,0 ; Track mov cl,2 ; Sector mov dh,0 ; Head | Drive is already loaded int 0x13 ; Call BIOS read disk function jc read ; motor error, try again mov ax,0xffff ; Doubtful, it says 0x100000, and it is 0xffff here mov es,ax mov bx, 0x2210 mov ah,2 ; Function to read disk mov al,18 ; Total sectors to read mov ch,0 ; Track mov cl,1 ; Sector mov dh,1 ; Head | Drive is already loaded int 0x13 mov dx,0x3F2 ; stop the motor mov al,0x0C ; from spinning out dx,al ;clear cs/ip/eip jmp CODESEL:FLUSH ; set cs to CODESEL [bits 32] FLUSH: ;refresh all segment registers mov eax,DATASEL mov ds,eax mov es,eax mov fs,eax mov gs,eax mov ss,eax mov esp,0xffff ;jump to k_init.asm jmp CODESEL:0x100000 hlt TIMES 510-($-$$) DB 0 SIGNATURE DW 0xAA55 </snip boot.asm> <snip k_init.asm> [bits 32] ; 32bit code here [global start] ; start is a global function [extern _k_main] ; this is the kernel function start: call _k_main ; jump to k_main() in kernel.c hlt ; halt the cpu </snip k_init.asm> <snip kernel.c> void k_main() { int num =5; /* To be modified later, first let me come here */ } </snip kernel.c> For compiling, i did the following nasm -o boot.bin boot.asm nasm -o k_init.o -f elf k_init.asm (because i can't compile it with the C code of kernel.c (ie., kernel.o ) if it is not in elf format) gcc -Wall -W -O2 -nostdinc -fno-builtin -c kernel.c ld kernel.o k_init.o -o kernel.bin dd if=/dev/zero of=flp.img bs=512 count=2880 dd if=boot.bin of=flp.img conv=notrunc dd if=kernel.bin of=flp.img seek=1 conv=notrunc Then use bochs for booting Everything is fine, just that, under bochs it just reboots a 100 times and fills the bochsout.txt. I feel there is something wrong while moving GDT to 0x500 step. because i have debugged using print statements and it prints till that point. A 1000 Thanks if somebody can just point the mistake, even if it is just the compiling step (maybe i must have used a.out format !...? !!) Don't know Thanks in advance ! |
Alright, i now understand something more
Alright, then interrupts are not allowed in protected mode (we do a "cli" before going to protected mode). Now, one more step, i tried it again with some modification, but still though i am able to print some string in protected mode using :
mov byte [ds:0xb8000], 'P' mov byte [ds:0xb8001], <attribute> commands, i am not able to jump to the kernel that has been written using C. Why is that not happening ? Is there anything wrong with the file format in which i compiled the stuff (aout and elf) ? I have heard that elf does not allow mentioning "_main() or _Kernel_start()" type of calls from assembly into C program functions |
Just a stab in the dark, but maybe the program 'mkelf' could help you.
|
Got it !
Yes,
I was wrong. What i was doing is, that after going into protected mode, i was trying to load kernel to memory < 1mb. Now it works fine |
So here it is. This was in some french website. The concept is thus :
1. Don't make an a20 line enable 2. Make the jump to the start of C Kernel in C itself 3. Initially, when copying the C Kernel (binary) from floppy to memory, we can now copy it to a location within 1mb itself (say 0x1000), and after we are in our C Kernel, we can do the rest of the job 4. Not doing A20 enable does not affect us because we are anyway going to protected mode, where we can access more than 1mb (=4GB) RAM at some stage ! Here is the code : <boot.asm> %define CS_ACCES 10011011b %define DS_ACCES 10010011b [bits 16] [org 0x0] ; Begins at 0x0:Whatever you define next jmp boot %include "GDT.INC" boot: mov ax,0x07C0 ; Define the beginning of boot at 0x0000:0x07c0 mov ds,ax mov es,ax mov ax,0x8000 ; Define Stack mov ss,ax mov sp,0xf000 ; Stack begins at 0xf000 and fills from there ; Read the kernel binary from floppy to your memory location ; at 0x100:0x0000 ; In real mode 0x7c00 = 0x7c0:0 or 0x7c0:0 = 0x07c0 shifted left once + 0 ; so 0x1000 = 0x100:0 = 0x0100 shifted left once + 0 ; 0x1000 = es, 0x0000 = bx ; First restart the motor xor ax,ax int 0x13 push es ; es has stack defined mov ax,0x100 mov es,ax mov bx,0 mov ah,2 mov al,30 mov ch,0 mov cl,2 mov dh,0 mov dl,0 int 0x13 pop es; Now es holds stack addresss ; Fill in the GDT now, before doing the lgdt ; Because GDT is all 0's now descInit 0,0xFFFFF,CS_ACCES,1101b,gdt_cs descInit 0,0xFFFFF,DS_ACCES,1101b,gdt_ds ; Before doing lgdt, we must supply two parameters to lgdt ; One is the limit, and the other is the linear address of GDT ; Refer to the GDT structure below ; Size of gdt = gdtend - gdt mov ax, gdtend mov bx, gdt sub ax,bx mov word [gdtptr], ax ; Linear address xor ax,ax ; Clear eax mov ax,ds ; Move segment 0x7c0 to ax mov bx,gdt; Move offset to bx call calcadr; Calculate linear address and store it in ecx mov dword [gdtptr+2], ecx ; Move it to gdtptr ; Load gdt using lgdt. Disable interrupts before that cli lgdt[gdtptr] ; Move to protected mode. Set cr0's first bit to 1 by oring it mov eax,cr0 or ax,1 mov cr0,eax ; Re define segments and offsets in protected mode ; cs -> as defined by gdt ie., using jmp instruction ; Define ds -> as the pointer to the data segment register's ; displacement with gdt, ie 0x10 jmp next next: mov ax,0x10 mov ds,ax mov es,ax mov fs,ax mov gs,ax mov ss,ax ; Stack also begins at data segment mov esp,0x9f000 ; Stack begins filling at this address ; code segment pointed by cs register at a displacement of 0x8 bytes ; from the beginning of gdt ; Now jump to the location of kernel binary w.r.t. code segment ; protected mode segmented address = cs:0x1000 jmp dword 0x8:0x1000 end: jmp end ; Just in case it slips the earlier step ! ; Definition of Zeroed out GDT ; GDT initially has 0's in all places ; GDT has 3, 8 byte segment descriptors defined ; The three are, NULL, Code segment and Data segment descriptor ; Remember the following are all variables gdt: gdt_null: dw 0,0,0,0 gdt_cs: dw 0,0,0,0 gdt_ds: dw 0,0,0,0 gdtend: ; gdtptr is supposed to be passed to lgdt. It has been zeroed in the beginning gdtptr: dw 0x0000 ; 16 bit size of GDT dd 0 ; 32 bit linear address of GDT ; Fill the rest of 510 bytes with NOP (0x144) times 510-($-$$) db 144 dw 0xaa55 ; The signature of boot sector </boot.asm> <kernel.c> void _start(void) { main(); } int main(void){ unsigned char *video = (unsigned char *) 0xb8000; char *string = "Kernel loaded"; while (*string !=0) { *video = *string; *video++; *video = 0x07; *video++; *string++; }; while(1); } </kernel.c> <GDT.INC> ;-------------------------------------------------------------------- ; CalcAdr ; Entree: ; ax -> segment ; bx -> offset ; Sortie: ; ecx -> adresse lineaire sur 32 bits ; Modifie: partie haute de ebx ;-------------------------------------------------------------------- calcadr: xor ecx,ecx mov cx,ax shl ecx,4 and ebx,0x0000FFFF add ecx,ebx ret ;-------------------------------------------------------------------- ;-------------------------------------------------------------------- ; descInit ;-------------------------------------------------------------------- %macro descInit 5 ; base(32),limite(20/32),acces(8),flags(4/8),adresse(32) push eax ; base : mov eax,%1 mov word [%5+2],ax shr eax,16 mov byte [%5+4],al shr eax,8 mov byte [%5+7],al ; limite : mov eax,%2 and eax,0x000FFFFF mov word [%5],ax ; ecrit (0..15) shr eax,16 ; place (16..19) sur le nibble inferieur mov byte [%5+6],0 ; initialise flags+lim(16..19) a 0 or [%5+6],al ; ecrit (16..19) ; flags : mov al,%4 and al,0x0F shl al,4 or [%5+6],al ; acces : mov byte [%5+5],%3 pop eax %endmacro ;-------------------------------------------------------------------- %define LDT_FLAG 0x2 ; ldt %define TASK_FLAG 0x5 ; task gate %define TSS_FLAG 0x9 ; TSS %define CALL_FLAG 0xC ; call gate %define INT_FLAG 0xE ; interrupt gate %define TRAP_FLAG 0xF ; trap gate %define PRESENT_F 0x80 ; present flag %define DPL0 0x00 %define DPL1 0x20 %define DPL2 0x40 %define DPL3 0x60 ;-------------------------------------------------------------------- ; intdesc ;-------------------------------------------------------------------- %macro gateInit 5 ; selecteur(16),offset(32),type (8),wc(5/8),adresse(32) push eax ; selecteur : mov word [%5+2],%1 ; offset : mov eax,%2 mov word [%5],ax shr eax,16 mov word [%5+6],ax ; type : mov byte [%5+5],%3 ; wc : mov byte [%5+4],%4 and byte [%5+4],00011111b pop eax %endmacro ;-------------------------------------------------------------------- </GDT.INC> <Compile steps> gcc -w -c kernel.c -o kernel.o nasm -f bin boot boot.asm ld -Ttext 0x1000 --oformat binary kernel.o -o kernel.bin cat boot kernel.bin > file.flp Use bochs to boot the image file.flp </Compile steps> Finally, thanks for all, This might help others. So let this post be here ! |
It seems like a lot of first-time kernel writers always start with writing a working boot loader (myself being one of them long ago). Getting yourself into 32-bit land can be a major pain in the butt. I suggest letting a multi-boot compliant loader like GRUB handle the setup and load your kernel.
http://www.osdev.org/osfaq2/index.php/BareBones |
All times are GMT -5. The time now is 03:00 AM. |