I haven't used GRUB, but what I gather from the Multiboot specification is that GRUB initializes a protected-mode environment, loads your code (be it a kernel or another bootloader) at an address that you specify (which however needs to be above the 1M mark), and executes it. You are required to load a new GDTR and IDTR, and provide a Multiboot header somewhere in the first 4096 bytes of the image to be loaded, if you don't use the ELF file format.InsightSoft wrote:This descriptor definition are made at C 32 code, right?
What I'm trying to do (maybe in a wrong way) is create the correct environment to execute the C code...
the tutorial: it seems to me that grubs bring the machine to the PM with simplest (or necessary) 32 bit descriptor to fit next 'to be loaded' 32 bit code (at 0x0010000)... (and the asm+c are compiled to start at that address [definition at link.ld script file])
This code, after jumped will set a new descriptors... (the real kernel specification)
I'm completely wrong?
What's problem with this code?
Re: What's problem with this code?
-
- Member
- Posts: 76
- Joined: Mon Aug 18, 2008 6:17 am
Re: What's problem with this code?
That's way I love this game!!!
I'm starting getting lost and... here I am, trying to figure it out!!! (:
Reviewing my code:
1) Boot
-load {Kernel.Stage.1} [asm 16 bits] to 7000:0000;
2) Kernel.Stage.1 (7000:0000) (16 bits)
-create and fill a simple GDT
gdt_tbl.dummy (0) - (you know...)
gdt_tbl.ks1code (0x9a, 0x0) - For first jmp to PM
gdt_tbl.ks1video (0x92, 0x0) - for simple print to...
gdt_tbl.ks2code (0x9a, 0xcf) - for the kernel final stage (asm+c 32 bit code) [9000:0000]
gdt_tbl.c32stack (0x92, 0xC0) - for stack 32 bit operations
gdt_tbl.c32code (0x9a, 0xcf) - not necessary/not used
gdt_tbl.c32data (0x92, 0xcf) - for DS, ES, etc
load code [asm+c] to 9000:0000
fill the addresses...
enable A20
jmp to PM (descriptor
already in PM
jmp to 24:0 (9000:0000)
3) at asm+c (9000:0000 or descriptor 24:0)
-asm
point SS to the 0x20:ffff (only 65535 bytes)
point DS, ES, FS and GS to descriptor 48
call C main function
-c main function
call gdt_install();
- define gp and gdt
gp.limit = (sizeof(struct gdt_entry) * 3) - 1;
gp.base = &gdt;
gdt_set_gate(0, 0, 0, 0, 0);
gdt_set_gate(1, 0, 0xFFFFFFFF, 0x9A, 0xCF);
gdt_set_gate(2, 0, 0xFFFFFFFF, 0x92, 0xCF);
works fine until next function...
gdt_flush(); (defined previous asm file) - crash here
...and as told (ru2aqare), I need to compensate the p.a. of _gp with the offset of starting point of the asm code...
But what's wrong on my sadly way to replace the grub (of course, on the very simple task, load and run the 'kernel')?...
And of course, i'm sorry for boring you with this kind of problems...
It has been a real bit by bit approach... sorry (:
I'm starting getting lost and... here I am, trying to figure it out!!! (:
Reviewing my code:
1) Boot
-load {Kernel.Stage.1} [asm 16 bits] to 7000:0000;
2) Kernel.Stage.1 (7000:0000) (16 bits)
-create and fill a simple GDT
gdt_tbl.dummy (0) - (you know...)
gdt_tbl.ks1code (0x9a, 0x0) - For first jmp to PM
gdt_tbl.ks1video (0x92, 0x0) - for simple print to...
gdt_tbl.ks2code (0x9a, 0xcf) - for the kernel final stage (asm+c 32 bit code) [9000:0000]
gdt_tbl.c32stack (0x92, 0xC0) - for stack 32 bit operations
gdt_tbl.c32code (0x9a, 0xcf) - not necessary/not used
gdt_tbl.c32data (0x92, 0xcf) - for DS, ES, etc
load code [asm+c] to 9000:0000
fill the addresses...
enable A20
jmp to PM (descriptor
already in PM
jmp to 24:0 (9000:0000)
3) at asm+c (9000:0000 or descriptor 24:0)
-asm
point SS to the 0x20:ffff (only 65535 bytes)
point DS, ES, FS and GS to descriptor 48
call C main function
-c main function
call gdt_install();
- define gp and gdt
gp.limit = (sizeof(struct gdt_entry) * 3) - 1;
gp.base = &gdt;
gdt_set_gate(0, 0, 0, 0, 0);
gdt_set_gate(1, 0, 0xFFFFFFFF, 0x9A, 0xCF);
gdt_set_gate(2, 0, 0xFFFFFFFF, 0x92, 0xCF);
works fine until next function...
gdt_flush(); (defined previous asm file) - crash here
...and as told (ru2aqare), I need to compensate the p.a. of _gp with the offset of starting point of the asm code...
But what's wrong on my sadly way to replace the grub (of course, on the very simple task, load and run the 'kernel')?...
And of course, i'm sorry for boring you with this kind of problems...
It has been a real bit by bit approach... sorry (:
Re: What's problem with this code?
If you have a working GDT when the C code receives control, why do you need to replace it? Other that that, I can't imagine why it triple faults.
-
- Member
- Posts: 76
- Joined: Mon Aug 18, 2008 6:17 am
Re: What's problem with this code?
Well, I just want to execute the the C kernel code from the tutorial (without grub)... it's all I want...
but, anyway, it seems that I need to resolve the address of _gp...
the major problem is that I'm trying to accommodate that code to a new reality... while I'm starting to learn this first stage of OS dev... but, I will be there (:
but, anyway, it seems that I need to resolve the address of _gp...
the major problem is that I'm trying to accommodate that code to a new reality... while I'm starting to learn this first stage of OS dev... but, I will be there (:
Re: What's problem with this code?
If you know at compile time where your code will be loaded, then calculating the address of _gp is easy.InsightSoft wrote:Well, I just want to execute the the C kernel code from the tutorial (without grub)... it's all I want...
but, anyway, it seems that I need to resolve the address of _gp...
the major problem is that I'm trying to accommodate that code to a new reality... while I'm starting to learn this first stage of OS dev... but, I will be there (:
Code: Select all
call _prnt
mov ebx, 09000h*16 ; < segment where the code is loaded * 16
lgdt [ebx + _gp]
call _prnt
-
- Member
- Posts: 76
- Joined: Mon Aug 18, 2008 6:17 am
Re: What's problem with this code?
for sure it will help... I'm eager for information... I eat everything... (i'm just kidding)...
You can, if you want, send to [email protected] or if you prefer, to [email protected]...
I know that the code will be loaded to 9000:0000 (pa 90000)... and it will be something like [90000 + offset of _gp]... (I hope it works...) ...
You can, if you want, send to [email protected] or if you prefer, to [email protected]...
I know that the code will be loaded to 9000:0000 (pa 90000)... and it will be something like [90000 + offset of _gp]... (I hope it works...) ...
-
- Member
- Posts: 76
- Joined: Mon Aug 18, 2008 6:17 am
Re: What's problem with this code?
no way...
let start in hard way...
to compile 16 bit code
to compile a 32 bit code
let start in hard way...
Code: Select all
[BITS 16]
;structures
struc gdt_adr
.limit resw 1
.base resd 1
endstruc
struc gdt_tbl
.dummy resb 8
.ks1code resb 8
.ks1video resb 8
.ks2code resb 8
.c32stack resb 8
.c32code resb 8
.c32data resb 8
endstruc
_ptr equ 0x0
;External functions
;the code
global start
start:
xor eax, eax
mov ax, cs
mov ds, ax
shl eax, 4
add eax, gdt_tbl_start + _ptr
mov [gdt + gdt_adr.base + _ptr], eax
;kernel stage 1 gdt.limit
mov Eax, gdt_tbl_finish - gdt_tbl_start
mov [gdt + gdt_adr.limit + _ptr], ax
;code
xor eax, eax
mov ax, cs
and eax, 0xFFFF
shl eax, 4
add ax, start + _ptr
mov word [gdt_tbl_start + gdt_tbl.ks1code + _ptr + 2], ax
shr eax,16
mov byte [gdt_tbl_start + gdt_tbl.ks1code + _ptr + 4], al
;stack for 32 bits code
xor eax, eax
mov ax, 0x8000
and eax, 0xFFFF
shl eax, 4
add ax, 0
mov word [gdt_tbl_start + gdt_tbl.c32stack + _ptr + 2], ax
shr eax,16
mov byte [gdt_tbl_start + gdt_tbl.c32stack + _ptr + 4], al
;32Bit code
xor eax, eax
mov ax, 0x9000
and eax, 0xFFFF
shl eax, 4
add ax, 0
mov word [gdt_tbl_start + gdt_tbl.ks2code + _ptr + 2], ax
mov word [gdt_tbl_start + gdt_tbl.c32data + _ptr + 2], ax
shr eax,16
mov byte [gdt_tbl_start + gdt_tbl.ks2code + _ptr + 4], al
mov byte [gdt_tbl_start + gdt_tbl.c32data + _ptr + 4], al
cli
call Enable_A20
sti
;Load final kernel stage
mov ax, 0x9000
mov es, ax
xor bx, bx
mov ah, 0x02 ; service 2 (read disk sector)
mov al, 0x10 ; number of sector to read [(512 x 2=1024) = 512=CPU; 512=Library]
mov ch, 0x00 ; track/cylinder number (0-1023)
mov cl, 0x03 ; sector number (1-17)
mov dh, 0x00 ; head number (0-15)
mov dl, 0x00 ; drive a:
int 0x13 ; action
jnc _set_gdt
mov ax, 0
int 0x16
db 0xEA
dw 0x0000
dw 0xffff
_set_gdt:
;load gdt
cli
LGDT [gdt + _ptr]
;goto PM - no interrup, there's no IDT
mov eax, cr0
or eax, 1
mov cr0, eax
;jmp to PM code (clear buffer)
db 0xEA
dw pm_start + _ptr
dw 8 ;jump to 16Bits (PM) ; 00000000 00001000:x
; ||+-> 00=higest level
; |+--> 0=gdt
; +---> 1=selector 1
pm_start:
db 0xEA
dw 0
dw 24 ;jump to 32Bits (PM)
;-------------Enable A20 Gate-----------------------------------------------
Enable_A20:
call a20wait
mov al,0xAD
out 0x64,al
call a20wait
mov al,0xD0
out 0x64,al
call a20wait2
in al,0x60
push eax
call a20wait
mov al,0xD1
out 0x64,al
call a20wait
pop eax
or al,2
out 0x60,al
call a20wait
mov al,0xAE
out 0x64,al
call a20wait
ret
a20wait:
in al,0x64
test al,2
jnz a20wait
ret
a20wait2:
in al,0x64
test al,1
jz a20wait2
ret
;-------------Global Descriptor Table-----------------------------------------
gdt:
istruc gdt_adr
at gdt_adr.limit, dw 0x0000 ;at run-time
at gdt_adr.base, dd 0x00000000 ;at run-time
iend
gdt_tbl_start:
istruc gdt_tbl
at gdt_tbl.dummy, db 0 ;Empty
at gdt_tbl.ks1code, dw 0xFFFF ;[08] size 1 e 2
dw 0x0 ;base 1 e 2
db 0x0 ;base 3
db 0x9a ;flag1 > 1 00 1 1010 > [p=1 (in mem); dpl=10 (level); s=0 (system); segtype=1010] _1 10 0 1010
db 0x0 ;flag2 > 0 0 0 1 1010 > [g=0 (len in byts); d/b=0 (); 0; avl=1; seg size nib=1010 (); ] 00011010
db 0x0 ;base4
; 00 00 00 00 (32 bits) (0000:0000)
; 04|03|01|02 (7000:0000)
at gdt_tbl.ks1video, dw 0x0FA0 ;[16] at run-time:fixed/size 0fA0=4.000 bytes
dw 0x8000 ;base1 e 2
db 0x0B ;base3
db 0x92 ;flag1 > 1 00 1 0010 > [p=1 (in mem); dpl=00 (level); s=1 (not system); segtype=0010]
db 0x0 ;flag2 > 0 0 0 0 0000 > [g=0 (len in bytes); d/b=0 (data); 0; avl=0 (?); sg size nib=0000]
db 0x0 ;at run-time:fixed
;000B8000 32 bits (b800:0000 bits)
;04030102
at gdt_tbl.ks2code, dw 0xffff ;[24] - 32 Bits ---- asm c bootstrap
dw 0x0 ;
db 0x0 ;
db 0x9a ;
db 0xcf ;
db 0x0 ;
at gdt_tbl.c32stack, dw 0xFFFF ;[32] - Stack
dw 0x0
db 0x0
db 0x92
db 0xC0
db 0x0
at gdt_tbl.c32code, dw 0xffff ;[40] - 32 Bits ---- C EntryPoint
dw 0x0 ;
db 0x0 ;
db 0x9a ;
db 0xcf ;
db 0x0 ;
at gdt_tbl.c32data, dw 0xffff ;[48] - 32 Bits ---- C Data
dw 0x0 ;
db 0x0 ;
db 0x92 ;
db 0xcf ;
db 0x0 ;
iend
gdt_tbl_finish:
;-------------end of Global Descriptor Table-----------------------------------
Code: Select all
[BITS 32]
global KernelEntry
KernelEntry:
;setting up the 32 bit stack
mov eax, 0x0020
mov ss, ax
mov esp, 0x0000ffff
;setting up segments
mov ax, 48
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
;call C Kernel
extern _kernel
call _kernel
;loop endless
jmp $
global _prnt
_prnt:
mov ax, 16
mov es, ax
mov byte [es:0x0], bl
ret
global _gdt_flush
extern _gp
_gdt_flush:
lgdt [_gp]
mov bl, 'x'
call _prnt
mov ax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
jmp 0x08:flush2
flush2:
ret
Code: Select all
typedef int size_t;
extern size_t strlen(const char *str);
struct gdt_entry
{
unsigned short limit_low;
unsigned short base_low;
unsigned char base_middle;
unsigned char access;
unsigned char granularity;
unsigned char base_high;
} __attribute__((packed));
struct gdt_ptr
{
unsigned short limit;
unsigned int base;
} __attribute__((packed));
struct gdt_entry gdt[3];
struct gdt_ptr gp;
extern void gdt_flush();
void gdt_set_gate(int num, unsigned long base, unsigned long limit, unsigned char access, unsigned char gran)
{
gdt[num].base_low = (base & 0xFFFF);
gdt[num].base_middle = (base >> 16) & 0xFF;
gdt[num].base_high = (base >> 24) & 0xFF;
gdt[num].limit_low = (limit & 0xFFFF);
gdt[num].granularity = ((limit >> 16) & 0x0F);
gdt[num].granularity |= (gran & 0xF0);
gdt[num].access = access;
}
void gdt_install()
{
gp.limit = (sizeof(struct gdt_entry) * 3) - 1;
gp.base = &gdt;
gdt_set_gate(0, 0, 0, 0, 0);
gdt_set_gate(1, 0, 0xFFFFFFFF, 0x9A, 0xCF);
gdt_set_gate(2, 0, 0xFFFFFFFF, 0x92, 0xCF);
gdt_flush();
}
extern void prnt();
int kernel()
{
gdt_install();
for (;;);
return 0;
}
to compile 16 bit code
Code: Select all
OUTPUT_FORMAT("binary")
ENTRY(start)
phys = 0x0;
SECTIONS
{
.text phys : AT(phys) {
code = .;
*(.text)
*(.rodata)
. = ALIGN(512);
}
.data : AT(phys + (data - code))
{
data = .;
*(.data)
. = ALIGN(512);
}
.bss : AT(phys + (bss - code))
{
bss = .;
*(.bss)
. = ALIGN(512);
}
end = .;
}
Code: Select all
OUTPUT_FORMAT("binary")
ENTRY(KernelEntry)
phys = 0x00001000;
SECTIONS
{
.text phys : AT(phys) {
code = .;
*(.text)
*(.rodata)
. = ALIGN(4096);
}
.data : AT(phys + (data - code))
{
data = .;
*(.data)
. = ALIGN(4096);
}
.bss : AT(phys + (bss - code))
{
bss = .;
*(.bss)
. = ALIGN(4096);
}
end = .;
}
Re: What's problem with this code?
And how do I compile this code? I am using Cygwin.InsightSoft wrote:no way...
let start in hard way...
Code: Select all
$ nasm -f win32 a16.asm
a16.asm:36: error: COFF format does not support non-32-bit relocations
a16.asm:40: error: COFF format does not support non-32-bit relocations
...
$ nasm b32.asm
b32.asm:20: error: binary output format does not support external references
b32.asm:35: error: binary output format does not support external references
-
- Member
- Posts: 76
- Joined: Mon Aug 18, 2008 6:17 am
Re: What's problem with this code?
ECHO Building: BOOT
%tools%\masm611\bin\ml boot.asm /AT
ECHO Building: KRNLS1 (Kernel first stage)
%tools%\nasm\nasm -f aout -o KRNLS1.o KRNLS1.asm
ECHO Building: KRNLS2 (Kernel second stage)
%tools%\nasm\nasm -f aout -o KRNLS2.o krn32.asm
echo Building: KRNLS3 (Kernel 3rt and final stage)
gcc -Wall -O -fstrength-reduce -fomit-frame-pointer -finline-functions -nostdinc -fno-builtin -I./include -c -o krn32.o krn32.c
echo linking together...
ld -T _scrpt16.ld -o _KRNLS1.bin KRNLS1.o
ld -T _scrpt32.ld -o _KRNL32.bin KRNLS2.o krn32.o
for the boot: masm611
asm 16 bits: nasm (with BITS 16)
asm 32 bits: nasm (with BITS 32)
c 32 bits (djgpp gcc)...
%tools%\masm611\bin\ml boot.asm /AT
ECHO Building: KRNLS1 (Kernel first stage)
%tools%\nasm\nasm -f aout -o KRNLS1.o KRNLS1.asm
ECHO Building: KRNLS2 (Kernel second stage)
%tools%\nasm\nasm -f aout -o KRNLS2.o krn32.asm
echo Building: KRNLS3 (Kernel 3rt and final stage)
gcc -Wall -O -fstrength-reduce -fomit-frame-pointer -finline-functions -nostdinc -fno-builtin -I./include -c -o krn32.o krn32.c
echo linking together...
ld -T _scrpt16.ld -o _KRNLS1.bin KRNLS1.o
ld -T _scrpt32.ld -o _KRNL32.bin KRNLS2.o krn32.o
for the boot: masm611
asm 16 bits: nasm (with BITS 16)
asm 32 bits: nasm (with BITS 32)
c 32 bits (djgpp gcc)...
Re: What's problem with this code?
ld fails for me at this step. All previous steps complete, butInsightSoft wrote:ECHO Building: BOOT
echo linking together...
ld -T _scrpt16.ld -o _KRNLS1.bin KRNLS1.o
ld -T _scrpt32.ld -o _KRNL32.bin KRNLS2.o krn32.o
Code: Select all
$ ld -T link16.ld -o krnls1.bin krnls1.o
krnls1.o: file not recognized: File format not recognized
Re: What's problem with this code?
GCC Cross-Compiler... no need to tinker, just download && compile && there you are.ru2aqare wrote:The reason is, the Cygwin toolchain supports only COFF format. I don't really have the time to download the sources and tinker with them...
Every good solution is obvious once you've found it.
Re: What's problem with this code?
GRUB sets up your environment such that both the CS and the {SS=DS=ES=FS=GS} have a base of zero and a length of 0xffffffff. So, the kernel itself is linked to 0x100000, not 0x0 as yours will be.InsightSoft wrote:That's way I love this game!!!
I'm starting getting lost and... here I am, trying to figure it out!!! (:
Reviewing my code:
1) Boot
-load {Kernel.Stage.1} [asm 16 bits] to 7000:0000;
2) Kernel.Stage.1 (7000:0000) (16 bits)
-create and fill a simple GDT
gdt_tbl.dummy (0) - (you know...)
gdt_tbl.ks1code (0x9a, 0x0) - For first jmp to PM
gdt_tbl.ks1video (0x92, 0x0) - for simple print to...
gdt_tbl.ks2code (0x9a, 0xcf) - for the kernel final stage (asm+c 32 bit code) [9000:0000]
gdt_tbl.c32stack (0x92, 0xC0) - for stack 32 bit operations
gdt_tbl.c32code (0x9a, 0xcf) - not necessary/not used
gdt_tbl.c32data (0x92, 0xcf) - for DS, ES, etc
load code [asm+c] to 9000:0000
fill the addresses...
enable A20
jmp to PM (descriptor
already in PM
jmp to 24:0 (9000:0000)
Not only that, but C compilers assume that SS=DS, which is not the case in your environment. You're violating assumptions made by the compiler, and that will always lead to trouble.
Cheers,
James
-
- Member
- Posts: 76
- Joined: Mon Aug 18, 2008 6:17 am
Re: What's problem with this code?
Ohh, I see... it's more easy...JamesM wrote: GRUB sets up your environment such that both the CS and the {SS=DS=ES=FS=GS} have a base of zero and a length of 0xffffffff. So, the kernel itself is linked to 0x100000, not 0x0 as yours will be.
Ok... I didn't knew that... I always thought we could define a empty zone to serve as stack (that's one of my problems)....JamesM wrote: Not only that, but C compilers assume that SS=DS, which is not the case in your environment. You're violating assumptions made by the compiler, and that will always lead to trouble.
Thus, setting the descriptors from 0 to 0xffffffff, covering all the 16bit bios code/data will be at level 0, and starting somewhere in between (for exemple 0x0010000) the kernel will take place... is definitely more simple...
I know that the starting point of my kernel I can define it at LD.exe script file... right? (and it will be only for internal propose, to calculate correctly the displacement of offsets... right?)
Any way, you open my eyes... thanks all of you... you are the greatest
-
- Member
- Posts: 76
- Joined: Mon Aug 18, 2008 6:17 am
Re: What's problem with this code?
Other question:
Why, in Bran's Kernel Development Tutorial, he sets a new gdt with exactly the same base and limit?
Why, in Bran's Kernel Development Tutorial, he sets a new gdt with exactly the same base and limit?
void gdt_install()
{
/* Setup the GDT pointer and limit */
gp.limit = (sizeof(struct gdt_entry) * 3) - 1;
gp.base = &gdt;
/* Our NULL descriptor */
gdt_set_gate(0, 0, 0, 0, 0);
/* The second entry is our Code Segment. The base address
* is 0, the limit is 4GBytes, it uses 4KByte granularity,
* uses 32-bit opcodes, and is a Code Segment descriptor.
* Please check the table above in the tutorial in order
* to see exactly what each value means */
gdt_set_gate(1, 0, 0xFFFFFFFF, 0x9A, 0xCF);
/* The third entry is our Data Segment. It's EXACTLY the
* same as our code segment, but the descriptor type in
* this entry's access byte says it's a Data Segment */
gdt_set_gate(2, 0, 0xFFFFFFFF, 0x92, 0xCF);
/* Flush out the old GDT and install the new changes! */
gdt_flush();
}
Re: What's problem with this code?
I can only think that the tutorial is loaded by GRUB, and GRUB leaves the state of the GDT and IDT undefined. Otherwise I have no clue.