diff --git a/Makefile b/Makefile index ad23dce..1321178 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,8 @@ objects = entry.o kernel.o task.o handler.o interrupt.o v86.o print.o tss.o gdt.o\ paging.o fault.o tests.o kbd.o helper.o disk.o file.o fs.o dosfs/dosfs.o fs_dos.o\ progs.o hexedit.o textedit.o -CFLAGS = -target "i686-elf" -m32 -mgeneral-regs-only -ffreestanding\ - -march=i686 -fno-stack-protector -Wno-int-conversion -nostdlib -c +CFLAGS = -target "i386-elf" -m32 -mgeneral-regs-only -ffreestanding\ + -march=i386 -fno-stack-protector -Wno-int-conversion -nostdlib -c LFLAGS = -Wl,--gc-sections -Wl,--print-gc-sections -m32 -nostartfiles -nostdlib ifeq ($(OUTFILE),) diff --git a/fault.nasm b/fault.nasm index 9f1170f..8a3f6ba 100644 --- a/fault.nasm +++ b/fault.nasm @@ -70,10 +70,13 @@ jmp _fault_coda _gpf_old_ds: dw 0 extern get_key extern get_scancode +extern get_key_no_wait +extern get_scancode_no_wait extern task_ptr extern _enter_v86_internal_no_task extern return_prev_task extern v86Interrupt +extern TIMERVAL gpf_handler_32: push eax mov eax, dword [esp+8] ; EIP @@ -91,9 +94,25 @@ jne .s1 call get_key jmp .return_to_offender .s1: cmp al, 0x01 ; get scancode -jne .s86 +jne .s2 call get_scancode jmp .return_to_offender +.s2: cmp al, 0x02 ; get key no wait +jne .s3 +call get_key_no_wait +jmp .return_to_offender +.s3: cmp al, 0x03 ; get scancode no wait +jne .s4 +call get_scancode_no_wait +jmp .return_to_offender +.s4: cmp al, 0x04 ; get timer +jne .s5 +mov eax, dword [TIMERVAL] +jmp .return_to_offender +.s5: cmp al, 0x05 ; short wait (hlt) +jne .s86 +hlt +jmp .return_to_offender .s86: cmp al, 0x86 ; v86 interrupt call je .v86int cmp ax, 0x86D8 ; get v86 data pointer diff --git a/handler.nasm b/handler.nasm index 7598156..1617d9a 100644 --- a/handler.nasm +++ b/handler.nasm @@ -1,71 +1,38 @@ -oldscancodesToAscii: db 0, 0 ; 0x00 - 0x01 -db "1234567890" ; 0x02 - 0x0B -db "-=" ; 0x0C - 0x0D -db 0, 0 ; 0x0E - 0x0F -db "qwertyuiop[]" ; 0x10 - 0x1B -db 0, 0 ; 0x1C - 0x1D -db "asdfghjkl;'`" ; 0x1E - 0x29 -db 0 ; 0x2A -db "\zxcvbnm,./" ; 0x2B - 0x35 -db 0 ; 0x36 -db '*' ; 0x37 -db 0 ; 0x38 -db ' ' ; 0x39 -db 'C' -scancodesToAsciiEnd: -cursorCurrent: dd 0xb8000 + (80*6*2) -global oldkeyboardHandler -oldkeyboardHandler: -push eax -push ebx -push ds -mov ax, 0x10 -mov ds, ax -xor eax, eax -in al, 0x60 -cmp eax, 0x3A -jg .done -mov al, [oldscancodesToAscii+eax] -test al, al -jz .done -mov ebx, [cursorCurrent] -mov byte [ebx], al -add dword [cursorCurrent], 2 -mov byte [KBDWAIT], 1 -.done: -mov al, 0x20 -out 0x20, al -pop ds -pop ebx -pop eax -iret - -KBDWAIT: db 0 -oldkbd_wait: -mov byte [KBDWAIT], 0 -.loop: -hlt -movzx eax, byte [KBDWAIT] -test eax, eax -jz .loop -ret +global PROC_SWITCH +PROC_SWITCH: dw 0 global TIMERVAL +global TIMERVAL_FRAC TIMERVAL: dd 0 +TIMERVAL_FRAC: dd 0 global timerHandler timerHandler: push eax push ds mov ax, 0x10 mov ds, ax -;inc byte [(0xb8000 + (80*8*2))] -inc dword [TIMERVAL] +; Value from PITPgrm +add dword [TIMERVAL_FRAC], 0xfffdb8d0 +adc dword [TIMERVAL], 0 mov al, 0x20 out 0x20, al +; Check task switch +movzx eax, word [PROC_SWITCH] +test eax, eax +jnz task_switch pop ds pop eax iret +extern save_current_task_int +task_switch: +mov word [PROC_SWITCH], 0 +pop ds +pop eax +call save_current_task_int +iret + + global picInit picInit: mov al, 0x11 ; initialization sequence diff --git a/kbd.c b/kbd.c index 895edc5..7a79320 100644 --- a/kbd.c +++ b/kbd.c @@ -1,6 +1,11 @@ #include "interrupt.h" #include "kbd.h" +extern uint16_t PROC_SWITCH; +__attribute__((__noreturn__)) +__attribute__((__no_caller_saved_registers__)) +extern void ret_current_task_int(); + uint8_t scancodesToAscii[0x3B] = "\0\0" // 0x00 - 0x01 "1234567890" // 0x02 - 0x0B @@ -69,6 +74,8 @@ void keyboardHandler(struct interrupt_frame *frame) { "mov %%ax, %%ds\n" ::"a"(old_ds) ); + if (key == KEY_F12) PROC_SWITCH = 1; + if (key == KEY_F11) ret_current_task_int(); } __attribute((__no_caller_saved_registers__)) @@ -106,3 +113,18 @@ uint16_t get_scancode() { return k; } +__attribute((__no_caller_saved_registers__)) +uint8_t get_key_no_wait() { + uint8_t k = _LSTKEY_ASCII; + _LSTKEY_ASCII = 0; + return k; +} + +__attribute((__no_caller_saved_registers__)) +uint16_t get_scancode_no_wait() { + uint16_t k = _LSTKEY_SCAN | (_LSTKEY_ASCII << 8); + _LSTKEY_SCAN = 0; + _LSTKEY_ASCII = 0; + return k; +} + diff --git a/kernel.c b/kernel.c index 91d9842..273a6f2 100644 --- a/kernel.c +++ b/kernel.c @@ -190,9 +190,11 @@ Protected Only (1MB+) 280000 - 300000 Disk Cache (512kB) 300000 - 310000 Task Stack (64kB) 310000 - 320000 Interrupt Stack (64kB) -320000 - 400000 Kernel Stack (896kB) -400000 - 700000 Usermode Code (3mB) -700000 - 800000 Usermode Stack (1mB) +320000 - 400000 Free (896kB) +400000 - 700000 Free (3mB) +700000 - 800000 Kernel Stack (1mB) + 800000 - F00000 Usermode Code (7mB) + F00000 - 1000000 Usermode Stack (1mB) */ void DrawScreen(uint16_t *vga) { @@ -247,13 +249,8 @@ void RestoreVGA() { } int32_t fileCount, fileOffset; -// We store dir entries in usermode space, -// which is nice because there's nothing after, -// but it does mean we need to reload the dir -// after every task called. This might be fine, -// since the task might have modified the directory. extern char _USERMODE; -dirent *const DirEntries = (dirent*)&_USERMODE; +dirent *const DirEntries = (dirent*)0x400000; #define MAXDISPFILES 16 void PrintFileList(uint16_t *vga) { uint16_t *vga_text = &((uint16_t *)vga)[80*6+3]; @@ -377,7 +374,20 @@ void FileSelect() { for (int i = 0; i < DirEntries[fileHovered].namelen; i++) current_path[current_path_end + i] = DirEntries[fileHovered].name[i]; current_path[current_path_end + DirEntries[fileHovered].namelen] = 0; - create_child(GetFreeStack(), (uintptr_t)HexEditor, 1, current_path); + { + // Give userspace mem (8MB) + uintptr_t process_page0 = add_pages(4|2|1); + uintptr_t process_page1 = add_pages(4|2|1); + asm("xchg %bx,%bx"); + // Page blocks are 4MB + move_pages(process_page0 >> 22, (uintptr_t)&_USERMODE >> 22); + move_pages(process_page1 >> 22, ((uintptr_t)&_USERMODE >> 22) + 1); + asm("xchg %bx,%bx"); + create_child(GetFreeStack(), (uintptr_t)HexEditor, 1, current_path); + // Free userspace mem + free_pages(process_page0 >> 22); + free_pages(process_page1 >> 22); + } current_path[current_path_end] = 0; RestoreVGA(); reload = 1; @@ -388,7 +398,18 @@ void FileSelect() { for (int i = 0; i < DirEntries[fileHovered].namelen; i++) current_path[current_path_end + i] = DirEntries[fileHovered].name[i]; current_path[current_path_end + DirEntries[fileHovered].namelen] = 0; - create_child(GetFreeStack(), (uintptr_t)TextViewTest, 1, current_path); + { + // Give userspace mem (8MB) + uintptr_t process_page0 = add_pages(4|2|1); + uintptr_t process_page1 = add_pages(4|2|1); + // Page blocks are 4MB + move_pages(process_page0 >> 22, (uintptr_t)&_USERMODE >> 22); + move_pages(process_page1 >> 22, ((uintptr_t)&_USERMODE >> 22) + 1); + create_child(GetFreeStack(), (uintptr_t)TextViewTest, 1, current_path); + // Free userspace mem + free_pages(process_page0 >> 22); + free_pages(process_page1 >> 22); + } current_path[current_path_end] = 0; RestoreVGA(); reload = 1; @@ -455,6 +476,34 @@ void FileSelect() { } } +// TODO Move this somewhere else, maybe configurable & with timer handler +void PITPgrm() { + const uint32_t base_clk = 3579545; + const uint16_t reload_v = 1194; + // (base_clk / 3) / reload_v = ~999.31463Hz + + // 32.32 Fixed point + const uint64_t irq_ms = (uint64_t)reload_v * 3000LL * ((1LL << 42) / (uint64_t)base_clk / (1LL << 10)); + // irq_ms = 4294818000 (0xfffdb8d0) + // We don't actually use this here, but in handler.nasm + // for now, eventually this should be in C + + asm volatile( + "cli\n" + "movb $0b00110100, %%al\n" + "outb %%al, $0x43\n" + :::"%eax" + ); + uint32_t clobber_eax; + asm volatile( + "outb %%al, $0x40\n" + "movb %%ah, %%al\n" + "outb %%al, $0x40\n" + "sti\n" + :"=a"(clobber_eax):"a"(reload_v) + ); +} + int MakeSystemVolume(uint8_t sysPartition); void MakeMBRPartitions(); void SystemRun(uint8_t sysPartition) { @@ -463,6 +512,7 @@ void SystemRun(uint8_t sysPartition) { DrawScreen((uint16_t*)0xb8000); InitDisk(); + PITPgrm(); // Check for FAT partition { diff --git a/paging.c b/paging.c index c752793..8b7d687 100644 --- a/paging.c +++ b/paging.c @@ -1,7 +1,7 @@ #include "paging.h" uint32_t page_directory[1024] __attribute__((aligned(4096))); -uint32_t page_tables[4][1024] __attribute__((aligned(4096))); +uint32_t page_tables[128][1024] __attribute__((aligned(4096))); void enable_paging() { asm( @@ -21,7 +21,6 @@ void map_pages(uintptr_t page, uintptr_t num_pages, uintptr_t phy_address, char // Each page is 4K, Each page table is 4M uintptr_t page_dir_entry = page >> 10; - if (page_dir_entry >= 4) return; uintptr_t page_index = page & 0x3ff; if (page_index + num_pages > 1024) return; @@ -32,11 +31,93 @@ void map_pages(uintptr_t page, uintptr_t num_pages, uintptr_t phy_address, char page_tables[page_dir_entry][page_index + i] = phy_address + (i * 0x1000) | access_flags; } +// Marks a 4MB page directory index unused, *without* freeing physical memory +void mark_pages_unused(uint16_t page_dir_idx) { + if (page_dir_idx >= 1024) return; + // Supervisor, R/W, Not Present + page_directory[page_dir_idx] = 2; +} + +// Bitmap of physical memory usage +uint8_t PhyMapArr[1024/8]; + +// Marks a 4MB page directory index unused, along with underlying physical memory +void free_pages(uint16_t page_dir_idx) { + // Get first entry of page table + uint32_t *page_table = (uint32_t*)(page_directory[page_dir_idx] != 2 ? (uintptr_t)page_directory[page_dir_idx] & 0xfffff000 : 0); + if (!page_table) return; + + // Free physical memory + uint32_t phy_address = page_table[0] & 0xfffff000; + uint32_t phy_address_4mb = phy_address >> 22; + uint32_t map_idx = phy_address_4mb >> 3; + uint32_t bit_idx = phy_address_4mb & 7; + // Clear bit + PhyMapArr[map_idx] &= ~(1 << bit_idx); + + // Mark unused + mark_pages_unused(page_dir_idx); +} + +// Gets an unused 4MB page directory index. Return -1 if no free pages. +uint16_t get_unused_pages() { + for (int i = 0; i < 1024; i++) + if (page_directory[i] == 2) return i; + return -1; +} + +// Gets physical address of 4MB unused area of physical memory. This will have to be mapped to virtual memory space to be used. +uint32_t get_phy_mem() { + for (int i = 0; i < sizeof(PhyMapArr); i++) { + // Fully used + if (PhyMapArr[i] == 0xff) continue; + // At least one bit must be free + uint8_t bits = PhyMapArr[i]; + for (int j = 0; j < 8; j++) + if (!(bits & (1 << j))) return (i << 25) + (j << 22); + } + return -1; +} + // User, R/W, Present #define USERACCESS (4|2|1) // Supervisor, R/W, Present #define SUPERACCESS (2|1) +// Returns a pointer to a newly mapped 4MB memory chunk +uintptr_t add_pages(char access_flags) { + // Get free physical space + uint32_t phy_mem = get_phy_mem(); + if (phy_mem == -1) return -1; + + // Get free virtual space + uint16_t free_ent = get_unused_pages(); + if ((int16_t)free_ent == -1) return -1; + uintptr_t page = free_ent << 10; + + // Mark page directory entry present, pointing to page table + page_directory[free_ent] = ((uintptr_t)&page_tables[free_ent]) | USERACCESS; + + // Map pages to physical address + map_pages(page, 1024, phy_mem, access_flags); + + // Mark physical memory used + uint32_t phy_address_4mb = phy_mem >> 22; + uint32_t map_idx = phy_address_4mb >> 3; + uint32_t bit_idx = phy_address_4mb & 7; + // Set bit + PhyMapArr[map_idx] |= 1 << bit_idx; + + // Return virtual address + return page << 12; +} + +// Copies 4MB groups of pages +void move_pages(uint16_t src_page_dir_idx, uint16_t dest_page_dir_idx) { + if (src_page_dir_idx >= 1024 || dest_page_dir_idx >= 1024) return; + page_directory[dest_page_dir_idx] = page_directory[src_page_dir_idx]; +} + void init_paging() { for (int i = 0; i < 1024; i++) // Supervisor, R/W, Not Present @@ -51,13 +132,15 @@ void init_paging() { // Next 4MB: Kernel map_pages(0x00400000 >> 12, 1024, 0x00400000, SUPERACCESS); - // Next 8MB: Usermode - map_pages(0x00800000 >> 12, 1024, 0x00800000, USERACCESS); - map_pages(0x00C00000 >> 12, 1024, 0x00C00000, USERACCESS); - // We aren't using page directory permissions for (int i = 0; i < 4; i++) page_directory[i] = ((uintptr_t)&page_tables[i]) | USERACCESS; + // Mark all physical memory free TODO Check memory size + for (int i = 0; i < sizeof(PhyMapArr); i++) + PhyMapArr[i] = 0; + // Mark lowest 8MB as used + PhyMapArr[0] = 3; + enable_paging(); } diff --git a/paging.h b/paging.h index dca4f26..71db16e 100644 --- a/paging.h +++ b/paging.h @@ -1,3 +1,6 @@ #include +void free_pages(uint16_t page_dir_idx); +uintptr_t add_pages(char access_flags); +void move_pages(uint16_t src_page_dir_idx, uint16_t dest_page_dir_idx); void init_paging(); diff --git a/progs.c b/progs.c index cc93a10..a9827d5 100644 --- a/progs.c +++ b/progs.c @@ -1,21 +1,23 @@ #include "progs.h" +#include "paging.h" #include "file.h" extern char _USERMODE, _USERMODE_END; +static uintptr_t USERMODE_STACK = (uintptr_t)&_USERMODE_END - 0x100000; extern uint32_t create_user_child(uint32_t esp, uint32_t eip, uint32_t argc, ...); -void ProgramLoadTest(char *path) { +void programLoadTestInner(char *path) { uint16_t *vga_text = (uint16_t *)0xb8000; for (int i = 0; i < 80*25; i++) vga_text[i] = 0x0f00; uint32_t successcount; + uint8_t *diskReadBuf = (uint8_t *)&_USERMODE; { uint32_t err; dirent de; err = path_getinfo(path, &de); - if (de.size > 0x300000 || err) { + if (de.size > 0xE00000 || err) { vga_text += printStr("File too large or error.", vga_text); - kbd_wait(); return; } FILE file; @@ -45,7 +47,8 @@ void ProgramLoadTest(char *path) { vga_text = nextLine(vga_text,(uint16_t*)0xb8000); vga_text += printStr("Press any key to run.", vga_text); kbd_wait(); - uint32_t res = create_user_child((uintptr_t)&_USERMODE_END, (uintptr_t)&_USERMODE, 0); + uint32_t res = create_user_child(USERMODE_STACK, (uintptr_t)&_USERMODE, 0); + union V86Regs_t regs; regs.w.ax = 3; // text mode V8086Int(0x10, ®s); @@ -57,5 +60,18 @@ void ProgramLoadTest(char *path) { vga_text += printStr(" (0x", vga_text); vga_text += printDword(res, vga_text); vga_text += printStr("). Press any key to exit.", vga_text); - kbd_wait(); +} +void ProgramLoadTest(char *path) { + // Get free memory (8MB) + uintptr_t process_page0 = add_pages(4|2|1); + uintptr_t process_page1 = add_pages(4|2|1); + // Map usermode memory + move_pages(process_page0 >> 22, (uintptr_t)&_USERMODE >> 22); + move_pages(process_page1 >> 22, ((uintptr_t)&_USERMODE >> 22) + 1); + // Inner program + programLoadTestInner(path); + kbd_wait(); + // Free process memory + free_pages(process_page0 >> 22); + free_pages(process_page1 >> 22); } diff --git a/task.nasm b/task.nasm index 654c1c2..68f5bf5 100644 --- a/task.nasm +++ b/task.nasm @@ -5,9 +5,8 @@ ltr ax ret global task_ptr -task_ptr: equ (0x310000-4) +task_ptr: equ (0x310000) -; TODO Is varargs too compiler dependent? ; extern void create_child(uint32_t esp, uint32_t eip, uint32_t argc, ...); global create_child create_child: @@ -60,6 +59,101 @@ push 0x18 | 3 push eax iret +; save task +; stack contains caller, eip, cs, eflags(, esp, ss) +global save_current_task_new +save_current_task_new: +push ds +push ebx +push eax +push 0x10 +pop ds +; 0 eax, 4 ebx, 8 ds, 12 caller, 16 eip, 20 cs, 24 eflags(, 28 esp, 32 ss) +; location to save task +mov ebx, dword [0x318000] +; code & stack +mov eax, dword [esp+16] +mov dword [ebx+0], eax ; eip +mov eax, dword [esp+20] +mov dword [ebx+4], eax ; cs +cmp ax, 0x08 +; if we're not in code segment, save stack info +je .curr_stack +mov eax, dword [esp+24] +mov dword [ebx+8], eax ; eflags +mov eax, dword [esp+28] +mov dword [ebx+12], eax ; esp +mov eax, dword [esp+32] +mov dword [ebx+16], eax ; ss +jmp .seg_regs +; if we're in code segment, we save current info, minus interrupt stack +.curr_stack: +mov eax, dword [esp+24] +mov dword [ebx+8], eax ; eflags +lea eax, [esp+28] +mov dword [ebx+12], eax ; esp before interrupt +mov eax, ss +mov dword [ebx+16], eax +; segment regs +.seg_regs: +mov eax, dword [esp+8] +mov dword [ebx+20], eax ; ds +mov eax, es +mov dword [ebx+24], eax +mov eax, fs +mov dword [ebx+28], eax +mov eax, gs +mov dword [ebx+32], eax +; general regs besides esp +mov eax, dword [esp+0] +mov dword [ebx+36], eax ; eax +mov eax, dword [esp+4] +mov dword [ebx+40], eax ; ebx +mov dword [ebx+44], ecx +mov dword [ebx+48], edx +mov dword [ebx+52], esi +mov dword [ebx+56], edi +mov dword [ebx+60], ebp +pop eax +pop ebx +pop ds +xchg bx,bx +ret + +; return to current task +global ret_current_task +ret_current_task: +xchg bx,bx +push 0x10 +pop ds +; location to get task +mov ebx, dword [0x318000] +mov ebp, [ebx+60] +mov edi, [ebx+56] +mov esi, [ebx+52] +mov edx, [ebx+48] +mov ecx, [ebx+44] +mov gs, [ebx+32] +mov fs, [ebx+28] +mov es, [ebx+24] +mov ds, [ebx+20] +mov eax, [ebx+4] ; cs +cmp ax, 0x08 +je .restore_stack +mov eax, [ebx+36] +mov esp, ebx +mov ebx, [ebx+40] +iret +.restore_stack: +mov esp, [ebx+12] +mov ss, [ebx+16] +mov eax, [ebx+36] +push dword [ebx+8] +push dword [ebx+4] +push dword [ebx] +mov ebx, [ebx+40] +iret + ; return address in EAX ; return stack in ECX ; we can modify EAX, ECX, EDX diff --git a/tests.c b/tests.c index 03dbd7d..b038397 100644 --- a/tests.c +++ b/tests.c @@ -1,4 +1,5 @@ #include "tests.h" +#include "paging.h" extern char *user_test(); extern uint32_t create_user_child(uint32_t esp, uint32_t eip, uint32_t argc, ...); @@ -26,8 +27,17 @@ void ReloadUser() { *d++ = *s++; } char TestUser() { + // Get free memory (8MB) + uintptr_t process_page0 = add_pages(4|2|1); + uintptr_t process_page1 = add_pages(4|2|1); + // Map usermode memory + move_pages(process_page0 >> 22, (uintptr_t)&_USERMODE >> 22); + move_pages(process_page1 >> 22, ((uintptr_t)&_USERMODE >> 22) + 1); ReloadUser(); - char *vga = (char *)(uintptr_t)create_user_child((uintptr_t)&_USERMODE_END, (uintptr_t)&_USERMODE, 0); + char *vga = (char *)(uintptr_t)create_user_child((uintptr_t)&_USERMODE_END - 0x100000, (uintptr_t)&_USERMODE, 0); + // Free process memory + free_pages(process_page0 >> 22); + free_pages(process_page1 >> 22); if ((uintptr_t)vga != 0xA0000) { return 1; } diff --git a/tss.c b/tss.c index 715c890..2b3b310 100644 --- a/tss.c +++ b/tss.c @@ -41,8 +41,8 @@ void write_tss() { tss_data->iomap_base = 0x80; // not technically TSS but set up task pointer - uint32_t *current_task_ptr = (uint32_t*)(0x310000-4); - *current_task_ptr = 0x310000-(20*4); // each task is 12 dwords, plus 1 for pointer + uint32_t *current_task_ptr = (uint32_t*)(0x310000); + *current_task_ptr = 0x310000-(1*4); // each task is 12 dwords, plus 1 for pointer /* TODO setup null recovery task at start */ } extern void flushTSS(); diff --git a/usermode.nasm b/usermode.nasm index 3d75ccd..079403d 100644 --- a/usermode.nasm +++ b/usermode.nasm @@ -27,6 +27,8 @@ mov dword [0xb8000+800], 0x0f000f00 | 'g' | ':' << 16 mov dword [0xb8004+800], 0x0f000f00 | ' ' | '#' << 16 mov dword [0xb8008+800], 0x0f000f00 | 'G' | 'P' << 16 mov dword [0xb800C+800], 0x0f000f00 | 'F' | ' ' << 16 +.busy: +jmp .busy mov eax, 0 ; command = 00h, get key int 0x21 ; OS call cmp al, 'd'