LinuxQuestions.org
Welcome to the most active Linux Forum on the web.
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 11-02-2006, 11:15 AM   #1
suchi
Member
 
Registered: Nov 2003
Location: india
Posts: 46
Blog Entries: 1

Rep: Reputation: 15
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 [ESI]
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 !
 
Old 11-03-2006, 03:46 AM   #2
suchi
Member
 
Registered: Nov 2003
Location: india
Posts: 46

Original Poster
Blog Entries: 1

Rep: Reputation: 15
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
 
Old 11-03-2006, 02:25 PM   #3
gnashley
Amigo developer
 
Registered: Dec 2003
Location: Germany
Distribution: Slackware
Posts: 4,928

Rep: Reputation: 612Reputation: 612Reputation: 612Reputation: 612Reputation: 612Reputation: 612
Just a stab in the dark, but maybe the program 'mkelf' could help you.
 
Old 11-04-2006, 11:27 AM   #4
suchi
Member
 
Registered: Nov 2003
Location: india
Posts: 46

Original Poster
Blog Entries: 1

Rep: Reputation: 15
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
 
Old 11-12-2006, 09:44 PM   #5
suchi
Member
 
Registered: Nov 2003
Location: india
Posts: 46

Original Poster
Blog Entries: 1

Rep: Reputation: 15
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 !
 
Old 11-15-2006, 06:42 AM   #6
jinksys
Member
 
Registered: Aug 2003
Location: 63123
Distribution: OpenSuSE/Ubuntu
Posts: 419

Rep: Reputation: 35
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
 
  


Reply


Thread Tools Search this Thread
Search this Thread:

Advanced Search

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
small probly stupid kernel question SciYro Linux - General 7 11-03-2006 10:53 AM
External USB SB Live 24 with snd-usb-audio: mixer too rudimentary LaoNiu Linux - Hardware 0 05-28-2006 10:22 AM
Small question about /etc/rc.local - Symbolic link question Arodef Linux - General 4 05-13-2006 02:29 AM
Rudimentary dependency checker :) Tinkster Slackware 3 08-18-2004 03:15 PM
Just a small question Mingthefirst Slackware 2 07-07-2004 09:12 PM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 02:48 AM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration