Fix paging #PF, simplify init_tasks
Unsure what fixed the paging #PF in init_tasks, but allocating a page
now works as expected. Originally a #PF would occur if alloc_page was
called after a certain point in init_tasks.
diff --git a/src/kernel/x86_32/.gdbinit b/src/kernel/.gdbinit
similarity index 100%
rename from src/kernel/x86_32/.gdbinit
rename to src/kernel/.gdbinit
diff --git a/src/kernel/Jmk2 b/src/kernel/Jmk2
index ffd134c..57a657a 100644
--- a/src/kernel/Jmk2
+++ b/src/kernel/Jmk2
@@ -16,3 +16,5 @@
}
type custom_link
+
+jmk_finalize
diff --git a/src/kernel/x86_32/boot.s b/src/kernel/x86_32/boot.s
index 047b326..9828985 100644
--- a/src/kernel/x86_32/boot.s
+++ b/src/kernel/x86_32/boot.s
@@ -13,7 +13,7 @@
;; Index in page directory
KERNEL_PAGE_NUMBER equ (KERNEL_VIRTUAL_BASE >> 22)
-STACK_SIZE equ 0x4000
+STACK_SIZE equ 0x1000
@@ -23,7 +23,8 @@
page_directory:
dd 0b010000011 ; Identity map first 4 megs
times (KERNEL_PAGE_NUMBER - 1) dd 0
- dd 0b010000011 ; Map kernel memory to zero page too
+ dd 0b010000011 ; Map kernel memory (4 megs) to zero
+ ; page too
times (1024 - KERNEL_PAGE_NUMBER - 1) dd 0
gdt:
diff --git a/src/kernel/x86_32/faults.c b/src/kernel/x86_32/faults.c
index e7c4180..761558c 100644
--- a/src/kernel/x86_32/faults.c
+++ b/src/kernel/x86_32/faults.c
@@ -12,8 +12,22 @@
#define ADD_INTERRUPT(sym, num) add_interrupt_handler(num, sym##_h)
-DECLARE_INTERRUPT(gp, "#GP")
-DECLARE_INTERRUPT(pf, "#PF")
+DECLARE_INTERRUPT(gp, "#GP");
+
+static void pf_h(struct registers *regs)
+{
+ uint err = regs->error_code;
+
+ kprintf(ERROR "Fault #PF: eip=0x%x, err=0x%x\n", regs->eip, err);
+ kprintf("\tDue to: %c%c%c%c\n",
+ err & 1 ? 'P' : ' ',
+ err & (1<<1) ? 'W' : ' ',
+ err & (1<<2) ? 'U' : ' ',
+ err & (1<<4) ? 'I' : ' ');
+
+ asm("cli");
+ kpanic("#PF");
+}
void init_faults()
{
diff --git a/src/kernel/x86_32/log.c b/src/kernel/x86_32/log.c
index 0ea6822..9f0807f 100644
--- a/src/kernel/x86_32/log.c
+++ b/src/kernel/x86_32/log.c
@@ -72,7 +72,6 @@
{
if (!condition)
{
- vga_set_color(LIGHT_RED, BLACK);
kprintf(ERROR "ASSERTION FAILED: %s:%d\n%s\n", file, line, message);
while (1)
diff --git a/src/kernel/x86_32/main.c b/src/kernel/x86_32/main.c
index 17a54d7..4d3d868 100644
--- a/src/kernel/x86_32/main.c
+++ b/src/kernel/x86_32/main.c
@@ -66,9 +66,22 @@
asm("sti");
init_tasks();
+ kprintf(OKAY "Tasks initialized\n");
init_sync();
+#ifdef TEST_PAGING
+ test_paging();
+#endif
+
+#ifdef TEST_THREADS
+ kprintf(DEBUG "Spawning test thread\n");
+ spawn_thread(other_thread, NULL);
+
+ greet();
+#endif
+
pci_init();
+ kprintf(OKAY "PCI initialized\n");
// Register PCI drivers
ide_register();
@@ -77,12 +90,6 @@
kprintf(OKAY "Loaded PCI device drivers\n");
-#ifdef TEST_THREADS
- spawn_thread(other_thread, NULL);
-
- greet();
-#endif
-
#ifdef TEST_PCI
pci_print_devices();
pci_print_drivers();
diff --git a/src/kernel/x86_32/paging.c b/src/kernel/x86_32/paging.c
index f959a92..c77f499 100644
--- a/src/kernel/x86_32/paging.c
+++ b/src/kernel/x86_32/paging.c
@@ -61,7 +61,7 @@
/* found unused frame */
uint frame = i * BITS + j;
kprintf(DEBUG "first_free_frame returning %d\n", frame);
-// kpanic("asdf");
+
return frame;
}
}
@@ -103,10 +103,12 @@
uint *get_or_create_table(uint *dir, uint table, bool user, bool rw)
{
- // If used AND NOT 4mb page (see figure 4-4, page 115 of Intel
+ // If used AND NOT 4mb page (see figure 4-4, page 114 of Intel
// manual volume 3)
- if (dir[table] & 1 && dir[table] ^ 1 << 7)
+ // bit 0 = used; bit 7 = 4MB?
+ if (dir[table] & 1 && dir[table] ^ (1 << 7))
{
+ // 12 LSBs are metadata
return (uint *)(size_t)PHYS_TO_VIRT((dir[table] & ~0xfff));
}
@@ -146,11 +148,13 @@
table[page] = 0;
}
+// TODO: This entire function seems wrong.
void map_page_to(uint *dir, void *virt, void *frame_p, bool writable, bool user)
{
uint page = ((size_t)virt / 0x1000) % 1024;
uint *table = get_or_create_table(dir, (size_t)virt >> 22, false, false);
+ // TODO: is the xor here correct?
table[page] = (((uint)frame_p) ^ 0xfff) | 1 | writable << 1 | user << 2;
}
@@ -164,12 +168,12 @@
// Page number % pages per table
uint page = ((size_t)virt / 0x1000) % 1024;
uint *table = get_or_create_table(dir, (size_t)virt >> 22, false, false);
- kprintf(DEBUG "table = 0x%x (virt)\n", table);
- kprintf(DEBUG "dir entry = 0x%x\n", dir[(size_t)virt >> 22]);
+ // kprintf(DEBUG "table = 0x%x (virt)\n", table);
+ // kprintf(DEBUG "dir entry = 0x%x\n", dir[(size_t)virt >> 22]);
alloc_frame(&table[page], false, false);
- kprintf(DEBUG "alloc_page table[page] = %d (0x%x)\n", table[page], table[page]);
+ // kprintf(DEBUG "alloc_page table[page] = %d (0x%x)\n", table[page], table[page]);
return;
}
@@ -231,13 +235,20 @@
map_4mb(kernel_page_directory, (size_t)KERNEL_VIRTUAL_BASE, 0, false,
false);
- load_page_directory((uint)kernel_page_directory - 0xC0000000);
-
- add_interrupt_handler(14, page_fault);
+ load_page_directory(VIRT_TO_PHYS(kernel_page_directory));
}
-void page_fault(struct registers *regs)
+void test_paging()
{
- kprintf(ERROR "Page fault! eip = %d\n", regs->eip);
- kpanic("Page fault");
+ // a random page base address
+ uint *base = (uint *)0xFFFFFE000;
+
+ kprintf(INFO "Allocating page (expect frame 1024)\n");
+ alloc_page(kernel_page_directory, base);
+
+ kprintf(INFO "Writing 10 words to page\n");
+ for (int i = 0; i < 10; i++)
+ {
+ base[i] = i;
+ }
}
diff --git a/src/kernel/x86_32/paging.h b/src/kernel/x86_32/paging.h
index e264356..c06b3a2 100644
--- a/src/kernel/x86_32/paging.h
+++ b/src/kernel/x86_32/paging.h
@@ -11,13 +11,19 @@
/* defined in switch_table.s */
extern uint load_page_directory(uint table_address);
extern void enable_paging();
+extern void invalidate_page(void *address);
extern uint kernel_page_directory[1024];
void init_paging();
void map_page_to(uint *dir, void *virt, void *frame_p, bool writable, bool user);
void alloc_frame(uint *page_table_entry, bool user, bool writable);
+
+/* Map the 4kb of virtual memory starting at `page` to a physical page
+ * in the page directory `dir` (pointer to page directory in virtual
+ * memory) */
void alloc_page(uint *dir, uint *page);
void alloc_kernel_page(uint *page);
-void page_fault(struct registers *regs);
uint *new_page_directory_v();
+
+void test_paging();
diff --git a/src/kernel/x86_32/switch_table.s b/src/kernel/x86_32/switch_table.s
index c8e6260..e7d80f9 100644
--- a/src/kernel/x86_32/switch_table.s
+++ b/src/kernel/x86_32/switch_table.s
@@ -7,9 +7,8 @@
mov cr3, ecx
ret
- [global enable_paging]
-enable_paging:
- mov ecx, cr0
- or eax, 0x80000000
- mov cr0, ecx
+ [global invalidate_page]
+invalidate_page: ; void (void *page)
+ mov eax, [esp + 4]
+ invlpg [eax]
ret
diff --git a/src/kernel/x86_32/task.c b/src/kernel/x86_32/task.c
index 1ec1337..2f8c3a8 100644
--- a/src/kernel/x86_32/task.c
+++ b/src/kernel/x86_32/task.c
@@ -5,28 +5,14 @@
#include "paging.h"
#include "pic.h"
-struct process processes[1024] = {0};
-struct ll_task_i *first_task = NULL, *last_task = NULL, *current_task = NULL;
+static struct process processes[1024] = {0};
+static struct ll_task_i *first_task = NULL, *last_task = NULL, *current_task = NULL;
static uint next_task_id = 0;
bool tasks_initialized = false;
-void _init_tasks(struct registers *regs);
-
void init_tasks()
{
- add_interrupt_handler(INIT_TASKS_INTERRUPT, _sys_init_tasks_h);
-
- asm("int $0x81");
-}
-
-void _sys_init_tasks_h(struct registers *regs)
-{
- _init_tasks(regs);
-}
-
-void _init_tasks(struct registers *regs)
-{
processes[0] = (struct process){
.exists = true,
.id = 0,
@@ -49,9 +35,12 @@
first_task->next = NULL;
first_task->prev = NULL;
memset(&first_task->task, 0, sizeof(struct task));
+ // state will be filled in upon task switch. TODO: task switch
+ // before spawning thread (or fill in CS, etc manually) to avoid
+ // #GP on task switch. New threads based on parent task
first_task->task = (struct task){
.proc = &processes[0],
- .state = *regs,
+ .state = { 0 },
.id = next_task_id++,
.waiting = false,
};
@@ -89,17 +78,18 @@
// Virtual location of new stack, with space reserved for argument
// and return address to kill_this_thread().
- uint new_stack_base_v = proc->last_stack_pos;
+ uint *new_stack_base_v = (uint *)proc->last_stack_pos;
proc->last_stack_pos -= 0x1000;
// Alloc a new page in the current process mapping to the new stack
alloc_page(dir_v, (void *)proc->last_stack_pos);
- kprintf(INFO "new_stack_base_v = %p\n", new_stack_base_v);
- new_stack_base_v -= sizeof(uint);
- *((uint *)new_stack_base_v) = (size_t)data;
- new_stack_base_v -= sizeof(uint);
- *((uint *)new_stack_base_v) = (size_t)&kill_this_thread;
+ kprintf(INFO "new_stack_base_v = %p, last_stack_pos = %p\n",
+ new_stack_base_v, proc->last_stack_pos);
+
+ // Write data argument and return pointer to new threads stack
+ new_stack_base_v[-1] = (size_t)data;
+ new_stack_base_v[-2] = (size_t)&kill_this_thread;
kprintf(DEBUG "Set stack\n");
@@ -113,7 +103,7 @@
// Namely a new TID
task->id = next_task_id++;
// And stack, frame, and instruction pointers
- task->state.ebp = task->state.esp = new_stack_base_v;
+ task->state.ebp = task->state.esp = (uint)(new_stack_base_v - 2);
task->state.eip = (uint)function;
task->waiting = false;
@@ -162,6 +152,8 @@
asm("sti");
}
+extern uint _get_cr3();
+
extern void _switch_to_task(uint page_directory, struct registers ctx);
#if 0
{
@@ -174,6 +166,8 @@
void switch_to_task(struct task *task)
{
+ kprintf(DEBUG "switch_to_task cr3=0x%x\n", task->proc->page_directory_p);
+
_switch_to_task(task->proc->page_directory_p, task->state);
__builtin_unreachable();
}
@@ -183,7 +177,9 @@
// Resetting eflags in _switch_to_task iret will switch this back
asm("cli");
- kprintf(DEBUG "switching tasks\n");
+ kprintf(DEBUG "switching tasks, alleged cr3=0x%x, real cr3=0x%x\n",
+ current_task->task.proc->page_directory_p,
+ _get_cr3());
// save context for this task
current_task->task.state = regs;
diff --git a/src/kernel/x86_32/task_api.s b/src/kernel/x86_32/task_api.s
index fce9811..44c0b4e 100644
--- a/src/kernel/x86_32/task_api.s
+++ b/src/kernel/x86_32/task_api.s
@@ -9,3 +9,8 @@
popad ; Then the rest of the registers
add esp, 8 ; Then IRQ # and error #
iret ; And finally the saved state
+
+ [global _get_cr3]
+_get_cr3:
+ mov eax, cr3
+ ret
diff --git a/src/kernel/x86_32/x86_32.jmk b/src/kernel/x86_32/x86_32.jmk
index 9a67295..669e60a 100644
--- a/src/kernel/x86_32/x86_32.jmk
+++ b/src/kernel/x86_32/x86_32.jmk
@@ -1,8 +1,10 @@
# -*- tcl -*-
+cflags -DTEST_THREADS
ldflags -T[pwd]/link.ld -melf_i386
asmflags -felf -Fdwarf
-set qemuflags "-drive file=hd0_ext2.img,format=raw"
+set qemuflags ""
+# "-drive file=hd0_ext2.img,format=raw"
depends sys $root/src/libsys libsys.a
depends initrd $root/boot/initrd initrd.img
@@ -45,15 +47,15 @@
[lib sys]
rule debug-wait kernel.elf {
- shell "qemu-system-i386 -s -S $::qemuflags -kernel kernel.efl"
+ shell "qemu-system-i386 -s -S $::qemuflags -kernel kernel.elf"
}
rule debug kernel.elf {
- shell "qemu-system-i386 -s -S $::qemuflags -kernel kernel.efl"
+ shell "qemu-system-i386 -s -S $::qemuflags -kernel kernel.elf &"
shell "echo run target remote localhost:1234 to connect to qemu"
shell "gdb $::first_src"
shell "pkill qemu-system-i386"
}
-rule qemu "kernel.elf hd0_$::options(FS).img" {
+rule qemu "kernel.elf" {
shell "qemu-system-i386 $::qemuflags -d cpu_reset -monitor stdio -kernel kernel.elf -no-reboot"
}