#include "paging.h" #include "window.h" #include "drive.h" #include "panic.h" #include "cmos.h" #include "pmap.h" #include "task.h" #include "util.h" #include "elf.h" #include "idt.h" #include "kbd.h" #include "log.h" enum { IDT_PRESENT = 0x80, IDT_INT = 0x0e, }; struct idt_entry { uint16_t addr_low; uint16_t cs; uint8_t zero; uint8_t flags; uint16_t addr_high; } __attribute__ ((packed)); struct idt_entry idt[256]; struct { uint16_t limit; uint32_t start; } __attribute__ ((packed)) idtr = { .limit = 256 * sizeof(struct idt_entry) - 1, .start = (uint32_t)idt }; bool verify_file_handle(uint32_t handle, struct drive **d, uint32_t *f) { *d = drives + (handle >> 24); *f = handle & 0x00ffffff; if (!*f || (*d >= drives + n_drives)) { logf(LOG_WARN, "syscall with file handle 0x%h", handle); return false; } else return true; } uint32_t sc_open_file(const char *path) { struct drive *did; const char *sub; map_path(path, &did, &sub); if (!did) return 0; const file_id_t fid = did->get_file(did, sub); return fid ? ((did - drives) << 24) + fid : 0; } void sc_close_file(uint32_t handle) { struct drive *d; uint32_t f; if (verify_file_handle(handle, &d, &f)) d->free_file(d, f); } uint32_t sc_file_get_size(uint32_t handle) { struct drive *d; uint32_t f; if (verify_file_handle(handle, &d, &f)) return d->get_file_length(d, f); else return 0; } void sc_file_set_size(uint32_t handle, uint32_t new_size) { struct drive *d; uint32_t f; if (verify_file_handle(handle, &d, &f)) d->set_file_length(d, f, new_size); } uint32_t sc_file_read(uint32_t handle, uint32_t file_offset, uint32_t count, void *buffer) { struct drive *d; uint32_t f; if (verify_file_handle(handle, &d, &f)) { const uint32_t l = d->get_file_length(d, f); if (file_offset >= l) return 0; if (file_offset + count > l) count = l - file_offset; fmcpy(buffer, d, f, file_offset, count); return count; } else return 0; } uint32_t sc_file_write(uint32_t handle, uint32_t file_offset, uint32_t count, void *buffer) { struct drive *d; uint32_t f; if (verify_file_handle(handle, &d, &f)) { if (!d->is_writable(d, f)) return 0; const uint32_t l = d->get_file_length(d, f); if (file_offset >= l) return 0; if (file_offset + count > l) count = l - file_offset; mfcpy(d, f, file_offset, buffer, count); return l; } else return 0; } uint32_t sc_start_task(char *path, const char *pass, uint32_t io_task) { struct drive *d; const char *f; map_path(path, &d, &f); if (!d) return 0; switch_to_kernel_cr3(); uint32_t process_id = try_elf_run(d, vma_to_pma(active_task->page_directory, f), pass, io_task); switch_to_task_cr3(); return process_id; } void *sc_allocate_ram(uint32_t pages) { return pd_user_allocate_anywhere_writable(active_task->page_directory, pages); } enum mi_arg { MI_KERNEL_MAX, MI_KERNEL_LEFT, MI_USER_MAX, MI_USER_LEFT, MI_TASK_LEFT }; __attribute__ ((pure)) uint32_t sc_memory_info(enum mi_arg arg) { switch (arg) { case MI_KERNEL_MAX: return max_kernel_pages; case MI_KERNEL_LEFT: return kernel_pages_left; case MI_USER_MAX: return max_user_pages; case MI_USER_LEFT: return user_pages_left; case MI_TASK_LEFT: PANIC("TODO: memory info task left"); default: return -1; } } void sc_wait_for_task(uint32_t handle) { add_wait((struct wait){.mode = PROCESS_END, .task = tasks + handle - 1}); } uint32_t sc_enumerate_dir(const char *path, struct directory_content_info *buffer, uint32_t max_entries) { //not static to ensure sysv abi struct drive *d; const char *f; map_path(path, &d, &f); return d ? d->enumerate_dir(d, f, buffer, max_entries) : 0; } uint32_t sc_count_of_dir(const char *path) { struct drive *d; const char *f; map_path(path, &d, &f); return d ? d->n_dir_entries(d, f) : 0; } void sc_get_next_window_action(struct window *w, struct window_action *action) { *action = next_window_action(w); } void sc_wait_window_action() { add_wait((struct wait){.mode = WINDOW_ACTION}); } void sc_wait_ipc_sent(uint32_t task_handle) { add_wait((struct wait){.mode = IPC_SENT, .task = tasks + task_handle - 1}); } void sc_system_log(const char *sz) { logf(LOG_USER, "[%s] %s", active_task->name, sz); } void sc_wait_any_ipc_sent() { add_wait((struct wait){.mode = IPC_SENT_ANY}); } void sc_wait_ipc_read(uint32_t handle) { add_wait((struct wait){.mode = IPC_READ, .task = tasks + handle - 1}); } //returns a uint32_t to ensure upper twenty-four bits of // eax are set to zero - otherwise userland stuff break __attribute__ ((pure)) uint32_t sc_is_task_running(uint32_t handle) { return tasks[handle - 1].page_directory ? 1 : 0; } static const uint16_t days_into_four_years_per_month[] = { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366, 366+31, 366+59, 366+90, 366+120, 366+151, 366+181, 366+212, 366+243, 366+273, 366+304, 366+334, 731, 731+31, 731+59, 731+90, 731+120, 731+151, 731+181, 731+212, 731+243, 731+273, 731+304, 731+334, 1096, 1096+31, 1096+59, 1096+90, 1096+120, 1096+151, 1096+181, 1096+212, 1096+243, 1096+273, 1096+304, 1096+334 }; //gregorian, assumes year is in 2001 to 2099 uint32_t sc_get_timestamp() { const struct rtc_time time = get_rtc_time(); const uint32_t secs_into_month = time.seconds + time.minutes * 60 + time.hours * 3600 + (time.day_of_month - 1) * 86400; const uint32_t days_to_month_into_four_years = days_into_four_years_per_month[time.month + (time.year % 4) * 12 - 1]; return secs_into_month + days_to_month_into_four_years * 86400 + (time.year / 4) * (365 * 4 + 1) * 86400; } void const *syscall_table[] = { &sc_open_file, &sc_close_file, &sc_file_read, &sc_file_get_size, &sc_start_task, &ipc_send, &ipc_read, &sc_allocate_ram, &sc_memory_info, &sc_wait_for_task, &sc_enumerate_dir, &sc_system_log, &sc_count_of_dir, &new_window, &del_window, &resize_window, &reassign_pixel_buffer, &push_window_paint, &sc_get_next_window_action, &sc_wait_window_action, &sc_wait_ipc_sent, &sc_wait_any_ipc_sent, &find_unread_ipc, &sc_wait_ipc_read, &sc_is_task_running, &sc_get_timestamp, &sc_file_write, &sc_file_set_size, &window_wants_mouse_movements }; //these aren't really void ()'s, but gcc complains if we take an address of a void, so we give it a type typedef void isr_t(); extern isr_t syscall_isr; extern isr_t quit_isr; extern isr_t yield_isr; extern isr_t kbd_isr; extern isr_t mouse_isr; extern isr_t udf_isr; extern isr_t dfa_isr; extern isr_t tsf_isr; extern isr_t npf_isr; extern isr_t ssf_isr; extern isr_t gpf_isr; extern isr_t pff_isr; enum { F_ID = 0x00200000, F_VIP = 0x00100000, F_VIF = 0x00080000, F_AC = 0x00040000, F_VM = 0x00020000, F_RF = 0x00010000, F_NT = 0x00004000, F_OF = 0x00000800, F_DF = 0x00000400, F_IF = 0x00000200, F_TF = 0x00000100, F_SF = 0x00000080, F_ZF = 0x00000040, F_AF = 0x00000010, F_PF = 0x00000004, F_CF = 0x00000001, F_IOPL_MASK = 0x00003000 }; __attribute__ ((noreturn)) void exception_halt(uint32_t eax, uint32_t ebx, uint32_t ecx, uint32_t edx, uint32_t esi, uint32_t edi, const char *id, uint32_t code, uint32_t eip, uint32_t cs, uint32_t eflags, uint32_t esp, uint32_t ss) { if (code) logf(LOG_ERROR, "Exception #%s (0x%h) in %s", id, code, active_task->name); else logf(LOG_ERROR, "Exception #%s in %s:", id, active_task->name); logf(LOG_ERROR, " cs: 0x%hb ss: 0x%hb", cs, ss); logf(LOG_ERROR, " eip: 0x%h esp: 0x%h", eip, esp); logf(LOG_ERROR, " eax: 0x%h ebx: 0x%h", eax, ebx); logf(LOG_ERROR, " ecx: 0x%h edx: 0x%h", ecx, edx); logf(LOG_ERROR, " esi: 0x%h edi: 0x%h", esi, edi); logf(LOG_ERROR, " eflags:%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", eflags & F_ID ? " ID" : "", eflags & F_VIP ? " VIP" : "", eflags & F_VIF ? " VIF" : "", eflags & F_AC ? " AC" : "", eflags & F_VM ? " VM" : "", eflags & F_RF ? " RF" : "", eflags & F_NT ? " NT" : "", eflags & F_OF ? " OF" : "", eflags & F_DF ? " DF" : "", eflags & F_IF ? " IF" : "", eflags & F_TF ? " TF" : "", eflags & F_SF ? " SF" : "", eflags & F_ZF ? " ZF" : "", eflags & F_AF ? " AF" : "", eflags & F_PF ? " PF" : "", eflags & F_CF ? " CF" : "", eflags & ~F_IOPL_MASK ? "" : " none"); logf(LOG_ERROR, " iopl: %d", (eflags >> 12) & 3); logf(LOG_INFO, "Killing %s.", active_task->name); quit_isr(); __builtin_unreachable(); } #define MAX_STACK_EXPAND_PAGES 256 //returns true if stack was expanded bool pf_check_stack(uint32_t cr2) { if (cr2 >= active_task->stack_bottom - 0x1000 * MAX_STACK_EXPAND_PAGES) { switch_to_kernel_cr3(); pd_user_allocate(active_task->page_directory, active_task->stack_bottom -= 4096, 1, true); switch_to_task_cr3(); return true; } else { logf(LOG_ERROR, "Illegal access of 0x%h", cr2); return false; } } static void register_int(uint8_t n, isr_t *isr, uint8_t dpl) { idt[n].addr_low = (uint32_t)isr & 0xffff; idt[n].addr_high = (uint32_t)isr >> 16; idt[n].cs = 0x10; idt[n].flags = IDT_PRESENT | (dpl << 5) | IDT_INT; } enum { PIC_MCMD = 0x0020, PIC_MDATA = 0x0021, PIC_SCMD = 0x00a0, PIC_SDATA = 0x00a1 }; enum { PIC_RESET = 0x11 }; void init_idt() { for (uint16_t i = 0; i < 256; ++i) { idt[i].flags = 0; idt[i].zero = 0; } register_int(0x30, &syscall_isr, 3); register_int(0x38, &quit_isr, 3); register_int(0x39, &yield_isr, 3); register_int(0x21, &kbd_isr, 0); register_int(0x2c, &mouse_isr, 0); register_int(0x08, &udf_isr, 0); register_int(0x08, &dfa_isr, 0); register_int(0x0a, &tsf_isr, 0); register_int(0x0b, &npf_isr, 0); register_int(0x0c, &ssf_isr, 0); register_int(0x0d, &gpf_isr, 0); register_int(0x0e, &pff_isr, 0); outb(PIC_MCMD, PIC_RESET); outb(PIC_SCMD, PIC_RESET); outb(PIC_MDATA, 0x20); outb(PIC_SDATA, 0x28); outb(PIC_MDATA, 0x04); outb(PIC_SDATA, 0x02); outb(PIC_MDATA, 0x01); outb(PIC_SDATA, 0x01); outb(PIC_MDATA, 0xf9); outb(PIC_SDATA, 0xef); asm volatile ( "lidt %0" : : "m" (idtr) : "al"); }