Import the "gerbert" kernel I worked on earlier this year.

It's a lot crappier than I remembered it. It's gonna need a lot of work.
This commit is contained in:
Andreas Kling 2018-10-16 11:01:38 +02:00
parent f608629704
commit 9396108034
55 changed files with 4600 additions and 0 deletions

4
Kernel/.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
*.o
.floppy-image
Boot/boot.bin
kernel

8
Kernel/Assertions.h Normal file
View file

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

106
Kernel/Boot/boot.asm Executable file
View file

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

18
Kernel/CMOS.cpp Normal file
View file

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

10
Kernel/CMOS.h Normal file
View file

@ -0,0 +1,10 @@
#pragma once
#include "types.h"
namespace CMOS {
BYTE read(BYTE index);
void write(BYTE index, BYTE data);
}

75
Kernel/DataBuffer.cpp Normal file
View file

@ -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> DataBuffer::createUninitialized(size_t length)
{
return adoptRef(new DataBuffer(length));
}
RefPtr<DataBuffer> DataBuffer::copy(const BYTE* data, size_t length)
{
return adoptRef(new DataBuffer(const_cast<BYTE*>(data), length, Copy));
}
RefPtr<DataBuffer> DataBuffer::wrap(BYTE* data, size_t length)
{
return adoptRef(new DataBuffer(data, length, Wrap));
}
RefPtr<DataBuffer> DataBuffer::adopt(BYTE* data, size_t length)
{
return adoptRef(new DataBuffer(data, length, Adopt));
}

39
Kernel/DataBuffer.h Normal file
View file

@ -0,0 +1,39 @@
#pragma once
#include "types.h"
#include "RefCounted.h"
#include "RefPtr.h"
class DataBuffer : public RefCounted<DataBuffer> {
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<DataBuffer> copy(const BYTE*, size_t length);
static RefPtr<DataBuffer> wrap(BYTE*, size_t length);
static RefPtr<DataBuffer> adopt(BYTE*, size_t length);
static RefPtr<DataBuffer> 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 };
};

195
Kernel/Disk.cpp Normal file
View file

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

35
Kernel/Disk.h Normal file
View file

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

166
Kernel/DoublyLinkedList.h Normal file
View file

@ -0,0 +1,166 @@
#pragma once
#include "Assertions.h"
#include "types.h"
template<typename T> class DoublyLinkedListNode {
public:
DoublyLinkedListNode();
void setPrev(T*);
void setNext(T*);
T* prev() const;
T* next() const;
};
template<typename T> inline DoublyLinkedListNode<T>::DoublyLinkedListNode()
{
setPrev(0);
setNext(0);
}
template<typename T> inline void DoublyLinkedListNode<T>::setPrev(T* prev)
{
static_cast<T*>(this)->m_prev = prev;
}
template<typename T> inline void DoublyLinkedListNode<T>::setNext(T* next)
{
static_cast<T*>(this)->m_next = next;
}
template<typename T> inline T* DoublyLinkedListNode<T>::prev() const
{
return static_cast<const T*>(this)->m_prev;
}
template<typename T> inline T* DoublyLinkedListNode<T>::next() const
{
return static_cast<const T*>(this)->m_next;
}
template<typename T> 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<T>&);
private:
T* m_head { nullptr };
T* m_tail { nullptr };
};
template<typename T> inline size_t DoublyLinkedList<T>::size() const
{
size_t size = 0;
for (T* node = m_head; node; node = node->next())
++size;
return size;
}
template<typename T> inline void DoublyLinkedList<T>::clear()
{
m_head = 0;
m_tail = 0;
}
template<typename T> inline void DoublyLinkedList<T>::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<typename T> inline void DoublyLinkedList<T>::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<typename T> inline void DoublyLinkedList<T>::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<typename T> inline T* DoublyLinkedList<T>::removeHead()
{
T* node = head();
if (node)
remove(node);
return node;
}
template<typename T> inline void DoublyLinkedList<T>::append(DoublyLinkedList<T>& 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;
}

74
Kernel/Ext2FileSystem.h Normal file
View file

@ -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<DataBuffer> 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<typename F> void forEachBlockIn(ext2_inode&, F func);
template<typename F> void traverseDirectory(ext2_dir_entry&, DWORD blockCount, F);
template<typename F> void traverseDirectory(ext2_inode&, F);
RefPtr<DataBuffer> readFile(ext2_inode&);
RefPtr<DataBuffer> 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<ext2_super_block> 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<Ext2VirtualNode> 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 };
};

35
Kernel/FileSystem.h Normal file
View file

@ -0,0 +1,35 @@
#pragma once
#include "types.h"
#include "RefCounted.h"
namespace FileSystem {
void initialize();
class VirtualNode : public RefCounted<VirtualNode> {
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<VirtualNode> createVirtualNode(String&& path);
}

41
Kernel/IO.cpp Normal file
View file

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

15
Kernel/IO.h Normal file
View file

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

104
Kernel/IPC.cpp Normal file
View file

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

81
Kernel/IPC.h Normal file
View file

@ -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<DataBuffer>&& 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<DataBuffer> m_data;
DWORD m_type { 0 };
Handle m_sender;
bool m_isValid { false };
};
Message receive(Handle);
void send(Handle, Message&&);
void notify(Handle);
}

135
Kernel/Keyboard.cpp Normal file
View file

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

16
Kernel/Keyboard.h Normal file
View file

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

58
Kernel/Makefile Normal file
View file

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

78
Kernel/OwnPtr.h Normal file
View file

@ -0,0 +1,78 @@
#pragma once
#include "types.h"
template<typename T>
class OwnPtr {
public:
OwnPtr() { }
explicit OwnPtr(T* ptr) : m_ptr(ptr) { }
OwnPtr(OwnPtr&& other) : m_ptr(other.leakPtr()) { }
template<typename U> OwnPtr(OwnPtr<U>&& other) : m_ptr(static_cast<T*>(other.leakPtr())) { }
~OwnPtr() { clear(); }
OwnPtr& operator=(OwnPtr&& other)
{
if (this != &other) {
delete m_ptr;
m_ptr = other.leakPtr();
}
return *this;
}
template<typename U>
OwnPtr& operator=(OwnPtr<U>&& other)
{
if (this != static_cast<void*>(&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<class T, class... Args> inline OwnPtr<T>
make(Args&&... args)
{
return OwnPtr<T>(new T(forward<Args>(args)...));
}

97
Kernel/PIC.cpp Normal file
View file

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

19
Kernel/PIC.h Normal file
View file

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

95
Kernel/Queue.h Normal file
View file

@ -0,0 +1,95 @@
#pragma once
#include "types.h"
template<typename T>
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 };
};

48
Kernel/RefCounted.h Normal file
View file

@ -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<typename T>
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<const T*>(this);
}
protected:
RefCounted() { }
~RefCounted() { }
};

100
Kernel/RefPtr.h Normal file
View file

@ -0,0 +1,100 @@
#pragma once
#include "types.h"
#define SANITIZE_REFPTR
template<typename T> class RefPtr;
template<typename T> RefPtr<T> adoptRef(T*);
template<typename T>
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<typename U>
RefPtr(RefPtr<U>&& other)
: m_ptr(static_cast<T*>(other.leakPtr()))
{
}
template<typename U>
RefPtr& operator=(RefPtr<U>&& other)
{
if (this == &other)
return *this;
m_ptr = static_cast<T*>(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<typename U> friend class RefPtr;
friend RefPtr adoptRef<T>(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<typename T>
inline RefPtr<T> adoptRef(T* ptr)
{
ASSERT(ptr->refCount() == 1);
return RefPtr<T>(RefPtr<T>::Adopt, ptr);
}

55
Kernel/StdLib.cpp Normal file
View file

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

10
Kernel/StdLib.h Normal file
View file

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

86
Kernel/String.cpp Normal file
View file

@ -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> String::split(char separator) const
{
Vector<String> 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;
}

31
Kernel/String.h Normal file
View file

@ -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<String> split(char separator) const;
String substring(size_t start, size_t length) const;
private:
RefPtr<DataBuffer> m_data;
};

144
Kernel/Syscall.cpp Normal file
View file

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

27
Kernel/Syscall.h Normal file
View file

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

21
Kernel/TSS.h Normal file
View file

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

515
Kernel/Task.cpp Normal file
View file

@ -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<Task>* 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<Task>;
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<FileSystem::VirtualNode> 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;
}

126
Kernel/Task.h Normal file
View file

@ -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<Task> {
friend class DoublyLinkedListNode<Task>;
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<FileHandle*> 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;

42
Kernel/Userspace.cpp Normal file
View file

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

15
Kernel/Userspace.h Normal file
View file

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

237
Kernel/VGA.cpp Normal file
View file

@ -0,0 +1,237 @@
#include "types.h"
#include "VGA.h"
#include "i386.h"
#include "IO.h"
#include "StdLib.h"
#include "Task.h"
#include <stdarg.h>
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);
}

11
Kernel/VGA.h Normal file
View file

@ -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, ...);

90
Kernel/Vector.h Normal file
View file

@ -0,0 +1,90 @@
#pragma once
#include "Assertions.h"
#include "kmalloc.h"
#define SANITIZE_VECTOR
template<typename T>
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<typename T>
Vector<T>::~Vector()
{
clear();
#ifdef SANITIZE_VECTOR
m_elements = (T*)0xdddddddd;
m_size = 0x8a8a8a8a;
m_capacity = 0xa8a8a8a8;
#endif
}
template<typename T>
void Vector<T>::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<typename T>
void Vector<T>::append(T&& element)
{
ensureCapacity(m_size + 1);
new (&m_elements[m_size]) T(move(element));
++m_size;
}
template<typename T>
void Vector<T>::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;
}

BIN
Kernel/_fs_contents Normal file

Binary file not shown.

15
Kernel/_start.cpp Normal file
View file

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

150
Kernel/ext2fs.h Normal file
View file

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

451
Kernel/fs.cpp Normal file
View file

@ -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<DataBuffer> 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<ext2_super_block>();
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<typename F>
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<typename F>
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<String> 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<typename F>
void Ext2FileSystem::traverseDirectory(ext2_inode& inode, F func)
{
Task::checkSanity("traverseDirectory2");
//kprintf("in traverseDir\n");
forEachBlockIn(inode, [this, &func] (RefPtr<DataBuffer>&& data) {
auto* directory = (ext2_dir_entry*)data->data();
traverseDirectory<F>(*directory, 1, func);
});
//kprintf("out traverseDir\n");
}
RefPtr<DataBuffer> 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<DataBuffer>&& 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<DataBuffer> 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> 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<VirtualNode> 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);
}
}

180
Kernel/i386.cpp Normal file
View file

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

77
Kernel/i386.h Normal file
View file

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

169
Kernel/i8253.cpp Normal file
View file

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

9
Kernel/i8253.h Normal file
View file

@ -0,0 +1,9 @@
#pragma once
#define TICKS_PER_SECOND 600
namespace PIT {
void initialize();
}

135
Kernel/init.cpp Normal file
View file

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

169
Kernel/kmalloc.cpp Normal file
View file

@ -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<<j)) )
{
if( chunks_here == 0 )
{
/* Mark where potential allocation starts. */
first_chunk = i * 8 + j;
}
chunks_here++;
if( chunks_here == chunks_needed )
{
auto* a = (allocation_t *)(BASE_PHYS + (first_chunk * CHUNK_SIZE));
BYTE *ptr = (BYTE *)a;
ptr += sizeof(allocation_t);
a->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);
}

11
Kernel/kmalloc.h Normal file
View file

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

44
Kernel/linker.ld Normal file
View file

@ -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. */
}

46
Kernel/panel.cpp Normal file
View file

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

5
Kernel/run Executable file
View file

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

12
Kernel/system.h Normal file
View file

@ -0,0 +1,12 @@
#pragma once
#include "types.h"
struct system_t
{
time_t uptime;
DWORD nprocess;
DWORD nblocked;
};
extern system_t system;

65
Kernel/types.h Normal file
View file

@ -0,0 +1,65 @@
#pragma once
#define PACKED __attribute__ ((packed))
#define NORETURN __attribute__ ((noreturn))
#define PURE __attribute__ ((pure))
#define PUBLIC
#define PRIVATE static
template <typename T>
T&& move(T& arg)
{
return static_cast<T&&>(arg);
}
template<typename T>
T min(T a, T b)
{
return (a < b) ? a : b;
}
template<typename T>
T max(T a, T b)
{
return (a > b) ? a : b;
}
template<typename T, typename U>
void swap(T& a, U& b)
{
U tmp = move((U&)a);
a = (T&&)move(b);
b = move(tmp);
}
template<typename T>
struct identity {
typedef T type;
};
template<typename T>
T&& forward(typename identity<T>::type&& param)
{ return static_cast<typename identity<T>::type&&>(param); }
template<typename T, typename U>
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;