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.
This commit is contained in:
Andreas Kling 2018-10-23 10:12:50 +02:00
parent 72514c8b97
commit fe237ee215
Notes: sideshowbarker 2024-07-19 18:44:58 +09:00
29 changed files with 276 additions and 32 deletions

View File

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

View File

@ -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;
}

View File

@ -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 };
};

View File

@ -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;
}

View File

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

View File

@ -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()

View File

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

View File

@ -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();
}

View File

@ -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
{

Binary file not shown.

View File

@ -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" \

View File

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

View File

@ -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;

View File

@ -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();
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));

View File

@ -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
Kernel/sync-sh Executable file
View File

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

View File

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

12
LibC/process.cpp Normal file
View File

@ -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
LibC/process.h Normal file
View File

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

View File

@ -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
LibC/string.cpp Normal file
View File

@ -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
LibC/string.h Normal file
View File

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

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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
Userland/.gitignore vendored
View File

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

View File

@ -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
Userland/sh.cpp Normal file
View File

@ -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;
}

Binary file not shown.