diff --git a/Kernel/.gitignore b/Kernel/.gitignore new file mode 100644 index 00000000000000..7fa55bf91cfc0f --- /dev/null +++ b/Kernel/.gitignore @@ -0,0 +1,4 @@ +*.o +.floppy-image +Boot/boot.bin +kernel diff --git a/Kernel/Assertions.h b/Kernel/Assertions.h new file mode 100644 index 00000000000000..97c2c0a0b3e363 --- /dev/null +++ b/Kernel/Assertions.h @@ -0,0 +1,8 @@ +#pragma once + +#include "VGA.h" + +#define CRASH() do { asm volatile("cli;hlt"); } while(0) +#define ASSERT(x) do { if (!(x)) { vga_set_attr(0x4f); 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) diff --git a/Kernel/Boot/boot.asm b/Kernel/Boot/boot.asm new file mode 100755 index 00000000000000..10f7f49c956b7d --- /dev/null +++ b/Kernel/Boot/boot.asm @@ -0,0 +1,106 @@ +; asmsyntax=nasm + +[org 0x7c00] +[bits 16] + +boot: + push cs + pop ds + xor bx, bx + mov ah, 0x0e + mov si, message + lodsb +.lewp: + int 0x10 + lodsb + cmp al, 0 + jne .lewp + + mov bx, 0x1000 + mov es, bx + xor bx, bx ; Load kernel @ 0x10000 + + mov ah, 0x02 ; cmd 0x02 - Read Disk Sectors + mov al, 72 ; 72 sectors (max allowed by bochs BIOS) + mov ch, 0 ; track 0 + mov cl, 10 ; sector 10 + mov dh, 0 ; head 0 + mov dl, 0 ; drive 0 (fd0) + int 0x13 + + jc fug + + mov ah, 0x02 + mov al, 32 + add bx, 0x9000 + mov ch, 2 + mov cl, 10 + mov dh, 0 + mov dl, 0 + int 0x13 + + jc fug + + lgdt [cs:test_gdt_ptr] + + mov eax, cr0 + or al, 1 + mov cr0, eax + + jmp 0x08:pmode + +pmode: +[bits 32] + mov ax, 0x10 + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + + mov ss, ax + mov esp, 0x2000 + + jmp 0x10000 + + hlt + +test_gdt_ptr: + dw (test_gdt_end-test_gdt) + dd test_gdt + +test_gdt: + dd 0 + dd 0 + dd 0x0000ffff + dd 0x00cf9a00 + dd 0x0000ffff + dd 0x00cf9200 + dd 0 + dd 0 + dd 0 + dd 0 +test_gdt_end: + +[bits 16] +fug: + xor bx, bx + mov ah, 0x0e + mov si, fug_message + lodsb +.lewp: + int 0x10 + lodsb + cmp al, 0 + jne .lewp + + cli + hlt + +message: + db "boot!", 0x0d, 0x0a, 0 + +fug_message: + db "FUG!", 0x0d, 0x0a, 0 + +times 510-($-$$) db 0 +dw 0xaa55 diff --git a/Kernel/CMOS.cpp b/Kernel/CMOS.cpp new file mode 100644 index 00000000000000..c4e8e5538a3feb --- /dev/null +++ b/Kernel/CMOS.cpp @@ -0,0 +1,18 @@ +#include "CMOS.h" +#include "IO.h" + +namespace CMOS { + +BYTE read(BYTE index) +{ + IO::out8(0x70, index); + return IO::in8(0x71); +} + +void write(BYTE index, BYTE data) +{ + IO::out8(0x70, index); + IO::out8(0x71, data); +} + +} diff --git a/Kernel/CMOS.h b/Kernel/CMOS.h new file mode 100644 index 00000000000000..b02557f6b5ff11 --- /dev/null +++ b/Kernel/CMOS.h @@ -0,0 +1,10 @@ +#pragma once + +#include "types.h" + +namespace CMOS { + +BYTE read(BYTE index); +void write(BYTE index, BYTE data); + +} diff --git a/Kernel/DataBuffer.cpp b/Kernel/DataBuffer.cpp new file mode 100644 index 00000000000000..9be5bce330903f --- /dev/null +++ b/Kernel/DataBuffer.cpp @@ -0,0 +1,75 @@ +#include "DataBuffer.h" +#include "StdLib.h" + +#define SANITIZE_DATABUFFER + +DataBuffer::DataBuffer(size_t length) + : m_length(length) + , m_owned(true) +{ + m_data = new BYTE[m_length]; +#ifdef SANITIZE_DATABUFFER + memset(m_data, 0x1a, length); +#endif +} + +DataBuffer::DataBuffer(BYTE* data, size_t length, ConstructionMode mode) + : m_length(length) +{ + switch (mode) { + case Copy: + m_data = new BYTE[m_length]; + memcpy(m_data, data, m_length); + m_owned = true; + break; + case Adopt: + m_data = data; + m_owned = true; + break; + case Wrap: + m_data = data; + m_owned = false; + break; + } +} + +DataBuffer::~DataBuffer() +{ + clear(); +#ifdef SANITIZE_DATABUFFER + m_data = (BYTE*)0x88888888; +#endif +} + +void DataBuffer::clear() +{ + if (m_owned) { +#ifdef SANITIZE_DATABUFFER + memset(m_data, 0x99, m_length); +#endif + delete [] m_data; + } + m_owned = false; + m_data = nullptr; + m_length = 0; +} + +RefPtr DataBuffer::createUninitialized(size_t length) +{ + return adoptRef(new DataBuffer(length)); +} + +RefPtr DataBuffer::copy(const BYTE* data, size_t length) +{ + return adoptRef(new DataBuffer(const_cast(data), length, Copy)); +} + +RefPtr DataBuffer::wrap(BYTE* data, size_t length) +{ + return adoptRef(new DataBuffer(data, length, Wrap)); +} + +RefPtr DataBuffer::adopt(BYTE* data, size_t length) +{ + return adoptRef(new DataBuffer(data, length, Adopt)); +} diff --git a/Kernel/DataBuffer.h b/Kernel/DataBuffer.h new file mode 100644 index 00000000000000..ae7fef83088e52 --- /dev/null +++ b/Kernel/DataBuffer.h @@ -0,0 +1,39 @@ +#pragma once + +#include "types.h" +#include "RefCounted.h" +#include "RefPtr.h" + +class DataBuffer : public RefCounted { +public: + ~DataBuffer(); + + BYTE operator[](size_t i) const { return m_data[i]; } + bool isEmpty() const { return !m_length; } + size_t length() const { return m_length; } + BYTE* data() { return m_data; } + const BYTE* data() const { return m_data; } + + static RefPtr copy(const BYTE*, size_t length); + static RefPtr wrap(BYTE*, size_t length); + static RefPtr adopt(BYTE*, size_t length); + static RefPtr createUninitialized(size_t length); + + void clear(); + void leak() { m_data = nullptr; m_length = 0; m_owned = false; } + +private: + DataBuffer() { } + DataBuffer(DataBuffer&&) = delete; + DataBuffer& operator=(DataBuffer&&) = delete; + + enum ConstructionMode { Copy, Wrap, Adopt }; + explicit DataBuffer(size_t length); + DataBuffer(BYTE*, size_t length, ConstructionMode); + DataBuffer(const DataBuffer&) = delete; + DataBuffer& operator=(const DataBuffer&) = delete; + + BYTE* m_data { nullptr }; + size_t m_length { 0 }; + bool m_owned { false }; +}; diff --git a/Kernel/Disk.cpp b/Kernel/Disk.cpp new file mode 100644 index 00000000000000..ed6eb7c1a2474e --- /dev/null +++ b/Kernel/Disk.cpp @@ -0,0 +1,195 @@ +#include "types.h" +#include "Task.h" +#include "IPC.h" +#include "VGA.h" +#include "Disk.h" +#include "kmalloc.h" +#include "StdLib.h" +#include "IO.h" +#include "i386.h" +#include "DataBuffer.h" +#include "PIC.h" + +//#define DISK_DEBUG + +extern "C" void handle_interrupt(); + +namespace Disk { + +ide_drive_t drive[4]; +static volatile bool interrupted; + +#define IRQ_FIXED_DISK 14 + +extern "C" void ide_ISR(); + +asm( + ".globl ide_ISR \n" + "ide_ISR: \n" + " pusha\n" + " pushw %ds\n" + " pushw %es\n" + " pushw %ss\n" + " pushw %ss\n" + " popw %ds\n" + " popw %es\n" + " call handle_interrupt\n" + " popw %es\n" + " popw %ds\n" + " popa\n" + " iret\n" +); + +static void enableIRQ() +{ + PIC::enable(IRQ_FIXED_DISK); +} + +static void disableIRQ() +{ + PIC::disable(IRQ_FIXED_DISK); +} + +static bool waitForInterrupt() +{ +#ifdef DISK_DEBUG + kprintf("disk: waiting for interrupt...\n"); +#endif + // FIXME: Add timeout. + while (!interrupted) { + yield(); + } +#ifdef DISK_DEBUG + kprintf("disk: got interrupt!\n"); +#endif + return true; +} + +void interrupt() +{ + IRQHandlerScope scope(IRQ_FIXED_DISK); +#ifdef DISK_DEBUG + BYTE status = IO::in8(0x1f7); + kprintf("disk:interrupt: DRQ=%u BUSY=%u DRDY=%u\n", (status & DRQ) != 0, (status & BUSY) != 0, (status & DRDY) != 0); +#endif + interrupted = true; +} + +void initialize() +{ + disableIRQ(); + interrupted = false; + registerInterruptHandler(IRQ_VECTOR_BASE + IRQ_FIXED_DISK, ide_ISR); + + while (IO::in8(IDE0_STATUS) & BUSY); + + IO::out8(0x1F6, 0xA0); // 0xB0 for 2nd device + IO::out8(IDE0_COMMAND, IDENTIFY_DRIVE); + + enableIRQ(); + waitForInterrupt(); + + RefPtr wbuf = DataBuffer::createUninitialized(512); + BYTE* byteBuffer = new BYTE[512]; + BYTE* b = byteBuffer; + WORD* wbufbase = (WORD*)wbuf->data(); + WORD* w = (WORD*)wbuf->data(); + + for (DWORD i = 0; i < 256; ++i) { + WORD data = IO::in16(IDE0_DATA); + *(w++) = data; + *(b++) = MSB(data); + *(b++) = LSB(data); + } + + // "Unpad" the device name string. + for (DWORD i = 93; i > 54 && byteBuffer[i] == ' '; --i) + byteBuffer[i] = 0; + + drive[0].cylinders = wbufbase[1]; + drive[0].heads = wbufbase[3]; + drive[0].sectors_per_track = wbufbase[6]; + + kprintf( + "ide0: Master=\"%s\", C/H/Spt=%u/%u/%u\n", + byteBuffer + 54, + drive[0].cylinders, + drive[0].heads, + drive[0].sectors_per_track + ); + + delete byteBuffer; +} + +struct CHS { + DWORD cylinder; + WORD head; + WORD sector; +}; + +static CHS lba2chs(BYTE drive_index, DWORD lba) +{ + ide_drive_t& d = drive[drive_index]; + CHS chs; + chs.cylinder = lba / (d.sectors_per_track * d.heads); + chs.head = (lba / d.sectors_per_track) % d.heads; + chs.sector = (lba % d.sectors_per_track) + 1; + return chs; +} + +bool readSectors(DWORD startSector, WORD count, BYTE* outbuf) +{ +#ifdef DISK_DEBUG + kprintf("%s: Disk::readSectors request (%u sector(s) @ %u)\n", + current->name().characters(), + count, + startSector); +#endif + Task::checkSanity("Disk::readSectors"); + + disableIRQ(); + + CHS chs = lba2chs(IDE0_DISK0, startSector); + + while (IO::in8(IDE0_STATUS) & BUSY); + +#ifdef DISK_DEBUG + kprintf("ide0: Reading %u sector(s) @ LBA %u (%u/%u/%u)\n", count, startSector, chs.cylinder, chs.head, chs.sector); +#endif + + IO::out8(0x1F2, count == 256 ? 0 : LSB(count)); + IO::out8(0x1F3, chs.sector); + IO::out8(0x1F4, LSB(chs.cylinder)); + IO::out8(0x1F5, MSB(chs.cylinder)); + + IO::out8(0x1F6, 0xA0 | chs.head); /* 0xB0 for 2nd device */ + + IO::out8(0x3F6, 0x08); + while (!(IO::in8(IDE0_STATUS) & DRDY)); + + IO::out8(IDE0_COMMAND, READ_SECTORS); + interrupted = false; + enableIRQ(); + waitForInterrupt(); + + BYTE status = IO::in8(0x1f7); + if (status & DRQ) { +#ifdef DISK_DEBUG + kprintf("Retrieving %u bytes (status=%b), outbuf=%p...\n", count * 512, status, outbuf); +#endif + for (DWORD i = 0; i < (count * 512); i += 2) { + WORD w = IO::in16(IDE0_DATA); + outbuf[i] = LSB(w); + outbuf[i+1] = MSB(w); + } + } + + return true; +} + +} + +extern "C" void handle_interrupt() +{ + Disk::interrupt(); +} diff --git a/Kernel/Disk.h b/Kernel/Disk.h new file mode 100644 index 00000000000000..28126b292219bb --- /dev/null +++ b/Kernel/Disk.h @@ -0,0 +1,35 @@ +#pragma once + +#include "types.h" +#include "DataBuffer.h" + +#define IDE0_DATA 0x1F0 +#define IDE0_STATUS 0x1F7 +#define IDE0_COMMAND 0x1F7 +#define BUSY 0x80 +#define DRDY 0x40 +#define DRQ 0x08 +#define IDENTIFY_DRIVE 0xEC +#define READ_SECTORS 0x21 + +#define IDE0_DISK0 0 +#define IDE0_DISK1 1 +#define IDE1_DISK0 2 +#define IDE1_DISK1 3 + +typedef struct +{ + WORD cylinders; + WORD heads; + WORD sectors_per_track; +} ide_drive_t; + +extern void ide_init(); +extern ide_drive_t drive[4]; + +namespace Disk { + +void initialize(); +bool readSectors(DWORD sectorIndex, WORD count, BYTE* buffer); + +} diff --git a/Kernel/DoublyLinkedList.h b/Kernel/DoublyLinkedList.h new file mode 100644 index 00000000000000..5184e34d29897c --- /dev/null +++ b/Kernel/DoublyLinkedList.h @@ -0,0 +1,166 @@ +#pragma once + +#include "Assertions.h" +#include "types.h" + +template class DoublyLinkedListNode { +public: + DoublyLinkedListNode(); + + void setPrev(T*); + void setNext(T*); + + T* prev() const; + T* next() const; +}; + +template inline DoublyLinkedListNode::DoublyLinkedListNode() +{ + setPrev(0); + setNext(0); +} + +template inline void DoublyLinkedListNode::setPrev(T* prev) +{ + static_cast(this)->m_prev = prev; +} + +template inline void DoublyLinkedListNode::setNext(T* next) +{ + static_cast(this)->m_next = next; +} + +template inline T* DoublyLinkedListNode::prev() const +{ + return static_cast(this)->m_prev; +} + +template inline T* DoublyLinkedListNode::next() const +{ + return static_cast(this)->m_next; +} + +template class DoublyLinkedList { +public: + DoublyLinkedList() { } + + bool isEmpty() const { return !m_head; } + size_t size() const; + void clear(); + + T* head() const { return m_head; } + T* removeHead(); + + T* tail() const { return m_tail; } + + void prepend(T*); + void append(T*); + void remove(T*); + void append(DoublyLinkedList&); + +private: + T* m_head { nullptr }; + T* m_tail { nullptr }; +}; + +template inline size_t DoublyLinkedList::size() const +{ + size_t size = 0; + for (T* node = m_head; node; node = node->next()) + ++size; + return size; +} + +template inline void DoublyLinkedList::clear() +{ + m_head = 0; + m_tail = 0; +} + +template inline void DoublyLinkedList::prepend(T* node) +{ + if (!m_head) { + ASSERT(!m_tail); + m_head = node; + m_tail = node; + node->setPrev(0); + node->setNext(0); + return; + } + + ASSERT(m_tail); + m_head->setPrev(node); + node->setNext(m_head); + node->setPrev(0); + m_head = node; +} + +template inline void DoublyLinkedList::append(T* node) +{ + if (!m_tail) { + ASSERT(!m_head); + m_head = node; + m_tail = node; + node->setPrev(0); + node->setNext(0); + return; + } + + ASSERT(m_head); + m_tail->setNext(node); + node->setPrev(m_tail); + node->setNext(0); + m_tail = node; +} + +template inline void DoublyLinkedList::remove(T* node) +{ + if (node->prev()) { + ASSERT(node != m_head); + node->prev()->setNext(node->next()); + } else { + ASSERT(node == m_head); + m_head = node->next(); + } + + if (node->next()) { + ASSERT(node != m_tail); + node->next()->setPrev(node->prev()); + } else { + ASSERT(node == m_tail); + m_tail = node->prev(); + } +} + +template inline T* DoublyLinkedList::removeHead() +{ + T* node = head(); + if (node) + remove(node); + return node; +} + +template inline void DoublyLinkedList::append(DoublyLinkedList& other) +{ + if (!other.head()) + return; + + if (!head()) { + m_head = other.head(); + m_tail = other.tail(); + other.clear(); + return; + } + + ASSERT(tail()); + ASSERT(other.head()); + T* otherHead = other.head(); + T* otherTail = other.tail(); + other.clear(); + + ASSERT(!m_tail->next()); + m_tail->setNext(otherHead); + ASSERT(!otherHead->prev()); + otherHead->setPrev(m_tail); + m_tail = otherTail; +} diff --git a/Kernel/Ext2FileSystem.h b/Kernel/Ext2FileSystem.h new file mode 100644 index 00000000000000..9509531d484f54 --- /dev/null +++ b/Kernel/Ext2FileSystem.h @@ -0,0 +1,74 @@ +#pragma once + +#include "ext2fs.h" +#include "OwnPtr.h" +#include "DataBuffer.h" +#include "FileSystem.h" + +static const size_t bytesPerSector = 512; + +class Ext2VirtualNode; + +class Ext2FileSystem { +public: + Ext2FileSystem() { } + ~Ext2FileSystem(); + + void initialize(); + RefPtr loadFile(ext2_dir_entry*); + + ext2_inode* findInode(DWORD index); + ext2_inode* findPath(const String& path, DWORD& inodeIndex); + +private: + friend class Ext2VirtualNode; + + void readSuperBlock(); + void readBlockGroup(DWORD); + void readInodeTable(DWORD); + void dumpDirectory(ext2_inode&); + void dumpFile(ext2_inode&); + + template void forEachBlockIn(ext2_inode&, F func); + template void traverseDirectory(ext2_dir_entry&, DWORD blockCount, F); + template void traverseDirectory(ext2_inode&, F); + RefPtr readFile(ext2_inode&); + + RefPtr readBlocks(DWORD blockIndex, BYTE count); + void readDiskSector(DWORD sectorIndex, BYTE* buffer); + + size_t blockSize() const { return 1024 << m_superBlock->s_log_frag_size; } + size_t sectorsPerBlock() const { return blockSize() / bytesPerSector; } + DWORD blockGroupForInode(DWORD inode) const; + DWORD toInodeTableIndex(DWORD inode) const; + + ext2_super_block& superBlock() { ASSERT(m_superBlock); return *m_superBlock; } + + OwnPtr m_superBlock; + ext2_inode* m_root { nullptr }; // raw pointer into one of the m_inodeTables + + size_t m_blockGroupCount { 0 }; + ext2_group_descriptor* m_groupTable { nullptr }; + ext2_inode** m_inodeTables { nullptr }; +}; + +class Ext2VirtualNode final : public FileSystem::VirtualNode { +public: + static RefPtr create(DWORD index, String&& path, Ext2FileSystem&, DWORD inodeNumber); + + virtual ~Ext2VirtualNode(); + + virtual size_t size() const override { return m_inode.i_size; } + virtual uid_t uid() const override { return m_inode.i_uid; } + virtual gid_t gid() const override { return m_inode.i_gid; } + virtual size_t mode() const override { return m_inode.i_mode; } + + virtual size_t read(BYTE* outbuf, size_t start, size_t maxLength) override; + +private: + Ext2VirtualNode(DWORD index, String&& path, Ext2FileSystem&, ext2_inode&, DWORD inodeNumber); + + Ext2FileSystem& m_fileSystem; + ext2_inode& m_inode; + DWORD m_inodeNumber { 0 }; +}; diff --git a/Kernel/FileSystem.h b/Kernel/FileSystem.h new file mode 100644 index 00000000000000..7fff99868a30aa --- /dev/null +++ b/Kernel/FileSystem.h @@ -0,0 +1,35 @@ +#pragma once + +#include "types.h" +#include "RefCounted.h" + +namespace FileSystem { + +void initialize(); + +class VirtualNode : public RefCounted { +public: + DWORD saneValue = 0x850209; + virtual ~VirtualNode(); + + DWORD index() const { return m_index; } + const String& path() const { return m_path; } + + virtual size_t size() const = 0; + virtual uid_t uid() const = 0; + virtual gid_t gid() const = 0; + virtual size_t mode() const = 0; + + virtual size_t read(BYTE* outbuf, size_t start, size_t maxLength) = 0; + +protected: + VirtualNode(DWORD index, String&& path); + +private: + DWORD m_index { 0 }; + String m_path; +}; + +RefPtr createVirtualNode(String&& path); + +} diff --git a/Kernel/IO.cpp b/Kernel/IO.cpp new file mode 100644 index 00000000000000..baeaa1bfec850c --- /dev/null +++ b/Kernel/IO.cpp @@ -0,0 +1,41 @@ +#include "IO.h" + +namespace IO { + +BYTE in8(WORD port) +{ + BYTE value; + asm("inb %%dx, %%al":"=a"(value):"d"(port)); + return value; +} + +WORD in16(WORD port) +{ + WORD value; + asm("inw %%dx, %%ax":"=a"(value):"d"(port)); + return value; +} + +DWORD in32(DWORD port) +{ + DWORD value; + asm("inl %%dx, %%eax":"=a"(value):"d"(port)); + return value; +} + +void out8(WORD port, BYTE value) +{ + asm("outb %%al, %%dx"::"d"(port), "a"(value)); +} + +void out16(WORD port, WORD value) +{ + asm("outw %%ax, %%dx"::"d"(port), "a"(value)); +} + +void out32(WORD port, WORD value) +{ + asm("outl %%eax, %%dx"::"d"(port), "a"(value)); +} + +} diff --git a/Kernel/IO.h b/Kernel/IO.h new file mode 100644 index 00000000000000..94e4ebe28770d3 --- /dev/null +++ b/Kernel/IO.h @@ -0,0 +1,15 @@ +#pragma once + +#include "types.h" + +namespace IO { + +BYTE in8(WORD port); +WORD in16(WORD port); +DWORD in32(WORD port); + +void out8(WORD port, BYTE data); +void out16(WORD port, WORD data); +void out32(WORD port, DWORD data); + +} diff --git a/Kernel/IPC.cpp b/Kernel/IPC.cpp new file mode 100644 index 00000000000000..5e5b0af5405f49 --- /dev/null +++ b/Kernel/IPC.cpp @@ -0,0 +1,104 @@ +#include "IPC.h" +#include "Task.h" +#include "i386.h" +#include "StdLib.h" +#include "VGA.h" +#include "system.h" + +namespace IPC { + +Message receive(Handle src) +{ + for (;;) { + current->ipc.src = src; + block(Task::BlockedReceive); + if (src == Handle::Any && current->ipc.notifies) { + for (BYTE i = 0; i < 32; ++i) { + if (current->ipc.notifies & (1 << i)) { + // FIXME: Source PID is `i' here. Do something with it? + current->ipc.notifies &= ~(1 << i); + break; + } + } + return Message(MSG_NOTIFY); + } + + if (src == Handle::Any || src == current->ipc.msg.sender()) { + return move(current->ipc.msg); + } + + // Why are we here? + ASSERT_NOT_REACHED(); + } +} + +void send(Handle dst, Message&& msg) +{ + Task* task; + + // TODO: Block waiting for `dst' to spawn. + for (;;) { + task = Task::fromIPCHandle(dst); + if (task) + break; + yield(); + } + + // I'll fill this in myself thankyouverymuch. + msg.setSender(current->handle()); + + // Block until `dst' is ready to receive a message. + current->ipc.dst = dst; + block(Task::BlockedSend); + + ASSERT(msg.isValid()); + task->ipc.msg = move(msg); +} + +void notify(Handle dst) +{ + Task* task = Task::fromIPCHandle(dst); + + if (!task) { + // Can't really block here since we might be coming from + // an interrupt handler and that'd be devastating... + // XXX: Need to figure that one out. + kprintf("notify(): no such task %u\n", dst.data()); + return; + } + + if (current->pid() >= 32) { + kprintf( "notify(): PID must be < 32\n" ); + return; + } + + task->ipc.notifies |= 1 << current->pid(); +} + +Message::Message(Message&& other) + : m_data(move(other.m_data)) + , m_type(other.m_type) + , m_sender(other.m_sender) + , m_isValid(other.m_isValid) +{ + other.m_type = 0; + other.m_sender = Handle(); + other.m_isValid = false; +} + +Message& Message::operator=(Message&& other) +{ + if (this == &other) + return *this; + m_data = move(other.m_data); + m_type = other.m_type; + m_sender = other.m_sender; + m_isValid = other.m_isValid; + other.m_type = 0; + other.m_sender = Handle(); + other.m_isValid = false; + return *this; +} + + +} diff --git a/Kernel/IPC.h b/Kernel/IPC.h new file mode 100644 index 00000000000000..100714ceb0fae0 --- /dev/null +++ b/Kernel/IPC.h @@ -0,0 +1,81 @@ +#pragma once + +#include "types.h" +#include "DataBuffer.h" +#include "RefPtr.h" + +/* IPC message types. There will be moar. */ +#define MSG_INTERRUPT 0x00000001 +#define MSG_KILL 0x00000002 +#define MSG_NOTIFY 0x00000003 + +#define DEV_READ 0x00000004 + +#define FS_OPEN 0x00000100 +#define FS_CLOSE 0x00000101 +#define FS_READ 0x00000102 + +#define SYS_KILL 0x00000666 + +namespace IPC { + +class Handle { +public: + // If Handle::Any is passed as the `src' parameter of receive(), + // any process can send us a message. + enum AnyHandle { Any }; + Handle(AnyHandle) : m_data(0xffffffff) { } + + enum KernelTask { + PanelTask = 4001, + DiskTask = 4002, + FileSystemTask = 4003, + MotdTask = 4004, + UserTask = 4005, + }; + Handle(KernelTask task) : m_data((DWORD)task) { } + + Handle() { } + explicit Handle(DWORD data) : m_data(data) { } + + DWORD data() const { return m_data; } + bool operator==(const Handle& o) const { return m_data == o.m_data; } + bool operator!=(const Handle& o) const { return m_data != o.m_data; } + +private: + DWORD m_data { 0 }; +}; + +class Message { +public: + Message() { } + explicit Message(DWORD type) : m_type(type), m_isValid(true) { } + Message(DWORD type, RefPtr&& d) : m_data(move(d)), m_type(type), m_isValid(true) { } + Message(Message&&); + Message& operator=(Message&&); + + size_t length() const { return m_data ? m_data->length() : 0; } + const BYTE* data() const { return m_data ? m_data->data() : nullptr; } + BYTE* data() { return m_data ? m_data->data() : nullptr; } + + bool isValid() const { return m_isValid; } + + DWORD type() const { return m_type; } + Handle sender() const { return m_sender; } + + void setType(DWORD t) { m_type = t; } + void setSender(Handle s) { m_sender = s; } + +private: + RefPtr m_data; + DWORD m_type { 0 }; + Handle m_sender; + bool m_isValid { false }; +}; + +Message receive(Handle); +void send(Handle, Message&&); +void notify(Handle); + +} + diff --git a/Kernel/Keyboard.cpp b/Kernel/Keyboard.cpp new file mode 100644 index 00000000000000..11bb8eecf9a36b --- /dev/null +++ b/Kernel/Keyboard.cpp @@ -0,0 +1,135 @@ +#include "types.h" +#include "i386.h" +#include "IO.h" +#include "IPC.h" +#include "Task.h" +#include "VGA.h" +#include "PIC.h" +#include "Keyboard.h" + +#define IRQ_KEYBOARD 1 + +#define I8042_BUFFER 0x60 +#define I8042_STATUS 0x64 + +#define SET_LEDS 0xED +#define DATA_AVAILABLE 0x01 +#define I8042_ACK 0xFA + +extern "C" void handleKeyboardInterrupt(); +extern "C" void keyboard_ISR(); + +static BYTE s_ledState; + +asm( + ".globl keyboard_ISR \n" + "keyboard_ISR: \n" + " pusha\n" + " pushw %ds\n" + " pushw %es\n" + " pushw %ss\n" + " pushw %ss\n" + " popw %ds\n" + " popw %es\n" + " call handleKeyboardInterrupt\n" + " popw %es\n" + " popw %ds\n" + " popa\n" + " iret\n" +); + +void handleKeyboardInterrupt() +{ + IRQHandlerScope scope(IRQ_KEYBOARD); + Keyboard::handleInterrupt(); +} + +namespace Keyboard { + +#define MOD_ALT 1 +#define MOD_CTRL 2 +#define MOD_SHIFT 4 + +static char map[0x100] = +{ + 0, 0, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', 0, 0, + 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', 0, 0, + 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', 0, '\\', + 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', +}; + +static char shift_map[0x100] = +{ + 0, 0, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', 0, 0, + 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', 0, 0, + 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '~', 0, '|', + 'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<', '>', '?', +}; + +static BYTE s_modifiers; + +void handleInterrupt() +{ + while (IO::in8(0x64) & 1) { + BYTE ch = IO::in8(0x60); + switch (ch) { + case 0x38: s_modifiers |= MOD_ALT; break; + case 0xB8: s_modifiers &= ~MOD_ALT; break; + case 0x1D: s_modifiers |= MOD_CTRL; break; + case 0x9D: s_modifiers &= ~MOD_CTRL; break; + case 0x2A: s_modifiers |= MOD_SHIFT; break; + case 0xAA: s_modifiers &= ~MOD_SHIFT; break; + case 0x1C: /* enter */ kprintf("\n"); break; + case 0xFA: /* i8042 ack */ break; + default: + if (ch & 0x80) { + // key has been depressed + break; + } + if (!s_modifiers) + kprintf("%c", map[ch]); + else if (s_modifiers & MOD_SHIFT) + kprintf("%c", shift_map[ch]); + else if (s_modifiers & MOD_CTRL) + kprintf("^%c", shift_map[ch]); + } + //break; + } +} + +void initialize() +{ + s_modifiers = 0; + s_ledState = 0; + + // Empty the buffer of any pending data. + // I don't care what you've been pressing until now! + while (IO::in8(I8042_STATUS ) & DATA_AVAILABLE) + IO::in8(I8042_BUFFER); + + registerInterruptHandler(IRQ_VECTOR_BASE + IRQ_KEYBOARD, keyboard_ISR); + + PIC::enable(IRQ_KEYBOARD); +} + +void setLED(LED led) +{ + s_ledState |= (BYTE)led & 7; + + while (IO::in8(I8042_STATUS) & DATA_AVAILABLE); + IO::out8(I8042_BUFFER, SET_LEDS); + while (IO::in8(I8042_BUFFER) != I8042_ACK); + IO::out8(I8042_BUFFER, s_ledState); +} + +void unsetLED(LED led) +{ + s_ledState &= ~((BYTE)led & 7); + + while (IO::in8(I8042_STATUS) & DATA_AVAILABLE); + IO::out8(I8042_BUFFER, SET_LEDS); + while (IO::in8(I8042_BUFFER) != I8042_ACK); + IO::out8(I8042_BUFFER, s_ledState); +} + +} diff --git a/Kernel/Keyboard.h b/Kernel/Keyboard.h new file mode 100644 index 00000000000000..a2ed06fa7edbb0 --- /dev/null +++ b/Kernel/Keyboard.h @@ -0,0 +1,16 @@ +#pragma once + +namespace Keyboard { + +enum class LED { + ScrollLock = 1 << 0, + NumLock = 1 << 1, + CapsLock = 1 << 2, +}; + +void initialize(); +void setLED(LED); +void unsetLED(LED); +void handleInterrupt(); + +} diff --git a/Kernel/Makefile b/Kernel/Makefile new file mode 100644 index 00000000000000..ad506f2f190a73 --- /dev/null +++ b/Kernel/Makefile @@ -0,0 +1,58 @@ +OBJS = \ + _start.o \ + init.o \ + VGA.o \ + kmalloc.o \ + StdLib.o \ + i386.o \ + Task.o \ + i8253.o \ + Keyboard.o \ + IPC.o \ + CMOS.o \ + IO.o \ + PIC.o \ + Syscall.o \ + DataBuffer.o \ + String.o \ + panel.o \ + Disk.o \ + fs.o \ + Userspace.o +NASM = nasm +KERNEL = kernel +BOOTLOADER = Boot/boot.bin +IMAGE = .floppy-image +ARCH_FLAGS = +STANDARD_FLAGS = -std=c++17 -nostdinc++ -nostdlib +KERNEL_FLAGS = -ffreestanding -fno-stack-protector -fno-ident +WARNING_FLAGS = -Wextra -Wall -Wundef -Wcast-qual -Wwrite-strings +FLAVOR_FLAGS = -fomit-frame-pointer -mregparm=3 -march=i386 -m32 -fno-exceptions -fno-rtti -ffunction-sections -fdata-sections -fmerge-all-constants -fno-unroll-loops -falign-functions=1 -falign-jumps=1 -falign-loops=1 +#FLAVOR_FLAGS = -fomit-frame-pointer -mregparm=3 -march=i386 -m32 -fno-exceptions -fno-rtti -ffunction-sections -fdata-sections +OPTIMIZATION_FLAGS = -Os -fno-asynchronous-unwind-tables +INCLUDE_FLAGS = -Iinclude/ -I. +CXXFLAGS = $(WARNING_FLAGS) $(OPTIMIZATION_FLAGS) $(KERNEL_FLAGS) $(FLAVOR_FLAGS) $(ARCH_FLAGS) $(STANDARD_FLAGS) $(INCLUDE_FLAGS) +#CXX = /usr/local/gcc-4.8.1-for-linux64/bin/x86_64-pc-linux-g++ +#LD = /usr/local/gcc-4.8.1-for-linux64/bin/x86_64-pc-linux-ld +CXX = g++ +LD = ld +LDFLAGS = -T linker.ld --strip-debug -melf_i386 --gc-sections --build-id=none -z norelro -z now + + +all: $(KERNEL) $(IMAGE) + +$(KERNEL): $(OBJS) + @echo "LD $@"; $(LD) $(LDFLAGS) -o $@ -Ttext 0x10000 $(OBJS) + +$(IMAGE): $(KERNEL) $(BOOTLOADER) + @echo "CREATE $@"; cat $(BOOTLOADER) $(KERNEL) > $(IMAGE) + +$(BOOTLOADER): Boot/boot.asm + @echo "NASM $<"; $(NASM) -f bin -o $@ $< + +.cpp.o: + @echo "CXX $<"; $(CXX) $(CXXFLAGS) -o $@ -c $< + +clean: + @echo "CLEAN"; rm -f $(KERNEL) $(OBJS) $(BOOTLOADER) $(IMAGE) + diff --git a/Kernel/OwnPtr.h b/Kernel/OwnPtr.h new file mode 100644 index 00000000000000..855bafcf01e338 --- /dev/null +++ b/Kernel/OwnPtr.h @@ -0,0 +1,78 @@ +#pragma once + +#include "types.h" + +template +class OwnPtr { +public: + OwnPtr() { } + explicit OwnPtr(T* ptr) : m_ptr(ptr) { } + OwnPtr(OwnPtr&& other) : m_ptr(other.leakPtr()) { } + template OwnPtr(OwnPtr&& other) : m_ptr(static_cast(other.leakPtr())) { } + ~OwnPtr() { clear(); } + + OwnPtr& operator=(OwnPtr&& other) + { + if (this != &other) { + delete m_ptr; + m_ptr = other.leakPtr(); + } + return *this; + } + + template + OwnPtr& operator=(OwnPtr&& other) + { + if (this != static_cast(&other)) { + delete m_ptr; + m_ptr = other.leakPtr(); + } + return *this; + } + + OwnPtr& operator=(T* ptr) + { + if (m_ptr != ptr) + delete m_ptr; + m_ptr = ptr; + return *this; + } + + void clear() + { + delete m_ptr; + m_ptr = nullptr; + } + + bool operator!() const { return !m_ptr; } + + typedef T* OwnPtr::*UnspecifiedBoolType; + operator UnspecifiedBoolType() const { return m_ptr ? &OwnPtr::m_ptr : nullptr; } + + T* leakPtr() + { + T* leakedPtr = m_ptr; + m_ptr = nullptr; + return leakedPtr; + } + + T* ptr() { return m_ptr; } + const T* ptr() const { return m_ptr; } + + T* operator->() { return m_ptr; } + const T* operator->() const { return m_ptr; } + + T& operator*() { return *m_ptr; } + const T& operator*() const { return *m_ptr; } + + operator bool() { return !!m_ptr; } + +private: + T* m_ptr = nullptr; +}; + +template inline OwnPtr +make(Args&&... args) +{ + return OwnPtr(new T(forward(args)...)); +} diff --git a/Kernel/PIC.cpp b/Kernel/PIC.cpp new file mode 100644 index 00000000000000..b458dd17b6be58 --- /dev/null +++ b/Kernel/PIC.cpp @@ -0,0 +1,97 @@ +#include "types.h" +#include "i386.h" +#include "IO.h" +#include "VGA.h" +#include "PIC.h" +#include "Assertions.h" + +// The slave 8259 is connected to the master's IRQ2 line. +// This is really only to enhance clarity. +#define SLAVE_INDEX 2 + +#define PIC0_CTL 0x20 +#define PIC0_CMD 0x21 +#define PIC1_CTL 0xA0 +#define PIC1_CMD 0xA1 + +#ifdef DEBUG_PIC +static bool initialized; +#endif + +namespace PIC { + +void disable(BYTE irq) +{ + BYTE imr; + if (irq & 8) { + imr = IO::in8(PIC1_CMD); + imr |= 1 << (irq - 8); + IO::out8(PIC1_CMD, imr); + } else { + imr = IO::in8(PIC0_CMD); + imr |= 1 << irq; + IO::out8(PIC0_CMD, imr); + } +} + +void enable(BYTE irq) +{ + BYTE imr; + if (irq & 8) { + imr = IO::in8(PIC1_CMD); + imr &= ~(1 << (irq - 8)); + IO::out8(PIC1_CMD, imr); + } else { + imr = IO::in8(PIC0_CMD); + imr &= ~(1 << irq); + IO::out8(PIC0_CMD, imr); + } +} + +void eoi(BYTE irq) +{ + if (irq & 8) + IO::out8(PIC1_CTL, 0x20); + else + IO::out8(PIC0_CTL, 0x20); +} + +void initialize() +{ +#ifdef DEBUG_PIC + ASSERT(!initialized); +#endif + + /* ICW1 (edge triggered mode, cascading controllers, expect ICW4) */ + IO::out8(PIC0_CTL, 0x11); + IO::out8(PIC1_CTL, 0x11); + + /* ICW2 (upper 5 bits specify ISR indices, lower 3 idunno) */ + IO::out8(PIC0_CMD, IRQ_VECTOR_BASE); + IO::out8(PIC1_CMD, IRQ_VECTOR_BASE + 0x08); + + /* ICW3 (configure master/slave relationship) */ + IO::out8(PIC0_CMD, 1 << SLAVE_INDEX); + IO::out8(PIC1_CMD, SLAVE_INDEX); + + /* ICW4 (set x86 mode) */ + IO::out8(PIC0_CMD, 0x01); + IO::out8(PIC1_CMD, 0x01 ); + + // Mask -- enable all interrupts on both PICs. + // Not really what I want here, but I'm unsure how to + // selectively enable secondary PIC IRQs... + IO::out8(PIC0_CMD, 0x00); + IO::out8(PIC1_CMD, 0x00); + + // HACK: Disable busmouse IRQ for now. + disable(5); + + kprintf("PIC(i8259): cascading mode, vectors 0x%b-0x%b\n", IRQ_VECTOR_BASE, IRQ_VECTOR_BASE + 0x08); + +#ifdef DEBUG_PIC + initialized = true; +#endif +} + +} diff --git a/Kernel/PIC.h b/Kernel/PIC.h new file mode 100644 index 00000000000000..b1c3ed3a726d69 --- /dev/null +++ b/Kernel/PIC.h @@ -0,0 +1,19 @@ +#pragma once + +namespace PIC { + +void enable(BYTE number); +void disable(BYTE number); +void eoi(BYTE number); +void initialize(); + +} + +class IRQHandlerScope { +public: + explicit IRQHandlerScope(BYTE irq) : m_irq(irq) { } + ~IRQHandlerScope() { PIC::eoi(m_irq); } + +private: + BYTE m_irq { 0 }; +}; diff --git a/Kernel/Queue.h b/Kernel/Queue.h new file mode 100644 index 00000000000000..7686aed7fc1328 --- /dev/null +++ b/Kernel/Queue.h @@ -0,0 +1,95 @@ +#pragma once + +#include "types.h" + +template +class Queue { +public: + struct Node { + explicit Node(T&& value) + : value(move(value)) + { } + + Node* next { nullptr }; + Node* prev { nullptr }; + T value; + }; + + Queue() { } + ~Queue() + { + while (!isEmpty()) + dequeue(); + } + + bool isEmpty() const { return !m_head; } + void enqueue(T&& item) + { + auto* newNode = new Node(move(item)); + if (!m_head) { + m_head = newNode; + m_tail = newNode; + } else if (m_tail) { + newNode->prev = m_tail; + m_tail->next = newNode; + m_tail = newNode; + } + dump("enqueue"); + } + + T dequeue() + { + ASSERT(m_head); + T value = move(m_head->value); + auto* oldHead = m_head; + if (oldHead->next) { + oldHead->next->prev = nullptr; + m_head = oldHead->next; + } else { + m_head = nullptr; + } + if (m_tail == oldHead) + m_tail = nullptr; + delete oldHead; + dump("dequeue"); + //asm volatile("cli;hlt"); + return value; + } + + Node* head() { return m_head; } + + T take(Node& node) + { + T value = move(node.value); + if (node.prev) { + node.prev->next = node.next; + } else { + m_head = node.next; + } + if (node.next) { + node.next->prev = node.prev; + } else { + m_tail = node.prev; + } + delete &node; + dump("take"); + return value; + } + +private: + void dump(const char* op) + { + return; + asm volatile("cli"); + ASSERT(m_head != (void*)0xaaaaaaaa); + ASSERT(m_tail != (void*)0xaaaaaaaa); + kprintf("Queue %p after %s: {m_head=%p, m_tail=%p}\n", this, op, m_head, m_tail); + for (auto* node = m_head; node; node = node->next) { + kprintf(" Queue::Node %p%s%s next=%p prev=%p\n", node, node == m_head ? " (head)" : "", node == m_tail ? " (tail)" : "", node->next, node->prev); + } + asm volatile("sti"); + } + + Node* m_head { nullptr }; + Node* m_tail { nullptr }; +}; diff --git a/Kernel/RefCounted.h b/Kernel/RefCounted.h new file mode 100644 index 00000000000000..88a611cdcd529a --- /dev/null +++ b/Kernel/RefCounted.h @@ -0,0 +1,48 @@ +#pragma once + +#include "Assertions.h" +#include "VGA.h" + +#define DEBUG_REFCOUNTED + +class RefCountedBase { + +protected: + bool derefBase() const + { + return !--m_refCount; + } + mutable size_t m_refCount { 1 }; +#ifdef DEBUG_REFCOUNTED + //mutable bool m_adopted { false }; +#endif +}; + +template +class RefCounted : public RefCountedBase { +public: + size_t refCount() const { return m_refCount; } + + void ref() const + { +#ifdef DEBUG_REFCOUNTED + ASSERT(m_refCount); + //ASSERT(m_adopted); +#endif + ++m_refCount; + } + + void deref() const + { +#ifdef DEBUG_REFCOUNTED + ASSERT(m_refCount); + //ASSERT(m_adopted); +#endif + if (derefBase()) + delete static_cast(this); + } + +protected: + RefCounted() { } + ~RefCounted() { } +}; diff --git a/Kernel/RefPtr.h b/Kernel/RefPtr.h new file mode 100644 index 00000000000000..3fa3d30c4977c9 --- /dev/null +++ b/Kernel/RefPtr.h @@ -0,0 +1,100 @@ +#pragma once + +#include "types.h" + +#define SANITIZE_REFPTR + +template class RefPtr; +template RefPtr adoptRef(T*); + +template +class RefPtr { +public: + RefPtr() { } + RefPtr(T* ptr) : m_ptr(ptr) { refIfNotNull(m_ptr); } + + ~RefPtr() + { + derefIfNotNull(m_ptr); +#ifdef SANITIZE_REFPTR + m_ptr = (T*)(0xeeeeeeee); +#endif + } + + RefPtr(RefPtr&& other) + : m_ptr(other.leakPtr()) + { + } + + RefPtr& operator=(RefPtr&& other) + { + if (this == &other) + return *this; + m_ptr = other.leakPtr(); + return *this; + } + + template + RefPtr(RefPtr&& other) + : m_ptr(static_cast(other.leakPtr())) + { + } + + template + RefPtr& operator=(RefPtr&& other) + { + if (this == &other) + return *this; + m_ptr = static_cast(other.leakPtr()); + return *this; + } + + RefPtr(const RefPtr& other) + : m_ptr(other.m_ptr) + { + refIfNotNull(m_ptr); + } + + RefPtr& operator=(const RefPtr& other) + { + if (this == &other) + return *this; + m_ptr = other.m_ptr; + refIfNotNull(m_ptr); + return *this; + } + + T* ptr() { return m_ptr; } + const T* ptr() const { return m_ptr; } + T* operator->() { return m_ptr; } + const T* operator->() const { return m_ptr; } + T& operator*() { return *m_ptr; } + const T& operator*() const { return *m_ptr; } + operator bool() const { return m_ptr; } + + T* leakPtr() + { + T* ptr = m_ptr; + m_ptr = nullptr; + return ptr; + } + +private: + template friend class RefPtr; + friend RefPtr adoptRef(T*); + + enum AdoptTag { Adopt }; + RefPtr(AdoptTag, T* ptr) : m_ptr(ptr) { } + + inline void refIfNotNull(T* ptr) { if (ptr) ptr->ref(); } + inline void derefIfNotNull(T* ptr) { if (ptr) ptr->deref(); } + + T* m_ptr { nullptr }; +}; + +template +inline RefPtr adoptRef(T* ptr) +{ + ASSERT(ptr->refCount() == 1); + return RefPtr(RefPtr::Adopt, ptr); +} diff --git a/Kernel/StdLib.cpp b/Kernel/StdLib.cpp new file mode 100644 index 00000000000000..50759b4c2ddb71 --- /dev/null +++ b/Kernel/StdLib.cpp @@ -0,0 +1,55 @@ +#include "types.h" +#include "Assertions.h" +#include "kmalloc.h" + +void memcpy(void *dest, const void *src, DWORD n) +{ + BYTE* bdest = (BYTE*)dest; + const BYTE* bsrc = (const BYTE*)src; + for (; n; --n) + *(bdest++) = *(bsrc++); +} + +void strcpy(char* dest, const char *src) +{ + while (*src) + *(dest++) = *(src++); +} + +void* memset(void* dest, BYTE c, DWORD n) +{ + BYTE *bdest = (BYTE *)dest; + for (; n; --n) + *(bdest++) = c; + return dest; +} + +DWORD strlen(const char* str) +{ + DWORD len = 0; + while (*(str++)) + ++len; + return len; +} + +int strcmp(const char *s1, const char *s2) +{ + for (; *s1 == *s2; ++s1, ++s2) { + if (*s1 == 0) + return 0; + } + return *(const BYTE*)s1 < *(const BYTE*)s2 ? -1 : 1; +} + +char* strdup(const char *str) +{ + DWORD len = strlen(str); + char *s = (char*)kmalloc(len); + memcpy(s, str, len); + return s; +} + +extern "C" void __cxa_pure_virtual() +{ + ASSERT_NOT_REACHED(); +} diff --git a/Kernel/StdLib.h b/Kernel/StdLib.h new file mode 100644 index 00000000000000..177fdde0eb8fc3 --- /dev/null +++ b/Kernel/StdLib.h @@ -0,0 +1,10 @@ +#pragma once + +#include "types.h" + +void memcpy(void *, const void *, DWORD); +void strcpy(char *, const char *); +int strcmp(char const *, const char *); +DWORD strlen(const char *) PURE; +void *memset(void *, BYTE, DWORD); +char *strdup(const char *); diff --git a/Kernel/String.cpp b/Kernel/String.cpp new file mode 100644 index 00000000000000..6b155cbea7b900 --- /dev/null +++ b/Kernel/String.cpp @@ -0,0 +1,86 @@ +#include "String.h" +#include "StdLib.h" + +String::String() +{ +} + +String::String(const char* characters) + : m_data(DataBuffer::copy((const BYTE*)characters, strlen(characters) + 1)) +{ +} + +String::String(const char* characters, size_t length) + : m_data(DataBuffer::createUninitialized(length + 1)) +{ + memcpy(m_data->data(), characters, length); + m_data->data()[length] = '\0'; +} + +String::String(String&& other) + : m_data(move(other.m_data)) +{ +} + +String::String(const String& other) + : m_data(other.m_data) +{ +} + +String& String::operator=(const String& other) +{ + if (this == &other) + return *this; + m_data = other.m_data; + return *this; +} + +String& String::operator=(const String&& other) +{ + if (this == &other) + return *this; + m_data = move(other.m_data); + return *this; +} + +String::~String() +{ +} + +bool String::operator==(const String& other) const +{ + if (length() != other.length()) + return false; + return strcmp(characters(), other.characters()) == 0; +} + +String String::substring(size_t start, size_t length) const +{ + ASSERT(start + length <= m_data->length()); + // FIXME: This needs some input bounds checking. + auto buffer = DataBuffer::createUninitialized(length + 1); + memcpy(buffer->data(), characters() + start, length); + buffer->data()[length] = '\0'; + String s; + s.m_data = move(buffer); + return s; +} + +Vector String::split(char separator) const +{ + Vector v; + size_t substart = 0; + for (size_t i = 0; i < length(); ++i) { + char ch = characters()[i]; + if (ch == separator) { + size_t sublen = i - substart; + if (sublen != 0) + v.append(substring(substart, sublen)); + substart = i + 1; + } + } + size_t taillen = length() - 1 - substart; + if (taillen != 0) + v.append(substring(substart, taillen)); + return v; +} diff --git a/Kernel/String.h b/Kernel/String.h new file mode 100644 index 00000000000000..c55b6e89966453 --- /dev/null +++ b/Kernel/String.h @@ -0,0 +1,31 @@ +#pragma once + +#include "DataBuffer.h" +#include "Vector.h" + +class String { +public: + String(); + String(const char* characters); + String(const char* characters, size_t length); + String(String&&); + String(const String&); + String& operator=(const String&); + String& operator=(const String&&); + ~String(); + + bool isEmpty() const { return m_data ? m_data->isEmpty() : true; } + size_t length() const { return m_data ? m_data->length() : 0; } + + char operator[](size_t i) const { return (char)m_data->data()[i]; } + + const char* characters() const { return m_data ? (const char*)m_data->data() : nullptr; } + + bool operator==(const String&) const; + + Vector split(char separator) const; + String substring(size_t start, size_t length) const; + +private: + RefPtr m_data; +}; diff --git a/Kernel/Syscall.cpp b/Kernel/Syscall.cpp new file mode 100644 index 00000000000000..0540dd82475081 --- /dev/null +++ b/Kernel/Syscall.cpp @@ -0,0 +1,144 @@ +#include "i386.h" +#include "VGA.h" +#include "Task.h" +#include "Syscall.h" + +struct RegisterDump { + WORD gs; + WORD fs; + WORD es; + WORD ds; + DWORD edi; + DWORD esi; + DWORD ebp; + DWORD esp; + DWORD ebx; + DWORD edx; + DWORD ecx; + DWORD eax; + DWORD eip; + WORD cs; + WORD __csPadding; + DWORD eflags; +} PACKED; + +extern "C" void syscall_entry(); +extern "C" void syscall_ISR(); +extern volatile RegisterDump* syscallRegDump; + +asm( + ".globl syscall_ISR \n" + ".globl syscallRegDump \n" + "syscallRegDump: \n" + ".long 0\n" + "syscall_ISR:\n" + " pusha\n" + " pushw %ds\n" + " pushw %es\n" + " pushw %fs\n" + " pushw %gs\n" + " pushw %ss\n" + " pushw %ss\n" + " pushw %ss\n" + " pushw %ss\n" + " popw %ds\n" + " popw %es\n" + " popw %fs\n" + " popw %gs\n" + " mov %esp, syscallRegDump\n" + " call syscall_entry\n" + " popw %gs\n" + " popw %fs\n" + " popw %es\n" + " popw %ds\n" + " popa\n" + " iret\n" +); + +namespace Syscall { + +void initialize() +{ + registerUserCallableInterruptHandler(0x80, syscall_ISR); + + kprintf("syscall: int 0x80 handler installed\n"); +} + +DWORD invoke(DWORD function) +{ + DWORD result; + asm("int $0x80":"=a"(result):"a"(function)); + return result; +} + +DWORD invoke(DWORD function, DWORD arg1) +{ + DWORD result; + asm("int $0x80":"=a"(result):"a"(function),"d"(arg1)); + return result; +} + +DWORD invoke(DWORD function, DWORD arg1, DWORD arg2) +{ + DWORD result; + asm("int $0x80":"=a"(result):"a"(function),"d"(arg1),"c"(arg2)); + return result; +} + +DWORD invoke(DWORD function, DWORD arg1, DWORD arg2, DWORD arg3) +{ + DWORD result; + asm volatile("int $0x80":"=a"(result):"a"(function),"d"(arg1),"c"(arg2),"b"(arg3)); + return result; +} + +DWORD handle(DWORD function, DWORD arg1, DWORD arg2, DWORD arg3) +{ + switch (function) { + case Syscall::Yield: + yield(); + break; + case 0x1235: // putch + kprintf( "%c", arg1 & 0xFF ); + break; + case Syscall::Sleep: + kprintf("syscall: sleep(%d)\n", arg1); + current->sys$sleep(arg1); + break; + case Syscall::PosixOpen: + Task::checkSanity("syscall"); + 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); + return current->sys$close((int)arg1); + case Syscall::PosixRead: + 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() + kprintf("syscall: seek(%d, %p, %u)\n", arg1, arg2, arg3); + return current->sys$read((int)arg1, (void*)arg2, (size_t)arg3); + case Syscall::PosixKill: + kprintf("syscall: kill(%d, %d)\n", arg1, arg2); + return current->sys$kill((pid_t)arg1, (int)arg2); + case Syscall::PosixGetuid: + return current->sys$getuid(); + default: + kprintf("int0x80: Unknown function %x requested {%x, %x, %x}\n", function, arg1, arg2, arg3); + break; + } + return 0; +} + +} + +void syscall_entry() +{ + auto& regs = *syscallRegDump; + DWORD function = regs.eax; + DWORD arg1 = regs.edx; + DWORD arg2 = regs.ecx; + DWORD arg3 = regs.ebx; + regs.eax = Syscall::handle(function, arg1, arg2, arg3); +} diff --git a/Kernel/Syscall.h b/Kernel/Syscall.h new file mode 100644 index 00000000000000..82e3c43f129da3 --- /dev/null +++ b/Kernel/Syscall.h @@ -0,0 +1,27 @@ +#pragma once + +#define DO_SYSCALL_A0(function) Syscall::invoke((DWORD)(function)) +#define DO_SYSCALL_A1(function, arg1) Syscall::invoke((DWORD)(function), (DWORD)(arg1)) +#define DO_SYSCALL_A2(function, arg1, arg2) Syscall::invoke((DWORD)(function), (DWORD)(arg1), (DWORD)(arg2)) +#define DO_SYSCALL_A3(function, arg1, arg2, arg3) Syscall::invoke((DWORD)(function), (DWORD)(arg1), (DWORD)(arg2), (DWORD)arg3) + +namespace Syscall { + +enum Function { + Sleep = 0x1982, + Yield = 0x1983, + PosixOpen = 0x1985, + PosixClose = 0x1986, + PosixRead = 0x1987, + PosixSeek = 0x1988, + PosixKill = 0x1989, + PosixGetuid = 0x1990, +}; + +void initialize(); +DWORD invoke(DWORD function); +DWORD invoke(DWORD function, DWORD arg1); +DWORD invoke(DWORD function, DWORD arg1, DWORD arg2); +DWORD invoke(DWORD function, DWORD arg1, DWORD arg2, DWORD arg3); + +} diff --git a/Kernel/TSS.h b/Kernel/TSS.h new file mode 100644 index 00000000000000..86d49d0fd8f4ff --- /dev/null +++ b/Kernel/TSS.h @@ -0,0 +1,21 @@ +#pragma once + +struct TSS32 { + unsigned short backlink, __blh; + unsigned long esp0; + unsigned short ss0, __ss0h; + unsigned long esp1; + unsigned short ss1, __ss1h; + unsigned long esp2; + unsigned short ss2, __ss2h; + unsigned long cr3, eip, eflags; + unsigned long eax,ecx,edx,ebx,esp,ebp,esi,edi; + unsigned short es, __esh; + unsigned short cs, __csh; + unsigned short ss, __ssh; + unsigned short ds, __dsh; + unsigned short fs, __fsh; + unsigned short gs, __gsh; + unsigned short ldt, __ldth; + unsigned short trace, iomapbase; +} PACKED; diff --git a/Kernel/Task.cpp b/Kernel/Task.cpp new file mode 100644 index 00000000000000..672883930d5fe7 --- /dev/null +++ b/Kernel/Task.cpp @@ -0,0 +1,515 @@ +#include "types.h" +#include "Task.h" +#include "kmalloc.h" +#include "VGA.h" +#include "StdLib.h" +#include "i386.h" +#include "system.h" +#include "FileSystem.h" + +Task* current; +static Task* kt; +Task* Task::s_kernelTask; + +static pid_t next_pid; +static DoublyLinkedList* s_tasks; + +PRIVATE void context_switch(Task*); + +static void redo_kt_td() +{ + Descriptor td; + + td.setBase(&kt->tss()); + td.setLimit(0xffff); + td.dpl = 0; + td.segment_present = 1; + td.granularity = 1; + td.zero = 0; + td.operation_size = 1; + td.descriptor_type = 0; + td.type = 9; + + if (!kt->selector()) + kt->setSelector(allocateGDTEntry()); + + writeGDTEntry(kt->selector(), td); + flushGDT(); +} + +void Task::initialize() +{ + current = nullptr; + next_pid = 0; + Task::s_kernelTask = nullptr; + s_tasks = new DoublyLinkedList; + + kt = new Task(0, "dummy", IPC::Handle::Any, Task::Ring0); + + redo_kt_td(); +} + +#ifdef TASK_SANITY_CHECKS +void Task::checkSanity(const char* msg) +{ + char ch = current->name()[0]; + kprintf("<%p> %s{%u}%b [%d] :%b: sanity check <%s>\n", + current->name().characters(), + current->name().characters(), + current->name().length(), + current->name()[current->name().length() - 1], + current->pid(), ch, msg ? msg : ""); + ASSERT((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')); +} +#endif + +void Task::allocateLDT() +{ + ASSERT(!m_tss.ldt); + static const WORD numLDTEntries = 4; + WORD newLDTSelector = allocateGDTEntry(); + m_ldtEntries = new Descriptor[numLDTEntries]; +#if 1 + kprintf("new ldt selector = %x\n", newLDTSelector); + kprintf("new ldt table at = %p\n", m_ldtEntries); + kprintf("new ldt table size = %u\n", (numLDTEntries * 8) - 1); +#endif + Descriptor& ldt = getGDTEntry(newLDTSelector); + ldt.setBase(m_ldtEntries); + ldt.setLimit(numLDTEntries * 8 - 1); + ldt.dpl = 0; + ldt.segment_present = 1; + ldt.granularity = 0; + ldt.zero = 0; + ldt.operation_size = 1; + ldt.descriptor_type = 0; + ldt.type = Descriptor::LDT; + m_tss.ldt = newLDTSelector; +} + +Task::Task(void (*e)(), const char* n, IPC::Handle h, RingLevel ring) + : m_name(n) + , m_entry(e) + , m_pid(next_pid++) + , m_handle(h) + , m_state(Runnable) + , m_ring(ring) +{ + memset(&m_tss, 0, sizeof(m_tss)); + memset(&m_ldtEntries, 0, sizeof(m_ldtEntries)); + + if (ring == Ring3) { + allocateLDT(); + } + + // Only IF is set when a task boots. + m_tss.eflags = 0x0202; + + WORD dataSegment; + WORD stackSegment; + WORD codeSegment; + + if (ring == Ring0) { + codeSegment = 0x08; + dataSegment = 0x10; + stackSegment = dataSegment; + } else { + codeSegment = 0x1b; + dataSegment = 0x23; + stackSegment = dataSegment; + } + + m_tss.ds = dataSegment; + m_tss.es = dataSegment; + m_tss.fs = dataSegment; + m_tss.gs = dataSegment; + m_tss.ss = stackSegment; + m_tss.cs = codeSegment; + + m_tss.eip = (DWORD)m_entry; + + // NOTE: Each task gets 4KB of stack. + // This memory is leaked ATM. + // But uh, there's also no process termination, so I guess it's not technically leaked... + static const DWORD defaultStackSize = 4096; + m_stackTop = ((DWORD)kmalloc(defaultStackSize) + defaultStackSize) & 0xffffff8; + m_tss.esp = m_stackTop; + + if (ring == Ring3) { + // Set up a separate stack for Ring0. + // FIXME: Don't leak this stack either. + DWORD ring0StackTop = ((DWORD)kmalloc(defaultStackSize) + defaultStackSize) & 0xffffff8; + m_tss.ss0 = 0x10; + m_tss.esp0 = ring0StackTop; + } + + // HACK: Ring2 SS in the TSS is the current PID. + m_tss.ss2 = m_pid; + + // Get a nice GDT entry @ next context switch. + m_selector = 0; + + // Don't add task 0 (kernel dummy task) to task list. + // FIXME: This doesn't belong in the constructor. + if (m_pid == 0) + return; + + if (m_pid == 1) + s_kernelTask = this; + + // Add it to head of task list (meaning it's next to run too, ATM.) + s_tasks->prepend(this); + + system.nprocess++; + + kprintf("Task %u (%s) spawned @ %p\n", m_pid, m_name.characters(), m_entry); +} + +Task::~Task() +{ + delete [] m_ldtEntries; + m_ldtEntries = nullptr; +} + +void yield() +{ + if (!current) { + kprintf( "PANIC: yield() with !current" ); + HANG; + } + + current->setTicksLeft(1); + + // HACK: To avoid ridiculous clock skew, decrement the system uptime + // counter. It's incremented by int 0x50... + --system.uptime; + asm("int $0x50"); +} + +void sched() +{ + if (!current) { + // XXX: The first ever context_switch() goes to the idle task. + // This to setup a reliable place we can return to. + context_switch(Task::kernelTask()); + return; + } + + // Check and unblock tasks whose wait conditions have been met. + for (auto* task = s_tasks->head(); task; task = task->next()) { + if (task->state() == Task::BlockedReceive && (task->ipc.msg.isValid() || task->ipc.notifies)) { + task->unblock(); + continue; + } + + if (task->state() == Task::BlockedSend) { + Task* peer = Task::fromIPCHandle(task->ipc.dst); + if (peer && peer->state() == Task::BlockedReceive && peer->acceptsMessageFrom(*task)) { + task->unblock(); + continue; + } + } + + if (task->state() == Task::BlockedSleep) { + if (task->wakeupTime() <= system.uptime) { + task->unblock(); + continue; + } + } + } + + auto* prevHead = s_tasks->head(); + for (;;) { + // Move head to tail. + s_tasks->append(s_tasks->removeHead()); + auto* task = s_tasks->head(); + + if (task->state() == Task::Runnable || task->state() == Task::Running) { + //kprintf("switch to %s\n", task->name().characters()); + context_switch(task); + return; + } + + if (task == prevHead) { + // Back at task_head, nothing wants to run. + context_switch(Task::kernelTask()); + return; + } + } +} + +static void drawSchedulerBanner(Task& task) +{ + auto c = vga_get_cursor(); + auto a = vga_get_attr(); + vga_set_cursor(0, 50); + vga_set_attr(0x20); + kprintf(" "); + kprintf(" "); + kprintf(" "); + vga_set_cursor(0, 50); + kprintf("pid: %u ", task.pid()); + vga_set_cursor(0, 58); + kprintf("%s", task.name().characters()); + vga_set_cursor(0, 65); + kprintf("eip: %p", task.tss().eip); + vga_set_attr(a); + vga_set_cursor(c); +} + +static void context_switch(Task* t) +{ + t->setTicksLeft(5); + + // If the last task hasn't blocked (still marked as running), + // mark it as runnable for the next round. + if (current->state() == Task::Running) + current->setState(Task::Runnable); + + current = t; + t->setState(Task::Running); + + // You might be looking at the slowest i386 context switcher ever made. + // (But I don't think so.) + + Descriptor td; + + td.limit_hi = 0; + td.limit_lo = 0xFFFF; + td.base_lo = (DWORD)(&t->tss()) & 0xFFFF; + td.base_hi = ((DWORD)(&t->tss()) >> 16) & 0xFF; + td.base_hi2 = ((DWORD)(&t->tss()) >> 24) & 0xFF; + td.dpl = 0; + td.segment_present = 1; + td.granularity = 1; + td.zero = 0; + td.operation_size = 1; + td.descriptor_type = 0; + td.type = 11; + + if (!t->selector()) { + t->setSelector(allocateGDTEntry()); + writeGDTEntry(t->selector(), td); + flushGDT(); + } else { + writeGDTEntry(t->selector(), td); + } + + drawSchedulerBanner(*t); + + redo_kt_td(); + kt->tss().backlink = t->selector(); + loadTaskRegister(kt->selector()); +} + +Task* Task::fromPID(pid_t pid) +{ + for (auto* task = s_tasks->head(); task; task = task->next()) { + if (task->pid() == pid) + return task; + } + return nullptr; +} + +Task* Task::fromIPCHandle(IPC::Handle handle) +{ + for (auto* task = s_tasks->head(); task; task = task->next()) { + if (task->handle() == handle) + return task; + } + return nullptr; +} + +class FileHandle { +public: + FileHandle() { } + ~FileHandle() { } + + int seek(int offset); + size_t read(void* buffer, int bufferSize); + int fd() const { return m_fd; } + + static FileHandle* fromFileDescriptor(int fd); + +//private: + RefPtr m_vnode; + int m_fd { -1 }; + size_t m_offset { 0 }; +}; + +size_t FileHandle::read(void* buffer, int bufferSize) +{ + Task::checkSanity("FileHandle::read"); + size_t nread = m_vnode->read((BYTE*)buffer, m_offset, bufferSize); + m_offset += nread; + return nread; +} + +int FileHandle::seek(int offset) +{ + if (!m_vnode) + return -1; + ASSERT(offset >= 0); + if ((unsigned)offset >= m_vnode->size()) + return -1; + m_offset = offset; + return m_offset; +} + +FileHandle* FileHandle::fromFileDescriptor(int fd) +{ + return current->fileHandleIfExists(fd); +} + +FileHandle* Task::fileHandleIfExists(int fd) +{ + if (fd < 0) + return nullptr; + if ((unsigned)fd < m_fileHandles.size()) + return m_fileHandles[fd]; + return nullptr; +} + +int Task::sys$seek(int fd, int offset) +{ + auto* handle = FileHandle::fromFileDescriptor(fd); + if (!handle) + return -1; + return handle->seek(offset); +} + +int Task::sys$read(int fd, void* outbuf, size_t nread) +{ + Task::checkSanity("Task::sys$read"); + kprintf("Task::sys$read: called(%d, %p, %u)\n", fd, outbuf, nread); + auto* handle = FileHandle::fromFileDescriptor(fd); + kprintf("Task::sys$read: handle=%p\n", handle); + if (!handle) { + kprintf("Task::sys$read: handle not found :(\n"); + return -1; + } + kprintf("call read on handle=%p\n", handle); + nread = handle->read(outbuf, nread); + kprintf("called read\n"); + kprintf("Task::sys$read: nread=%u\n", nread); + return nread; +} + +int Task::sys$close(int fd) +{ + auto* handle = FileHandle::fromFileDescriptor(fd); + if (!handle) + return -1; + return 0; +} + +int Task::sys$open(const char* path, size_t pathLength) +{ + Task::checkSanity("sys$open"); + kprintf("Task::sys$open(): PID=%u, path=%s {%u}\n", m_pid, path, pathLength); + auto* handle = current->openFile(String(path, pathLength)); + if (handle) + return handle->fd(); + return -1; +} + +FileHandle* Task::openFile(String&& path) +{ + auto vnode = FileSystem::createVirtualNode(move(path)); + if (!vnode) { + kprintf("createVirtualNode failed\n"); + return nullptr; + } +#if 1 + FileHandle* fh = new FileHandle; + kprintf("made new FileHandle\n"); + fh->m_fd = m_fileHandles.size(); + kprintf(" fd = %d\n", fh->m_fd); + fh->m_vnode = move(vnode); + kprintf(" vnode = %p\n", fh->m_vnode.ptr()); + m_fileHandles.append(move(fh)); // FIXME: allow non-move Vector::append + kprintf("Task::openFile(): FileHandle{%p} fd=%d\n", fh, fh->m_fd); + return fh; +#endif + return nullptr; +} + +int Task::sys$kill(pid_t pid, int sig) +{ + (void) sig; + if (pid == 0) { + // FIXME: Send to same-group processes. + ASSERT(pid != 0); + } + if (pid == -1) { + // FIXME: Send to all processes. + ASSERT(pid != -1); + } + ASSERT_NOT_REACHED(); + Task* peer = Task::fromPID(pid); + if (!peer) { + // errno = ESRCH; + return -1; + } +#if 0 + send(peer->handle(), IPC::Message(SYS_KILL, DataBuffer::copy((BYTE*)&sig, sizeof(sig)))); + IPC::Message response = receive(peer->handle()); + return *(int*)response.data(); +#endif + return -1; +} + +uid_t Task::sys$getuid() +{ + return m_uid; +} + +bool Task::acceptsMessageFrom(Task& peer) +{ + return !ipc.msg.isValid() && (ipc.src == IPC::Handle::Any || ipc.src == peer.handle()); +} + +void Task::unblock() +{ + ASSERT(m_state != Task::Runnable && m_state != Task::Running); + system.nblocked--; + m_state = Task::Runnable; +} + +void Task::block(Task::State state) +{ + ASSERT(current->state() == Task::Running); + system.nblocked++; + current->setState(state); +} + +void block(Task::State state) +{ + current->block(state); + yield(); +} + +void sleep(DWORD ticks) +{ + ASSERT(current->state() == Task::Running); + current->setWakeupTime(system.uptime + ticks); + current->block(Task::BlockedSleep); + yield(); +} + +void Task::sys$sleep(DWORD ticks) +{ + ASSERT(this == current); + sleep(ticks); +} + +Task* Task::kernelTask() +{ + ASSERT(s_kernelTask); + return s_kernelTask; +} + +void Task::setError(int error) +{ + m_error = error; +} diff --git a/Kernel/Task.h b/Kernel/Task.h new file mode 100644 index 00000000000000..fcaa9848fe354a --- /dev/null +++ b/Kernel/Task.h @@ -0,0 +1,126 @@ +#pragma once + +#include "types.h" +#include "IPC.h" +#include "DoublyLinkedList.h" +#include "String.h" +#include "TSS.h" +#include "Vector.h" +#include "i386.h" + +//#define TASK_SANITY_CHECKS + +class FileHandle; + +class Task : public DoublyLinkedListNode { + friend class DoublyLinkedListNode; +public: +#ifdef TASK_SANITY_CHECKS + static void checkSanity(const char* msg = nullptr); +#else + static void checkSanity(const char*) { } +#endif + + enum State { + Invalid = 0, + Runnable = 1, + Running = 2, + BlockedReceive = 3, + BlockedSend = 4, + BlockedSleep = 5, + Terminated = 6, + }; + + enum RingLevel { + Ring0 = 0, + Ring3 = 3, + }; + + static Task* fromPID(pid_t); + static Task* fromIPCHandle(IPC::Handle); + static Task* kernelTask(); + + Task(void (*entry)(), const char* name, IPC::Handle, RingLevel); + ~Task(); + + const String& name() const { return m_name; } + pid_t pid() const { return m_pid; } + DWORD ticks() const { return m_ticks; } + WORD selector() const { return m_selector; } + TSS32& tss() { return m_tss; } + State state() const { return m_state; } + IPC::Handle handle() const { return m_handle; } + + FileHandle* fileHandleIfExists(int fd); + FileHandle* createFileHandle(); + + bool acceptsMessageFrom(Task&); + + void block(Task::State); + void unblock(); + + void setWakeupTime(DWORD t) { m_wakeupTime = t; } + DWORD wakeupTime() const { return m_wakeupTime; } + + bool tick() { ++m_ticks; return --m_ticksLeft; } + void setTicksLeft(DWORD t) { m_ticksLeft = t; } + + void setSelector(WORD s) { m_selector = s; } + void setState(State s) { m_state = s; } + + uid_t sys$getuid(); + int sys$open(const char* path, size_t pathLength); + int sys$close(int fd); + int sys$read(int fd, void* outbuf, size_t nread); + int sys$seek(int fd, int offset); + int sys$kill(pid_t pid, int sig); + int sys$geterror() { return m_error; } + void sys$sleep(DWORD ticks); + + struct + { + IPC::Message msg; + IPC::Handle dst; + IPC::Handle src; + DWORD notifies { 0 }; + } ipc; + + static void initialize(); + void setError(int); + +private: + FileHandle* openFile(String&&); + + void allocateLDT(); + + Task* m_prev { nullptr }; + Task* m_next { nullptr }; + + String m_name; + void (*m_entry)() { nullptr }; + pid_t m_pid { 0 }; + uid_t m_uid { 0 }; + DWORD m_ticks { 0 }; + DWORD m_ticksLeft { 0 }; + IPC::Handle m_handle { 0 }; + DWORD m_stackTop { 0 }; + WORD m_selector { 0 }; + State m_state { Invalid }; + DWORD m_wakeupTime { 0 }; + TSS32 m_tss; + Descriptor* m_ldtEntries { nullptr }; + Vector m_fileHandles; + RingLevel m_ring { Ring0 }; + int m_error { 0 }; + + static Task* s_kernelTask; +}; + +extern void task_init(); +extern void yield(); +extern void sched(); +extern void block(Task::State); +extern void sleep(DWORD ticks); + +/* The currently executing task. NULL during kernel bootup. */ +extern Task* current; diff --git a/Kernel/Userspace.cpp b/Kernel/Userspace.cpp new file mode 100644 index 00000000000000..539d94748dec73 --- /dev/null +++ b/Kernel/Userspace.cpp @@ -0,0 +1,42 @@ +#include "Userspace.h" +#include "Syscall.h" +#include "StdLib.h" + +namespace Userspace { + +int open(const char* path) +{ + return DO_SYSCALL_A2(Syscall::PosixOpen, path, strlen(path)); +} + +int close(int fd) +{ + return DO_SYSCALL_A1(Syscall::PosixClose, fd); +} + +int read(int fd, void* outbuf, size_t nread) +{ + return DO_SYSCALL_A3(Syscall::PosixRead, fd, outbuf, nread); +} + +int seek(int fd, int offset) +{ + return DO_SYSCALL_A2(Syscall::PosixRead, fd, offset); +} + +int kill(pid_t pid, int sig) +{ + return DO_SYSCALL_A2(Syscall::PosixKill, pid, sig); +} + +uid_t getuid() +{ + return DO_SYSCALL_A0(Syscall::PosixGetuid); +} + +void sleep(DWORD ticks) +{ + DO_SYSCALL_A1(Syscall::Sleep, ticks); +} + +} diff --git a/Kernel/Userspace.h b/Kernel/Userspace.h new file mode 100644 index 00000000000000..3f83556b6cd594 --- /dev/null +++ b/Kernel/Userspace.h @@ -0,0 +1,15 @@ +#pragma once + +#include "types.h" + +namespace Userspace { + +int open(const char* path); +int close(int fd); +int read(int fd, void* outbuf, size_t nread); +int seek(int fd, int offset); +int kill(pid_t pid, int sig); +uid_t getuid(); +void sleep(DWORD ticks); + +} diff --git a/Kernel/VGA.cpp b/Kernel/VGA.cpp new file mode 100644 index 00000000000000..c9916a3b79a50e --- /dev/null +++ b/Kernel/VGA.cpp @@ -0,0 +1,237 @@ +#include "types.h" +#include "VGA.h" +#include "i386.h" +#include "IO.h" +#include "StdLib.h" +#include "Task.h" +#include + +PRIVATE BYTE *vga_mem = 0L; +PRIVATE BYTE current_attr = 0x07; + +PRIVATE volatile WORD soft_cursor; + +PRIVATE void print_num( DWORD ); +PRIVATE void print_hex( DWORD, BYTE fields ); +static void printSignedNumber(int); + +PRIVATE void +putch( char ch ) +{ + WORD row; + + switch( ch ) + { + case '\n': + row = soft_cursor / 80; + if( row == 23 ) + { + memcpy( vga_mem, vga_mem + 160, 160 * 23 ); + memset( vga_mem + (160 * 23), 0, 160 ); + soft_cursor = row * 80; + } + else + soft_cursor = (row + 1) * 80; + return; + default: + vga_mem[soft_cursor * 2] = ch; + vga_mem[soft_cursor * 2 + 1] = current_attr; + soft_cursor++; + } + row = soft_cursor / 80; + if ((row >= 24 && current->handle() != IPC::Handle::PanelTask)) { + memcpy( vga_mem, vga_mem + 160, 160 * 23 ); + memset( vga_mem + (160 * 23), 0, 160 ); + soft_cursor = 23 * 80; + } +} + +PUBLIC void +kprintf( const char *fmt, ... ) +{ + const char *p; + va_list ap; + + soft_cursor = vga_get_cursor(); + + va_start( ap, fmt ); + + for( p = fmt; *p; ++p ) + { + if( *p == '%' && *(p + 1) ) + { + ++p; + switch( *p ) + { + case 's': + { + const char* sp = va_arg(ap, const char*); + //ASSERT(sp != nullptr); + if (!sp) { + putch('<'); + putch('N'); + putch('u'); + putch('L'); + putch('>'); + } else { + for (; *sp; ++sp) + putch(*sp); + } + } + break; + + case 'd': + printSignedNumber(va_arg(ap, int)); + break; + + case 'u': + print_num( va_arg( ap, DWORD )); + break; + + case 'x': + print_hex( va_arg( ap, DWORD ), 8 ); + break; + + case 'b': + print_hex( va_arg( ap, int ), 2 ); + break; + + case 'c': + putch( (char)va_arg( ap, int )); + break; + + case 'p': + putch( '0' ); + putch( 'x' ); + print_hex( va_arg( ap, DWORD ), 8 ); + break; + } + } + else + { + putch( *p ); + } + } + + /* va_arg( ap, type ); */ + va_end( ap ); + + vga_set_cursor( soft_cursor ); +} + +PRIVATE void +print_hex( DWORD number, BYTE fields ) +{ + static const char h[] = { + '0','1','2','3','4','5','6','7', + '8','9','a','b','c','d','e','f' + }; + + BYTE shr_count = fields * 4; + while( shr_count ) + { + shr_count -= 4; + putch( h[(number >> shr_count) & 0x0F] ); + } +} + +PRIVATE void +print_num( DWORD number ) +{ + DWORD divisor = 1000000000; + char ch; + char padding = 1; + + for( ;; ) + { + ch = '0' + (number / divisor); + number %= divisor; + + if( ch != '0' ) + padding = 0; + + if( !padding || divisor == 1 ) + putch( ch ); + + if( divisor == 1 ) + break; + divisor /= 10; + } +} + +static void printSignedNumber(int number) +{ + if (number < 0) { + putch('-'); + print_num(0 - number); + } else { + print_num(number); + } +} + +PUBLIC void +vga_set_attr( BYTE attr ) +{ + current_attr = attr; +} + +PUBLIC BYTE +vga_get_attr() +{ + return current_attr; +} + +PUBLIC void +vga_init() +{ + DWORD i; + + current_attr = 0x07; + vga_mem = (BYTE *)0xb8000; + + for( i = 0; i < (80 * 24); ++i ) + { + vga_mem[i*2] = ' '; + vga_mem[i*2 + 1] = 0x07; + } + + /* Fill the bottom line with blue. */ + for( i = (80 * 24); i < (80 * 25); ++i ) + { + vga_mem[i*2] = ' '; + vga_mem[i*2 + 1] = 0x17; + } + vga_set_cursor( 0 ); +} + +PUBLIC WORD +vga_get_cursor() +{ + WORD value; + IO::out8(0x3d4, 0x0e); + value = IO::in8(0x3d5) << 8; + IO::out8(0x3d4, 0x0f); + value |= IO::in8(0x3d5); + return value; +} + +PUBLIC void +vga_set_cursor( WORD value ) +{ + if( value >= (80 * 25) ) + { + /* XXX: If you try to move the cursor off the screen, I will go reddish pink! */ + vga_set_cursor( 0 ); + current_attr = 0x0C; + return; + } + IO::out8(0x3d4, 0x0e); + IO::out8(0x3d5, MSB(value)); + IO::out8(0x3d4, 0x0f); + IO::out8(0x3d5, LSB(value)); +} + +void vga_set_cursor(BYTE row, BYTE column) +{ + vga_set_cursor(row * 80 + column); +} diff --git a/Kernel/VGA.h b/Kernel/VGA.h new file mode 100644 index 00000000000000..747e064509b0fa --- /dev/null +++ b/Kernel/VGA.h @@ -0,0 +1,11 @@ +#pragma once + +#include "types.h" + +void vga_init(); +BYTE vga_get_attr(); +void vga_set_attr(BYTE); +void vga_set_cursor(WORD); +void vga_set_cursor(BYTE row, BYTE column); +WORD vga_get_cursor(); +void kprintf(const char *fmt, ...); diff --git a/Kernel/Vector.h b/Kernel/Vector.h new file mode 100644 index 00000000000000..7b5fbded3897c4 --- /dev/null +++ b/Kernel/Vector.h @@ -0,0 +1,90 @@ +#pragma once + +#include "Assertions.h" +#include "kmalloc.h" + +#define SANITIZE_VECTOR + +template +class Vector { +public: + Vector() { } + ~Vector(); + + Vector(const Vector&&); + Vector& operator=(const Vector&&); + + bool isEmpty() const { return m_size == 0; } + size_t size() const { return m_size; } + size_t capacity() const { return m_capacity; } + + void append(T&&); + void clear(); + + const T& operator[](size_t i) const { return m_elements[i]; } + T& operator[](size_t i) { return m_elements[i]; } + +private: + Vector(const Vector&) = delete; + Vector& operator=(const Vector&) = delete; + + void ensureCapacity(size_t); + + T* m_elements { nullptr }; + size_t m_size { 0 }; + size_t m_capacity { 0 }; +}; + +template +Vector::~Vector() +{ + clear(); +#ifdef SANITIZE_VECTOR + m_elements = (T*)0xdddddddd; + m_size = 0x8a8a8a8a; + m_capacity = 0xa8a8a8a8; +#endif +} + +template +void Vector::clear() +{ + if (!m_elements) + return; + for (size_t i = 0; i < m_size; ++i) { + m_elements[i].~T(); + } + kfree(m_elements); + m_elements = nullptr; + m_size = 0; + m_capacity = 0; +} + +template +void Vector::append(T&& element) +{ + ensureCapacity(m_size + 1); + new (&m_elements[m_size]) T(move(element)); + ++m_size; +} + +template +void Vector::ensureCapacity(size_t neededCapacity) +{ + if (neededCapacity <= m_capacity) + return; + size_t newCapacity = (neededCapacity + 8) & ~7; + // FIXME: We need to support further growth here, jeez... + ASSERT(m_capacity == 0); + ASSERT(!m_elements); + m_capacity = newCapacity; + T* newElements = (T*)kmalloc(m_capacity * sizeof(T)); +#ifdef SANITIZE_VECTOR + memset(newElements, 0x66, m_capacity * sizeof(T)); +#endif + if (m_elements) { + memcpy(newElements, m_elements, m_size * sizeof(T)); + kfree(m_elements); + } + m_elements = newElements; +} diff --git a/Kernel/_fs_contents b/Kernel/_fs_contents new file mode 100644 index 00000000000000..ccb991c9bb9a1c Binary files /dev/null and b/Kernel/_fs_contents differ diff --git a/Kernel/_start.cpp b/Kernel/_start.cpp new file mode 100644 index 00000000000000..4820b3c611fe41 --- /dev/null +++ b/Kernel/_start.cpp @@ -0,0 +1,15 @@ +/* + * _start() + * + * This is where we land immediately after leaving the bootloader. + * + * ATM it's really shaky so don't put code before it ^_^ + */ + +extern void init(); + +extern "C" void _start() +{ + init(); + asm volatile("cli; hlt"); +} diff --git a/Kernel/ext2fs.h b/Kernel/ext2fs.h new file mode 100644 index 00000000000000..638c4002d44e9d --- /dev/null +++ b/Kernel/ext2fs.h @@ -0,0 +1,150 @@ +#ifndef __ext2fs_h__ +#define __ext2fs_h__ + +#include "types.h" + +#define EXT2_MAGIC 0xef53 +#define EXT2_NDIR_BLOCKS 12 +#define EXT2_IND_BLOCK EXT2_NDIR_BLOCKS +#define EXT2_DIND_BLOCK (EXT2_IND_BLOCK + 1) +#define EXT2_TIND_BLOCK (EXT2_DIND_BLOCK + 1) +#define EXT2_N_BLOCKS (EXT2_TIND_BLOCK + 1) +#define EXT2_NAME_LEN 255 + +#define EXT2_FT_UNKNOWN 0 +#define EXT2_FT_REG_FILE 1 +#define EXT2_FT_DIR 2 +#define EXT2_FT_CHRDEV 3 +#define EXT2_FT_BLKDEV 4 +#define EXT2_FT_FIFO 5 +#define EXT2_FT_SOCK 6 +#define EXT2_FT_SYMLINK 7 + +typedef struct +{ + __u32 s_inodes_count; /* Inodes count */ + __u32 s_blocks_count; /* Blocks count */ + __u32 s_r_blocks_count; /* Reserved blocks count */ + __u32 s_free_blocks_count; /* Free blocks count */ + __u32 s_free_inodes_count; /* Free inodes count */ + __u32 s_first_data_block; /* First Data Block */ + __u32 s_log_block_size; /* Block size */ + __s32 s_log_frag_size; /* Fragment size */ + __u32 s_blocks_per_group; /* # Blocks per group */ + __u32 s_frags_per_group; /* # Fragments per group */ + __u32 s_inodes_per_group; /* # Inodes per group */ + __u32 s_mtime; /* Mount time */ + __u32 s_wtime; /* Write time */ + __u16 s_mnt_count; /* Mount count */ + __s16 s_max_mnt_count; /* Maximal mount count */ + __u16 s_magic; /* Magic signature */ + __u16 s_state; /* File system state */ + __u16 s_errors; /* Behaviour when detecting errors */ + __u16 s_pad; + __u32 s_lastcheck; /* time of last check */ + __u32 s_checkinterval; /* max. time between checks */ + __u32 s_creator_os; /* OS */ + __u32 s_rev_level; /* Revision level */ + __u16 s_def_resuid; /* Default uid for reserved blocks */ + __u16 s_def_resgid; /* Default gid for reserved blocks */ + __u32 s_first_ino; // First non-reserved inode + __u16 s_inode_size; // inode size + __u16 s_block_group_nr; // Index of block group hosting this superblock + __u32 s_feature_compat; + __u32 s_feature_incompat; + __u32 s_feature_ro_compat; + __u8 s_uuid[16]; + __u32 s_reserved[226]; /* Padding to the end of the block */ +} PACKED ext2_super_block; + +typedef struct +{ + __u32 bg_block_bitmap; + __u32 bg_inode_bitmap; + __u32 bg_inode_table; + __u16 bg_free_blocks_count; + __u16 bg_free_inodes_count; + __u16 bg_used_dirs_count; + __u16 bg_pad; + __u32 bg_reserved; +} PACKED ext2_group_descriptor; + +typedef struct +{ + __u16 i_mode; /* File mode */ + __u16 i_uid; /* Owner Uid */ + __u32 i_size; /* 4: Size in bytes */ + __u32 i_atime; /* Access time */ + __u32 i_ctime; /* 12: Creation time */ + __u32 i_mtime; /* Modification time */ + __u32 i_dtime; /* 20: Deletion Time */ + __u16 i_gid; /* Group Id */ + __u16 i_links_count; /* 24: Links count */ + __u32 i_blocks; /* Blocks count */ + __u32 i_flags; /* 32: File flags */ + union + { + struct + { + __u32 l_i_reserved1; + } + linux1; + struct + { + __u32 h_i_translator; + } + hurd1; + struct + { + __u32 m_i_reserved1; + } + masix1; + } + osd1; /* OS dependent 1 */ + __u32 i_block[EXT2_N_BLOCKS]; /* 40: Pointers to blocks */ + __u32 i_version; /* File version (for NFS) */ + __u32 i_file_acl; /* File ACL */ + __u32 i_dir_acl; /* Directory ACL */ + __u32 i_faddr; /* Fragment address */ + union + { + struct + { + __u8 l_i_frag; /* Fragment number */ + __u8 l_i_fsize; /* Fragment size */ + __u16 i_pad1; + __u32 l_i_reserved2[2]; + } + linux2; + struct + { + __u8 h_i_frag; /* Fragment number */ + __u8 h_i_fsize; /* Fragment size */ + __u16 h_i_mode_high; + __u16 h_i_uid_high; + __u16 h_i_gid_high; + __u32 h_i_author; + } + hurd2; + struct + { + __u8 m_i_frag; /* Fragment number */ + __u8 m_i_fsize; /* Fragment size */ + __u16 m_pad1; + __u32 m_i_reserved2[2]; + } + masix2; + } + osd2; /* OS dependent 2 */ +} PACKED ext2_inode; + +typedef struct +{ + __u32 d_inode; + __u16 d_rec_len; + __u8 d_name_len; + __u8 d_file_type; + char d_name[EXT2_NAME_LEN]; +} PACKED ext2_dir_entry; + +#endif diff --git a/Kernel/fs.cpp b/Kernel/fs.cpp new file mode 100644 index 00000000000000..23cadacfec9289 --- /dev/null +++ b/Kernel/fs.cpp @@ -0,0 +1,451 @@ +#include "Disk.h" +#include "Task.h" +#include "VGA.h" +#include "kmalloc.h" +#include "Ext2FileSystem.h" +#include "i386.h" +#include "StdLib.h" +#include "OwnPtr.h" +#include "FileSystem.h" +#include "String.h" + +//#define FS_DEBUG + +Ext2FileSystem::~Ext2FileSystem() +{ + kprintf("fs: kill Ext2FileSystem\n"); + ASSERT(false); + kfree(m_groupTable); + m_groupTable = nullptr; + if (m_inodeTables) { + for (DWORD i = 0; i < m_blockGroupCount; ++i) + delete [] m_inodeTables[i]; + } + delete [] m_inodeTables; + m_inodeTables = nullptr; +} + +static Ext2FileSystem* fileSystem; + +void Ext2FileSystem::readDiskSector(DWORD sectorIndex, BYTE* buffer) +{ + Task::checkSanity("Ext2FileSystem::readDiskSector"); + bool success = Disk::readSectors(sectorIndex, 1, buffer); + (void) success; +} + +RefPtr Ext2FileSystem::readBlocks(DWORD blockIndex, BYTE count) +{ + Task::checkSanity("readBlocks"); + if (!m_superBlock) { + kprintf("fs: Attempt to read blocks without superblock!\n"); + HANG; + } + +#ifdef FS_DEBUG + kprintf("Read %u block(s) starting at %u\n", count, blockIndex); +#endif + // FIXME: This is broken up into 1-sector reads because the disk task can't handle multi-sector reads yet. + auto buffer = DataBuffer::createUninitialized(count * sectorsPerBlock() * bytesPerSector); + BYTE* bufptr = (BYTE*)buffer->data(); + for (DWORD i = 0; i < count; ++i) { + readDiskSector(((blockIndex + i) * sectorsPerBlock()) + 0, bufptr); + readDiskSector(((blockIndex + i) * sectorsPerBlock()) + 1, bufptr + bytesPerSector); + bufptr += bytesPerSector * 2; + } + return buffer; +} + +void Ext2FileSystem::readSuperBlock() +{ + ASSERT(!m_superBlock); + ASSERT(!m_groupTable); + + m_superBlock = make(); + readDiskSector(2, (BYTE*)m_superBlock.ptr()); + + if (m_superBlock->s_magic != EXT2_MAGIC) { + kprintf("fs: PANIC! No ext2 filesystem found\n"); + HANG; + } + + kprintf("fs: ext2 filesystem found -- %u inodes, %u blocks\n", + m_superBlock->s_inodes_count, + m_superBlock->s_blocks_count); + + const BYTE* u = m_superBlock->s_uuid; + kprintf("fs: UUID: %b%b%b%b-%b%b-%b%b-%b%b-%b%b%b%b%b%b\n", + u[0], u[1], u[2], u[3], u[4], u[5], u[6], u[7], u[8], + u[9], u[10], u[11], u[12], u[13], u[14], u[15], u[16]); + +#ifdef FS_DEBUG + kprintf("fs: Block size is %u bytes\n", 1024 << m_superBlock->s_log_frag_size); + kprintf("fs: Blocks per group: %u\n", m_superBlock->s_blocks_per_group); +#endif + + m_blockGroupCount = m_superBlock->s_blocks_count / m_superBlock->s_blocks_per_group; + if ((m_superBlock->s_blocks_count % m_superBlock->s_blocks_per_group) != 0) + ++m_blockGroupCount; + + m_inodeTables = new ext2_inode*[m_blockGroupCount]; + memset(m_inodeTables, 0, sizeof(ext2_inode*) * m_blockGroupCount); +} + +void Ext2FileSystem::readBlockGroup(DWORD index) +{ + Task::checkSanity("readBlockGroup"); + DWORD superBlockBI = m_superBlock->s_first_data_block + (m_superBlock->s_blocks_per_group * index); + DWORD descriptorTableBI = superBlockBI + 1; + //DWORD blockBitmapBI = descriptorTableBI + 1; + //DWORD inodeBitmapBI = blockBitmapBI + 1; + //DWORD inodeTableBI = inodeBitmapBI + 1; + + auto buffer = readBlocks(descriptorTableBI, 1); + +#ifdef FS_DEBUG + kprintf("Inodes per group = %u\n", superBlock().s_inodes_per_group); + kprintf("First data block = %u\n", superBlock().s_first_data_block); +#endif + + m_groupTable = (ext2_group_descriptor*)kmalloc(blockSize()); + memcpy(m_groupTable, buffer->data(), buffer->length()); + +#ifdef FS_DEBUG + kprintf("[%u] block bitmap: %u\n", index, m_groupTable[index].bg_block_bitmap); + kprintf("[%u] inode bitmap: %u\n", index, m_groupTable[index].bg_inode_bitmap); + kprintf("[%u] inode table: %u\n", index, m_groupTable[index].bg_inode_table); +#endif +} + +template +void Ext2FileSystem::traverseDirectory(ext2_dir_entry& firstEntry, DWORD blockCount, F func) +{ + Task::checkSanity("traverseDirectory1"); + auto* entry = &firstEntry; + char* name = new char[EXT2_NAME_LEN + 1]; + auto* end = (ext2_dir_entry*)((BYTE*)entry + blockCount * blockSize()); + + while (entry < end) { + if (entry->d_inode != 0) { + memcpy(name, entry->d_name, entry->d_name_len); + name[entry->d_name_len] = 0; + func(name, *entry); + } + entry = (ext2_dir_entry*)((BYTE *)entry + entry->d_rec_len); + } + + delete [] name; +} + +void Ext2FileSystem::readInodeTable(DWORD blockGroup) +{ + Task::checkSanity("readInodeTable"); + ext2_inode*& inodeTable = m_inodeTables[blockGroup]; + + if (!inodeTable) + inodeTable = new ext2_inode[m_superBlock->s_inodes_per_group]; + + DWORD inodeTableBlocks = (m_superBlock->s_inodes_per_group * sizeof(ext2_inode)) / blockSize(); +// kprintf("inode table blocks: %u\n", inodeTableBlocks); + + auto buffer = readBlocks(m_groupTable[blockGroup].bg_inode_table, inodeTableBlocks); + memcpy(inodeTable, buffer->data(), m_superBlock->s_inodes_per_group * sizeof(ext2_inode)); + m_root = &inodeTable[1]; + +#ifdef FS_DEBUG + kprintf("Root directory inode:\n"); + kprintf("sizeof(ext2_inode): %u\n", sizeof(ext2_inode)); + kprintf("sizeof(ext2_dir_entry): %u\n", sizeof(ext2_dir_entry)); + kprintf("Mode: %u, Owner: %u/%u, Size: %u\n", m_root->i_mode, m_root->i_uid, m_root->i_gid, m_root->i_size); + + kprintf("Directory blocks: { "); + for (DWORD i = 0; i < 12; ++i) { + kprintf( "%u ", m_root->i_block[i] ); + } + kprintf("}\n"); +#endif +} + +template +void Ext2FileSystem::forEachBlockIn(ext2_inode& inode, F func) +{ + Task::checkSanity("forEachBlockIn"); + DWORD blockCount = inode.i_blocks / (2 << m_superBlock->s_log_block_size); + // FIXME: Support indirect blocks + for (DWORD i = 0; i < blockCount; ++i) { + //kprintf(" [blk %u]\n", inode.i_block[i]); + auto buffer = readBlocks(inode.i_block[i], 1); + func(move(buffer)); + } +} + +DWORD Ext2FileSystem::blockGroupForInode(DWORD inode) const +{ + // FIXME: Implement + (void)inode; + return 0; +} + +DWORD Ext2FileSystem::toInodeTableIndex(DWORD inode) const +{ + // FIXME: Implement + return inode - 1; +} + +ext2_inode* Ext2FileSystem::findInode(DWORD index) +{ + if (index >= m_superBlock->s_inodes_count) + return nullptr; + return &m_inodeTables[blockGroupForInode(index)][toInodeTableIndex(index)]; +} + +ext2_inode* Ext2FileSystem::findPath(const String& path, DWORD& inodeIndex) +{ + Task::checkSanity("findPath entry"); + ASSERT(m_root); + Task::checkSanity("findPath entry2"); + + if (path.isEmpty()) + return nullptr; + if (path[0] != '/') + return nullptr; + + ext2_inode* dir = m_root; + + Task::checkSanity("findPath pre-vector"); + Vector pathParts = path.split('/'); + Task::checkSanity("findPath post-split"); + + for (size_t i = 0; i < pathParts.size(); ++i) { + //kprintf("[%u] %s\n", i, pathParts[i].characters()); + auto& part = pathParts[i]; + bool foundPart = false; + //kprintf("looking for part '%s' in inode{%p}\n", part.characters(), dir); + traverseDirectory(*dir, [&] (const char* name, ext2_dir_entry& entry) { + //kprintf(" ?= %s\n", name); + if (String(name) == part) { + foundPart = true; + //kprintf("found part ==> inode %u (type %b)\n", entry.d_inode, entry.d_file_type); + dir = findInode(entry.d_inode); + inodeIndex = entry.d_inode; + // FIXME: don't try to traverse files as if they're directories + // FIXME: need a way to skip the remaining traverseDirectory() callbacks + } + }); + if (!foundPart) + return nullptr; + } + + return dir; +} + +template +void Ext2FileSystem::traverseDirectory(ext2_inode& inode, F func) +{ + Task::checkSanity("traverseDirectory2"); + //kprintf("in traverseDir\n"); + forEachBlockIn(inode, [this, &func] (RefPtr&& data) { + auto* directory = (ext2_dir_entry*)data->data(); + traverseDirectory(*directory, 1, func); + }); + //kprintf("out traverseDir\n"); +} + +RefPtr Ext2FileSystem::readFile(ext2_inode& inode) +{ + auto buffer = DataBuffer::createUninitialized(inode.i_size + 1); + BYTE* bufptr = buffer->data(); + size_t dataRemaining = inode.i_size; + forEachBlockIn(inode, [this, &bufptr, &dataRemaining] (RefPtr&& data) { + memcpy(bufptr, data->data(), min(dataRemaining, data->length())); + dataRemaining -= blockSize(); + bufptr += blockSize(); + }); + // HACK: This is silly, but let's just null terminate here for comfort. + buffer->data()[buffer->length() - 1] = '\0'; + return buffer; +} + +void Ext2FileSystem::dumpFile(ext2_inode& inode) +{ + auto buffer = readFile(inode); + kprintf("%s", buffer->data()); +} + +void Ext2FileSystem::dumpDirectory(ext2_inode& inode) +{ + traverseDirectory(inode, [this] (const char* name, ext2_dir_entry& entry) { + bool isDirectory = entry.d_file_type == EXT2_FT_DIR; + ext2_inode& inode = m_inodeTables[blockGroupForInode(entry.d_inode)][toInodeTableIndex(entry.d_inode)]; + kprintf("i:%x %b %u:%u %x %s%s\n", + entry.d_inode, + entry.d_file_type, + inode.i_uid, + inode.i_gid, + inode.i_size, + name, + isDirectory ? "/" : ""); + }); +} + +#if 0 +void Ext2FileSystem::readRoot() +{ + auto buffer = readBlocks(m_root->i_block[0], 1); + auto* dir_block = (ext2_dir_entry*)buffer.data(); + + traverseDirectory(dir_block, [this] (const char* name, ext2_dir_entry* entry) { + if (!strcmp(name, "test2")) { + auto test2_entry = loadFile(entry); + new Task((void (*)())test2_entry.data(), "test2", IPC::Handle::Any, Task::Ring3); + // HACK: Don't delete the code we just started running :) + test2_entry.leak(); + } else if (!strcmp( name, "motd.txt")) { + auto motd_txt = loadFile(entry); + kprintf("===============================================\n\n"); + vga_set_attr(0x03); + kprintf("%s\n", motd_txt.data()); + vga_set_attr(0x07); + kprintf("===============================================\n"); + } + }); +} +#endif + +void Ext2FileSystem::initialize() +{ + readSuperBlock(); + readBlockGroup(0); + readInodeTable(0); + +#ifdef FS_DEBUG + dumpDirectory(*m_root); +#endif + + DWORD inodeIndex; + auto* file = findPath("/motd.txt", inodeIndex); + dumpFile(*file); +} + +RefPtr Ext2FileSystem::loadFile(ext2_dir_entry* dirent) +{ + Task::checkSanity("loadFile"); + DWORD inode_group = (dirent->d_inode - 1) / superBlock().s_inodes_per_group; + +#ifdef FS_DEBUG + kprintf("inode: %u (group %u)\n", dirent->d_inode, inode_group); + kprintf("inode table at block %u\n", m_groupTable[inode_group].bg_inode_table); +#endif + + // Calculate interesting offset into inode block. + DWORD inode_index = (dirent->d_inode - 1) % superBlock().s_inodes_per_group; + + // Load the relevant inode block. + auto buffer = readBlocks(m_groupTable[inode_group].bg_inode_table, 4); + auto* inode_table = (ext2_inode*)buffer->data(); + +#ifdef FS_DEBUG + kprintf("inode index: %u\n", inode_index); +#endif + + ext2_inode* inode = &inode_table[inode_index]; + +#ifdef FS_DEBUG + kprintf("Mode: %u UID: %u GID: %u Size: %u Block0: %u\n", inode->i_mode, inode->i_uid, inode->i_gid, inode->i_size, inode->i_block[0]); +#endif + + auto fileContents = readBlocks(inode->i_block[0], 1); + +#ifdef FS_DEBUG + kprintf("File @ %p\n", fileContents->data()); + kprintf("File contents: %b %b %b %b %b\n", + (*fileContents)[0], + (*fileContents)[1], + (*fileContents)[2], + (*fileContents)[3], + (*fileContents)[4]); +#endif + + return fileContents; +} + +RefPtr Ext2VirtualNode::create(DWORD index, String&& path, Ext2FileSystem& fs, DWORD inodeNumber) +{ + Task::checkSanity("enter E2VN::create"); + ext2_inode* inode = fs.findInode(inodeNumber); + Task::checkSanity("post findInode"); + if (!inode) + return nullptr; + auto* v = new Ext2VirtualNode(index, move(path), fs, *inode, inodeNumber); + kprintf("v=%p\n", v); + auto r = adoptRef(v); + kprintf("adopted(v)=%p\n", r.ptr()); + return r; +} + +Ext2VirtualNode::Ext2VirtualNode(DWORD index, String&& path, Ext2FileSystem& fs, ext2_inode& inode, DWORD inodeNumber) + : VirtualNode(index, move(path)) + , m_fileSystem(fs) + , m_inode(inode) + , m_inodeNumber(inodeNumber) +{ + Task::checkSanity("Ext2VirtualNode::Ext2VirtualNode"); +} + +Ext2VirtualNode::~Ext2VirtualNode() +{ + Task::checkSanity("Ext2VirtualNode::~Ext2VirtualNode"); +} + +size_t Ext2VirtualNode::read(BYTE* outbuf, size_t start, size_t maxLength) +{ + Task::checkSanity("Ext2VirtualNode::read"); + kprintf("Ext2VirtualNode::read\n"); + if (start >= size()) + return 0; + + auto fileContents = m_fileSystem.readFile(m_inode); + if (!fileContents) + return 0; + ASSERT(start < fileContents->length()); + size_t nread = min(maxLength, fileContents->length() - start); + memcpy(outbuf, fileContents->data(), nread); + return nread; +} + +namespace FileSystem { + +static DWORD nextVNodeIndex; + +void initialize() +{ + nextVNodeIndex = 0; + fileSystem = new Ext2FileSystem; + fileSystem->initialize(); +} + +VirtualNode::VirtualNode(DWORD index, String&& path) + : m_index(index) + , m_path(move(path)) +{ +} + +VirtualNode::~VirtualNode() +{ +} + +RefPtr createVirtualNode(String&& path) +{ + Task::checkSanity("createVirtualNode"); + DWORD inodeIndex = 0x12345678; + Task::checkSanity("pre-findPath"); + kprintf("path: '%s'\n", path.characters()); + auto* inode = fileSystem->findPath(path, inodeIndex); + Task::checkSanity("post-findPath"); + if (!inode) + return nullptr; + kprintf("creating e2vn\n"); + return Ext2VirtualNode::create(nextVNodeIndex++, move(path), *fileSystem, inodeIndex); +} + +} diff --git a/Kernel/i386.cpp b/Kernel/i386.cpp new file mode 100644 index 00000000000000..a3c0ed3816887b --- /dev/null +++ b/Kernel/i386.cpp @@ -0,0 +1,180 @@ +#include "types.h" +#include "kmalloc.h" +#include "VGA.h" +#include "i386.h" +#include "Assertions.h" +#include "Task.h" + +struct DescriptorTablePointer { + WORD size; + void* address; +} PACKED; + +static DescriptorTablePointer s_idtr; +static DescriptorTablePointer s_gdtr; +static Descriptor* s_idt; +static Descriptor* s_gdt; + +static WORD s_gdtLength; + +WORD allocateGDTEntry() +{ + // FIXME: This should not grow indefinitely. + ASSERT(s_gdtLength < 256); + WORD newGDTEntry = s_gdtLength * 8; + s_gdtLength++; + return newGDTEntry; +} + +#define EH(i, msg) \ + static void _exception ## i () \ + { \ + vga_set_attr(0x0a); \ + kprintf(msg"\n"); \ + DWORD cr0, cr2, cr3, cr4; \ + asm ("movl %%cr0, %%eax":"=a"(cr0)); \ + asm ("movl %%cr2, %%eax":"=a"(cr2)); \ + asm ("movl %%cr3, %%eax":"=a"(cr3)); \ + asm ("movl %%cr4, %%eax":"=a"(cr4)); \ + kprintf("CR0=%x CR2=%x CR3=%x CR4=%x\n", cr0, cr2, cr3, cr4); \ + HANG; \ + } + +EH(0, "Divide error") +EH(1, "Debug exception") +EH(2, "Unknown error") +EH(3, "Breakpoint") +EH(4, "Overflow") +EH(5, "Bounds check") +EH(6, "Invalid opcode") +EH(7, "Coprocessor not available") +EH(8, "Double fault") +EH(9, "Coprocessor segment overrun") +EH(10, "Invalid TSS") +EH(11, "Segment not present") +EH(12, "Stack exception") +EH(13, "General protection fault") +EH(14, "Page fault") +EH(15, "Unknown error") +EH(16, "Coprocessor error") + +static void writeRawGDTEntry(WORD selector, DWORD low, DWORD high) +{ + WORD i = (selector & 0xfffc) >> 3; + s_gdt[i].low = low; + s_gdt[i].high = high; + + if (i > s_gdtLength) { + s_gdtr.size = (s_gdtLength + 1) * 8; + } +} + +void writeGDTEntry(WORD selector, Descriptor& descriptor) +{ + writeRawGDTEntry(selector, descriptor.low, descriptor.high); +} + +Descriptor& getGDTEntry(WORD selector) +{ + WORD i = (selector & 0xfffc) >> 3; + return *(Descriptor*)(&s_gdt[i]); +} + +void flushGDT() +{ + s_gdtr.address = s_gdt; + s_gdtr.size = (s_gdtLength * 8) - 1; + asm("lgdt %0"::"m"(s_gdtr)); +} + +void gdt_init() +{ + s_gdt = new Descriptor[256]; + s_gdtLength = 5; + + s_gdtr.address = s_gdt; + s_gdtr.size = (s_gdtLength * 8) - 1; + + writeRawGDTEntry(0x0000, 0x00000000, 0x00000000); + writeRawGDTEntry(0x0008, 0x0000ffff, 0x00cf9a00); + writeRawGDTEntry(0x0010, 0x0000ffff, 0x00cf9200); + writeRawGDTEntry(0x0018, 0x0000ffff, 0x00cffa00); + writeRawGDTEntry(0x0020, 0x0000ffff, 0x00cff200); + + flushGDT(); +} + +static void unimp_trap() +{ + kprintf("Unhandled IRQ."); + HANG; +} + +void registerInterruptHandler(BYTE index, void (*f)()) +{ + s_idt[index].low = 0x00080000 | LSW((f)); + s_idt[index].high = ((DWORD)(f) & 0xffff0000) | 0x8e00; + flushIDT(); +} + +void registerUserCallableInterruptHandler(BYTE index, void (*f)()) +{ + s_idt[index].low = 0x00080000 | LSW((f)); + s_idt[index].high = ((DWORD)(f) & 0xffff0000) | 0xee00; + flushIDT(); +} + +void flushIDT() +{ + asm("lidt %0"::"m"(s_idtr)); +} + +/* If an 8259 gets cranky, it'll generate a spurious IRQ7. + * ATM I don't have a clear grasp on when/why this happens, + * so I ignore them and assume it makes no difference. + */ + +extern "C" void irq7_handler(); +asm( + ".globl irq7_handler \n" + "irq7_handler: \n" + " iret\n" +); + +void idt_init() +{ + s_idt = new Descriptor[256]; + + s_idtr.address = s_idt; + s_idtr.size = 0x100 * 8; + + for (BYTE i = 0xff; i > 0x10; --i) + registerInterruptHandler(i, unimp_trap); + + registerInterruptHandler(0x00, _exception0); + registerInterruptHandler(0x01, _exception1); + registerInterruptHandler(0x02, _exception2); + registerInterruptHandler(0x03, _exception3); + registerInterruptHandler(0x04, _exception4); + registerInterruptHandler(0x05, _exception5); + registerInterruptHandler(0x06, _exception6); + registerInterruptHandler(0x07, _exception7); + registerInterruptHandler(0x08, _exception8); + registerInterruptHandler(0x09, _exception9); + registerInterruptHandler(0x0a, _exception10); + registerInterruptHandler(0x0b, _exception11); + registerInterruptHandler(0x0c, _exception12); + registerInterruptHandler(0x0d, _exception13); + registerInterruptHandler(0x0e, _exception14); + registerInterruptHandler(0x0f, _exception15); + registerInterruptHandler(0x10, _exception16); + + registerInterruptHandler(0x57, irq7_handler); + + flushIDT(); +} + +void loadTaskRegister(WORD selector) +{ + asm("ltr %0"::"r"(selector)); +} diff --git a/Kernel/i386.h b/Kernel/i386.h new file mode 100644 index 00000000000000..86c8003fb96f68 --- /dev/null +++ b/Kernel/i386.h @@ -0,0 +1,77 @@ +#pragma once + +#include "types.h" + +union Descriptor { + struct { + WORD limit_lo; + WORD base_lo; + BYTE base_hi; + BYTE type : 4; + BYTE descriptor_type : 1; + BYTE dpl : 2; + BYTE segment_present : 1; + BYTE limit_hi : 4; + BYTE : 1; + BYTE zero : 1; + BYTE operation_size : 1; + BYTE granularity : 1; + BYTE base_hi2; + }; + struct { + DWORD low; + DWORD high; + }; + + enum Type { + Invalid = 0, + AvailableTSS_16bit = 0x1, + LDT = 0x2, + BusyTSS_16bit = 0x3, + CallGate_16bit = 0x4, + TaskGate = 0x5, + InterruptGate_16bit = 0x6, + TrapGate_16bit = 0x7, + AvailableTSS_32bit = 0x9, + BusyTSS_32bit = 0xb, + CallGate_32bit = 0xc, + InterruptGate_32bit = 0xe, + TrapGate_32bit = 0xf, + }; + + void setBase(void* b) + { + base_lo = (DWORD)(b) & 0xffff; + base_hi = ((DWORD)(b) >> 16) & 0xff; + base_hi2 = ((DWORD)(b) >> 24) & 0xff; + } + + void setLimit(DWORD l) + { + limit_lo = (DWORD)l & 0xffff; + limit_hi = ((DWORD)l >> 16) & 0xff; + } +} PACKED; + +void gdt_init(); +void idt_init(); +void registerInterruptHandler(BYTE number, void (*f)()); +void registerUserCallableInterruptHandler(BYTE number, void (*f)()); +void flushIDT(); +void flushGDT(); +void loadTaskRegister(WORD selector); +WORD allocateGDTEntry(); +Descriptor& getGDTEntry(WORD selector); +void writeGDTEntry(WORD selector, Descriptor&); + +#define HANG asm volatile( "cli; hlt" ); +#define LSW(x) ((DWORD)(x) & 0xFFFF) +#define MSW(x) (((DWORD)(x) >> 16) & 0xFFFF) +#define LSB(x) ((x) & 0xFF) +#define MSB(x) (((x)>>8) & 0xFF) + +#define disableInterrupts() asm volatile("cli"); +#define enableInterrupts() asm volatile("sti"); + +/* Map IRQ0-15 @ ISR 0x50-0x5F */ +#define IRQ_VECTOR_BASE 0x50 diff --git a/Kernel/i8253.cpp b/Kernel/i8253.cpp new file mode 100644 index 00000000000000..3b114c0f73c494 --- /dev/null +++ b/Kernel/i8253.cpp @@ -0,0 +1,169 @@ +#include "i8253.h" +#include "i386.h" +#include "IO.h" +#include "VGA.h" +#include "Task.h" +#include "system.h" +#include "PIC.h" + +#define IRQ_TIMER 0 + +extern "C" void tick_ISR(); +extern "C" void clock_handle(); + +extern volatile DWORD state_dump; + +asm( + ".globl tick_ISR \n" + ".globl state_dump \n" + "state_dump: \n" + ".long 0\n" + "tick_ISR: \n" + " pusha\n" + " pushw %ds\n" + " pushw %es\n" + " pushw %fs\n" + " pushw %gs\n" + " pushw %ss\n" + " pushw %ss\n" + " pushw %ss\n" + " pushw %ss\n" + " popw %ds\n" + " popw %es\n" + " popw %fs\n" + " popw %gs\n" + " mov %esp, state_dump\n" + " call clock_handle\n" + " popw %gs\n" + " popw %fs\n" + " popw %es\n" + " popw %ds\n" + " popa\n" + " iret\n" +); + +/* Timer related ports */ +#define TIMER0_CTL 0x40 +#define TIMER1_CTL 0x41 +#define TIMER2_CTL 0x42 +#define PIT_CTL 0x43 + +/* Building blocks for PIT_CTL */ +#define TIMER0_SELECT 0x00 +#define TIMER1_SELECT 0x40 +#define TIMER2_SELECT 0x80 + +#define MODE_COUNTDOWN 0x00 +#define MODE_ONESHOT 0x02 +#define MODE_RATE 0x04 +#define MODE_SQUARE_WAVE 0x06 + +#define WRITE_WORD 0x30 + +/* Miscellaneous */ +#define BASE_FREQUENCY 1193182 + +struct RegisterDump { + WORD gs; + WORD fs; + WORD es; + WORD ds; + DWORD edi; + DWORD esi; + DWORD ebp; + DWORD esp; + DWORD ebx; + DWORD edx; + DWORD ecx; + DWORD eax; + DWORD eip; + WORD cs; + WORD __csPadding; + DWORD eflags; +} PACKED; + +void clock_handle() +{ + IRQHandlerScope scope(IRQ_TIMER); + + if (!current) + return; + + system.uptime++; + + if (current->tick()) + return; + + + auto& regs = *reinterpret_cast(state_dump); + current->tss().gs = regs.gs; + current->tss().fs = regs.fs; + current->tss().es = regs.es; + current->tss().ds = regs.ds; + current->tss().edi = regs.edi; + current->tss().esi = regs.esi; + current->tss().ebp = regs.ebp; + current->tss().ebx = regs.ebx; + current->tss().edx = regs.edx; + current->tss().ecx = regs.ecx; + current->tss().eax = regs.eax; + current->tss().eip = regs.eip; + current->tss().cs = regs.cs; + current->tss().eflags = regs.eflags; + +#if 0 + BYTE a = vga_get_attr(); + WORD foo = vga_get_cursor(); + + vga_set_attr(0x50); + vga_set_cursor(1600); + + kprintf("Task %u interrupted at %x\n", current->pid(), regs.eip ); + kprintf("EAX=%x EBX=%x ECX=%x EDX=%x\n", regs.eax, regs.ebx, regs.ecx, regs.edx); + kprintf("ESI=%x EDI=%x EBP=%x ESP=%x\n", regs.esi, regs.edi, regs.ebp, regs.esp); + kprintf("FLAGS=%x", regs.eflags); + + vga_set_cursor(foo); + vga_set_attr(a); +#endif + + // Compute task ESP. + // Add 12 for CS, EIP, EFLAGS (interrupt mechanic) + current->tss().esp = regs.esp + 12; + + // Prepare a new task to run. + sched(); + + // Set the NT (nested task) flag. + // sched() has LTRed a dummy task with a backlink to the next task. + // This is probably super slow/stupid, but I'm just learning... + asm( + "pushf\n" + "orl $0x00004000, (%esp)\n" + "popf\n" + ); +} + +namespace PIT { + +void initialize() +{ + WORD timer_reload; + + IO::out8(PIT_CTL, TIMER0_SELECT | WRITE_WORD | MODE_SQUARE_WAVE); + + timer_reload = (BASE_FREQUENCY / TICKS_PER_SECOND); + + /* Send LSB and MSB of timer reload value. */ + + kprintf("PIT(i8253): %u Hz, square wave (%x)\n", TICKS_PER_SECOND, timer_reload); + + IO::out8(TIMER0_CTL, LSB(timer_reload)); + IO::out8(TIMER0_CTL, MSB(timer_reload)); + + registerInterruptHandler(IRQ_VECTOR_BASE + IRQ_TIMER, tick_ISR); + + PIC::enable(IRQ_TIMER); +} + +} diff --git a/Kernel/i8253.h b/Kernel/i8253.h new file mode 100644 index 00000000000000..58c00af1e538db --- /dev/null +++ b/Kernel/i8253.h @@ -0,0 +1,9 @@ +#pragma once + +#define TICKS_PER_SECOND 600 + +namespace PIT { + +void initialize(); + +} diff --git a/Kernel/init.cpp b/Kernel/init.cpp new file mode 100644 index 00000000000000..7351e0c5af3b15 --- /dev/null +++ b/Kernel/init.cpp @@ -0,0 +1,135 @@ +#include "types.h" +#include "VGA.h" +#include "kmalloc.h" +#include "i386.h" +#include "i8253.h" +#include "Keyboard.h" +#include "Task.h" +#include "IPC.h" +#include "system.h" +#include "Disk.h" +#include "PIC.h" +#include "StdLib.h" +#include "Syscall.h" +#include "CMOS.h" +#include "FileSystem.h" +#include "Userspace.h" + +#if 0 +/* Keyboard LED disco task ;^) */ + +static void led_disco() NORETURN; + +static void led_disco() +{ + BYTE b = 0; + for (;;) { + sleep(0.5 * TICKS_PER_SECOND); + Keyboard::unsetLED((Keyboard::LED)b++); + b &= 7; + Keyboard::setLED((Keyboard::LED)b); + } +} +#endif + +static void motd_main() NORETURN; +static void motd_main() +{ + kprintf("Hello in motd_main!\n"); + int fd = Userspace::open("/test.asm"); + kprintf("motd: fd=%d\n", fd); + ASSERT(fd != -1); + DO_SYSCALL_A3(0x2000, 1, 2, 3); + kprintf("getuid(): %u\n", Userspace::getuid()); + auto buffer = DataBuffer::createUninitialized(33); + memset(buffer->data(), 0, buffer->length()); + int nread = Userspace::read(fd, buffer->data(), buffer->length() - 1); + kprintf("read(): %d\n", nread); + buffer->data()[nread] = 0; + kprintf("read(): '%s'\n", buffer->data()); + for (;;) { + //kill(4, 5); + sleep(1 * TICKS_PER_SECOND); + } +} + +static void user_main() NORETURN; +static void user_main() +{ + DO_SYSCALL_A3(0x3000, 2, 3, 4); + // Crash ourselves! + char* x = reinterpret_cast(0xbeefbabe); + *x = 1; + //HANG; + for (;;) { + // nothing? + Userspace::sleep(1 * TICKS_PER_SECOND); + } +} + +system_t system; + +void banner() +{ + kprintf("\n"); + vga_set_attr(0x0a); + kprintf(" _____ _ _ \n"); + vga_set_attr(0x0b); + kprintf("| __|___ ___| |_ ___ ___| |_ \n"); + vga_set_attr(0x0c); + kprintf("| | | -_| _| . | -_| _| _|\n"); + vga_set_attr(0x0d); + kprintf("|_____|___|_| |___|___|_| |_| \n"); + vga_set_attr(0x07); + kprintf("\n"); +} + +void init() +{ + disableInterrupts(); + + kmalloc_init(); + vga_init(); + + PIC::initialize(); + gdt_init(); + idt_init(); + + // Anything that registers interrupts goes *after* PIC and IDT for obvious reasons. + Syscall::initialize(); + PIT::initialize(); + Keyboard::initialize(); + Task::initialize(); + + memset(&system, 0, sizeof(system)); + + WORD base_memory = (CMOS::read(0x16) << 8) | CMOS::read(0x15); + WORD ext_memory = (CMOS::read(0x18) << 8) | CMOS::read(0x17); + + kprintf("%u kB base memory\n", base_memory); + kprintf("%u kB extended memory\n", ext_memory); + + extern void panel_main(); + + new Task(0, "KernelTask", IPC::Handle::Any, Task::Ring0); + new Task(panel_main, "panel", IPC::Handle::PanelTask, Task::Ring0); + + //new Task(led_disco, "led-disco", IPC::Handle::Any, Task::Ring0); + + sched(); + enableInterrupts(); + + banner(); + + Disk::initialize(); + FileSystem::initialize(); + +// new Task(motd_main, "motd", IPC::Handle::MotdTask, Task::Ring0); + new Task(user_main, "user", IPC::Handle::UserTask, Task::Ring3); + + // The idle task will spend its eternity here for now. + for (;;) { + asm("hlt"); + } +} + diff --git a/Kernel/kmalloc.cpp b/Kernel/kmalloc.cpp new file mode 100644 index 00000000000000..09982e2e474e6d --- /dev/null +++ b/Kernel/kmalloc.cpp @@ -0,0 +1,169 @@ +/* + * Really really *really* Q&D malloc() and free() implementations + * just to get going. Don't ever let anyone see this shit. :^) + */ + +#include "types.h" +#include "kmalloc.h" +#include "StdLib.h" +#include "i386.h" +#include "VGA.h" +#include "system.h" +#include "Assertions.h" + +#define SANITIZE_KMALLOC + +typedef struct +{ + DWORD start; + DWORD nchunk; +} PACKED allocation_t; + +#define CHUNK_SIZE 128 +#define POOL_SIZE (512 * 1024) + +#define BASE_PHYS 0x200000 + +PRIVATE BYTE alloc_map[POOL_SIZE / CHUNK_SIZE / 8]; + +DWORD sum_alloc = 0; +DWORD sum_free = POOL_SIZE; + +PUBLIC void +kmalloc_init() +{ + memset( &alloc_map, 0, sizeof(alloc_map) ); + memset( (void *)BASE_PHYS, 0, POOL_SIZE ); + + sum_alloc = 0; + sum_free = POOL_SIZE; +} + +PUBLIC void * +kmalloc( DWORD size ) +{ + DWORD chunks_needed, chunks_here, first_chunk; + DWORD real_size; + DWORD i, j, k; + + /* We need space for the allocation_t structure at the head of the block. */ + real_size = size + sizeof(allocation_t); + + if (sum_free < real_size) { + kprintf("kmalloc(): PANIC! Out of memory (sucks, dude)\nsum_free=%u, real_size=%x\n", sum_free, real_size); + HANG; + return 0L; + } + + chunks_needed = real_size / CHUNK_SIZE; + if( real_size % CHUNK_SIZE ) + chunks_needed++; + + chunks_here = 0; + first_chunk = 0; + + for( i = 0; i < (POOL_SIZE / CHUNK_SIZE / 8); ++i ) + { + for( j = 0; j < 8; ++j ) + { + if( !(alloc_map[i] & (1<nchunk = chunks_needed; + a->start = first_chunk; + + for( k = first_chunk; k < (first_chunk + chunks_needed); ++k ) + { + alloc_map[k / 8] |= 1 << (k % 8); + } + + sum_alloc += a->nchunk * CHUNK_SIZE; + sum_free -= a->nchunk * CHUNK_SIZE; +#ifdef SANITIZE_KMALLOC + memset(ptr, 0xbb, (a->nchunk * CHUNK_SIZE) - sizeof(allocation_t)); +#endif + return ptr; + } + } + else + { + /* This is in use, so restart chunks_here counter. */ + chunks_here = 0; + } + } + } + + kprintf( "kmalloc(): PANIC! Out of memory (no suitable block)" ); + HANG; + + return 0L; +} + +PUBLIC void +kfree( void *ptr ) +{ + if( !ptr ) + return; + + allocation_t *a = (allocation_t *)((((BYTE *)ptr) - sizeof(allocation_t))); + +#if 0 + DWORD hdr = (DWORD)a; + DWORD mhdr = hdr & ~0x7; + kprintf("hdr / mhdr %p / %p\n", hdr, mhdr); + ASSERT(hdr == mhdr); +#endif + + for (DWORD k = a->start; k < (a->start + a->nchunk); ++k) { + alloc_map[k / 8] &= ~(1 << (k % 8)); + } + + sum_alloc -= a->nchunk * CHUNK_SIZE; + sum_free += a->nchunk * CHUNK_SIZE; + +#ifdef SANITIZE_KMALLOC + memset(a, 0xaa, a->nchunk * CHUNK_SIZE); +#endif +} + +void* operator new(unsigned int size) +{ + return kmalloc(size); +} + +void* operator new[](unsigned int size) +{ + return kmalloc(size); +} + +void operator delete(void* ptr) +{ + return kfree(ptr); +} + +void operator delete[](void* ptr) +{ + return kfree(ptr); +} + +void operator delete(void* ptr, unsigned int) +{ + return kfree(ptr); +} + +void operator delete[](void* ptr, unsigned int) +{ + return kfree(ptr); +} diff --git a/Kernel/kmalloc.h b/Kernel/kmalloc.h new file mode 100644 index 00000000000000..b5f31046bc6c2b --- /dev/null +++ b/Kernel/kmalloc.h @@ -0,0 +1,11 @@ +#pragma once + +void kmalloc_init(); +void *kmalloc(DWORD size) __attribute__ ((malloc)); +void kfree(void*); + +extern DWORD sum_alloc; +extern DWORD sum_free; + +inline void* operator new(size_t, void* p) { return p; } +inline void* operator new[](size_t, void* p) { return p; } diff --git a/Kernel/linker.ld b/Kernel/linker.ld new file mode 100644 index 00000000000000..c86217cc943711 --- /dev/null +++ b/Kernel/linker.ld @@ -0,0 +1,44 @@ +/* The bootloader will look at this image and start execution at the symbol + designated as the entry point. */ +ENTRY(_start) + +/* Tell where the various sections of the object files will be put in the final + kernel image. */ +SECTIONS +{ + /* Begin putting sections at 1 MiB, a conventional place for kernels to be + loaded at by the bootloader. */ + . = 0x10000; + + /* First put the multiboot header, as it is required to be put very early + early in the image or the bootloader won't recognize the file format. + Next we'll put the .text section. */ + .text BLOCK(4K) : ALIGN(4K) + { + _start.o + *(.multiboot) + *(.text) + } + + /* Read-only data. */ + .rodata BLOCK(4K) : ALIGN(4K) + { + *(.rodata) + } + + /* Read-write data (initialized) */ + .data BLOCK(4K) : ALIGN(4K) + { + *(.data) + } + + /* Read-write data (uninitialized) and stack */ + .bss BLOCK(4K) : ALIGN(4K) + { + *(COMMON) + *(.bss) + } + + /* The compiler may produce other sections, by default it will put them in + a segment with the same name. Simply add stuff here as needed. */ +} diff --git a/Kernel/panel.cpp b/Kernel/panel.cpp new file mode 100644 index 00000000000000..2a127619a65746 --- /dev/null +++ b/Kernel/panel.cpp @@ -0,0 +1,46 @@ +#include "types.h" +#include "Task.h" +#include "VGA.h" +#include "system.h" +#include "i386.h" +#include "i8253.h" +#include "kmalloc.h" + +PUBLIC void panel_main() NORETURN; + +PUBLIC void +panel_main() +{ + WORD c; + BYTE a; + + for( ;; ) + { + c = vga_get_cursor(); + a = vga_get_attr(); + + /* HACK: Avoid getting interrupted while painting since + * that could lead to fugly artifacts ;P */ + disableInterrupts(); + + vga_set_attr( 0x17 ); + vga_set_cursor( 80 * 24 ); + + kprintf( + " Uptime: %u -- %u tasks (%u blocked) kmalloc: %u/%u ", + system.uptime / TICKS_PER_SECOND, + system.nprocess, + system.nblocked, + sum_alloc, + sum_free + ); + + vga_set_attr( a ); + vga_set_cursor( c ); + + /* HACK cont.d */ + enableInterrupts(); + + sleep( 1 * TICKS_PER_SECOND ); + } +} diff --git a/Kernel/run b/Kernel/run new file mode 100755 index 00000000000000..067e05dfead5a4 --- /dev/null +++ b/Kernel/run @@ -0,0 +1,5 @@ +#!/bin/sh + +#bochs -q -f .bochs.conf + +qemu-system-i386 -drive format=raw,file=.floppy-image,if=floppy -drive format=raw,file=_fs_contents $@ diff --git a/Kernel/system.h b/Kernel/system.h new file mode 100644 index 00000000000000..a40569a02b7a77 --- /dev/null +++ b/Kernel/system.h @@ -0,0 +1,12 @@ +#pragma once + +#include "types.h" + +struct system_t +{ + time_t uptime; + DWORD nprocess; + DWORD nblocked; +}; + +extern system_t system; diff --git a/Kernel/types.h b/Kernel/types.h new file mode 100644 index 00000000000000..168960ccaebf80 --- /dev/null +++ b/Kernel/types.h @@ -0,0 +1,65 @@ +#pragma once + +#define PACKED __attribute__ ((packed)) +#define NORETURN __attribute__ ((noreturn)) +#define PURE __attribute__ ((pure)) +#define PUBLIC +#define PRIVATE static + +template +T&& move(T& arg) +{ + return static_cast(arg); +} + +template +T min(T a, T b) +{ + return (a < b) ? a : b; +} + +template +T max(T a, T b) +{ + return (a > b) ? a : b; +} + +template +void swap(T& a, U& b) +{ + U tmp = move((U&)a); + a = (T&&)move(b); + b = move(tmp); +} + +template +struct identity { + typedef T type; +}; +template +T&& forward(typename identity::type&& param) +{ return static_cast::type&&>(param); } + +template +T exchange(T& a, U&& b) +{ + T tmp = move(a); + a = move(b); + return tmp; +} + +typedef unsigned char BYTE; +typedef unsigned short WORD; +typedef unsigned int DWORD; + +typedef DWORD __u32; +typedef WORD __u16; +typedef BYTE __u8; +typedef int __s32; +typedef short __s16; + +typedef DWORD uid_t; +typedef DWORD gid_t; +typedef int pid_t; +typedef DWORD time_t; +typedef DWORD size_t;