Brendan wrote:
Code: Select all
struct gdt_ptr
{
uint16 limit;
uint8 base;
} __attribute__ ((__packed__));
Why is "gp.base" an unsigned 8-bit value?
Why are you type-casting it to an unsigned 16-bit data type?
It should be an unsigned 32-bit value, or perhaps even "gdt_entry *base;"...
Of course this might not help if your GDT limit is zero - not sure what's causing that.
Also, I don't think failing to fill your ".bss" section with zero would cause this specific bug - everything being relied on is set to something before being used.
Hiya, Brendan! Thanks for your response.
I nearly panicked there for a moment when I saw the uint's! My initial posting's attachment has the code as I work on it now. I had attempted to add some "portability" into the code by setting up some defines, but I realized that it was cluttering up the mind. I set things back to using unsigned int, unsigned short, etc.
Here is what
gdt.h and
gdt.c look like now (using a bit of typedef shortcutting).
Code: Select all
/* gdt.h */
/* define gdt structure */
typedef 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__)) gdt_entry;
/* pointer structure to gdt */
typedef struct _gdt_ptr {
unsigned short limit;
unsigned long base;
} __attribute__ ((__packed__)) gdt_ptr;
/* gdt_flush is in start.asm */
extern void gdt_flush();
/* function prototypes */
void gdt_set_gate(int num, unsigned long base, unsigned long limit,
unsigned char access, unsigned char gran);
void gdt_install();
/* end gdt.h */
Code: Select all
/* gdt.c */
#include <gdt.h>
gdt_entry gdt[3];
gdt_ptr gdtp;
/* set up a descriptor in the gdt */
void gdt_set_gate(int num, unsigned long base, unsigned long limit,
unsigned char access, unsigned char gran)
{
/* setup descriptor base address */
gdt[num].base_low = (base & 0xFFFF);
gdt[num].base_middle = (base >> 16) & 0xFF;
gdt[num].base_high = (base >> 24) & 0xFF;
/* setup descriptor limits */
gdt[num].limit_low = (limit & 0xFFFF);
gdt[num].granularity = ((limit >> 16) & 0x0F);
/* set up granularity and access flags */
gdt[num].granularity |= (gran & 0xF0);
gdt[num].access = access;
} /* gdt_set_gate() */
/* initialize the gdt */
void gdt_install()
{
/* set up gdt pointer and limit */
gdtp.limit = (sizeof(gdt_entry) * 3) - 1;
gdtp.base = &gdt;
/* null descriptor */
gdt_set_gate(0, 0, 0, 0, 0);
/* code segment descriptor */
gdt_set_gate(1, 0, 0xFFFFFFFF, 0x9A, 0xCF);
/* data segment descriptor */
gdt_set_gate(2, 0, 0xFFFFFFFF, 0x92, 0xCF);
/* flush old gdt and install new one */
gdt_flush();
} /* gdt_install() */
/* end gdt.c */
The limit for the each null segment is 0, and 0xFFFFFFFF for code and data segments. What makes this so much fun is the inconsistencies I get. JamesM even reports that he was able to run the code.
Code: Select all
<bochs:1> c
Next at t=25605718
(0) [0x0010003e] 0008:10003e (unk. ctxt): mov ds, ax ; 8ed8
<bochs:2> info cpu
eax:0x00000000, ebx:0x00000000, ecx:0x00000000, edx:0x00000543
ebp:0x00000000, esp:0x00000000, esi:0x00000000, edi:0x00000000
eip:0x0000fff0, eflags:0x00000002, inhibit_mask:0
cs:s=0xf000, dl=0x0000ffff, dh=0xff009bff, valid=1
ss:s=0x0000, dl=0x0000ffff, dh=0x00009300, valid=1
ds:s=0x0000, dl=0x0000ffff, dh=0x00009300, valid=1
es:s=0x0000, dl=0x0000ffff, dh=0x00009300, valid=1
fs:s=0x0000, dl=0x0000ffff, dh=0x00009300, valid=1
gs:s=0x0000, dl=0x0000ffff, dh=0x00009300, valid=1
ldtr:s=0x0000, dl=0x0000ffff, dh=0x00008200, valid=1
tr:s=0x0000, dl=0x0000ffff, dh=0x00008300, valid=1
gdtr:base=0x00000000, limit=0xffff
idtr:base=0x00000000, limit=0xffff
dr0:0x00000000, dr1:0x00000000, dr2:0x00000000
dr3:0x00000000, dr6:0xffff0ff0, dr7:0x00000400
cr0:0x00000010, cr1:0x00000000, cr2:0x00000000
cr3:0x00000000, cr4:0x00000000
done
<bochs:3>
This is despite the code for gdt_flush saying:
Code: Select all
global gdt_flush ; allow c code to find gdt_flush()
extern gdtp ; gp is in gdt.h
gdt_flush:
lgdt [gdtp] ; load gdt
mov ax, 0x10 ; offset in gdt to data segment
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
jmp 0x08:gdt_flush2 ; offset to code segmend - far jmp
gdt_flush2:
ret
I see that
eax holds 0x0, yet I
mov ax, 0x10. Bochs dies when
eip get to
mov ds, ax. Ho hum... For giggles I've attached the latest tarball.