syscalls: fix execve crash and heap-related problems
This commit is contained in:
+31
-3
@@ -53,6 +53,31 @@ static void kheap_bitmap_clear(uint32_t index) {
|
||||
// Initialize kernel heap
|
||||
void kheap_init() {
|
||||
memset(kheap_bitmap, 0, sizeof(kheap_bitmap));
|
||||
|
||||
void* temp_phys = alloc_page();
|
||||
if (!temp_phys) {
|
||||
printf("CRITICAL: Failed to init heap - No physical memory\n");
|
||||
while(1);
|
||||
}
|
||||
|
||||
uint32_t heap_limit = 4 * 1024 * 1024;
|
||||
|
||||
for (uint32_t vaddr = KHEAP_START; vaddr < KHEAP_START + heap_limit; vaddr += 0x400000) {
|
||||
|
||||
// This call checks if the Page Table exists.
|
||||
// If not, it allocates a new Page Table and inserts it into the Current PD.
|
||||
map_page(temp_phys, (void*)vaddr, PAGE_PRESENT | PAGE_RW);
|
||||
|
||||
// We don't actually want the page mapped, we just wanted the Side Effect
|
||||
// of creating the Page Table. So we unmap the page immediately.
|
||||
// unmap_page clears the Entry, but DOES NOT free the Page Table itself.
|
||||
unmap_page((void*)vaddr);
|
||||
}
|
||||
|
||||
// Free the dummy page
|
||||
free_page(temp_phys);
|
||||
|
||||
printf("Kernel Heap Initialized (Pre-allocated tables for 0x%X MB)\n", heap_limit / 1024 / 1024);
|
||||
}
|
||||
|
||||
// Allocate contiguous virtual pages
|
||||
@@ -88,7 +113,8 @@ void* kvalloc(size_t npages) {
|
||||
for (uint32_t i = 0; i < npages; i++) {
|
||||
void* phys = alloc_page();
|
||||
if (!phys) {
|
||||
// Out of physical memory! Unmap what we just did and fail.
|
||||
debug_log("NO MEMORY FOR KVALLOC!!\n");
|
||||
while(1){asm volatile("cli; hlt");}
|
||||
kvfree((void*)vaddr, i);
|
||||
return NULL;
|
||||
}
|
||||
@@ -117,15 +143,17 @@ void kvfree(void* addr, size_t npages) {
|
||||
|
||||
for (uint32_t i = 0; i < npages; i++) {
|
||||
uint32_t current_vaddr = vaddr + i * PAGE_SIZE;
|
||||
|
||||
void* phys = get_physaddr((void*)current_vaddr);
|
||||
|
||||
if (phys) {
|
||||
free_page(phys);
|
||||
unmap_page((void*)current_vaddr);
|
||||
} else {
|
||||
printf("kvfree: no physical mapping for 0x%x\n", current_vaddr);
|
||||
debug_log("kvfree: no phys mapping for %x\n", current_vaddr);
|
||||
}
|
||||
|
||||
unmap_page((void*)current_vaddr);
|
||||
|
||||
kheap_bitmap_clear(start + i);
|
||||
}
|
||||
}
|
||||
@@ -49,6 +49,7 @@ void init_allocator() {
|
||||
}
|
||||
|
||||
void* alloc_page() {
|
||||
asm volatile("cli");
|
||||
int i, j;
|
||||
// Проходим по всему битмапу
|
||||
for (i = 0; i < BITMAP_SIZE; i++) {
|
||||
@@ -64,6 +65,7 @@ void* alloc_page() {
|
||||
//debug_log("allocating page %X\n", page_num * PAGE_SIZE);
|
||||
|
||||
page_bitmap[i] |= (1 << j); // Помечаем как занятый
|
||||
asm volatile("sti");
|
||||
return (void*)(page_num * PAGE_SIZE);
|
||||
}
|
||||
}
|
||||
@@ -71,6 +73,7 @@ void* alloc_page() {
|
||||
}
|
||||
|
||||
printf("no bitches? no free pages?\n");
|
||||
asm volatile("sti");
|
||||
return (void*)0; // Нет свободных страниц
|
||||
}
|
||||
|
||||
@@ -86,5 +89,7 @@ void free_page(void* physaddr) {
|
||||
// Вычисляем номер страницы
|
||||
uint32_t page_num = addr / PAGE_SIZE;
|
||||
|
||||
asm volatile("cli");
|
||||
clear_bit(page_num);
|
||||
asm volatile("sti");
|
||||
}
|
||||
|
||||
+26
-7
@@ -348,8 +348,9 @@ int sys_execve(TrapFrame *tf) {
|
||||
if (!kernel_argv) return -1;
|
||||
|
||||
for(int i = 0; i < argc; i++) {
|
||||
kernel_argv[i] = malloc(EXECVE_MAX_ARGUMENT_SIZE);
|
||||
kernel_argv[i] = malloc(EXECVE_MAX_ARGUMENT_SIZE+1);
|
||||
strcpy(kernel_argv[i], argv[i]);
|
||||
kernel_argv[i][EXECVE_MAX_ARGUMENT_SIZE] = '\0';
|
||||
}
|
||||
kernel_argv[argc] = NULL;
|
||||
|
||||
@@ -357,8 +358,9 @@ int sys_execve(TrapFrame *tf) {
|
||||
if (!kernel_envp) return -1; // TODO: cleanup kernel_argv
|
||||
|
||||
for(int i = 0; i < envc; i++) {
|
||||
kernel_envp[i] = malloc(EXECVE_MAX_ARGUMENT_SIZE);
|
||||
kernel_envp[i] = malloc(EXECVE_MAX_ARGUMENT_SIZE+1);
|
||||
strcpy(kernel_envp[i], envp[i]);
|
||||
kernel_envp[i][EXECVE_MAX_ARGUMENT_SIZE] = '\0';
|
||||
}
|
||||
kernel_envp[envc] = NULL;
|
||||
|
||||
@@ -367,12 +369,22 @@ int sys_execve(TrapFrame *tf) {
|
||||
if (err != 0) {
|
||||
// TODO: Free kernel_argv/envp
|
||||
debug_log("execve: failed to read file\n");
|
||||
for(int i=0; i<argc; i++) free(kernel_argv[i]);
|
||||
free(kernel_argv);
|
||||
for(int i=0; i<envc; i++) free(kernel_envp[i]);
|
||||
free(kernel_envp);
|
||||
free(file_buffer);
|
||||
return -1;
|
||||
}
|
||||
|
||||
Elf32_Ehdr *elf_header = (Elf32_Ehdr*)file_buffer;
|
||||
if (memcmp(elf_header->e_ident, "\x7F" "ELF", 4) != 0) {
|
||||
// TODO: Free resources
|
||||
for(int i=0; i<argc; i++) free(kernel_argv[i]);
|
||||
free(kernel_argv);
|
||||
for(int i=0; i<envc; i++) free(kernel_envp[i]);
|
||||
free(kernel_envp);
|
||||
free(file_buffer);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -423,7 +435,8 @@ int sys_execve(TrapFrame *tf) {
|
||||
|
||||
uint32_t user_esp = USER_STACK_TOP;
|
||||
|
||||
uint32_t envp_pointers[envc + 1];
|
||||
//uint32_t envp_pointers[envc + 1];
|
||||
uint32_t* envp_pointers = malloc((envc + 1) * sizeof(uint32_t));
|
||||
for (int i = envc - 1; i >= 0; i--) {
|
||||
size_t len = strlen(kernel_envp[i]) + 1;
|
||||
user_esp -= len;
|
||||
@@ -432,7 +445,8 @@ int sys_execve(TrapFrame *tf) {
|
||||
}
|
||||
envp_pointers[envc] = 0;
|
||||
|
||||
uint32_t argv_pointers[argc + 1];
|
||||
//uint32_t argv_pointers[argc + 1];
|
||||
uint32_t* argv_pointers = malloc((argc + 1) * sizeof(uint32_t));
|
||||
for (int i = argc - 1; i >= 0; i--) {
|
||||
size_t len = strlen(kernel_argv[i]) + 1;
|
||||
user_esp -= len;
|
||||
@@ -447,31 +461,36 @@ int sys_execve(TrapFrame *tf) {
|
||||
user_esp -= (argc + 1) * sizeof(uint32_t);
|
||||
memcpy((void*)user_esp, argv_pointers, (argc + 1) * sizeof(uint32_t));
|
||||
|
||||
free(argv_pointers);
|
||||
free(envp_pointers);
|
||||
|
||||
user_esp -= sizeof(uint32_t);
|
||||
*((uint32_t*)user_esp) = argc;
|
||||
|
||||
current->brk = (void*)(DivRoundUp(highest_vaddr, PAGE_SIZE) * PAGE_SIZE);
|
||||
|
||||
uint32_t entry_point = elf_header->e_entry;
|
||||
|
||||
free(file_buffer);
|
||||
for(int i=0; i<argc; i++) free(kernel_argv[i]);
|
||||
free(kernel_argv);
|
||||
for(int i=0; i<envc; i++) free(kernel_envp[i]);
|
||||
free(kernel_envp);
|
||||
|
||||
destroy_page_dir(old_page_dir_to_free);
|
||||
|
||||
tf->gs = SEG_UDATA | DPL_USER;
|
||||
tf->fs = SEG_UDATA | DPL_USER;
|
||||
tf->es = SEG_UDATA | DPL_USER;
|
||||
tf->ds = SEG_UDATA | DPL_USER;
|
||||
tf->eax = 0; tf->ecx = 0; tf->edx = 0; tf->ebx = 0;
|
||||
tf->ebp = 0; tf->esi = 0; tf->edi = 0;
|
||||
tf->eip = elf_header->e_entry;
|
||||
tf->eip = entry_point;
|
||||
tf->cs = SEG_UCODE | DPL_USER;
|
||||
tf->eflags = FL_IF;
|
||||
tf->usermode_esp = user_esp;
|
||||
tf->usermode_ss = SEG_UDATA | DPL_USER;
|
||||
|
||||
destroy_page_dir(old_page_dir_to_free);
|
||||
|
||||
execve_return(tf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
+33
-73
@@ -119,78 +119,58 @@ void schedule() {
|
||||
Process* prev = NULL;
|
||||
p = queue;
|
||||
while (p) {
|
||||
if (p->state == Terminated) {
|
||||
Process* next_proc = p->next;
|
||||
if (p->state == Terminated && p != current) {
|
||||
Process* to_free = p;
|
||||
|
||||
// Unlink from queue
|
||||
if (prev) {
|
||||
prev->next = next_proc;
|
||||
prev->next = p->next;
|
||||
p = p->next; // Move to next
|
||||
} else {
|
||||
queue = next_proc;
|
||||
queue = p->next;
|
||||
p = queue; // Move to head
|
||||
}
|
||||
|
||||
// If we are terminating the currently running process, we must find a new one.
|
||||
if (p == current) {
|
||||
current = NULL;
|
||||
// Safe to free resources now because we are NOT running on this stack
|
||||
destroy_page_dir(to_free->pagedir);
|
||||
free_page((void*)virt_to_phys(to_free->kstack));
|
||||
free_page((void*)virt_to_phys(to_free));
|
||||
|
||||
// Continue loop without advancing prev (since we removed p)
|
||||
continue;
|
||||
}
|
||||
|
||||
// Free the terminated process's resources.
|
||||
destroy_page_dir(p->pagedir);
|
||||
free_page((void*)virt_to_phys(p->kstack));
|
||||
free_page((void*)virt_to_phys(p));
|
||||
|
||||
p = next_proc;
|
||||
} else {
|
||||
prev = p;
|
||||
p = p->next;
|
||||
}
|
||||
}
|
||||
|
||||
// If there are no processes left, we have a problem.
|
||||
if (!queue) {
|
||||
scheduler_unlock();
|
||||
// Ideally, you would panic the kernel here.
|
||||
// For now, we just return and hope an interrupt happens.
|
||||
return;
|
||||
}
|
||||
Process* start_search = (current && current->next) ? current->next : queue;
|
||||
if (!start_search) start_search = queue;
|
||||
|
||||
// Step 3: Find the next process to run using a round-robin algorithm.
|
||||
Process* start_search = NULL;
|
||||
if (current && current->state == Ready) {
|
||||
// If the current process is still ready, start searching from the next one.
|
||||
start_search = current->next;
|
||||
} else {
|
||||
// If the current process is not ready (e.g., it's Waiting or was Terminated),
|
||||
// start the search from the beginning of the queue.
|
||||
start_search = queue;
|
||||
}
|
||||
|
||||
// Ensure start_search is not NULL (handles wrapping around).
|
||||
if (!start_search) {
|
||||
start_search = queue;
|
||||
}
|
||||
|
||||
// Find the first available 'Ready' process.
|
||||
Process* next = start_search;
|
||||
if (next) { // Check if queue is empty
|
||||
do {
|
||||
if (next->state == Ready) {
|
||||
// We found a process to run.
|
||||
// If it's not the same one we're already running, switch to it.
|
||||
if (next != current) {
|
||||
tss.esp0 = next->kstack_top;
|
||||
switchProcess(next); // This will update 'current' and switch contexts.
|
||||
switchProcess(next);
|
||||
}
|
||||
// If next == current, we don't need to switch. Just continue execution.
|
||||
scheduler_unlock();
|
||||
return;
|
||||
}
|
||||
next = next->next;
|
||||
if (!next) {
|
||||
next = queue; // Wrap around to the beginning of the list.
|
||||
}
|
||||
if (!next) next = queue;
|
||||
} while (next != start_search);
|
||||
}
|
||||
|
||||
// If current is Terminated and we found no one else, we MUST run idle
|
||||
// otherwise we return to a dead stack.
|
||||
if (current->state == Terminated) {
|
||||
// Find idle task or panic.
|
||||
// Assuming first task (pid 1) is idle/init and never dies.
|
||||
// For now, just unlock (unsafe if current is dead) or loop.
|
||||
}
|
||||
|
||||
// If we get here, it means no process is in the 'Ready' state.
|
||||
// This can happen if all tasks are waiting for I/O or sleeping.
|
||||
// We just unlock and wait for the next interrupt to change a process's state.
|
||||
scheduler_unlock();
|
||||
}
|
||||
|
||||
@@ -220,34 +200,14 @@ void scheduler_init()
|
||||
}
|
||||
|
||||
void task_kill(Process* proc) {
|
||||
//TODO: that proc != current is sus
|
||||
if (!proc || proc != current) return;
|
||||
if (!proc) return;
|
||||
|
||||
scheduler_lock();
|
||||
proc->state = Terminated;
|
||||
|
||||
// Remove from process queue
|
||||
if (queue == proc) {
|
||||
queue = proc->next;
|
||||
} else {
|
||||
Process* prev = queue;
|
||||
while (prev && prev->next != proc) prev = prev->next;
|
||||
if (prev) prev->next = proc->next;
|
||||
if(proc == current)
|
||||
{
|
||||
schedule();
|
||||
}
|
||||
|
||||
// Schedule next process
|
||||
Process* next = queue;
|
||||
while (next && next->state != Ready) next = next->next;
|
||||
if (!next) next = queue; // Fallback to idle if needed
|
||||
|
||||
current = next;
|
||||
set_page_dir(virt_to_phys(next->pagedir));
|
||||
|
||||
// Free resources safely from new context
|
||||
destroy_page_dir(proc->pagedir);
|
||||
free_page((void*)virt_to_phys(proc->kstack));
|
||||
free_page((void*)virt_to_phys(proc));
|
||||
|
||||
scheduler_unlock();
|
||||
switchProcess(next);
|
||||
}
|
||||
Reference in New Issue
Block a user