Переглянути джерело

Lots of hacking:

- Turn Keyboard into a CharacterDevice (85,1) at /dev/keyboard.
- Implement MM::unmapRegionsForTask() and MM::unmapRegion()
- Save SS correctly on interrupt.
- Add a simple Spawn syscall for launching another process.
- Move a bunch of IO syscall debug output behind DEBUG_IO.
- Have ASSERT do a "cli" immediately when failing.
  This makes the output look proper every time.
- Implement a bunch of syscalls in LibC.
- Add a simple shell ("sh"). All it can do now is read a line
  of text from /dev/keyboard and then try launching the specified
  executable by calling spawn().

There are definitely bugs in here, but we're moving on forward.
Andreas Kling 6 роки тому
батько
коміт
fe237ee215

+ 1 - 1
AK/CircularQueue.h

@@ -1,7 +1,7 @@
 #pragma once
 
+#include "Assertions.h"
 #include "Types.h"
-#include "kstdio.h"
 
 namespace AK {
 

+ 23 - 5
Kernel/Keyboard.cpp

@@ -47,7 +47,7 @@ void Keyboard::handleIRQ()
         case 0x9D: m_modifiers &= ~MOD_CTRL; break;
         case 0x2A: m_modifiers |= MOD_SHIFT; break;
         case 0xAA: m_modifiers &= ~MOD_SHIFT; break;
-        case 0x1C: /* enter */ kprintf("\n"); break;
+        case 0x1C: /* enter */ m_queue.enqueue('\n'); break;
         case 0xFA: /* i8042 ack */ break;
         default:
             if (ch & 0x80) {
@@ -55,11 +55,14 @@ void Keyboard::handleIRQ()
                 break;
             }
             if (!m_modifiers)
-                kprintf("%c", map[ch]);
+                m_queue.enqueue(map[ch]);
             else if (m_modifiers & MOD_SHIFT)
-                kprintf("%c", shift_map[ch]);
-            else if (m_modifiers & MOD_CTRL)
-                kprintf("^%c", shift_map[ch]);
+                m_queue.enqueue(shift_map[ch]);
+            else if (m_modifiers & MOD_CTRL) {
+                // FIXME: This is obviously not a good enough way to process ctrl+whatever.
+                m_queue.enqueue('^');
+                m_queue.enqueue(shift_map[ch]);
+            }
         }
         //break;
     }
@@ -81,3 +84,18 @@ Keyboard::~Keyboard()
     ASSERT_NOT_REACHED();
 }
 
+ssize_t Keyboard::read(byte* buffer, size_t size)
+{
+    ssize_t nread = 0;
+    while (nread < size) {
+        if (m_queue.isEmpty())
+            break;
+        buffer[nread++] = m_queue.dequeue();
+    }
+    return nread;
+}
+
+ssize_t Keyboard::write(const byte* data, size_t size)
+{
+    return 0;
+}

+ 10 - 1
Kernel/Keyboard.h

@@ -1,16 +1,25 @@
 #pragma once
 
 #include <AK/Types.h>
+#include <AK/DoublyLinkedList.h>
+#include <AK/CircularQueue.h>
+#include <VirtualFileSystem/CharacterDevice.h>
 #include "IRQHandler.h"
 
-class Keyboard final : public IRQHandler {
+class Keyboard final : public IRQHandler, public CharacterDevice {
 public:
     virtual ~Keyboard() override;
     Keyboard();
 
 private:
+    // ^IRQHandler
     virtual void handleIRQ() override;
 
+    // ^CharacterDevice
+    virtual ssize_t read(byte* buffer, size_t) override;
+    virtual ssize_t write(const byte* buffer, size_t) override;
+
+    CircularQueue<byte, 16> m_queue;
     byte m_modifiers { 0 };
 };
 

+ 20 - 0
Kernel/MemoryManager.cpp

@@ -156,8 +156,28 @@ byte* MemoryManager::quickMapOnePage(PhysicalAddress physicalAddress)
     return (byte*)(4 * MB);
 }
 
+bool MemoryManager::unmapRegion(Task& task, Task::Region& region)
+{
+    auto& zone = *region.zone;
+    for (size_t i = 0; i < zone.m_pages.size(); ++i) {
+        auto laddr = region.linearAddress.offset(i * PAGE_SIZE);
+        auto pte = ensurePTE(laddr);
+        pte.setPhysicalPageBase(0);
+        pte.setPresent(false);
+        pte.setWritable(false);
+        pte.setUserAllowed(false);
+
+//        kprintf("MM: >> Unmapped L%x => P%x <<\n", laddr, zone.m_pages[i].get());
+    }
+    return true;
+}
+
 bool MemoryManager::unmapRegionsForTask(Task& task)
 {
+    for (auto& region : task.m_regions) {
+        if (!unmapRegion(task, *region))
+            return false;
+    }
     return true;
 }
 

+ 1 - 0
Kernel/MemoryManager.h

@@ -51,6 +51,7 @@ public:
     byte* quickMapOnePage(PhysicalAddress);
 
     bool mapRegion(Task&, Task::Region&);
+    bool unmapRegion(Task&, Task::Region&);
     bool mapRegionsForTask(Task&);
     bool unmapRegionsForTask(Task&);
 

+ 7 - 4
Kernel/Syscall.cpp

@@ -22,6 +22,7 @@ asm(
     "    pushw %ss\n"
     "    pushw %ss\n"
     "    pushw %ss\n"
+    "    pushw %ss\n"
     "    popw %ds\n"
     "    popw %es\n"
     "    popw %fs\n"
@@ -29,6 +30,7 @@ asm(
     "    mov %esp, syscallRegDump\n"
     "    call syscall_entry\n"
     "    popw %gs\n"
+    "    popw %gs\n"
     "    popw %fs\n"
     "    popw %es\n"
     "    popw %ds\n"
@@ -58,15 +60,16 @@ DWORD handle(DWORD function, DWORD arg1, DWORD arg2, DWORD arg3)
         //kprintf("syscall: sleep(%d)\n", arg1);
         current->sys$sleep(arg1);
         break;
+    case Syscall::Spawn:
+        return current->sys$spawn((const char*)arg1);
     case Syscall::PosixOpen:
-        Task::checkSanity("syscall");
-        kprintf("syscall: open('%s', %u)\n", arg1, arg2);
+        //kprintf("syscall: open('%s', %u)\n", arg1, arg2);
         return current->sys$open((const char*)arg1, (size_t)arg2);
     case Syscall::PosixClose:
-        kprintf("syscall: close(%d)\n", arg1);
+        //kprintf("syscall: close(%d)\n", arg1);
         return current->sys$close((int)arg1);
     case Syscall::PosixRead:
-        kprintf("syscall: read(%d, %p, %u)\n", arg1, arg2, arg3);
+        //kprintf("syscall: read(%d, %p, %u)\n", arg1, arg2, arg3);
         return current->sys$read((int)arg1, (void*)arg2, (size_t)arg3);
     case Syscall::PosixSeek:
         // FIXME: This has the wrong signature, should be like lseek()

+ 1 - 0
Kernel/Syscall.h

@@ -10,6 +10,7 @@
 namespace Syscall {
 
 enum Function {
+    Spawn = 0x1981,
     Sleep = 0x1982,
     Yield = 0x1983,
     PutCharacter = 1984,

+ 43 - 6
Kernel/Task.cpp

@@ -10,6 +10,8 @@
 #include <ELFLoader/ExecSpace.h>
 #include "MemoryManager.h"
 
+//#define DEBUG_IO
+
 Task* current;
 Task* s_kernelTask;
 
@@ -104,6 +106,14 @@ Task::Region* Task::allocateRegion(size_t size, String&& name)
     return m_regions.last().ptr();
 }
 
+int Task::sys$spawn(const char* path)
+{
+    auto* child = Task::create(path, m_uid, m_gid);
+    if (child)
+        return child->pid();
+    return -1;
+}
+
 Task* Task::create(const String& path, uid_t uid, gid_t gid)
 {
     auto parts = path.split('/');
@@ -118,6 +128,7 @@ Task* Task::create(const String& path, uid_t uid, gid_t gid)
     if (!elfData)
         return nullptr;
 
+    cli();
     Task* t = new Task(parts.takeLast(), uid, gid);
 
     ExecSpace space;
@@ -137,9 +148,14 @@ Task* Task::create(const String& path, uid_t uid, gid_t gid)
     }
 
     t->m_tss.eip = (dword)space.symbolPtr("_start");
+    if (!t->m_tss.eip) {
+        delete t;
+        return nullptr;
+    }
+
+    MemoryManager::the().unmapRegionsForTask(*t);
+    MemoryManager::the().mapRegionsForTask(*current);
 
-    // Add this task to head of task list (meaning it's next to run too, ATM.)
-    cli();
     s_tasks->prepend(t);
     system.nprocess++;
     kprintf("Task %u (%s) spawned @ %p\n", t->pid(), t->name().characters(), t->m_tss.eip);
@@ -357,10 +373,12 @@ void Task::taskDidCrash(Task* crashedTask)
 {
     // NOTE: This is called from an excepton handler, so interrupts are disabled.
     crashedTask->setState(Crashing);
-    crashedTask->dumpRegions();
+//  crashedTask->dumpRegions();
 
     s_tasks->remove(crashedTask);
 
+    MemoryManager::the().unmapRegionsForTask(*crashedTask);
+
     if (!scheduleNewTask()) {
         kprintf("Task::taskDidCrash: Failed to schedule a new task :(\n");
         HANG;
@@ -491,7 +509,16 @@ static bool contextSwitch(Task* t)
     // Some sanity checking to force a crash earlier.
     auto csRPL = t->tss().cs & 3;
     auto ssRPL = t->tss().ss & 3;
-    ASSERT(csRPL == ssRPL);
+
+    if (csRPL != ssRPL) {
+        kprintf("Fuckup! Switching from %s(%u) to %s(%u) has RPL mismatch\n",
+                current->name().characters(), current->pid(),
+                t->name().characters(), t->pid()
+                );
+        kprintf("code: %w:%x\n", t->tss().cs, t->tss().eip);
+        kprintf(" stk: %w:%x\n", t->tss().ss, t->tss().esp);
+        ASSERT(csRPL == ssRPL);
+    }
 
     if (current) {
         // If the last task hasn't blocked (still marked as running),
@@ -572,17 +599,24 @@ int Task::sys$seek(int fd, int offset)
 ssize_t Task::sys$read(int fd, void* outbuf, size_t nread)
 {
     Task::checkSanity("Task::sys$read");
+#ifdef DEBUG_IO
     kprintf("Task::sys$read: called(%d, %p, %u)\n", fd, outbuf, nread);
+#endif
     auto* handle = fileHandleIfExists(fd);
+#ifdef DEBUG_IO
     kprintf("Task::sys$read: handle=%p\n", handle);
+#endif
     if (!handle) {
         kprintf("Task::sys$read: handle not found :(\n");
         return -1;
     }
+#ifdef DEBUG_IO
     kprintf("call read on handle=%p\n", handle);
+#endif
     nread = handle->read((byte*)outbuf, nread);
-    kprintf("called read\n");
+#ifdef DEBUG_IO
     kprintf("Task::sys$read: nread=%u\n", nread);
+#endif
     return nread;
 }
 
@@ -598,7 +632,9 @@ int Task::sys$close(int fd)
 int Task::sys$open(const char* path, size_t pathLength)
 {
     Task::checkSanity("sys$open");
+#ifdef DEBUG_IO
     kprintf("Task::sys$open(): PID=%u, path=%s {%u}\n", m_pid, path, pathLength);
+#endif
     auto* handle = current->openFile(String(path, pathLength));
     if (handle)
         return handle->fd();
@@ -607,14 +643,15 @@ int Task::sys$open(const char* path, size_t pathLength)
 
 FileHandle* Task::openFile(String&& path)
 {
-    kprintf("calling vfs::open with vfs=%p, path='%s'\n", &VirtualFileSystem::the(), path.characters());
     auto handle = VirtualFileSystem::the().open(move(path));
     if (!handle) {
         kprintf("vfs::open() failed\n");
         return nullptr;
     }
     handle->setFD(m_fileHandles.size());
+#ifdef DEBUG_IO
     kprintf("vfs::open() worked! handle=%p, fd=%d\n", handle.ptr(), handle->fd());
+#endif
     m_fileHandles.append(move(handle)); // FIXME: allow non-move Vector::append
     return m_fileHandles.last().ptr();
 }

+ 1 - 0
Kernel/Task.h

@@ -90,6 +90,7 @@ public:
     int sys$geterror() { return m_error; }
     void sys$sleep(DWORD ticks);
     void sys$exit(int status);
+    int sys$spawn(const char* path);
 
     struct
     {

BIN
Kernel/_fs_contents


+ 4 - 0
Kernel/i386.cpp

@@ -78,6 +78,7 @@ asm( \
     "    pushw %ss\n" \
     "    pushw %ss\n" \
     "    pushw %ss\n" \
+    "    pushw %ss\n" \
     "    popw %ds\n" \
     "    popw %es\n" \
     "    popw %fs\n" \
@@ -85,6 +86,7 @@ asm( \
     "    mov %esp, exception_state_dump\n" \
     "    call exception_" # ec "_handler\n" \
     "    popw %gs\n" \
+    "    popw %gs\n" \
     "    popw %fs\n" \
     "    popw %es\n" \
     "    popw %ds\n" \
@@ -107,6 +109,7 @@ asm( \
     "    pushw %ss\n" \
     "    pushw %ss\n" \
     "    pushw %ss\n" \
+    "    pushw %ss\n" \
     "    popw %ds\n" \
     "    popw %es\n" \
     "    popw %fs\n" \
@@ -114,6 +117,7 @@ asm( \
     "    mov %esp, exception_state_dump\n" \
     "    call exception_" # ec "_handler\n" \
     "    popw %gs\n" \
+    "    popw %gs\n" \
     "    popw %fs\n" \
     "    popw %es\n" \
     "    popw %ds\n" \

+ 1 - 0
Kernel/i386.h

@@ -119,6 +119,7 @@ private:
 };
 
 struct RegisterDump {
+    WORD ss;
     WORD gs;
     WORD fs;
     WORD es;

+ 12 - 5
Kernel/i8253.cpp

@@ -28,6 +28,7 @@ asm(
     "    pushw %ss\n"
     "    pushw %ss\n"
     "    pushw %ss\n"
+    "    pushw %ss\n"
     "    popw %ds\n"
     "    popw %es\n"
     "    popw %fs\n"
@@ -35,6 +36,7 @@ asm(
     "    mov %esp, state_dump\n"
     "    call clock_handle\n"
     "    popw %gs\n"
+    "    popw %gs\n"
     "    popw %fs\n"
     "    popw %es\n"
     "    popw %ds\n"
@@ -117,13 +119,18 @@ void clock_handle()
     //        If this IRQ occurred while in a user task, wouldn't that also push the stack ptr?
     current->tss().esp = regs.esp + 12;
 
-    // FIXME: Is this really safe? What if the interrupted process didn't have SS==DS?
-    current->tss().ss = regs.ds;
+    current->tss().ss = regs.ss;
 
     if ((current->tss().cs & 3) != 0) {
-        // What do I do now?
-        kprintf("clk'ed across to ring0\n");
-        HANG;
+#if 0
+        kprintf("clock'ed across to ring0\n");
+        kprintf("code: %w:%x\n", current->tss().cs, current->tss().eip);
+        kprintf(" stk: %w:%x\n", current->tss().ss, current->tss().esp);
+        kprintf("astk: %w:%x\n", regs.ss_if_crossRing, regs.esp_if_crossRing);
+        //HANG;
+#endif
+        current->tss().ss = regs.ss_if_crossRing;
+        current->tss().esp = regs.esp_if_crossRing;
     }
 
     // Prepare a new task to run;

+ 7 - 6
Kernel/init.cpp

@@ -99,13 +99,12 @@ static void init_stage2()
 {
     kprintf("init stage2...\n");
 
-    // Anything that registers interrupts goes *after* PIC and IDT for obvious reasons.
     Syscall::initialize();
 
-    extern void panel_main();
+    auto keyboard = make<Keyboard>();
 
+    extern void panel_main();
     new Task(panel_main, "panel", IPC::Handle::PanelTask, Task::Ring0);
-
     //new Task(led_disco, "led-disco", IPC::Handle::Any, Task::Ring0);
 
     Disk::initialize();
@@ -125,6 +124,8 @@ static void init_stage2()
     auto dev_random = make<RandomDevice>();
     vfs->registerCharacterDevice(1, 8, *dev_random);
 
+    vfs->registerCharacterDevice(85, 1, *keyboard);
+
     auto dev_hd0 = IDEDiskDevice::create();
     auto e2fs = Ext2FileSystem::create(dev_hd0.copyRef());
     e2fs->initialize();
@@ -167,7 +168,9 @@ static void init_stage2()
     }
 #endif
 
-    auto* idTask = Task::create("/bin/id", (uid_t)209, (gid_t)1985);
+    //auto* idTask = Task::create("/bin/id", (uid_t)209, (gid_t)1985);
+
+    auto* shTask = Task::create("/bin/sh", (uid_t)100, (gid_t)100);
 
     //new Task(motd_main, "motd", IPC::Handle::MotdTask, Task::Ring0);
     //new Task(syscall_test_main, "syscall_test", IPC::Handle::MotdTask, Task::Ring3);
@@ -206,8 +209,6 @@ void init()
     VirtualFileSystem::initializeGlobals();
     StringImpl::initializeGlobals();
 
-    auto keyboard = make<Keyboard>();
-
     PIT::initialize();
 
     memset(&system, 0, sizeof(system));

+ 1 - 1
Kernel/kassert.h

@@ -3,6 +3,6 @@
 #include "kprintf.h"
 
 #define CRASH() do { asm volatile("ud2"); } while(0)
-#define ASSERT(x) do { if (!(x)) { kprintf("ASSERTION FAILED: " #x "\n%s:%u in %s\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); CRASH(); } } while(0)
+#define ASSERT(x) do { if (!(x)) { asm volatile("cli"); kprintf("ASSERTION FAILED: " #x "\n%s:%u in %s\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); CRASH(); } } while(0)
 #define RELEASE_ASSERT(x) do { if (!(x)) CRASH(); } while(0)
 #define ASSERT_NOT_REACHED() ASSERT(false)

+ 5 - 0
Kernel/sync-sh

@@ -0,0 +1,5 @@
+mkdir mnt
+mount -o loop _fs_contents mnt/
+cp ../Userland/sh mnt/bin/sh
+umount mnt
+sync

+ 2 - 0
LibC/Makefile

@@ -1,6 +1,8 @@
 OBJS = \
        stdio.o \
        unistd.o \
+       string.o \
+       process.o \
        entry.o
 
 LIBRARY = LibC.a

+ 12 - 0
LibC/process.cpp

@@ -0,0 +1,12 @@
+#include "process.h"
+#include <Kernel/Syscall.h>
+
+extern "C" {
+
+int spawn(const char* path)
+{
+    return Syscall::invoke(Syscall::Spawn, (dword)path);
+}
+
+}
+

+ 8 - 0
LibC/process.h

@@ -0,0 +1,8 @@
+#pragma once
+
+extern "C" {
+
+int spawn(const char* path);
+
+}
+

+ 2 - 1
LibC/stdio.cpp

@@ -141,7 +141,8 @@ extern "C" {
 
 int putchar(int ch)
 {
-    return ch;
+    Syscall::invoke(Syscall::PutCharacter, ch);
+    return (byte)ch;
 }
 
 int printf(const char* fmt, ...)

+ 14 - 0
LibC/string.cpp

@@ -0,0 +1,14 @@
+#include "string.h"
+
+extern "C" {
+
+size_t strlen(const char* str)
+{
+    size_t len = 0;
+    while (*(str++))
+        ++len;
+    return len;
+}
+
+}
+

+ 10 - 0
LibC/string.h

@@ -0,0 +1,10 @@
+#pragma once
+
+#include "types.h"
+
+extern "C" {
+
+size_t strlen(const char*);
+
+}
+

+ 7 - 0
LibC/types.h

@@ -6,9 +6,16 @@ typedef unsigned int dword;
 typedef unsigned short word;
 typedef unsigned char byte;
 
+typedef signed int signed_dword;
+typedef signed short signed_word;
+typedef signed char signed_byte;
+
 typedef dword uid_t;
 typedef dword gid_t;
 typedef dword pid_t;
 
+typedef dword size_t;
+typedef signed_dword ssize_t;
+
 }
 

+ 17 - 0
LibC/unistd.cpp

@@ -1,4 +1,5 @@
 #include "unistd.h"
+#include "string.h"
 #include <Kernel/Syscall.h>
 
 extern "C" {
@@ -18,5 +19,21 @@ uid_t getpid()
     return Syscall::invoke(Syscall::PosixGetpid);
 }
 
+int open(const char* path)
+{
+    size_t length = strlen(path);
+    return Syscall::invoke(Syscall::PosixOpen, (dword)path, (dword)length);
+}
+
+ssize_t read(int fd, void* buf, size_t count)
+{
+    return Syscall::invoke(Syscall::PosixRead, (dword)fd, (dword)buf, (dword)count);
+}
+
+int close(int fd)
+{
+    return Syscall::invoke(Syscall::PosixClose, fd);
+}
+
 }
 

+ 3 - 0
LibC/unistd.h

@@ -7,6 +7,9 @@ extern "C" {
 uid_t getuid();
 gid_t getgid();
 pid_t getpid();
+int open(const char* path);
+ssize_t read(int fd, void* buf, size_t count);
+int close(int fd);
 
 }
 

+ 1 - 0
Userland/.gitignore

@@ -1,2 +1,3 @@
 id
+sh
 *.o

+ 7 - 2
Userland/Makefile

@@ -1,8 +1,10 @@
 OBJS = \
-       id.o
+       id.o \
+       sh.o
 
 APPS = \
-       id
+       id \
+       sh
 
 ARCH_FLAGS =
 STANDARD_FLAGS = -std=c++17 -nostdinc++ -nostdlib
@@ -25,6 +27,9 @@ all: $(OBJS) $(APPS)
 id: id.o
 	$(LD) -o $@ $(LDFLAGS) $< ../LibC/LibC.a
 
+sh: sh.o
+	$(LD) -o $@ $(LDFLAGS) $< ../LibC/LibC.a
+
 .cpp.o:
 	@echo "CXX $<"; $(CXX) $(CXXFLAGS) -o $@ -c $<
 

+ 56 - 0
Userland/sh.cpp

@@ -0,0 +1,56 @@
+#include <LibC/stdio.h>
+#include <LibC/unistd.h>
+#include <LibC/process.h>
+
+static void prompt()
+{
+    if (getuid() == 0)
+        printf("# ");
+    else
+        printf("$ ");
+}
+
+static int runcmd(char* cmd)
+{
+    //printf("command: '%s'\n", cmd);
+    int ret = spawn(cmd);
+    if (ret == -1) {
+        printf("spawn failed: %s\n", cmd);
+    }
+    return 0;
+}
+
+int main(int c, char** v)
+{
+    char linebuf[128];
+    int linedx = 0;
+    linebuf[0] = '\0';
+
+    int fd = open("/dev/keyboard");
+    if (fd == -1) {
+        printf("failed to open /dev/keyboard :(\n");
+        return 1;
+    }
+    prompt();
+    for (;;) {
+        char keybuf[16];
+        ssize_t nread = read(fd, keybuf, sizeof(keybuf));
+        if (nread < 0) {
+            printf("failed to read :(\n");
+            return 2;
+        }
+        for (ssize_t i = 0; i < nread; ++i) {
+            putchar(keybuf[i]);
+            if (keybuf[i] != '\n') {
+                linebuf[linedx++] = keybuf[i];
+                linebuf[linedx] = '\0';
+            } else {
+                runcmd(linebuf);
+                linebuf[0] = '\0';
+                linedx = 0;
+                prompt();
+            }
+        }
+    }
+    return 0;
+}

BIN
VirtualFileSystem/small.fs