I have made a number of changes to Ordo, most notably to the
linker script - I now have the object files named explicitly in the script, rather than relying on the Makefile, and have added separate sections for several of the system elements, such as the GDT, IDT, and page tables. I also stubbed in a
stack_base section, for when I start remapping things in the new page tables.
Code:
/* The bootloader will look at this image and start execution at the symbol
designated at the entry point. */
ENTRY(kstart)
INPUT(
obj/kernel.o
obj/terminal.o
obj/mem.o
obj/idt.o
obj/gdt.o
obj/paging.o
obj/acpi.o
)
OUTPUT(kernel.elf)
OUTPUT_FORMAT(elf32-i386)
STARTUP(obj/kstart.o)
/* Tell where the various sections of the object files will be put in the final
kernel image. */
SECTIONS
{
/* Begin putting sections at the higher half. */
. = 0xC0000000;
/* the .text section. */
.text : ALIGN(4K)
{
*(.text)
}
/* Read-only data. */
.rodata : ALIGN(4K)
{
*(.rodata)
}
/* Read-write data (initialized) */
.data : ALIGN(4K)
{
*(.data)
}
/* Read-write data (uninitialized) and stack */
.bss : ALIGN(4K)
{
*(COMMON)
*(.bss)
}
/* hardware tables */
. = 0xC0100000;
.gdt BLOCK(64K) :
{
gdt = .;
. = . + 64K;
}
.tss BLOCK(4K) : ALIGN(4K)
{
default_tss = .;
. = . + 4K;
}
.idt BLOCK(4K) : ALIGN(4K)
{
idt = .;
. = . + 4K;
}
.paging BLOCK(4K) : ALIGN(4K)
{
page_directory = .;
. = . + 4K;
page_tables = .;
. = . + (1K * 4K);
}
/* set up the stack */
.stack : ALIGN(4M)
{
stack_base = .;
*(.stack)
}
}
I expect that I've made at least a few mistakes in this, but as things stand, I have the IDT pointing to the linker-defined section and it works correctly.
When I went to
replace the stub GDT from my bootloader with one constructed by and accessible to the kernel, it worked exactly as before.
Code:
void reset_gdt()
{
struct GDT_R gdt_r = { sizeof(union GDT_Entry) * MAX_GDT_ENTRIES, gdt };
union GDT_Entry *entry = gdt;
// set the null GDT entry
entry->raw_entry = 0;
// system code selector
set_gdt_entry(++entry, 0x0fffff, 0, true, true, RING_0);
// system data selector
set_gdt_entry(++entry, 0x0fffff, 0, false, true, RING_0);
// system TSS selector
set_gdt_entry(entry, (uint32_t) default_tss, sizeof(struct TSS), false, true, RING_0);
(entry++)->fields.access.non_sys = false;
// user code selector
set_gdt_entry(++entry, 0x08ffff, 0, true, true, RING_3);
// user data selector
set_gdt_entry(++entry, 0x08ffff, 0, false, true, RING_3);
// set the GDT register
__asm__ __volatile__ (
"lgdt %0"
:
: "m" (gdt_r));
}
The fact that it worked on the first go seemed suspicious to me, though, so I checked what happened when I commented out the null descriptor - and nothing changed. This leads me to think that
reset_gdt() is not, in fact, changing the GDTR.
I am not certain how to check this further. I know that there is a
SGDT instruction, and I suppose I could write a function to check the GDTR's values using that, but I am not certain just what that would accomplish.
Can anyone see anything I've missed?