Orion
Barry Importing existing Orion kernel d41a53c (3 years, 2 months ago)
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..4cae3ed
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+build/
+*.o
+orion
diff --git a/000.S b/000.S
new file mode 100644
index 0000000..6e8a1c0
--- /dev/null
+++ b/000.S
@@ -0,0 +1,25 @@
+[section .multiboot]
+[global header]
+header:
+ dd 0x1BADB002
+ dd 1 | 2
+ dd -(0x1BADB002 + (1 | 2))
+
+ dd 0, 0, 0, 0, 0
+
+[section .bss]
+stack_bottom:
+ resb 16384
+stack_top:
+
+[section .text]
+[extern kmain]
+[global _start]
+_start:
+ mov ebp, stack_top
+ mov esp, ebp
+ push ebx
+ push esp
+ call kmain
+ cli
+ jmp $
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..d0ad907
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,45 @@
+PRODUCT=orion
+
+CC=i686-orion-gcc
+CFLAGS=-ffreestanding -fno-pie #-I${SYSROOT}/usr/include/
+
+AS=nasm
+AFLAGS=-f elf32
+
+LD=i686-orion-gcc
+LFLAGS=-T linker.ld -ffreestanding -nostdlib
+
+AS_SOURCES := $(shell find . -name '*.S')
+OBJS = $(sort $(subst ./,build/,$(subst .S,.o,$(AS_SOURCES))))
+
+C_SOURCES := $(shell find . -name '*.c')
+OBJS += $(sort $(subst ./,build/,$(subst .c,.o,$(C_SOURCES))))
+
+.PHONY: clean all install
+
+all: $(PRODUCT)
+
+clean:
+ @echo "REMOVING OBJECT FILES"
+ @mkdir -p build
+ @rm -rf build
+ @touch $(PRODUCT)
+ @rm $(PRODUCT)
+
+install: $(PRODUCT)
+ @echo "INSTALLING $^"
+ @install -Dm 755 $(PRODUCT) -t ${SYSROOT}/boot/
+
+$(PRODUCT): $(OBJS)
+ @echo "LINKING $@"
+ @$(LD) -o $@ $^ $(LFLAGS)
+
+build/%.o: %.c
+ @echo "COMPILING $<"
+ @mkdir -p $(@D)
+ @$(CC) -c $< -o $@ $(CFLAGS)
+
+build/%.o: %.S
+ @echo "ASSEMBLING $<"
+ @mkdir -p $(@D)
+ @$(AS) $< -o $@ $(AFLAGS)
diff --git a/drivers/drivers.c b/drivers/drivers.c
new file mode 100644
index 0000000..5ae8c4b
--- /dev/null
+++ b/drivers/drivers.c
@@ -0,0 +1,102 @@
+/*
+ * This is the main file of the Driver core, it finds the devices attached to
+ * the system and also triggers any initialisation required for them.
+ */
+
+#include <stdint.h>
+#include <stddef.h>
+#include "drivers.h"
+#include "pci.h"
+#include "../vfs/vfs.h"
+#include "../screen.h"
+
+/* Structure for a Device */
+typedef struct Device {
+ uint16_t vendor, device;
+ char *name;
+ void (*init)(uint8_t, uint8_t, uint8_t);
+} Device;
+
+Device devices[] = {
+ /* Intel system devices */
+ {0x8086, 0x1237, "Intel i440FX Chipset", NULL},
+ {0x8086, 0x7000, "Intel PIIX3 PCI-to-ISA Bridge (Triton II)", NULL},
+ {0x8086, 0x7010, "Intel PIIX3 IDE Interface (Triton II)", ide_init},
+ {0x8086, 0x7020, "Intel PIIX3 USB (Natoma/Triton II)", NULL},
+ {0x8086, 0x7113, "Intel PIIX4/4E/4M Power Management Controller", NULL},
+
+ /* Network devices */
+ {0x10ec, 0x8139, "Realtek RTL8139 10/100 NIC", rtl8139_init},
+ {0x8086, 0x100e, "Intel Pro 1000/MT NIC", NULL},
+
+ /* VGA devices */
+ {0x1234, 0x1111, "QEMU/Bochs VBE Framebuffer", bga_init},
+};
+
+Driver *drivers = NULL;
+
+/* Register a driver */
+void
+register_driver(Driver *driver)
+{
+ Driver *prev;
+search:
+ if (!driver->major) /* Zero not allowed */
+ driver->major = 1;
+
+ /* Maintain an ordered (by major) list of drivers */
+ if (!drivers) {
+ drivers = driver;
+ return;
+ }
+ if (drivers->major > driver->major) {
+ driver->next = drivers;
+ drivers = driver;
+ return;
+ }
+
+ for (prev = drivers; prev->next; prev = prev->next) {
+ /* If major is taken, find next available slot */
+ if (prev->major == driver->major) {
+ driver->major++;
+ if (!driver->major) /* Overflow */
+ goto search;
+ }
+ if (prev->major < driver->major
+ && prev->next->major > driver->major)
+ break;
+ }
+ driver->next = prev->next;
+ prev->next = driver;
+}
+
+/* Initialise devices */
+void
+init_drivers(void)
+{
+ uint16_t bus, slot, func;
+ uint16_t vendor, device;
+ uint32_t dev;
+ kprintf("Enumerating PCI devices");
+ for (bus = 0; bus < 256; bus++)
+ for (slot = 0; slot < 32; slot++)
+ for (func = 0; func < 8; func++)
+ if ((vendor = pci_read_word(bus, slot, func, 0)) != 0xFFFF) {
+ device = pci_read_word(bus, slot, func, 2);
+ for (dev = 0; dev < sizeof(devices)/sizeof(devices[0]); dev++) {
+ if (devices[dev].vendor == vendor
+ && devices[dev].device == device) {
+ kprintf(" PCI(%d,%d,%d) \"%s\"",
+ bus, slot, func, devices[dev].name);
+ if (devices[dev].init)
+ devices[dev].init(bus, slot, func);
+ break;
+ }
+ }
+ if (devices[dev].vendor != vendor
+ || devices[dev].device != device) {
+ kprintf(" PCI(%d,%d,%d) = %#x:%#x",
+ bus, slot, func, vendor, device);
+ }
+ }
+}
diff --git a/drivers/drivers.h b/drivers/drivers.h
new file mode 100644
index 0000000..47cd243
--- /dev/null
+++ b/drivers/drivers.h
@@ -0,0 +1,30 @@
+#ifndef KERNEL_DRIVERS_H
+#define KERNEL_DRIVERS_H
+
+#include <sys/types.h>
+#include "../vfs/vfs.h"
+
+#define MKDEV(maj, min) ((dev_t) (((maj & 0xFFFF) << 16) | (min & 0xFFFF)))
+#define MAJOR(dev) ((dev >> 16) & 0xFFFF)
+#define MINOR(dev) (dev & 0xFFFF)
+
+typedef struct Driver Driver;
+
+/* Structure for a Driver */
+struct Driver {
+ unsigned short major;
+ FileOps *ops;
+ Driver *next;
+};
+
+extern Driver *drivers;
+
+void init_drivers(void);
+void register_driver(Driver *driver);
+
+/* Drivers */
+void ide_init(uint8_t bus, uint8_t slot, uint8_t func);
+void rtl8139_init(uint8_t bus, uint8_t dev, uint8_t func);
+void bga_init(uint8_t bus, uint8_t slot, uint8_t func);
+
+#endif
diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c
new file mode 100644
index 0000000..9e3b73a
--- /dev/null
+++ b/drivers/ide/ide.c
@@ -0,0 +1,685 @@
+/*
+ * This file is the driver for the PCI IDE devices, which handles access to
+ * ATA/ATAPI devices. It detects the drives connected to the system, and
+ * handles any access to them - with the ability to do DMA if possible. This
+ * driver will access the drives in the best available way, presenting a uniform
+ * interface for any other code to use. It also adds a device node to the DevFS
+ * mount for every device, to provide access to the rest of the OS easily.
+ */
+
+#include <stdint.h>
+#include <stddef.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include "../drivers.h"
+#include "../pci.h"
+#include "../../mem/frame.h"
+#include "../../proc/proc.h"
+#include "../../vfs/vfs.h"
+#include "../../io.h"
+#include "../../screen.h"
+
+struct partition {
+ uint8_t drive;
+ uint8_t shead, ssect, scyl;
+ uint8_t type;
+ uint8_t ehead, esect, ecyl;
+ uint32_t lba;
+ uint32_t size;
+};
+
+size_t ata_read(File *file, char *buf, size_t size, off_t offset);
+size_t ata_write(File *file, char *buf, size_t size, off_t offset);
+int ata_open(File *file);
+
+FileOps ideFileOps = {
+ .read = ata_read,
+ .write = ata_write,
+ .open = ata_open,
+};
+
+Driver ideDriver = {
+ .major = 2,
+ .ops = &ideFileOps,
+ .next = NULL,
+};
+
+page_t ataDmaArea;
+
+uint8_t ide_read(uint8_t chan, uint8_t reg);
+void ide_write(uint8_t chan, uint8_t reg, uint8_t data);
+
+/* Status */
+#define ATA_SR_BSY 0x80 /* Busy */
+#define ATA_SR_DRDY 0x40 /* Drive ready */
+#define ATA_SR_DF 0x20 /* Drive write fault */
+#define ATA_SR_DSC 0x10 /* Drive seek complete */
+#define ATA_SR_DRQ 0x08 /* Data request ready */
+#define ATA_SR_CORR 0x04 /* Corrected data */
+#define ATA_SR_IDX 0x02 /* Index */
+#define ATA_SR_ERR 0x01 /* Error */
+
+/* Error */
+#define ATA_ER_BBK 0x80 /* Bad block */
+#define ATA_ER_UNC 0x40 /* Uncorrectable data */
+#define ATA_ER_MC 0x20 /* Media changed */
+#define ATA_ER_IDNF 0x10 /* ID mark not found */
+#define ATA_ER_MCR 0x08 /* Media change request */
+#define ATA_ER_ABRT 0x04 /* Command aborted */
+#define ATA_ER_T0NF 0x02 /* Track 0 not found */
+#define ATA_ER_AMNF 0x01 /* No address mark */
+
+/* Command */
+#define ATA_CMD_READ_PIO 0x20
+#define ATA_CMD_READ_PIO_EXT 0x24
+#define ATA_CMD_READ_DMA 0xC8
+#define ATA_CMD_READ_DMA_EXT 0x25
+#define ATA_CMD_WRITE_PIO 0x30
+#define ATA_CMD_WRITE_PIO_EXT 0x34
+#define ATA_CMD_WRITE_DMA 0xCA
+#define ATA_CMD_WRITE_DMA_EXT 0x35
+#define ATA_CMD_CACHE_FLUSH 0xE7
+#define ATA_CMD_CACHE_FLUSH_EXT 0xEA
+#define ATA_CMD_PACKET 0xA0
+#define ATA_CMD_IDENTIFY_PACKET 0xA1
+#define ATA_CMD_IDENTIFY 0xEC
+
+#define ATAPI_CMD_READ 0xA8
+#define ATAPI_CMD_EJECT 0x1B
+
+#define ATA_IDENT_DEVICETYPE 0
+#define ATA_IDENT_CYLINDERS 2
+#define ATA_IDENT_HEADS 6
+#define ATA_IDENT_SECTORS 12
+#define ATA_IDENT_SERIAL 20
+#define ATA_IDENT_MODEL 54
+#define ATA_IDENT_CAPABILITIES 98
+#define ATA_IDENT_FIELDVALID 106
+#define ATA_IDENT_MAX_LBA 120
+#define ATA_IDENT_COMMANDSETS 164
+#define ATA_IDENT_MAX_LBA_EXT 200
+
+#define IDE_ATA 0
+#define IDE_ATAPI 1
+#define ATA_MASTER 0
+#define ATA_SLAVE 1
+#define ATA_PRIMARY 0
+#define ATA_SECONDARY 1
+#define ATA_READ 0
+#define ATA_WRITE 1
+
+/* Registers */
+#define ATA_REG_DATA 0x0
+#define ATA_REG_ERROR 0x1
+#define ATA_REG_FEATURES 0x1
+#define ATA_REG_SECCOUNT0 0x2
+#define ATA_REG_LBA0 0x3
+#define ATA_REG_LBA1 0x4
+#define ATA_REG_LBA2 0x5
+#define ATA_REG_HDDEVSEL 0x6
+#define ATA_REG_COMMAND 0x7
+#define ATA_REG_STATUS 0x7
+#define ATA_REG_SECCOUNT1 0x8
+#define ATA_REG_LBA3 0x9
+#define ATA_REG_LBA4 0xA
+#define ATA_REG_LBA5 0xB
+#define ATA_REG_CONTROL 0xC
+#define ATA_REG_ALTSTATUS 0xC
+#define ATA_REG_DEVADDRESS 0xD
+
+struct IDEChanRegs {
+ uint16_t base; /* IO base */
+ uint16_t ctrl; /* Control base */
+ uint16_t bmide; /* Bus Master IDE */
+ uint8_t noint; /* No interrupt */
+} channels[2];
+
+uint8_t ideBuf[2048] = {0};
+volatile unsigned static char ideIrqInvoked = 0;
+unsigned static char atapiPacket[12] = {0xA8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+struct IDEDevice {
+ uint8_t reserved;
+ uint8_t channel;
+ uint8_t drive;
+ uint16_t type;
+ uint16_t signature;
+ uint16_t capabilities;
+ uint32_t commandSets;
+ uint32_t size;
+ uint8_t model[41];
+} ideDevices[4];
+
+/* Read channel */
+uint8_t
+ide_read(uint8_t chan, uint8_t reg)
+{
+ uint8_t result;
+ if (reg > 0x07 && reg < 0x0C)
+ ide_write(chan, ATA_REG_CONTROL, 0x80 | channels[chan].noint);
+ if (reg < 0x08)
+ result = inb(channels[chan].base + reg - 0x00);
+ else if (reg < 0x0C)
+ result = inb(channels[chan].base + reg - 0x06);
+ else if (reg < 0x0E)
+ result = inb(channels[chan].ctrl + reg - 0x0A);
+ else if (reg < 0x16)
+ result = inb(channels[chan].bmide + reg - 0x0E);
+ if (reg > 0x07 && reg < 0x0C)
+ ide_write(chan, ATA_REG_CONTROL, channels[chan].noint);
+ return result;
+}
+
+/* Write channel */
+void
+ide_write(uint8_t chan, uint8_t reg, uint8_t data)
+{
+ if (reg > 0x07 && reg < 0x0C)
+ ide_write(chan, ATA_REG_CONTROL, 0x80 | channels[chan].noint);
+ if (reg < 0x08)
+ outb(channels[chan].base + reg - 0x00, data);
+ else if (reg < 0x0C)
+ outb(channels[chan].base + reg - 0x06, data);
+ else if (reg < 0x0E)
+ outb(channels[chan].ctrl + reg - 0x0A, data);
+ else if (reg < 0x16)
+ outb(channels[chan].bmide + reg - 0x0E, data);
+ if (reg > 0x07 && reg < 0x0C)
+ ide_write(chan, ATA_REG_CONTROL, channels[chan].noint);
+}
+
+/* Read identification space */
+void
+ide_read_buffer(uint8_t chan, uint8_t reg, void *buffer, uint32_t quads)
+{
+ if (reg > 0x07 && reg < 0x0C)
+ ide_write(chan, ATA_REG_CONTROL, 0x80 | channels[chan].noint);
+ asm volatile("pushw %es; movw %ds, %ax; movw %ax, %es");
+ if (reg < 0x08)
+ insl(channels[chan].base + reg - 0x00, buffer, quads);
+ else if (reg < 0x0C)
+ insl(channels[chan].base + reg - 0x06, buffer, quads);
+ else if (reg < 0x0E)
+ insl(channels[chan].ctrl + reg - 0x0A, buffer, quads);
+ else if (reg < 0x16)
+ insl(channels[chan].bmide + reg - 0x0E, buffer, quads);
+ asm volatile("popw %es");
+ if (reg > 0x07 && reg < 0x0C)
+ ide_write(chan, ATA_REG_CONTROL, channels[chan].noint);
+}
+
+/* Poll for status after command */
+uint8_t
+ide_polling(uint8_t chan, uint32_t check)
+{
+ uint8_t i, state;
+ /* Wait for BSY to set */
+ for (i = 0; i < 4; i++)
+ ide_read(chan, ATA_REG_ALTSTATUS);
+ /* Wait for BSY to clear */
+ while (ide_read(chan, ATA_REG_STATUS) & ATA_SR_BSY);
+
+ if (check) {
+ state = ide_read(chan, ATA_REG_STATUS);
+ if (state & ATA_SR_ERR)
+ return 2;
+ if (state & ATA_SR_DF)
+ return 1;
+ if ((state & ATA_SR_DRQ) == 0)
+ return 3;
+ }
+
+ return 0;
+}
+
+/* Print an IDE error */
+uint8_t
+ide_print_error(uint32_t drive, uint8_t err)
+{
+ if (err == 0)
+ return err;
+
+ uint8_t st;
+ kprintf("IDE:");
+ if (err == 1) {
+ kprintf(" - Device fault");
+ err = 19;
+ } else if (err == 2) {
+ st = ide_read(ideDevices[drive].channel, ATA_REG_ERROR);
+ if (st & ATA_ER_AMNF) {
+ kprintf(" - No address mark found");
+ err = 7;
+ }
+ if (st & ATA_ER_T0NF) {
+ kprintf(" - No media or media error");
+ err = 3;
+ }
+ if (st & ATA_ER_ABRT) {
+ kprintf(" - Command aborted");
+ err = 20;
+ }
+ if (st & ATA_ER_MCR) {
+ kprintf(" - No media or media error");
+ err = 3;
+ }
+ if (st & ATA_ER_IDNF) {
+ kprintf(" - ID mark not found");
+ err = 21;
+ }
+ if (st & ATA_ER_MC) {
+ kprintf(" - No media or media error");
+ err = 3;
+ }
+ if (st & ATA_ER_UNC) {
+ kprintf(" - Uncorrectable data error");
+ err = 22;
+ }
+ if (st & ATA_ER_BBK) {
+ kprintf(" - Bad sectors");
+ err = 13;
+ }
+ } else if (err == 3) {
+ kprintf(" - Read nothing");
+ err = 23;
+ } else if (err == 4) {
+ kprintf(" - Write protected");
+ err = 8;
+ }
+ kprintf(" - [%s %s] %s",
+ (char *[]){"Primary","Secondary"}[ideDevices[drive].channel],
+ (char *[]){"Master","Slave"}[ideDevices[drive].drive],
+ ideDevices[drive].model);
+ return err;
+}
+
+/* Initialise an IDE drive */
+void
+ide_drive_init(uint32_t bar0, uint32_t bar1, uint32_t bar2,
+ uint32_t bar3, uint32_t bar4)
+{
+ channels[ATA_PRIMARY].base = (bar0 & ~3) + 0x1F0 * (!bar0);
+ channels[ATA_PRIMARY].ctrl = (bar1 & ~3) + 0x3F6 * (!bar1);
+ channels[ATA_SECONDARY].base = (bar2 & ~3) + 0x170 * (!bar2);
+ channels[ATA_SECONDARY].ctrl = (bar3 & ~3) + 0x376 * (!bar3);
+ channels[ATA_PRIMARY].bmide = (bar4 & ~3) + 0;
+ channels[ATA_SECONDARY].bmide = (bar4 & ~3) + 8;
+ /* Disable IRQs */
+ ide_write(ATA_PRIMARY, ATA_REG_CONTROL, 2);
+ ide_write(ATA_SECONDARY, ATA_REG_CONTROL, 2);
+
+ uint32_t count = 0;
+ uint8_t i, j, k, err, type, status;
+ for (i = 0; i < 2; i++)
+ for (j = 0; j < 2; j++) {
+ err = 0;
+ type = IDE_ATA;
+ ideDevices[count].reserved = 0;
+ ide_write(i, ATA_REG_HDDEVSEL, 0xA0 | (j << 4));
+ sleep(1);
+ ide_write(i, ATA_REG_COMMAND, ATA_CMD_IDENTIFY);
+ sleep(1);
+ if (ide_read(i, ATA_REG_STATUS) == 0)
+ continue;
+
+ while (1) {
+ status = ide_read(i, ATA_REG_STATUS);
+ if ((status & ATA_SR_ERR)) {
+ err = 1;
+ break;
+ }
+ if (!(status & ATA_SR_BSY) && (status & ATA_SR_DRQ))
+ break;
+ }
+
+ if (err) {
+ uint8_t cl = ide_read(i, ATA_REG_LBA1);
+ uint8_t ch = ide_read(i, ATA_REG_LBA2);
+
+ if (cl == 0x14 && ch == 0xEB)
+ type = IDE_ATAPI;
+ else if (cl == 0x69 && ch == 0x96)
+ type = IDE_ATAPI;
+ else
+ continue;
+
+ ide_write(i, ATA_REG_COMMAND, ATA_CMD_IDENTIFY_PACKET);
+ sleep(1);
+ }
+
+ ide_read_buffer(i, ATA_REG_DATA, ideBuf, 128);
+
+ uint16_t sig, cap, set;
+ sig = *((uint16_t *) (ideBuf + ATA_IDENT_DEVICETYPE));
+ cap = *((uint16_t *) (ideBuf + ATA_IDENT_CAPABILITIES));
+ set = *((uint16_t *) (ideBuf + ATA_IDENT_COMMANDSETS));
+ ideDevices[count].reserved = 1;
+ ideDevices[count].type = type;
+ ideDevices[count].channel = i;
+ ideDevices[count].drive = j;
+ ideDevices[count].signature = sig;
+ ideDevices[count].capabilities = cap;
+ ideDevices[count].commandSets = set;
+
+ uint32_t size;
+ if (ideDevices[count].commandSets & (1 << 26))
+ size = *((uint32_t *) (ideBuf + ATA_IDENT_MAX_LBA_EXT));
+ else
+ size = *((uint32_t *) (ideBuf + ATA_IDENT_MAX_LBA));
+ ideDevices[count].size = size;
+ for (k = 0; k < 40; k += 2) {
+ ideDevices[count].model[k] =
+ ideBuf[ATA_IDENT_MODEL + k + 1];
+ ideDevices[count].model[k + 1] =
+ ideBuf[ATA_IDENT_MODEL + k];
+ }
+ ideDevices[count].model[40] = 0;
+ count++;
+ }
+}
+
+/* Wait for an IRQ to arrive */
+void
+ide_wait_irq(void)
+{
+ while (!ideIrqInvoked);
+ ideIrqInvoked = 0;
+}
+
+/* Access an ATA drive */
+uint8_t
+ide_ata_access(uint8_t write, uint8_t drive, uint32_t lba, uint8_t sectors,
+ uint16_t selector, uint32_t edi)
+{
+ uint8_t lbaMode, dma, cmd;
+ uint8_t lbaIO[6];
+ uint32_t chan = ideDevices[drive].channel;
+ uint32_t slave = ideDevices[drive].drive;
+ uint32_t bus = channels[chan].base;
+ uint32_t words = 256;
+ uint16_t cyl, i;
+ uint8_t head, sect, err;
+
+ ide_write(chan, ATA_REG_CONTROL,
+ channels[chan].noint = (ideIrqInvoked = 0) + 2);
+
+ if (lba >= 0x10000000) {
+ /* LBA48 */
+ lbaMode = 2;
+ lbaIO[0] = (lba & 0x000000FF) >> 0;
+ lbaIO[1] = (lba & 0x0000FF00) >> 8;
+ lbaIO[2] = (lba & 0x00FF0000) >> 16;
+ lbaIO[3] = (lba & 0xFF000000) >> 24;
+ lbaIO[4] = 0;
+ lbaIO[5] = 0;
+ head = 0;
+ } else if (ideDevices[drive].capabilities & 0x200) {
+ /* LBA28 */
+ lbaMode = 1;
+ lbaIO[0] = (lba & 0x000000FF) >> 0;
+ lbaIO[1] = (lba & 0x0000FF00) >> 8;
+ lbaIO[2] = (lba & 0x00FF0000) >> 16;
+ lbaIO[3] = 0;
+ lbaIO[4] = 0;
+ lbaIO[5] = 0;
+ head = (lba & 0x0F000000) >> 24;
+ } else {
+ /* CHS */
+ lbaMode = 0;
+ sect = (lba % 63) + 1;
+ cyl = (lba + 1 - sect) / (16 * 63);
+ lbaIO[0] = sect;
+ lbaIO[1] = (cyl >> 0) & 0xFF;
+ lbaIO[2] = (cyl >> 8) & 0xFF;
+ lbaIO[3] = 0;
+ lbaIO[4] = 0;
+ lbaIO[5] = 0;
+ head = (lba + 1 - sect) % (16 * 63) / 63;
+ }
+
+ dma = 0; /* XXX */
+
+ while (ide_read(chan, ATA_REG_STATUS) & ATA_SR_BSY);
+
+ if (lbaMode == 0)
+ ide_write(chan, ATA_REG_HDDEVSEL, 0xA0 | (slave << 4) | head);
+ else
+ ide_write(chan, ATA_REG_HDDEVSEL, 0xE0 | (slave << 4) | head);
+
+ if (lbaMode == 2) {
+ ide_write(chan, ATA_REG_SECCOUNT1, 0);
+ ide_write(chan, ATA_REG_LBA3, lbaIO[3]);
+ ide_write(chan, ATA_REG_LBA4, lbaIO[4]);
+ ide_write(chan, ATA_REG_LBA5, lbaIO[5]);
+ }
+ ide_write(chan, ATA_REG_SECCOUNT0, sectors);
+ ide_write(chan, ATA_REG_LBA0, lbaIO[0]);
+ ide_write(chan, ATA_REG_LBA1, lbaIO[1]);
+ ide_write(chan, ATA_REG_LBA2, lbaIO[2]);
+
+ if (lbaMode == 0 && !dma && !write) cmd = ATA_CMD_READ_PIO;
+ if (lbaMode == 1 && !dma && !write) cmd = ATA_CMD_READ_PIO;
+ if (lbaMode == 2 && !dma && !write) cmd = ATA_CMD_READ_PIO_EXT;
+ if (lbaMode == 0 && dma && !write) cmd = ATA_CMD_READ_DMA;
+ if (lbaMode == 1 && dma && !write) cmd = ATA_CMD_READ_DMA;
+ if (lbaMode == 2 && dma && !write) cmd = ATA_CMD_READ_DMA_EXT;
+ if (lbaMode == 0 && !dma && write) cmd = ATA_CMD_WRITE_PIO;
+ if (lbaMode == 1 && !dma && write) cmd = ATA_CMD_WRITE_PIO;
+ if (lbaMode == 2 && !dma && write) cmd = ATA_CMD_WRITE_PIO_EXT;
+ if (lbaMode == 0 && dma && write) cmd = ATA_CMD_WRITE_DMA;
+ if (lbaMode == 1 && dma && write) cmd = ATA_CMD_WRITE_DMA;
+ if (lbaMode == 2 && dma && write) cmd = ATA_CMD_WRITE_DMA_EXT;
+ ide_write(chan, ATA_REG_COMMAND, cmd);
+
+ if (dma) {
+ if (write) {
+ /* TODO */
+ } else {
+ /* TODO */
+ }
+ } else {
+ if (write) {
+ for (i = 0; i < sectors; i++) {
+ ide_polling(chan, 0);
+ outsw(bus, (void *) edi, words);
+ }
+ ide_write(chan, ATA_REG_COMMAND, (uint8_t []) {
+ ATA_CMD_CACHE_FLUSH,
+ ATA_CMD_CACHE_FLUSH,
+ ATA_CMD_CACHE_FLUSH_EXT,
+ }[lbaMode]);
+ ide_polling(chan, 0);
+ } else {
+ for (i = 0; i < sectors; i++) {
+ if (err = ide_polling(chan, 1))
+ return err;
+ insw(bus, (void *) edi, words);
+ edi += words * 2;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/* IDE IRQ handler */
+void
+ide_irq(InterruptFrame *frame)
+{
+ ideIrqInvoked = 1;
+}
+
+/* Initialise an IDE drive */
+void
+ide_init(uint8_t bus, uint8_t slot, uint8_t func)
+{
+ register_driver(&ideDriver);
+
+ uint8_t class, subclass, progif, header;
+ class = pci_read_byte(bus, slot, func, 11);
+ subclass = pci_read_byte(bus, slot, func, 10);
+ progif = pci_read_byte(bus, slot, func, 9);
+ header = pci_read_byte(bus, slot, func, 14);
+
+ uint8_t primaryNative = 0, primaryChange = 0,
+ secondaryNative = 0, secondaryChange = 0,
+ busMaster = 0;
+ if (progif & (1 << 0))
+ primaryNative = 1;
+ if (progif & (1 << 1))
+ primaryChange = 1;
+ if (progif & (1 << 2))
+ secondaryNative = 1;
+ if (progif & (1 << 3))
+ secondaryChange = 1;
+ if (progif & (1 << 7))
+ busMaster = 1;
+
+ uint32_t bar0 = 0x1F0, bar1 = 0x3F6,
+ bar2 = 0x170, bar3 = 0x376,
+ bar4 = 0x00;
+
+ if (primaryNative) {
+ bar0 = pci_read_dword(bus, slot, func, 0x10);
+ bar1 = pci_read_dword(bus, slot, func, 0x14);
+ }
+ if (secondaryNative) {
+ bar2 = pci_read_dword(bus, slot, func, 0x18);
+ bar3 = pci_read_dword(bus, slot, func, 0x1C);
+ }
+ if (busMaster)
+ bar4 = pci_read_dword(bus, slot, func, 0x20);
+
+ register_interrupt(14, ide_irq);
+ ataDmaArea = alloc_frames(1);
+
+ ide_drive_init(0x1F0, 0x3F6, 0x170, 0x376, 0x00);
+ uint8_t i, j;
+ char name[16];
+ for (i = 0; i < 4; i++) {
+ if (!ideDevices[i].reserved)
+ continue;
+ sprintf(name, "/dev/%cd%c",
+ (char []){'h','c'}[ideDevices[i].type],
+ (char []){'a','b'}[ideDevices[i].channel]);
+ mknod(name, S_IFBLK | 0600, MKDEV(ideDriver.major, i * 16));
+ kprintf(" %s drive [%s] %d MB - %s",
+ (char *[]){"ATA", "ATAPI"}[ideDevices[i].type],
+ name, ideDevices[i].size / 1024 / 2,
+ ideDevices[i].model);
+ /* Attempt to read partition table */
+ if (ideDevices[i].type != 0)
+ continue;
+ ide_ata_access(0, i, 0, 0x02, 1, ataDmaArea);
+ struct partition *part;
+ for (j = 0; j < 4; j++) {
+ part = (void *) ataDmaArea + 446 + (j * 16);
+ if (!part->type)
+ continue;
+ sprintf(name, "/dev/%cd%c%d",
+ (char []){'h','c'}[ideDevices[i].type],
+ (char []){'a','b'}[ideDevices[i].channel],
+ j + 1);
+ int asdf = mknod(name, S_IFBLK | 0600,
+ MKDEV(ideDriver.major, (i * 16) + (j + 1)));
+ kprintf(" Partition %d: %#.8x - %#.8x",
+ j+1, part->lba*512,
+ (part->lba + part->size) * 512);
+ }
+ }
+}
+
+/* Read a drive */
+size_t
+ata_read(File *file, char *buf, size_t size, off_t offset)
+{
+ uint16_t min, bufOffset = offset % 512;
+ size_t count = 0;
+ uint16_t dev = MINOR(file->inode->dev) / 16,
+ parti = MINOR(file->inode->dev) % 16;
+ uint32_t lba;
+ struct partition *part;
+ if (parti) {
+ ide_ata_access(0, dev, 0, 1, 0x02, ataDmaArea);
+ part = (void *) ataDmaArea + 446 + ((parti - 1) * 16);
+ if (offset > (part->lba + part->size) * 512)
+ return 0;
+ if (offset + size > (part->lba + part->size) * 512)
+ size = ((part->lba + part->size) * 512) - offset;
+ } else {
+ if (offset > ideDevices[dev].size * 512)
+ return 0;
+ if (offset + size > ideDevices[dev].size * 512)
+ size = (ideDevices[dev].size * 512) - offset;
+ }
+ while (size + bufOffset) {
+ min = (size > 0x1000) ? 0x1000 : size;
+ lba = (parti ? part->lba : 0) + ((offset + count) / 512);
+ ide_ata_access(0, dev, lba, 8, 0x02, ataDmaArea);
+ memcpy((void *) (buf + count),
+ (void *) ataDmaArea + bufOffset,
+ min);
+ size -= min;
+ count += min;
+ bufOffset = 0;
+ }
+ return count;
+}
+
+/* Write a drive */
+size_t
+ata_write(File *file, char *buf, size_t size, off_t offset)
+{
+ uint16_t min;
+ size_t count = 0;
+ uint16_t dev = MINOR(file->inode->dev) / 16,
+ parti = MINOR(file->inode->dev) % 16;
+ uint32_t lba;
+ struct partition *part;
+ if (parti) {
+ ide_ata_access(0, dev, 0, 1, 0x02, ataDmaArea);
+ part = (void *) ataDmaArea + 446 + ((parti - 1) * 16);
+ if (offset > (part->lba + part->size) * 512)
+ return 0;
+ if (offset + size > (part->lba + part->size) * 512)
+ size = ((part->lba + part->size) * 512) - offset;
+ } else {
+ if (offset > ideDevices[dev].size * 512)
+ return 0;
+ if (offset + size > ideDevices[dev].size * 512)
+ size = (ideDevices[dev].size * 512) - offset;
+ }
+ while (size) {
+ min = (size > 0x1000) ? 0x1000 : size;
+ memcpy((void *) ataDmaArea,
+// (void *) (((uint32_t) (buf + count) / 512) * 512),
+ (void *) (buf + count),
+ min);
+ lba = (parti ? part->lba : 0) + ((offset + count) / 512);
+ ide_ata_access(1, dev, lba, 8, 0x02, ataDmaArea);
+ size -= min;
+ count += min;
+ }
+ return count;
+ /*
+ * TODO: Currently, offset is sector-aligned, which we don't always
+ * want. This should be fixed by basically checking the offset
+ * for this and reading into the dma buffer before we write to the
+ * adjusted offset in the buffer, this way the surrounding data
+ * isn't deleted by accident with bad alignment.
+ */
+}
+
+/* Open a drive */
+int
+ata_open(File *file)
+{
+ if (ideDevices[MINOR(file->inode->dev)/16].reserved)
+ return 0;
+ if (ideDevices[MINOR(file->inode->dev)/16].type != 0)
+ return -EINVAL;
+ return -ENODEV;
+}
+
diff --git a/drivers/net/rtl8139.c b/drivers/net/rtl8139.c
new file mode 100644
index 0000000..e801845
--- /dev/null
+++ b/drivers/net/rtl8139.c
@@ -0,0 +1,148 @@
+/*
+ * The is the RTL8139 driver. It is a simple driver for a simple network card.
+ * It will just abstract the hardware away, and allows the network system to
+ * utilise it
+ */
+
+#include <stdint.h>
+#include <stddef.h>
+#include <string.h>
+#include "../pci.h"
+#include "../../mem/heap.h"
+#include "../../proc/proc.h"
+#include "../../net/net.h"
+#include "../../io.h"
+#include "../../screen.h"
+
+#define RTL_PORT_MAC 0x00
+#define RTL_PORT_RX_PTR 0x38
+#define RTL_PORT_RBSTART 0x30
+#define RTL_PORT_IMR 0x3C
+#define RTL_PORT_ISR 0x3E
+#define RTL_PORT_CMD 0x37
+#define RTL_PORT_RXMISS 0x4C
+#define RTL_PORT_TCR 0x40
+#define RTL_PORT_RCR 0x44
+#define RTL_PORT_CONFIG 0x52
+#define RTL_RX_BUF_SIZE (8192+16+1500)
+#define RTL_TX_BUF_SIZE (1536)
+
+#define RTL_CFG_AAP (1 << 0) /* Accept all packets */
+#define RTL_CFG_APM (1 << 1) /* Accept packets to NIC's MAC address */
+#define RTL_CFG_AM (1 << 2) /* Accept multicast packets */
+#define RTL_CFG_AB (1 << 3) /* Accept broadcast packets */
+
+#define RTL_ISR_ROK (1 << 0) /* Receive Okay */
+#define RTL_ISR_TOK (1 << 2) /* Transmit Okay */
+
+static NetIF *netif;
+
+void *txBuf, *rxBuf;
+uint8_t txIndex = 0;
+uint32_t rxIndex = 0;
+uint16_t port;
+
+uint16_t txStartRegs[] = {0x20, 0x24, 0x28, 0x2C};
+uint16_t txControlRegs[] = {0x10, 0x14, 0x18, 0x1C};
+
+void rtl8139_transmit(void *data, size_t len);
+void rtl8139_receive(void);
+
+/* Handle the RTL8139 interrupt */
+void
+rtl8139_irq(InterruptFrame *frame)
+{
+ uint16_t status;
+ /* Loop until everything has been handled */
+ while (1) {
+ status = inw(port + RTL_PORT_ISR);
+ outw(port + RTL_PORT_ISR, status);
+ if (!status)
+ break;
+ if (status & RTL_ISR_ROK)
+ rtl8139_receive();
+ }
+}
+
+/* Transmit a frame */
+void
+rtl8139_transmit(void *data, size_t len)
+{
+ memcpy(txBuf, data, len);
+ outl(port + txStartRegs[txIndex], (uintptr_t) txBuf);
+ outl(port + txControlRegs[txIndex], len);
+ txIndex++;
+ if (txIndex > 3)
+ txIndex = 0;
+}
+
+/* Receive a frame */
+void
+rtl8139_receive(void)
+{
+ uint8_t *frame, *buf = rxBuf;
+ uint32_t index = rxIndex;
+ uint32_t offset, len;
+
+ /* While the buffer is not empty */
+ while ((inb(port + RTL_PORT_CMD) & 0x01) == 0) {
+ offset = index % RTL_RX_BUF_SIZE;
+ len = (buf[3 + index] << 8) + buf[2 + index];
+
+ frame = kmalloc(len);
+ memcpy(frame, &buf[offset + 4], len);
+ ethernet_receive_frame(netif, frame, len);
+ kfree(frame);
+
+ index = (index + len + 7) & ~3;
+ outw(port + RTL_PORT_RX_PTR, index - 16);
+ }
+
+ rxIndex = index;
+}
+
+/* Get the RTL8139 MAC Address */
+void
+rtl8139_get_mac(char *buf)
+{
+ uint8_t i;
+ for (i = 0; i < 6; i++)
+ buf[i] = inb(port + RTL_PORT_MAC + i);
+}
+
+/* Initialise the RTL8139 Network card */
+void
+rtl8139_init(uint8_t bus, uint8_t dev, uint8_t func)
+{
+ /* Enable bus mastering */
+ uint16_t command = pci_read_word(bus, dev, func, 4);
+ if (!(command & (1 << 2)))
+ pci_write_word(bus, dev, func, 4, command | (1 << 2));
+
+ /* Turn device on */
+ port = pci_read_word(bus, dev, func, 0x10) & ~3;
+ outb(port + RTL_PORT_CONFIG, 0x00);
+ /* Software reset */
+ outb(port + RTL_PORT_CMD, 0x10);
+ while ((inb(port + RTL_PORT_CMD) & 0x10) != 0);
+ /* Initialise buffers */
+ txBuf = kmalloc(RTL_RX_BUF_SIZE);
+ rxBuf = kmalloc(RTL_TX_BUF_SIZE);
+ /* TODO: use dedicated DMA area */
+ outl(port + RTL_PORT_RBSTART, (uintptr_t) rxBuf);
+ /* IMR & ISR */
+ outw(port + RTL_PORT_IMR, 0x05);
+
+ /* Configure receive buffer */
+ outl(port + RTL_PORT_RCR, (1 << 7) |
+ RTL_CFG_AB | RTL_CFG_AM | RTL_CFG_APM | RTL_CFG_AAP);
+ /* Enable RX and TX */
+ outb(port + RTL_PORT_CMD, 0x0C);
+
+ /* Register interrupt handler */
+ uint8_t irq = pci_read_byte(bus, dev, func, 0x3C);
+ register_interrupt(irq, rtl8139_irq);
+
+ /* Setup Network Interface */
+ netif = net_create_interface(1, rtl8139_transmit, rtl8139_get_mac);
+}
diff --git a/drivers/pci.c b/drivers/pci.c
new file mode 100644
index 0000000..22db76e
--- /dev/null
+++ b/drivers/pci.c
@@ -0,0 +1,55 @@
+/*
+ * This file contains the routines for accessing the PCI devices. It just wraps
+ * the IO functions necessary to read/write the PCI Configuration Space. It
+ * uses PCI Configuration Space Access Mechanism #1, which is the most supported
+ * access mechanism.
+ */
+
+#include <stdint.h>
+#include "pci.h"
+#include "../io.h"
+
+/* Read PCI config */
+uint8_t
+pci_read_byte(int bus, int dev, int func, int off)
+{
+ outl(0xCF8, (1 << 31) | (bus << 16) | (dev << 11) |
+ (func << 8) | (off & 0xFC));
+ return inb(0xCFC + (off & 3));
+}
+uint16_t
+pci_read_word(int bus, int dev, int func, int off)
+{
+ outl(0xCF8, (1 << 31) | (bus << 16) | (dev << 11) |
+ (func << 8) | (off & 0xFC));
+ return inw(0xCFC + (off & 2));
+}
+uint32_t
+pci_read_dword(int bus, int dev, int func, int off)
+{
+ outl(0xCF8, (1 << 31) | (bus << 16) | (dev << 11) |
+ (func << 8) | (off & 0xFC));
+ return inl(0xCFC);
+}
+/* Write PCI config */
+void
+pci_write_byte(int bus, int dev, int func, int off, uint8_t value)
+{
+ outl(0xCF8, (1 << 31) | (bus << 16) | (dev << 11) |
+ (func << 8) | (off & 0xFC));
+ outb(0xCFC + (off & 3), value);
+}
+void
+pci_write_word(int bus, int dev, int func, int off, uint16_t value)
+{
+ outl(0xCF8, (1 << 31) | (bus << 16) | (dev << 11) |
+ (func << 8) | (off & 0xFC));
+ outw(0xCFC + (off & 2), value);
+}
+void
+pci_write_dword(int bus, int dev, int func, int off, uint32_t value)
+{
+ outl(0xCF8, (1 << 31) | (bus << 16) | (dev << 11) |
+ (func << 8) | (off & 0xFC));
+ outl(0xCFC, value);
+}
diff --git a/drivers/pci.h b/drivers/pci.h
new file mode 100644
index 0000000..44b65ae
--- /dev/null
+++ b/drivers/pci.h
@@ -0,0 +1,11 @@
+#ifndef KERNEL_PCI_H
+#define KERNEL_PCI_H
+
+uint8_t pci_read_byte(int bus, int dev, int func, int off);
+uint16_t pci_read_word(int bus, int dev, int func, int off);
+uint32_t pci_read_dword(int bus, int dev, int func, int off);
+void pci_write_byte(int bus, int dev, int func, int off, uint8_t value);
+void pci_write_word(int bus, int dev, int func, int off, uint16_t value);
+void pci_write_dword(int bus, int dev, int func, int off, uint32_t value);
+
+#endif
diff --git a/drivers/tty/font.c b/drivers/tty/font.c
new file mode 100644
index 0000000..bb35673
--- /dev/null
+++ b/drivers/tty/font.c
@@ -0,0 +1,260 @@
+#include <stdint.h>
+
+uint8_t font[4096] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7e, 0x81, 0xa5, 0x81, 0x81, 0xbd, 0x99, 0x81, 0x81, 0x7e, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7e, 0xff, 0xdb, 0xff, 0xff, 0xc3, 0xe7, 0xff, 0xff, 0x7e, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x6c, 0xfe, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0xe7, 0xe7, 0xe7, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x7e, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0x1e, 0x0e, 0x1a, 0x32, 0x78, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x30, 0x30, 0x70, 0xf0, 0xe0, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7f, 0x63, 0x7f, 0x63, 0x63, 0x63, 0x63, 0x67, 0xe7, 0xe6, 0xc0, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x18, 0x18, 0xdb, 0x3c, 0xe7, 0x3c, 0xdb, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfe, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x02, 0x06, 0x0e, 0x1e, 0x3e, 0xfe, 0x3e, 0x1e, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7f, 0xdb, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x7c, 0xc6, 0x60, 0x38, 0x6c, 0xc6, 0xc6, 0x6c, 0x38, 0x0c, 0xc6, 0x7c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x38, 0x7c, 0x7c, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x7c, 0x7c, 0x38, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x66, 0x66, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x7c, 0xc6, 0xc2, 0xc0, 0x7c, 0x06, 0x06, 0x86, 0xc6, 0x7c, 0x18, 0x18, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xc2, 0xc6, 0x0c, 0x18, 0x30, 0x60, 0xc6, 0x86, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x30, 0x30, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x30, 0x18, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3c, 0x66, 0xc3, 0xc3, 0xdb, 0xdb, 0xc3, 0xc3, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x38, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7c, 0xc6, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7c, 0xc6, 0x06, 0x06, 0x3c, 0x06, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x0c, 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x0c, 0x0c, 0x1e, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xfc, 0x06, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x60, 0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xfe, 0xc6, 0x06, 0x06, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x06, 0x06, 0x0c, 0x78, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x0c, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xde, 0xde, 0xde, 0xdc, 0xc0, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x66, 0x66, 0x66, 0x66, 0xfc, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xf8, 0x6c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xde, 0xc6, 0xc6, 0x66, 0x3a, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x1e, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xe6, 0x66, 0x66, 0x6c, 0x78, 0x78, 0x6c, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xf0, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc3, 0xe7, 0xff, 0xff, 0xdb, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0xde, 0x7c, 0x0c, 0x0e, 0x00, 0x00,
+ 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x60, 0x38, 0x0c, 0x06, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xff, 0xdb, 0x99, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xdb, 0xdb, 0xff, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x18, 0x3c, 0x66, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xff, 0xc3, 0x86, 0x0c, 0x18, 0x30, 0x60, 0xc1, 0xc3, 0xff, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x80, 0xc0, 0xe0, 0x70, 0x38, 0x1c, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00,
+ 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xe0, 0x60, 0x60, 0x78, 0x6c, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x1c, 0x0c, 0x0c, 0x3c, 0x6c, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xcc, 0x78, 0x00,
+ 0x00, 0x00, 0xe0, 0x60, 0x60, 0x6c, 0x76, 0x66, 0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x06, 0x06, 0x00, 0x0e, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x66, 0x66, 0x3c, 0x00,
+ 0x00, 0x00, 0xe0, 0x60, 0x60, 0x66, 0x6c, 0x78, 0x78, 0x6c, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0xff, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0x0c, 0x1e, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0x60, 0x38, 0x0c, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x10, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x30, 0x30, 0x36, 0x1c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xdb, 0xdb, 0xff, 0x66, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18, 0x3c, 0x66, 0xc3, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0xf8, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xcc, 0x18, 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x0e, 0x18, 0x18, 0x18, 0x70, 0x18, 0x18, 0x18, 0x18, 0x0e, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x70, 0x18, 0x18, 0x18, 0x0e, 0x18, 0x18, 0x18, 0x18, 0x70, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc0, 0xc2, 0x66, 0x3c, 0x0c, 0x06, 0x7c, 0x00, 0x00,
+ 0x00, 0x00, 0xcc, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x0c, 0x18, 0x30, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x38, 0x6c, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xcc, 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x60, 0x30, 0x18, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x38, 0x6c, 0x38, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x60, 0x60, 0x66, 0x3c, 0x0c, 0x06, 0x3c, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc6, 0x00, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x66, 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x18, 0x3c, 0x66, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x60, 0x30, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xc6, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00,
+ 0x38, 0x6c, 0x38, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x30, 0x60, 0x00, 0xfe, 0x66, 0x60, 0x7c, 0x60, 0x60, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x3b, 0x1b, 0x7e, 0xd8, 0xdc, 0x77, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3e, 0x6c, 0xcc, 0xcc, 0xfe, 0xcc, 0xcc, 0xcc, 0xcc, 0xce, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc6, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x30, 0x78, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x60, 0x30, 0x18, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc6, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0x78, 0x00,
+ 0x00, 0xc6, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xc6, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x18, 0x18, 0x7e, 0xc3, 0xc0, 0xc0, 0xc0, 0xc3, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, 0x60, 0x60, 0xe6, 0xfc, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xfc, 0x66, 0x66, 0x7c, 0x62, 0x66, 0x6f, 0x66, 0x66, 0x66, 0xf3, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x0e, 0x1b, 0x18, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0x70, 0x00, 0x00,
+ 0x00, 0x18, 0x30, 0x60, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x0c, 0x18, 0x30, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x18, 0x30, 0x60, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x18, 0x30, 0x60, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x76, 0xdc, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00,
+ 0x76, 0xdc, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x3c, 0x6c, 0x6c, 0x3e, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x30, 0x30, 0x00, 0x30, 0x30, 0x60, 0xc0, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xc0, 0xc0, 0xc2, 0xc6, 0xcc, 0x18, 0x30, 0x60, 0xce, 0x9b, 0x06, 0x0c, 0x1f, 0x00, 0x00,
+ 0x00, 0xc0, 0xc0, 0xc2, 0xc6, 0xcc, 0x18, 0x30, 0x66, 0xce, 0x96, 0x3e, 0x06, 0x06, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x6c, 0xd8, 0x6c, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x6c, 0x36, 0x6c, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44,
+ 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa,
+ 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x06, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x06, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0xd8, 0xd8, 0xd8, 0xdc, 0x76, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0xd8, 0xcc, 0xc6, 0xc6, 0xc6, 0xcc, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xfe, 0xc6, 0xc6, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xfe, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xfe, 0xc6, 0x60, 0x30, 0x18, 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xc0, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x7e, 0x18, 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x6c, 0x6c, 0x6c, 0xee, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x1e, 0x30, 0x18, 0x0c, 0x3e, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xdb, 0xdb, 0xdb, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x03, 0x06, 0x7e, 0xdb, 0xdb, 0xf3, 0x7e, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x1c, 0x30, 0x60, 0x60, 0x7c, 0x60, 0x60, 0x60, 0x30, 0x1c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x0e, 0x1b, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x7e, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xec, 0x6c, 0x6c, 0x3c, 0x1c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xd8, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x70, 0xd8, 0x30, 0x60, 0xc8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
diff --git a/drivers/tty/tty.c b/drivers/tty/tty.c
new file mode 100644
index 0000000..c65aeaf
--- /dev/null
+++ b/drivers/tty/tty.c
@@ -0,0 +1,424 @@
+/*
+ * This file contains the implementation of the TTY Device. Generates a device
+ * /dev/tty, which displays to the default text video output. This file handles
+ * all the formatting required for a TTY.
+ */
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/fb.h>
+#include <termios.h>
+#include "../drivers.h"
+#include "../../task/task.h"
+#include "../../proc/proc.h"
+#include "../../mem/heap.h"
+#include "../../io.h"
+
+size_t tty_read(File *file, char *buf, size_t size, off_t offset);
+size_t tty_write(File *file, char *buf, size_t size, off_t offset);
+int tty_ioctl(File *file, unsigned long request, uintptr_t argp);
+int tty_open(File *file);
+
+FileOps ttyFileOps = {
+ .read = tty_read,
+ .write = tty_write,
+ .ioctl = tty_ioctl,
+ .open = tty_open,
+};
+
+Driver ttyDriver = {
+ .major = 5,
+ .ops = &ttyFileOps,
+ .next = NULL,
+};
+
+extern uint8_t font[];
+
+Termios tty;
+uintptr_t vgaLfb;
+int vgaWidth, vgaHeight, vgaBpp;
+int ttyMode;
+int ttyX, ttyY;
+int ttyW = 80, ttyH = 25;
+char ttyAttr = 0x07;
+char *lineBuf, *line;
+int lineBufLen = 0, lineLen = 0;
+unsigned char shift = 0, ctrl = 0;
+
+uint32_t colours[] = {
+ 0x000000,
+ 0x0000FF,
+ 0x00FF00,
+ 0x00FFFF,
+ 0xFF0000,
+ 0xFF00FF,
+ 0x884422,
+ 0xCCCCCC,
+ 0x666666,
+ 0x6666FF,
+ 0x66FF66,
+ 0x66FFFF,
+ 0xFF6666,
+ 0xFF66FF,
+ 0xFFFF66,
+ 0xFFFFFF,
+};
+
+TaskQueue ttyWait;
+
+/* Map of keys on the keyboard */
+char kbmap[128] = {
+ 0, 27,
+ '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b',
+ '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n',
+ 0, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`',
+ 0, '#', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', 0,
+ '*', 0, ' ', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ '-', 0, 0, 0, '+', 0, 0, 0, 0, 0, 0, 0, '\\', 0, 0, 0
+};
+char skbmap[128] = {
+ 0, 27,
+ '!', '"', '$', '$', '%', '^', '&', '*', '(', ')', '_', '+', '\b',
+ '\t', 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', '\n',
+ 0, 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '@', '|',
+ 0, '~', 'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<', '>', '?', 0,
+ '*', 0, ' ', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ '-', 0, 0, 0, '+', 0, 0, 0, 0, 0, 0, 0, '|', 0, 0, 0
+};
+
+/* Convert a string to a number */
+static int
+number(char *str)
+{
+ int num = 0, col = 1, len;
+ for (len = 0; str[len] >= '0' && str[len] <= '9'; len++);
+ while (len--) {
+ num += (str[len] - '0') * col;
+ col *= 10;
+ }
+ return num;
+}
+
+/* Put a pixel on the screen */
+static void
+putpixel(int x, int y, uint32_t colour)
+{
+ /* This splits the framebuffer into bytes to be BPP agnostic */
+ uint8_t *screen = (uint8_t *) vgaLfb;
+ unsigned where = x*(vgaBpp/8) + y*vgaWidth*(vgaBpp/8);
+ screen[where + 0] = (colour >> 0) & 0xFF; /* BLUE */
+ screen[where + 1] = (colour >> 8) & 0xFF; /* GREEN */
+ screen[where + 2] = (colour >> 16) & 0xFF; /* RED */
+}
+
+/* Draw a character to the framebuffer using the font */
+static void
+draw_char(unsigned char c, int x, int y, uint32_t fgcolour, uint32_t bgcolour)
+{
+ int cx, cy;
+ unsigned char *glyph = font + ((int) c * 16);
+ for (cy = 0; cy < 16; cy++) {
+ for (cx = 0; cx < 8; cx++) {
+ putpixel(x + 7 - cx, y + cy,
+ (glyph[cy] & (1 << cx)) ? fgcolour : bgcolour);
+ }
+ }
+}
+
+/* Set the ANSI attribute */
+static void
+set_ansi_attribute(int mode, int foreground, int background)
+{
+ char bold;
+
+ switch (mode) {
+ case 0: /* RESET */
+ ttyAttr = 0x07;
+ return;
+ case 1:
+ bold = 1;
+ break;
+ }
+
+ switch (foreground) {
+ case 0: /* DEFAULT */
+ ttyAttr = (ttyAttr & 0xF0) + 7 + (bold ? 8 : 0);
+ break;
+ case 30: /* BLACK */
+ ttyAttr = (ttyAttr & 0xF0) + 0;
+ break;
+ case 31: /* RED */
+ ttyAttr = (ttyAttr & 0xF0) + 4 + (bold ? 8 : 0);
+ break;
+ case 32: /* GREEN */
+ ttyAttr = (ttyAttr & 0xF0) + 2 + (bold ? 8 : 0);
+ break;
+ case 33: /* YELLOW */
+ ttyAttr = (ttyAttr & 0xF0) + 14;
+ break;
+ case 34: /* BLUE */
+ ttyAttr = (ttyAttr & 0xF0) + 1 + (bold ? 8 : 0);
+ break;
+ case 35: /* MAGENTA */
+ ttyAttr = (ttyAttr & 0xF0) + 5 + (bold ? 8 : 0);
+ break;
+ case 36: /* CYAN */
+ ttyAttr = (ttyAttr & 0xF0) + 3 + (bold ? 8 : 0);
+ break;
+ }
+
+}
+
+/* Parse ANSI escape codes */
+static size_t
+ansi_escape(char *buf)
+{
+ int len;
+ char *end;
+ for (len = 0; buf[len] < 'A' || buf[len] > 'z'; len++);
+ end = buf + len + 1;
+ switch (buf[len]) {
+ case 'H':
+ /* Set the cursor position */
+ ttyX = 0;
+ ttyY = 0;
+ break;
+ case 'J':
+ /* 2J clears screen */
+ int jmode = 0;
+ jmode = number(buf);
+ if (jmode == 2) {
+ if (ttyMode == 0)
+ memset((void *) 0xB8000, '\0', ttyW * ttyH * 2);
+ else
+ memset((void *) vgaLfb, '\0',
+ vgaWidth * vgaHeight * (vgaBpp / 8));
+ }
+ break;
+ case 'm':
+ /* Set attribute */
+ int mode = 0, foreground = 0, background = 0;
+ mode = number(buf);
+ while (*buf >= '0' && *buf <= '9') buf++;
+ buf++;
+ if (buf == end)
+ goto set;
+ foreground = number(buf);
+ while (*buf >= '0' && *buf <= '9') buf++;
+ buf++;
+ if (buf == end)
+ goto set;
+ background = number(buf);
+set:
+ set_ansi_attribute(mode, foreground, background);
+ break;
+ }
+
+ return len + 1;
+}
+
+/* Print a character to the screen */
+void
+print_char(char c)
+{
+ char *screen = (char *) 0xB8000;
+
+ if (ttyMode == 0)
+ screen[(((ttyY * ttyW) + ttyX) * 2) + 1] = ttyAttr;
+ else
+ draw_char(' ', ttyX*8, ttyY*16, 0xFFFFFF, 0x000000);
+
+ /* Characters */
+ if (!(tty.lflag & ECHO) && c != '\n')
+ goto curctl;
+ switch (c) {
+ case '\r':
+ ttyX = 0;
+ break;
+ case '\n':
+ ttyX = 0;
+ ttyY++;
+ break;
+ case '\b':
+ ttyX--;
+ if (ttyMode == 0)
+ screen[((ttyY * ttyW) + ttyX) * 2] = ' ';
+ else
+ draw_char(' ', ttyX*8, ttyY*16, colours[ttyAttr & 0xF],
+ colours[ttyAttr >> 4]);
+ break;
+ case '\t':
+ ttyX += 8 - (ttyX % 8);
+ break;
+ default:
+ if (ttyMode == 0)
+ screen[((ttyY * ttyW) + ttyX) * 2] = c;
+ else
+ draw_char(c, ttyX*8, ttyY*16, colours[ttyAttr & 0xF],
+ colours[ttyAttr >> 4]);
+ ttyX++;
+ }
+
+ /* Control cursor */
+curctl:
+ if (ttyX >= ttyW) {
+ ttyX = 0;
+ ttyY++;
+ }
+ if (ttyX < 0) {
+ ttyX = ttyW - 1;
+ ttyY--;
+ }
+ if (ttyY >= ttyH) {
+ if (ttyMode == 0) {
+ memcpy(screen, screen + (ttyW*2), ttyW * (ttyH-1) * 2);
+ memset(screen + (ttyW * 2 * (ttyH-1)), 0, ttyW * 2);
+ } else {
+ memcpy((void *) vgaLfb, (void *) vgaLfb + (vgaWidth*(vgaBpp/8)*16),
+ vgaWidth*(vgaBpp/8)*(vgaHeight-16));
+ memset((void *) vgaLfb + (vgaWidth*(vgaBpp/8)*(vgaHeight-16)),
+ '\0', vgaWidth*(vgaBpp/8)*16);
+ }
+ ttyY--;
+ }
+ if (ttyY < 0)
+ ttyY++;
+
+ if (ttyMode == 0)
+ screen[(((ttyY * ttyW) + ttyX) * 2) + 1] =
+ ((ttyAttr & 0x0F) << 4) + ((ttyAttr >> 4) & 0x0F);
+ else
+ draw_char(' ', ttyX*8, ttyY*16, 0x000000, 0x888888);
+}
+
+/* Handle the keyboard interrupt */
+void
+keyboard_handler(InterruptFrame *frame)
+{
+ char c;
+ unsigned char key;
+ int i;
+ for (i = 0; i < 1000; i++) {
+ if ((inb(0x64) & 1) == 0)
+ continue;
+ key = inb(0x60);
+ break;
+ }
+ if (key == 0x1D)
+ ctrl = 128;
+ if (key == 0x9D)
+ ctrl = 0;
+ if (key == 0x2A || key == 0x36)
+ shift = 128;
+ if (key == 0xAA || key == 0xB6)
+ shift = 0;
+
+ if (key >= 128)
+ return;
+ if (shift)
+ c = skbmap[key];
+ else
+ c = kbmap[key];
+ if (!c)
+ return;
+
+ if (c == '\b' && lineBufLen == 0)
+ return;
+ if (c == '\b')
+ lineBuf[lineBufLen--] = '\0';
+ else
+ lineBuf[lineBufLen++] = c;
+ print_char(c);
+ if (c == '\n') {
+ memcpy(line, lineBuf, lineBufLen);
+ memset(lineBuf, 0, lineBufLen);
+ lineLen = lineBufLen;
+ lineBufLen = 0;
+ for (Task *tmp = ttyWait.start; tmp; tmp = tmp->next)
+ unblock_task(pop_from_queue(&ttyWait));
+ }
+}
+
+/* Initialise the TTY */
+void
+init_tty(void)
+{
+ register_driver(&ttyDriver);
+ mknod("/dev/tty", S_IFCHR | 0666, MKDEV(ttyDriver.major, 0));
+
+ tty.lflag = ICANON | ECHO;
+
+ int fd = open("/dev/fb", O_RDWR);
+ if (fd < 0) {
+ ttyMode = 0;
+ int x, y;
+ uint16_t *screen = (uint16_t *) 0xB8000;
+ for (y = 0; y < 25; y++)
+ for (x = 0; x < 80; x++)
+ screen[(y * 80) + x] = 0x0000;
+ } else {
+ FBVarInfo vinfo;
+ FBFixInfo finfo;
+ ioctl(fd, FBIOGET_VSCREENINFO, &vinfo);
+ ioctl(fd, FBIOGET_FSCREENINFO, &finfo);
+ close(fd);
+ vgaWidth = vinfo.xres;
+ vgaHeight = vinfo.yres;
+ vgaBpp = vinfo.bpp;
+ vgaLfb = finfo.fbmem;
+
+ ttyMode = 1;
+ ttyW = vgaWidth/8;
+ ttyH = vgaHeight/16;
+ }
+
+ lineBuf = kmalloc(4096);
+ line = kmalloc(4096);
+ register_interrupt(1, keyboard_handler);
+}
+
+/* Read from the TTY */
+size_t
+tty_read(File *file, char *buf, size_t size, off_t offset)
+{
+ if (!lineLen) {
+ add_to_queue(&ttyWait, current);
+ block_task(WAITING_FOR_READ);
+ }
+ size_t count = (size < lineLen) ? size : lineLen;
+ memcpy(buf, line, count);
+ if (size < lineLen) {
+ memcpy(line, line + size, lineLen - size);
+ lineLen -= size;
+ } else {
+ memset(line, 0, lineLen);
+ lineLen = 0;
+ }
+ return count;
+}
+
+/* Write to the TTY */
+size_t
+tty_write(File *file, char *buf, size_t size, off_t offset)
+{
+ size_t skip, count = 0;
+ while (size--) {
+ if (buf[0] == '\033' && buf[1] == '[') {
+ skip = ansi_escape(buf + 2);
+ buf += skip + 2;
+ size -= skip + 1;
+ continue;
+ }
+ print_char(*buf++);
+ count++;
+ }
+ return count;
+}
+
+/* Open the TTY */
+int
+tty_open(File *file)
+{
+ return 0;
+}
diff --git a/drivers/tty/tty_ioctl.c b/drivers/tty/tty_ioctl.c
new file mode 100644
index 0000000..5889c66
--- /dev/null
+++ b/drivers/tty/tty_ioctl.c
@@ -0,0 +1,39 @@
+/*
+ * This file implements the TTY ioctl() backend. It stores many of the settings
+ * which are used throughout the rest of the TTY driver.
+ */
+
+#include <stdint.h>
+#include <stddef.h>
+#include <string.h>
+#include <termios.h>
+#include "../../vfs/vfs.h"
+
+extern Termios tty;
+
+extern int ttyW, ttyH;
+extern int vgaWidth, vgaHeight;
+
+int
+tty_ioctl(File *file, unsigned long request, uintptr_t argp)
+{
+ Termios *ts;
+ Winsize *ws;
+ switch (request) {
+ case TCGETS:
+ ts = (void *) argp;
+ memcpy(ts, &tty, sizeof(Termios));
+ return 0;
+ case TCSETS:
+ ts = (void *) argp;
+ memcpy(&tty, ts, sizeof(Termios));
+ return 0;
+ case TCGWINSZ:
+ ws = (void *) argp;
+ ws->rows = ttyH;
+ ws->cols = ttyW;
+ ws->xres = vgaWidth;
+ ws->yres = vgaHeight;
+ return 0;
+ }
+}
diff --git a/drivers/vga/bga.c b/drivers/vga/bga.c
new file mode 100644
index 0000000..aff91aa
--- /dev/null
+++ b/drivers/vga/bga.c
@@ -0,0 +1,131 @@
+/*
+ * This file implements the driver for the Bochs Graphics Adapter video card.
+ * It is a simple card available in virtual machines. It creates a framebuffer
+ * device node, and handles the reading/writing to it, and also mapping the
+ * framebuffer into memory.
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include <sys/fb.h>
+#include "../drivers.h"
+#include "../pci.h"
+#include "../../mem/paging.h"
+#include "../../vfs/vfs.h"
+#include "../../io.h"
+
+size_t bga_write(File *file, char *buf, size_t size, off_t offset);
+int bga_ioctl(File *file, unsigned long request, uintptr_t argp);
+
+FileOps bgaFileOps = {
+ .write = bga_write,
+ .ioctl = bga_ioctl,
+};
+
+Driver bgaDriver = {
+ .major = 3,
+ .ops = &bgaFileOps,
+ .next = NULL,
+};
+
+static uint32_t bgaWidth, bgaHeight, bgaBpp;
+static uintptr_t lfb;
+
+enum BGAIndex {
+ BGA_INDEX_ID,
+ BGA_INDEX_XRES,
+ BGA_INDEX_YRES,
+ BGA_INDEX_BPP,
+ BGA_INDEX_ENABLE,
+ BGA_INDEX_BANK,
+ BGA_INDEX_VIRT_WIDTH,
+ BGA_INDEX_VIRT_HEIGHT,
+ BGA_INDEX_XOFF,
+ BGA_INDEX_YOFF,
+};
+
+/* Write a BGA register */
+void
+bga_write_register(uint16_t index, uint16_t data)
+{
+ outw(0x1CE, index);
+ outw(0x1CF, data);
+}
+
+/* Set the video mode */
+void
+bga_set_mode(uint32_t width, uint32_t height, uint32_t bpp)
+{
+ bgaWidth = width;
+ bgaHeight = height;
+ bgaBpp = bpp;
+ bga_write_register(BGA_INDEX_ENABLE, 0);
+ bga_write_register(BGA_INDEX_XRES, width);
+ bga_write_register(BGA_INDEX_YRES, height);
+ bga_write_register(BGA_INDEX_BPP, bpp);
+ bga_write_register(BGA_INDEX_ENABLE, 1 | 0x40);
+ /* 0x40 for framebuffer */
+}
+
+/* Initialise the BGA card */
+void
+bga_init(uint8_t bus, uint8_t slot, uint8_t func)
+{
+ register_driver(&bgaDriver);
+
+ const int width = 1280;
+ const int height = 720;
+ const int bpp = 32;
+ bga_set_mode(width, height, bpp);
+ lfb = pci_read_dword(bus, slot, func, 0x10) & 0xFFFFFFF0;
+ for (uintptr_t i = 0; i < width*height*(bpp/8); i += 0x1000)
+ *get_page((void *) lfb + i) = (0xFD000000 + i) | PTE_PRESENT
+ | PTE_WRITE | PTE_GLOBAL;
+ /* use the MMAP method to map pages of it into a page dir */
+ mknod("/dev/fb", S_IFCHR | 0600, MKDEV(bgaDriver.major, 0));
+}
+
+/* Write to the BGA framebuffer */
+size_t
+bga_write(File *file, char *buf, size_t size, off_t offset)
+{
+ size_t max, count = 0;
+ page_t oldpg;
+ while (size) {
+ max = (size < 0x1000) ? size : 0x1000;
+ acquire(&quickPageLock);
+ oldpg = quick_page(lfb + PG_ADDR(offset) + count);
+ memcpy(QUICK_PAGE, buf + count, max);
+ quick_page(oldpg);
+ release(&quickPageLock);
+ count += max;
+ size -= max;
+ }
+ return count;
+}
+
+/* Control the BGA framebuffer/device */
+int
+bga_ioctl(File *file, unsigned long request, uintptr_t argp)
+{
+ FBVarInfo *vinfo;
+ FBFixInfo *finfo;
+
+ switch (request) {
+ case FBIOGET_VSCREENINFO:
+ vinfo = (void *) argp;
+ vinfo->xres = bgaWidth;
+ vinfo->yres = bgaHeight;
+ vinfo->bpp = bgaBpp;
+ return 0;
+ case FBIOPUT_VSCREENINFO:
+ vinfo = (void *) argp;
+ bga_set_mode(vinfo->xres, vinfo->yres, vinfo->bpp);
+ return 0;
+ case FBIOGET_FSCREENINFO:
+ finfo = (void *) argp;
+ finfo->fbmem = 0xFD000000; // lfb;
+ finfo->fbmemLen = bgaWidth*bgaHeight*(bgaBpp/8);
+ return 0;
+ }
+}
diff --git a/io.h b/io.h
new file mode 100644
index 0000000..c7ac8d3
--- /dev/null
+++ b/io.h
@@ -0,0 +1,98 @@
+#ifndef KERNEL_IO_H
+#define KERNEL_IO_H
+
+#include <stdint.h>
+#include <stddef.h>
+
+/* Read byte from port */
+static inline uint8_t
+inb(uint16_t port)
+{
+ uint8_t value;
+ asm volatile("inb %w1, %0" : "=a" (value) : "Nd" (port));
+ return value;
+}
+/* Write byte from port */
+static inline void
+outb(uint16_t port, uint8_t value)
+{
+ asm volatile("outb %b0, %w1" : : "a" (value), "Nd" (port));
+}
+
+/* Read byte from port */
+static inline uint16_t
+inw(uint16_t port)
+{
+ uint16_t value;
+ asm volatile("inw %w1, %0" : "=a" (value) : "Nd" (port));
+ return value;
+}
+/* Write byte from port */
+static inline void
+outw(uint16_t port, uint16_t value)
+{
+ asm volatile("outw %w0, %w1" : : "a" (value), "Nd" (port));
+}
+
+/* Read byte from port */
+static inline uint32_t
+inl(uint16_t port)
+{
+ uint32_t value;
+ asm volatile("inl %1, %0" : "=a" (value) : "Nd" (port));
+ return value;
+}
+/* Write byte from port */
+static inline void
+outl(uint16_t port, uint32_t value)
+{
+ asm volatile("outl %0, %1" : : "a" (value), "Nd" (port));
+}
+
+/* Wait for IO to be ready */
+static inline void
+io_wait(void)
+{
+ outb(0x80, 0);
+}
+
+/* Read words into buffer */
+static inline void
+insw(uint16_t port, void *addr, size_t cnt)
+{
+ asm volatile(
+ "cld;"
+ "repne; insw;"
+ : "=D" (addr), "=c" (cnt)
+ : "d" (port), "0" (addr), "1" (cnt)
+ : "memory", "cc"
+ );
+}
+
+/* Write words out from buffer */
+static inline void
+outsw(uint16_t port, void *addr, size_t cnt)
+{
+ asm volatile(
+ "cld;"
+ "repne; outsw;"
+ : "=D" (addr), "=c" (cnt)
+ : "d" (port), "0" (addr), "1" (cnt)
+ : "memory", "cc"
+ );
+}
+
+/* Read dwords into buffer */
+static inline void
+insl(uint16_t port, void *addr, size_t cnt)
+{
+ asm volatile(
+ "cld;"
+ "repne; insl;"
+ : "=D" (addr), "=c" (cnt)
+ : "d" (port), "0" (addr), "1" (cnt)
+ : "memory", "cc"
+ );
+}
+
+#endif
diff --git a/linker.ld b/linker.ld
new file mode 100644
index 0000000..ac4b5cd
--- /dev/null
+++ b/linker.ld
@@ -0,0 +1,35 @@
+ENTRY(_start)
+
+SECTIONS {
+
+ .text 1M :
+ {
+ KEEP(*(.multiboot))
+ _code = .;
+ *(.text*)
+ . = ALIGN(4096);
+ }
+
+ .data :
+ {
+ _data = .;
+ *(.rodata*)
+ *(.data*)
+ . = ALIGN(4096);
+ }
+
+ .bss :
+ {
+ _bss = .;
+ *(.bss)
+ *(COMMON)
+ . = ALIGN(4096);
+ }
+
+ _end = .;
+
+ /DISCARD/ :
+ {
+ *(.comment)
+ }
+}
diff --git a/main.c b/main.c
new file mode 100644
index 0000000..d4e1421
--- /dev/null
+++ b/main.c
@@ -0,0 +1,118 @@
+/*
+ * This file contains the entrance point for the Kernel proper. The code here
+ * is called by the bootloader when it finishes. The main routine is
+ * responsible for setting up memory and starting the processes running.
+ * This file is also responsible for performing per-CPU setups, which is done
+ * before the Kernel starts multi-tasking.
+ */
+
+#include <stdint.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include "mem/frame.h"
+#include "mem/paging.h"
+#include "mem/heap.h"
+#include "mem/mem.h"
+#include "proc/proc.h"
+#include "task/task.h"
+#include "vfs/vfs.h"
+#include "drivers/drivers.h"
+#include "net/net.h"
+#include "io.h"
+#include "screen.h"
+#include "spinlock.h"
+#include "multiboot.h"
+
+#include <sys/mount.h>
+
+void init_tty(void);
+
+extern char _bss[], _end[];
+
+uintptr_t initialStack;
+void *initrd;
+
+/* Setup current CPU */
+void
+cpu_load(void)
+{
+ /* Initialise SSE */
+ asm volatile(
+ "mov %cr0, %eax;"
+ "andl $0xfffffffb, %eax;"
+ "orl $0x2, %eax;"
+ "mov %eax, %cr0;"
+ "mov %cr4, %eax;"
+ "orl $0x600, %eax;"
+ "mov %eax, %cr4;"
+ );
+
+ /* Initialise CR4.PGE */
+ asm volatile(
+ "mov %cr4, %eax;"
+ "orl $0x10, %eax;"
+ "mov %eax, %cr4;"
+ );
+
+ /* Tables */
+ cpu_load_idt();
+ cpu_load_gdt();
+}
+
+/* Kernel main function */
+void
+kmain(uint32_t esp, struct MultibootInfo *mbinfo)
+{
+ /* Disable the cursor */
+ outb(0x3D4, 0x0A);
+ outb(0x3D5, 0x20);
+
+ kprintf("Starting the Orion Operating System");
+ void *ebda = (void *) (*((uint16_t *) 0x040E) << 4);
+ initrd = (void *) *((uint32_t *) mbinfo->modsAddr);
+
+ /* Zero-fill the BSS */
+ memset(_bss, 0, _end - _bss);
+
+ /* Setup frame allocator */
+ init_frames(mbinfo->mmapLen, (void *) mbinfo->mmapAddr);
+
+ /* Processor startup */
+ initialStack = esp;
+ init_idt();
+ init_gdt();
+ init_pic();
+ cpu_load();
+ init_multicore(ebda);
+
+ /* Check if 64-bit CPU */
+ uint32_t lm;
+ asm("cpuid" : "=d" (lm) : "a" (0x80000001));
+ if ((lm >> 29) & 1)
+ kprintf("CPU supports 64-bit mode");
+
+ /* Initialise paging */
+ init_paging();
+ /* Initialise Multitasking */
+ init_tasking();
+ /* Initialise VFS */
+ init_vfs();
+ register_exception(14, page_fault_handler);
+ /* Search for devices */
+ init_drivers();
+ init_tty();
+ /* Initialise Networking */
+// init_net();
+
+ /* Mount root disk */
+ mkdir("root", 0);
+ mount("/dev/hda", "/root", "Ext2FS", MS_RDONLY, NULL);
+ chroot("/root");
+ chdir("/");
+ mount("devfs", "/dev", "DevFS", 0, NULL);
+ mount("procfs", "/proc", "ProcFS", 0, NULL);
+
+ char *argv[] = { "init", NULL };
+ execve("/bin/init", argv, NULL);
+ panic("Could not run init");
+}
diff --git a/mem/frame.c b/mem/frame.c
new file mode 100644
index 0000000..ee88fd8
--- /dev/null
+++ b/mem/frame.c
@@ -0,0 +1,207 @@
+/*
+ * This file handles allocating and freeing of page frames. It it the core of
+ * the physical memory manager. The Kernel heap and paging systems both sit on
+ * top of this. It keeps track of which frames of physical memory are free and
+ * hands them out as needed. To do this it keeps a simple bitmap of the frames.
+ */
+
+#include <stdint.h>
+#include "mem.h"
+#include "paging.h"
+#include "frame.h"
+#include "../screen.h"
+
+#define INDEX(a) ((a)/32)
+#define OFFSET(a) ((a)%32)
+
+/* Descriptor of a Frame Region */
+typedef struct FrameRegion FrameRegion;
+struct FrameRegion {
+ uint32_t base;
+ size_t numFrames;
+ size_t usedFrames;
+ FrameRegion *next;
+ uint32_t bitmap[];
+};
+
+size_t numFrames, usedFrames;
+FrameRegion *regions;
+
+/* Count the number of pages a number of bytes occupies */
+static size_t
+page_count(size_t bytes)
+{
+ size_t pages = bytes/0x1000;
+ if (bytes & 0xFFF)
+ pages++;
+ return pages;
+}
+
+/* Set a bit in the frame bitset */
+static void
+set_frame(FrameRegion *region, uint32_t idx)
+{
+ region->bitmap[INDEX(idx)] |= (1 << OFFSET(idx));
+ region->usedFrames++;
+ usedFrames++;
+}
+
+/* Clear a bit in the frame bitset */
+static void
+clear_frame(FrameRegion *region, uint32_t idx)
+{
+ region->bitmap[INDEX(idx)] &= ~(1 << OFFSET(idx));
+ region->usedFrames--;
+ usedFrames--;
+}
+
+/* Test a bit in the frame bitset */
+static uint32_t
+test_frame(FrameRegion *region, uint32_t idx)
+{
+ return (region->bitmap[INDEX(idx)] & (1 << OFFSET(idx)));
+}
+
+/* Get n bits from position p */
+static inline uint32_t
+get_bits(uint32_t x, uint8_t p, uint8_t n)
+{
+ return (x >> (p+1-n)) & ~(~0 << n);
+}
+
+/* Find first free frame */
+static uint32_t
+find_frames(FrameRegion *region, size_t frames)
+{
+ /* TODO: best fit */
+ /* FIXME: frames can be at most 32 */
+ uint32_t i, j;
+ for (i = 0; i < INDEX(region->numFrames); i++) {
+ if (!~region->bitmap[i])
+ continue;
+ for (j = 0; j < 32; j++) {
+ if (get_bits(~region->bitmap[i], j, frames) == ~(~0 <<
+ frames))
+ return (i*32+j)-frames+1;
+ /* TODO: check across uint32_t boundaries */
+ }
+ }
+ return (uint32_t) -1;
+}
+
+/* Allocate a set of contiguous page frames */
+uint32_t
+alloc_frames(size_t frames)
+{
+ uint32_t idx;
+ size_t i;
+ /* Walk the regions, first fit */
+ FrameRegion *region;
+ for (region = regions; region; region = region->next) {
+ idx = find_frames(region, frames);
+ if (idx != -1)
+ break;
+ }
+ if (idx == -1)
+ return -1;
+
+ for (i = 0; i < frames; i++)
+ set_frame(region, idx + i);
+
+ return (uint32_t) region->base + (idx << 12);
+}
+
+/* Free a page frame */
+void
+free_frame(uint32_t frame)
+{
+ /* Walk the regions */
+ FrameRegion *region = regions;
+ while (region) {
+ if ((uint32_t) region > frame) {
+ frame -= region->base;
+ clear_frame(region, frame >> 12);
+ break;
+ }
+ region = region->next;
+ }
+}
+
+/* Setup the frame allocator */
+void
+init_frames(uint32_t memMapSize, struct E820Entry *memMap)
+{
+ /* Relocate the memory map */
+ struct E820Entry *top = (void *) 0x20000;
+ if (memMap < top) {
+ memcpy(top, memMap, memMapSize);
+ memMap = (void *) top;
+ }
+
+ /*
+ * When the OS starts, the bootloader passes the Kernel a map of memory.
+ * This map must be read, so the memory manager can avoid bad areas of
+ * memory, that as mapped to hardware, ACPI, etc.
+ * The frame allocator should only create a bitmap for frames that it
+ * can actually allocate. This means setting avoiding bad areas, and
+ * avoiding the Kernel.
+ */
+ uint32_t i, j;
+ FrameRegion *prev = 0, *head = regions;
+ uint32_t bumpAlloc = 0x1000;
+ for (i = 0; i < memMapSize / sizeof(struct E820Entry); i++) {
+ kprintf("MemMap: base=%#.8x%.8x, length=%#.8x%.8x, type=%d",
+ memMap[i].baseHigh, memMap[i].base,
+ memMap[i].lengthHigh, memMap[i].length,
+ memMap[i].type);
+ if (memMap[i].baseHigh > 0
+ || memMap[i].base > 0xFFFFFFFF
+ || memMap[i].type != 1)
+ continue;
+
+ /* Usable region - create bitmap */
+ size_t frameSize = memMap[i].length / 0x1000;
+ size_t bitmapSize = (frameSize / 8);
+ head = (FrameRegion *) bumpAlloc;
+ bumpAlloc += bitmapSize + sizeof(FrameRegion);
+ head->base = memMap[i].base;
+ head->numFrames = frameSize;
+ head->usedFrames = 0;
+ memset(head->bitmap, 0, bitmapSize);
+ /* Set top bits to 1, so they're never allocated */
+ for (j = OFFSET(head->numFrames); j < 32; j++)
+ set_frame(head, head->numFrames + j);
+ if (prev) prev->next = head;
+ else regions = head;
+ prev = head;
+ }
+
+ /* Regions to be remapped */
+ struct {uint32_t start, end;} remaps[] = {
+ {.start = 0x0000, .end = bumpAlloc}, /* PMM bitmaps */
+ {.start = 0xB0000, .end = 0xC0000 }, /* VGA memory */
+ {.start = 0x100000, .end = 0x180000 }, /* Kernel */
+ };
+
+ kprintf("Bump allocator top @ %#.8x", bumpAlloc);
+
+ /* Check bitmaps */
+ usedFrames = 0;
+ numFrames = 0;
+ FrameRegion *region = regions;
+ uint32_t regionEnd;
+ while (region) {
+ numFrames += region->numFrames;
+ regionEnd = region->base + (region->numFrames * 0x1000);
+ /* Iterate the remaps[] to find overlapping regions */
+ for (i = 0; i < sizeof(remaps)/sizeof(remaps[0]); i++)
+ for (j = remaps[i].start; j < remaps[i].end
+ && j >= region->base && j < regionEnd;
+ j += 0x1000)
+ set_frame(region, (j - region->base) >> 12);
+ region = region->next;
+ }
+
+ if (numFrames < 1024) /* 4MB */
+ panic("Not enough memory");
+}
diff --git a/mem/frame.h b/mem/frame.h
new file mode 100644
index 0000000..ba68567
--- /dev/null
+++ b/mem/frame.h
@@ -0,0 +1,25 @@
+#ifndef KERNEL_MEM_FRAME_H
+#define KERNEL_MEM_FRAME_H
+
+#include <stdint.h>
+#include <stddef.h>
+
+/* Structure of an E820 entry */
+struct E820Entry {
+ uint32_t size;
+ uint32_t base, baseHigh;
+ uint32_t length, lengthHigh;
+ uint32_t type;
+} __attribute__((packed));
+
+/* Structure of an E820 Entry */
+//struct E820Entry {
+// uint64_t base, length;
+// uint32_t type, attr;
+//} __attribute__((packed));
+
+uint32_t alloc_frames(size_t frames);
+void free_frame(uint32_t frame);
+void init_frames(uint32_t memMapSize, struct E820Entry *memMap);
+
+#endif
diff --git a/mem/heap.c b/mem/heap.c
new file mode 100644
index 0000000..9f9bbe2
--- /dev/null
+++ b/mem/heap.c
@@ -0,0 +1,125 @@
+/*
+ * This file contains the functions related to the Kernel's heap. It uses a
+ * simple method of allocating and freeing blocks from a pool in the Kernel's
+ * memory space. This heap will be present in every Page Directory, so can be
+ * used to store any Kernel data structures.
+ */
+
+#include <stdint.h>
+#include <stddef.h>
+#include "mem.h"
+#include "heap.h"
+#include "frame.h"
+#include "../vfs/cache.h"
+#include "../screen.h"
+#include "../spinlock.h"
+
+#define KHEAP_START 0x200000 /* 2 MB */
+#define KHEAP_END 0x800000 /* 8 MB */
+#define BLOCK_SIZE 16
+
+/* Structure for a Memory Header */
+typedef struct Header {
+ struct Header *next, *prev;
+ size_t size;
+ char magic[4];
+} Header; /* 16 bytes */
+
+size_t objectsAllocated = 0;
+size_t freeSpace = KHEAP_END - KHEAP_START;
+
+/* Allocate a region of the heap */
+void *
+kmalloc(size_t size)
+{
+ size_t blockSize, gapSize;
+ uintptr_t blockEnd;
+ Header *prev, *head, *next;
+ head = prev = (void *) KHEAP_START;
+ next = NULL;
+ objectsAllocated++;
+
+ /* Minimum allocation */
+ if (size % BLOCK_SIZE)
+ size += BLOCK_SIZE - (size % BLOCK_SIZE);
+ freeSpace -= size + sizeof(Header);
+
+ /* Low-memory VFS cache reaper */
+ if (freeSpace < 0x10000) /* 64 KB */
+ cache_reaper();
+
+ /* Block does not exist, create heap */
+ if (head->prev != head) {
+ head->prev = head;
+ head->next = NULL;
+ head->size = size;
+ memcpy(head->magic, "HEAP", 4);
+ memset((void *) (head + 1), 0, size);
+ return (void *) (head + 1);
+ }
+
+ /* Find gap */
+ while (head->next) {
+ next = head->next;
+ blockSize = sizeof(Header) + head->size;
+ blockEnd = (uintptr_t) head + blockSize;
+ gapSize = (size_t) next - blockEnd;
+ prev = head;
+
+ /* Fit in gap */
+ if (gapSize >= size + sizeof(Header)) {
+ head = (void *) blockEnd;
+ head->next = next;
+ head->prev = prev;
+ prev->next = head;
+ next->prev = head;
+ head->size = size;
+ memcpy(head->magic, "HEAP", 4);
+ memset((void *) (head + 1), 0, size);
+ return (void *) (head + 1);
+ }
+
+ head = head->next;
+ }
+
+ /* Add to end */
+ blockSize = sizeof(Header) + head->size;
+ blockEnd = (uintptr_t) head + blockSize;
+ gapSize = (size_t) KHEAP_END - blockEnd;
+ /* Fit in gap */
+ if (gapSize >= size + sizeof(Header)) {
+ prev = head;
+ head = (void *) blockEnd;
+ head->next = NULL;
+ head->prev = prev;
+ prev->next = head;
+ head->size = size;
+ memcpy(head->magic, "HEAP", 4);
+ memset((void *) (head + 1), 0, size);
+ return (void *) (head + 1);
+ }
+
+ panic("Kernel heap exhausted");
+}
+
+/* Free an allocated region of the heap */
+void
+_kfree(void *addr, char *file, int line)
+{
+ Header *prev, *head, *next;
+ head = (Header *) addr - 1;
+ objectsAllocated--;
+ freeSpace += head->size + sizeof(Header);
+
+ if (memcmp(head->magic, "HEAP", 4))
+ panic("Bad Kernel heap reference\n"
+ "Invalid target @ %#.8x (%s:%d)", addr, file, line);
+ prev = head->prev;
+ next = head->next;
+ memset(head, 0, sizeof(Header));
+
+ if (prev != head)
+ prev->next = next;
+ if (next)
+ next->prev = prev;
+}
diff --git a/mem/heap.h b/mem/heap.h
new file mode 100644
index 0000000..1923583
--- /dev/null
+++ b/mem/heap.h
@@ -0,0 +1,11 @@
+#ifndef KERNEL_MEM_HEAP_H
+#define KERNEL_MEM_HEAP_H
+
+#include <stdint.h>
+#include <stddef.h>
+
+void *kmalloc(size_t size);
+void _kfree(void *addr, char *file, int line);
+#define kfree(a) _kfree(a, __FILE__, __LINE__)
+
+#endif
diff --git a/mem/mem.c b/mem/mem.c
new file mode 100644
index 0000000..80a7c89
--- /dev/null
+++ b/mem/mem.c
@@ -0,0 +1,106 @@
+/*
+ * This file contains a few routines for the manipulation of memory and strings.
+ * The functions would normally be part of a C library, but this is for the
+ * Kernel. The functions are standalone, and have no dependencies - they can be
+ * called immediately after boot.
+ */
+
+#include <stdint.h>
+#include <stddef.h>
+
+/* Fill a region of memory with the specified byte */
+void *
+memset(void *s, int c, size_t n)
+{
+ unsigned char *a = s;
+ if (n > 0) {
+ while (n-- > 0)
+ *a++ = c;
+ }
+ return s;
+}
+
+/* Copy one region of memory to another */
+void *
+memcpy(void *dest, void *src, size_t n)
+{
+ unsigned char *a = (unsigned char *) dest,
+ *b = (unsigned char *) src;
+ while (n-- > 0)
+ *a++ = *b++;
+ return dest;
+}
+
+/* Compare two regions of memory */
+int
+memcmp(void *s1, void *s2, size_t n)
+{
+ unsigned char *a = (unsigned char *) s1,
+ *b = (unsigned char *) s2;
+ while (n-- > 0)
+ if (*a++ != *b++)
+ return a[-1] - b[-1];
+ return 0;
+}
+
+/* Find the length of a string */
+size_t
+strlen(char *s)
+{
+ if (!s)
+ return 0;
+ size_t i;
+ for (i = 0; s[i]; i++);
+ return i;
+}
+
+/* Find the length of a string up to maximum */
+size_t
+strnlen(char *s, size_t maxlen)
+{
+ if (!s)
+ return 0;
+ size_t i;
+ for (i = 0; s[i] && i <= maxlen; i++);
+ return i;
+}
+
+/* Compare two strings */
+int
+strcmp(char *s1, char *s2)
+{
+ for (; *s1 == *s2 && *s1 && *s2; s1++, s2++);
+ return *(unsigned char *) s1 - *(unsigned char *) s2;
+}
+
+/* Compare two limited strings */
+int
+strncmp(char *s1, char *s2, size_t n)
+{
+ if (!n--) return 0;
+ for (; *s1 == *s2 && *s1 && *s2 && n; s1++, s2++, n--);
+ return *(unsigned char *) s1 - *(unsigned char *) s2;
+}
+
+/* Copy a string */
+char *
+strcpy(char *dest, const char *src)
+{
+ char *ret = dest;
+ while (*src)
+ *dest++ = *src++;
+ *dest = '\0';
+ return ret;
+}
+
+/* Copy a limited string */
+char *
+strncpy(char *dest, const char *src, size_t n)
+{
+ char *ret = dest;
+ while (*src && n--)
+ *dest++ = *src++;
+ *dest = '\0';
+ return ret;
+}
+
diff --git a/mem/mem.h b/mem/mem.h
new file mode 100644
index 0000000..fa6dfc4
--- /dev/null
+++ b/mem/mem.h
@@ -0,0 +1,17 @@
+#ifndef KERNEL_MEM_H
+#define KERNEL_MEM_H
+
+#include <stdint.h>
+#include <stddef.h>
+
+void *memset(void *s, int c, size_t n);
+void *memcpy(void *dest, void *src, size_t n);
+int memcmp(void *s1, void *s2, size_t n);
+size_t strlen(char *s);
+size_t strnlen(char *s, size_t maxlen);
+int strcmp(const char *s1, const char *s2);
+int strncmp(const char *s1, const char *s2, size_t n);
+char *strcpy(char *dest, const char *src);
+char *strncpy(char *dest, const char *src, size_t n);
+
+#endif
diff --git a/mem/page.S b/mem/page.S
new file mode 100644
index 0000000..5e1dd4a
--- /dev/null
+++ b/mem/page.S
@@ -0,0 +1,60 @@
+; This file implements some simple low level assembly helper functions for the
+; paging system. The functions are done in assembly for the speed benefit.
+; They may be called frequently by the memory manager, so the benefit will be
+; significant.
+
+[bits 32]
+
+; Enable paging
+[global enable_paging]
+enable_paging:
+ mov edx, cr0
+ or edx, 0x80000000
+ mov cr0, edx
+ ret
+
+; Disable paging
+[global disable_paging]
+disable_paging:
+ mov edx, cr0
+ and edx, 0x7FFFFFFF
+ mov cr0, edx
+ ret
+
+; Copy the contents of a page frame
+[global copy_page_frame]
+copy_page_frame:
+; push ebx
+; pushf
+; mov esi, [esp + 12]
+; mov edi, [esp + 16]
+; call disable_paging
+; mov ecx, 1024
+; rep movsd
+; call enable_paging
+; popf
+; pop ebx
+; ret
+ push ebx
+ pushf
+ cli
+ mov ebx, [esp + 12]
+ mov ecx, [esp + 16]
+ mov edx, cr0
+ and edx, 0x7FFFFFFF
+ mov cr0, edx
+ mov edx, 1024
+.loop:
+ mov eax, [ebx]
+ mov [ecx], eax
+ add ebx, 4
+ add ecx, 4
+ dec edx
+ jnz .loop
+ mov edx, cr0
+ or edx, 0x80000000
+ mov cr0, edx
+ sti
+ popf
+ pop ebx
+ ret
diff --git a/mem/pagefault.c b/mem/pagefault.c
new file mode 100644
index 0000000..14219b7
--- /dev/null
+++ b/mem/pagefault.c
@@ -0,0 +1,264 @@
+/*
+ * This is the page fault handler. It handles/dispatches all handlers for page
+ * faults. This includes various tasking functions.
+ */
+
+#include <stdint.h>
+#include <signal.h>
+#include "paging.h"
+#include "../vfs/cache.h"
+#include "../vfs/inode.h"
+#include "../vfs/tmpfs/fs.h"
+#include "../mem/heap.h"
+#include "../mem/mem.h"
+#include "../task/task.h"
+#include "../proc/proc.h"
+#include "../screen.h"
+
+extern size_t numFrames, usedFrames;
+
+void copy_page_frame(void *src, void *dest);
+
+/* Copy-On-Write */
+static void
+copy_on_write(VMRegion *region, uintptr_t addr)
+{
+ Page *page = NULL;
+ File *front = region->front,
+ *back = region->back;
+ off_t offset = ((addr & ~0xFFF) - region->start) + region->offset;
+ page_t *pg = get_page((void *) addr);
+
+ /* Create front if it doesn't exist and is needed */
+ uint8_t private = region->flags & MAP_PRIVATE;
+ uint8_t sharedanon = (region->flags & MAP_SHARED) &&
+ (region->flags & MAP_ANONYMOUS);
+ uint8_t created = 0;
+ if (!front && (private || sharedanon)) {
+ /*
+ * A private mapping will always write to the front. A shared
+ * mapping will write to the back. If a shared mapping is
+ * anonymous, then the back is the front. The front must be
+ * created if it is required - which means if the mapping is
+ * private, or if the mapping is shared & anonymous.
+ */
+ front = kmalloc(sizeof(File));
+ front->inode = inode_get(kmalloc(sizeof(Inode)));
+ front->ops = &tmpfsFileOps;
+ region->front = file_get(front);
+ created++;
+ }
+
+ /* Find original page frame */
+ Inode *inode;
+ if (!page && front) {
+ inode = front->inode;
+ ASSERT(inode);
+ page = page_find(inode, offset);
+ }
+ if (!page && back) {
+ inode = back->inode;
+ ASSERT(inode);
+ page = page_find(inode, offset);
+ }
+ ASSERT(page);
+
+ /* Copy already happened, just link */
+ if (page->usage == 1 && page->frame != zeroFrame) {
+ *pg |= PTE_WRITE;
+ return;
+ }
+ /* Put that page, and create a new one */
+ *pg = 0;
+ alloc_page(pg, PTE_PRESENT | PTE_USER | PTE_WRITE, -1);
+ copy_page_frame((void *) PG_ADDR(page->frame),
+ (void *) PG_ADDR(*pg));
+ page_remove(inode, page);
+ page = page_create(front->inode, PG_ADDR(*pg), offset);
+}
+
+/* Handle a not-present read page fault */
+static void
+not_present_read(VMRegion *region, uintptr_t addr)
+{
+ Page *page;
+ File *front = region->front,
+ *back = region->back;
+ off_t offset = ((addr & ~0xFFF) - region->start) + region->offset;
+ page_t *pg = get_page((void *) addr);
+
+ /* Handle uninitialised anonymous regions */
+ if (!front && (region->flags & MAP_ANONYMOUS)) {
+ front = kmalloc(sizeof(File));
+ front->inode = inode_get(kmalloc(sizeof(Inode)));
+ front->ops = &tmpfsFileOps;
+ region->front = file_get(front);
+ }
+
+ /* Attempt to use front */
+ if (front) {
+ page = page_find(front->inode, offset);
+ if (page) {
+ page_get(page);
+ alloc_page(pg, PTE_PRESENT | PTE_USER, page->frame);
+ return;
+ }
+ if (region->flags & MAP_ANONYMOUS) {
+ /* Must be anonymous, zero-fill */
+ alloc_page(pg, PTE_PRESENT | PTE_USER, zeroFrame);
+ page_create(front->inode, zeroFrame, offset);
+ return;
+ }
+ }
+
+ /* Use back */
+ ASSERT(back);
+ page = page_find(back->inode, offset);
+ if (page) {
+ page_get(page);
+ alloc_page(pg, PTE_PRESENT | PTE_USER, page->frame);
+ return;
+ }
+ /* Create new block cache entry */
+ alloc_page(pg, PTE_PRESENT | PTE_USER, -1);
+ file_mmap(back, (void *) PG_ADDR(addr), 0x1000, offset);
+ page_create(back->inode, PG_ADDR(*pg), offset);
+}
+
+/* Handle a not-present write page fault */
+static void
+not_present_write(VMRegion *region, uintptr_t addr)
+{
+ Page *page = NULL;
+ File *front = region->front,
+ *back = region->back;
+ off_t offset = ((addr & ~0xFFF) - region->start) + region->offset;
+ page_t *pg = get_page((void *) addr);
+
+ /* Handle uninitialised anonymous regions */
+ if (!front && ((region->flags & MAP_PRIVATE)
+ || (region->flags & MAP_ANONYMOUS))) {
+ /*
+ * This applies to all private regions, anonymous or not.
+ * Unless the region is shared, the process should write to the
+ * front, which will be the private copy. If the region is
+ * shared, and also anonymous, then the write will occur to the
+ * front too.
+ */
+ front = kmalloc(sizeof(File));
+ front->inode = inode_get(kmalloc(sizeof(Inode)));
+ front->ops = &tmpfsFileOps;
+ region->front = file_get(front);
+ }
+
+ /* Shared region, write-through to back */
+ if (region->flags & MAP_SHARED) {
+ if (region->flags & MAP_ANONYMOUS)
+ back = front;
+ ASSERT(back);
+ page = page_find(back->inode, offset);
+ if (page) {
+ page_get(page);
+ alloc_page(pg, PTE_PRESENT | PTE_USER | PTE_WRITE,
+ page->frame);
+ return;
+ }
+ *pg = 0;
+ alloc_page(pg, PTE_PRESENT | PTE_USER | PTE_WRITE, -1);
+ memset((void *) PG_ADDR(addr), 0, 0x1000);
+ page_create(back->inode, PG_ADDR(*pg), offset);
+ return;
+ }
+
+ /* Private region, copy to front */
+ alloc_page(pg, PTE_PRESENT | PTE_USER | PTE_WRITE, -1);
+ if (front)
+ page = page_find(front->inode, offset);
+ if (page) {
+ copy_page_frame((void *) PG_ADDR(page->frame),
+ (void *) PG_ADDR(*pg));
+ page_remove(front->inode, page);
+ page_create(front->inode, PG_ADDR(*pg), offset);
+ return;
+ }
+
+ /* Anonymous region, zero-fill */
+ if (region->flags & MAP_ANONYMOUS) {
+ memset((void *) PG_ADDR(addr), 0, 0x1000);
+ page_create(front->inode, PG_ADDR(*pg), offset);
+ return;
+ }
+
+ /* Use back */
+ ASSERT(back);
+ page = page_find(back->inode, offset);
+ if (page) {
+ copy_page_frame((void *) PG_ADDR(page->frame),
+ (void *) PG_ADDR(*pg));
+ page_remove(back->inode, page);
+ } else {
+ file_mmap(back, (void *) PG_ADDR(addr), 0x1000, offset);
+ }
+ page_create(front->inode, PG_ADDR(*pg), offset);
+}
+
+/* Page fault handler */
+void
+page_fault_handler(InterruptFrame *frame)
+{
+ uintptr_t addr;
+ asm volatile("mov %%cr2, %0" : "=r" (addr));
+ uint8_t present = frame->errCode & (1 << 0);
+ uint8_t write = frame->errCode & (1 << 1);
+ uint8_t user = frame->errCode & (1 << 2);
+
+ /* Iterate VM Regions */
+ VMRegion *region;
+ for (region = current->vm->regions; region; region = region->next) {
+ if (region->start <= addr && region->end > addr)
+ break;
+ }
+ if (!region && current->stack) {
+ region = current->stack;
+ if (region->start > addr || region->end <= addr)
+ region = NULL;
+ }
+ if (!region && current->tls) {
+ region = current->tls;
+ if (region->start > addr || region->end <= addr)
+ region = NULL;
+ }
+ /* Not in a region */
+ if (!region) {
+ page_t *pg = get_page((void *) addr);
+ panic("Page Fault [%d:%d] (%#.8x -> %#.8x [tbl:%d, pg:%d][%#.8x]), %s, %s, %s",
+ current->tgid, current->tid, frame->eip,
+ addr, (addr>>12) / 1024, (addr>>12) % 1024, *pg,
+ present ? "present" : "not present",
+ write ? "write" : "read",
+ user ? "user" : "kernel");
+ }
+
+ if (user && write && !(region->prot & PROT_WRITE))
+ return (void) kill(current->tgid, SIGSEGV);
+
+ if (present && write)
+ return copy_on_write(region, addr);
+
+ if (write)
+ return not_present_write(region, addr);
+ else
+ return not_present_read(region, addr);
+}
+
+/* Early (pre-VFS/tasking) page fault handler */
+void
+early_page_fault_handler(InterruptFrame *frame)
+{
+ uintptr_t addr;
+ asm volatile("mov %%cr2, %0" : "=r" (addr));
+ if (!PG_ADDR(addr))
+ panic("Null dereference @ %#.8x", frame->eip);
+ alloc_page(get_page((void *) addr),
+ PTE_PRESENT | PTE_WRITE | PTE_GLOBAL, -1);
+}
diff --git a/mem/paging.c b/mem/paging.c
new file mode 100644
index 0000000..b44e843
--- /dev/null
+++ b/mem/paging.c
@@ -0,0 +1,228 @@
+/*
+ * This file contains all functions used to manipulate the virtual address
+ * spaces. It has a static Kernel page directory and table, which it uses to
+ * initialsed an identity-paged environment for the Kernel to work in. This is
+ * enough for the heap to function in. The file also exposes several functions
+ * that allow a page directory to be manipulated and have pages added and moved.
+ * These functions are used by other components of the Kernel - mostly the heap
+ * and IPC. There are also functions to create new and destroy existing page
+ * directories. The paging system also implements features like copy-on-write.
+ */
+
+#include <stdint.h>
+#include <sys/mman.h>
+#include "frame.h"
+#include "heap.h"
+#include "mem.h"
+#include "paging.h"
+#include "../vfs/vfs.h"
+#include "../proc/proc.h"
+#include "../io.h"
+#include "../screen.h"
+
+Spinlock quickPageLock;
+
+page_table_t kernelDir;
+page_t zeroFrame;
+
+void enable_paging(void);
+void disable_paging(void);
+void copy_page_frame(void *src, void *dest);
+
+/* Switch page directory */
+static void
+switch_dir(page_dir_t dir)
+{
+ asm volatile("mov %0, %%cr3" :: "r" (dir));
+}
+
+/* Allocate a page a frame */
+void
+alloc_page(page_t *page, uint16_t flags, page_t frame)
+{
+ page_t *mappings = (void *) 0xFFC00000;
+ page_table_t *tables = (void *) 0xFFFFF000;
+ if ((tables[(page - mappings) / 1024] & PDE_PRESENT) == 0)
+ return;
+ if (*page & 0xFFFFF000)
+ return;
+
+ if (frame == (page_t) -1)
+ frame = alloc_frames(1);
+ if (frame == (page_t) -1)
+ return;
+
+ *page = frame | flags;
+ flush_tlb((page - mappings) << 12);
+}
+
+/* Release a page's frame */
+void
+free_page(page_t *page)
+{
+ page_t *mappings = (void *) 0xFFC00000;
+ page_table_t *tables = (void *) 0xFFFFF000;
+ if ((tables[(page - mappings) / 1024] & PDE_PRESENT) == 0)
+ return;
+ if ((*page & 0xFFFFF000) == 0)
+ return;
+
+ free_frame(*page & 0xFFFFF000);
+ *page = 0x00000000;
+ flush_tlb((page - mappings) << 12);
+}
+
+/* Get Page Table Entry from virtual address */
+page_t *
+get_page(void *addr)
+{
+ page_t *mappings = (void *) 0xFFC00000;
+ page_table_t *tables = (void *) 0xFFFFF000;
+ uint32_t address = (uint32_t) addr >> 12;
+ uint32_t tbl = address / 1024;
+ /* Create table not present */
+ if ((tables[tbl] & PDE_PRESENT) == 0) {
+ tables[tbl] = alloc_frames(1)
+ | PDE_PRESENT | PDE_WRITE | PDE_USER;
+ memset((void *) mappings + (tbl * 0x1000), 0, 0x1000);
+ }
+ return &mappings[address];
+}
+
+/* Clone a page directory */
+page_dir_t
+clone_dir(void)
+{
+ page_table_t *oldTables = (void *) 0xFFFFF000;
+ page_table_t *newTables = (void *) 0xFFFFE000;
+ page_t *oldTable, *newTable;
+ page_dir_t dir = alloc_frames(1);
+ uint16_t i, tbl, pg;
+
+ /* Temporarily link new paging structures into current directory */
+ page_table_t restore = oldTables[1022];
+ oldTables[1022] = dir | PDE_PRESENT | PDE_WRITE;
+ for (i = 0; i < 1024; i++)
+ flush_tlb((uintptr_t) newTables + (0x1000 * i));
+
+ /* Iterate tables */
+ for (tbl = 0; tbl < 1022; tbl++) {
+ if ((oldTables[tbl] & PDE_PRESENT) == 0)
+ continue;
+
+ /* Link Kernel tables */
+ if (tbl < 2 || tbl >= 1008) { /* TODO: define kernel mem */
+ newTables[tbl] = oldTables[tbl];
+ continue;
+ }
+
+ /* Copy everything else */
+ newTables[tbl] = alloc_frames(1) | PG_ATTR(oldTables[tbl]);
+ oldTable = (page_t *) 0xFFC00000 + (tbl * 1024);
+ newTable = (page_t *) 0xFF800000 + (tbl * 1024);
+ for (pg = 0; pg < 1024; pg++) {
+ if ((oldTable[pg] & PTE_PRESENT) == 0) {
+ newTable[pg] = 0;
+ continue;
+ }
+
+ /* Copy-On-Write behaviour */
+ if (tbl < 960) {
+ oldTable[pg] &= ~PTE_WRITE;
+ flush_tlb((uintptr_t) (((tbl * 1024) + pg) << 12));
+ newTable[pg] = oldTable[pg];
+ } else {
+ newTable[pg] = alloc_frames(1) | PG_ATTR(oldTable[pg]);
+ copy_page_frame((void *) PG_ADDR(oldTable[pg]),
+ (void *) PG_ADDR(newTable[pg]));
+ }
+ /* FIXME */
+ }
+ }
+ newTables[1023] = oldTables[1022];
+
+ /* Unlink paging structures */
+ oldTables[1022] = restore;
+ for (i = 0; i < 1024; i++)
+ flush_tlb((uintptr_t) newTables + (0x1000 * i));
+
+ return dir;
+}
+
+/* Free all (copied) pages in the current directory */
+void
+clean_dir(void)
+{
+ page_t *mappings = (void *) 0xFFC00000;
+ page_table_t *tables = (void *) 0xFFFFF000;
+ page_t *pages;
+ uint16_t tbl, pg;
+ for (tbl = 2; tbl < 1008; tbl++) {
+ if ((tables[tbl] & PDE_PRESENT) == 0)
+ continue;
+ pages = mappings + (tbl * 1024);
+ for (pg = 0; pg < 1024; pg++) {
+ if ((pages[pg] & PDE_PRESENT) == 0)
+ continue;
+ free_page(pages + pg);
+ }
+ }
+}
+
+/* Quickly map a page frame into view for temporary use */
+page_t
+quick_page(uintptr_t frame)
+{
+ page_t *mappings = (void *) 0xFFC00000;
+ page_t old;
+ old = mappings[2047];
+ mappings[2047] = PG_ADDR(frame) | PG_ATTR(old);
+ flush_tlb(0x7FF000);
+ return PG_ADDR(old);
+}
+
+/* Initialise paging */
+void
+init_paging(void)
+{
+ zeroFrame = alloc_frames(1);
+ memset((void *) zeroFrame, 0, 0x1000);
+
+ uint16_t tbl, pg;
+ page_t *table;
+ page_table_t *kernelTables;
+ kernelDir = alloc_frames(1);
+ kernelTables = (page_table_t *) kernelDir;
+ for (tbl = 0; tbl < 1024; tbl++)
+ kernelTables[tbl] = 0x00000000 | PDE_WRITE;
+ for (tbl = 0; tbl < 2; tbl++) {
+ table = (void *) alloc_frames(1);
+ kernelTables[tbl] = ((page_table_t) table)
+ | PDE_WRITE | PDE_PRESENT;
+ for (pg = 0; pg < 1024; pg++) {
+ if (!tbl && !pg)
+ continue;
+ table[pg] = (((tbl * 1024) + pg) << 12)
+ | PTE_WRITE | PTE_PRESENT | PTE_GLOBAL;
+ }
+ }
+ /* Map the directory into itself */
+ kernelTables[1023] = kernelDir | PDE_WRITE | PDE_PRESENT;
+
+ /* Use Kernel directory */
+ switch_dir(kernelDir);
+ register_exception(14, early_page_fault_handler);
+ enable_paging();
+
+ /* Identity page the APIC registers */
+ *get_page((void *) lapicPtr) = lapicPtr
+ | PTE_PRESENT | PTE_WRITE | PTE_GLOBAL;
+ *get_page((void *) ioapicPtr) = ioapicPtr
+ | PTE_PRESENT | PTE_WRITE | PTE_GLOBAL;
+
+ /* Allocate Kernel stack */
+ uintptr_t stk;
+ for (stk = 0xF0400000; stk < 0xF0800000; stk += 0x1000)
+ alloc_page(get_page((void *) stk),
+ PTE_PRESENT | PTE_WRITE | PTE_USER, -1);
+}
diff --git a/mem/paging.h b/mem/paging.h
new file mode 100644
index 0000000..a3acaf2
--- /dev/null
+++ b/mem/paging.h
@@ -0,0 +1,58 @@
+#ifndef KERNEL_MEM_PAGING_H
+#define KERNEL_MEM_PAGING_H
+
+#include <stdint.h>
+#include "../proc/proc.h"
+#include "../spinlock.h"
+
+#define PG_ADDR(pg) (pg & 0xFFFFF000)
+#define PG_ATTR(pg) (pg & 0x00000FFF)
+
+#define QUICK_PAGE ((void *) 0x7FF000)
+
+typedef uint32_t page_t;
+typedef uint32_t page_table_t;
+typedef uint32_t page_dir_t;
+
+/* Page flags */
+enum PageFlag {
+ PTE_PRESENT = (1 << 0),
+ PTE_WRITE = (1 << 1),
+ PTE_USER = (1 << 2),
+ PTE_THROUGH = (1 << 3),
+ PTE_NOCACHE = (1 << 4),
+ PTE_ACCESS = (1 << 5),
+ PTE_DIRTY = (1 << 6),
+ PTE_GLOBAL = (1 << 8),
+};
+/* Page Table flags */
+enum PageTableFlag {
+ PDE_PRESENT = (1 << 0),
+ PDE_WRITE = (1 << 1),
+ PDE_USER = (1 << 2),
+ PDE_THROUGH = (1 << 3),
+ PDE_NOCACHE = (1 << 4),
+ PDE_ACCESS = (1 << 5),
+};
+
+/* Flush Translation Lookaside Buffer */
+static inline void
+flush_tlb(uintptr_t addr)
+{
+ asm volatile("invlpg (%0)" :: "r" (addr) : "memory");
+}
+
+extern Spinlock quickPageLock;
+extern page_t zeroFrame;
+
+void init_paging(void);
+void early_page_fault_handler(InterruptFrame *frame);
+void page_fault_handler(InterruptFrame *frame);
+void alloc_page(page_t *page, uint16_t flags, page_t frame);
+void free_page(page_t *page);
+page_t *get_page(void *addr);
+page_dir_t clone_dir(void);
+void clean_dir(void);
+page_t quick_page(page_t frame);
+
+#endif
diff --git a/mem/user.c b/mem/user.c
new file mode 100644
index 0000000..b9ed273
--- /dev/null
+++ b/mem/user.c
@@ -0,0 +1,37 @@
+/*
+ * This file handles safely getting data from userspace for the Kernel. This is
+ * for security reasons to prevent the user from tricking a syscall into
+ * manipulating/leaking Kernel data structures. User memory is defined as any
+ * address range that completely sits in a Virtual Memory Region.
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include "vm.h"
+#include "../task/task.h"
+
+/* User can read this address */
+int
+verify_access(const void *addr, size_t len, int prot)
+{
+ if (!in_syscall() || !addr || !len)
+ return 1;
+
+ /* Iterate all user memory regions */
+ VMRegion *head;
+ for (head = current->vm->regions; head; head = head->next) {
+ if ((uintptr_t) addr >= head->start
+ && ((uintptr_t) addr + len) < head->end)
+ break;
+ }
+ if (!head) {
+ head = current->stack;
+ if ((uintptr_t) addr < head->start
+ || ((uintptr_t) addr + len) >= head->end)
+ head = NULL;
+ }
+ /* No fitting region */
+ if (!head)
+ return 0;
+ return (head->prot & prot);
+}
diff --git a/mem/vm.c b/mem/vm.c
new file mode 100644
index 0000000..46b40fc
--- /dev/null
+++ b/mem/vm.c
@@ -0,0 +1,261 @@
+/*
+ * This file handles the Virtual Memory system for processes. It splits each
+ * process into several memory regions, and points each of those reasons to a
+ * memory object. Each object can be modified on demand, and can be made up of
+ * several pages, and backed by various stores. This allows objects such as
+ * files to be easily mapped into an address space, or for large regions to be
+ * shared between processes.
+ */
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <errno.h>
+#include "heap.h"
+#include "paging.h"
+#include "vm.h"
+#include "../vfs/vfs.h"
+#include "../vfs/inode.h"
+#include "../vfs/cache.h"
+#include "../vfs/tmpfs/fs.h"
+#include "../task/task.h"
+#include "../proc/proc.h"
+#include "../screen.h"
+
+/* Unmap a range of pages from page directory */
+static void
+unmap_page_range(uintptr_t start, uintptr_t end)
+{
+ uintptr_t addr;
+ for (addr = start; addr < end; addr += 0x1000) {
+ *get_page((void *) addr) = 0x00000000;
+ flush_tlb(addr);
+ }
+}
+
+/* Remove a range of pages from a region's page cache */
+static void
+remove_cache_range(VMRegion *region, uintptr_t start, uintptr_t end)
+{
+ Page *page;
+ Inode *inode;
+ uintptr_t p;
+ for (p = 0; p < end - start; p += 0x1000) {
+ page = NULL;
+ if (!page && region->front) {
+ inode = region->front->inode;
+ page = page_find(inode, region->offset + p);
+ }
+ if (!page && region->back) {
+ inode = region->back->inode;
+ page = page_find(inode, region->offset + p);
+ }
+ if (page)
+ page_remove(inode, page);
+ }
+}
+
+/* Create a new VM Region */
+VMRegion *
+vm_create_region(void *addr, size_t len, int prot, int flags, off_t offset,
+ File *back)
+{
+ /* Create new region */
+ VMRegion *head, *next, *insert, *region = kmalloc(sizeof(VMRegion));
+ region->end = (uintptr_t) addr + len;
+ if (region->end % 0x1000)
+ region->end += 0x1000 - (region->end % 0x1000);
+ region->start = (uintptr_t) addr & ~0xFFF;
+ region->prot = prot;
+ region->flags = flags;
+ region->offset = offset;
+ region->front = NULL;
+ region->back = NULL;
+ if (back)
+ region->back = file_get(back);
+
+ /* Create new list */
+ if (!current->vm->regions) {
+ current->vm->regions = region;
+ return region;
+ }
+
+ /* Fix overlaps */
+ uintptr_t p;
+ for (head = current->vm->regions; head; head = next) {
+ next = head->next; /* head may be destroyed during iteration */
+ if (head->start >= region->end || head->end <= region->start)
+ continue;
+
+ /* Middle eclipsed */
+ if (head->start < region->start && head->end > region->end) {
+ /* Create region after current */
+ insert = kmalloc(sizeof(VMRegion));
+ insert->end = head->end;
+ insert->start = head->end = region->start;
+ insert->prot = head->prot;
+ insert->flags = head->flags;
+ insert->offset = head->offset;
+ insert->offset += (insert->start - head->start);
+ if (head->front)
+ insert->front = file_get(head->front);
+ if (head->back)
+ insert->back = file_get(head->back);
+ /* Insert into list */
+ insert->next = head->next;
+ head->next = insert;
+ insert->prev = head;
+ insert->next->prev = insert;
+ /* Inserted region will be dealt with on next pass */
+ }
+ /* Start eclipsed */
+ if (head->start >= region->start && head->end > region->end) {
+ unmap_page_range(head->start, region->end);
+ remove_cache_range(head, head->start, region->end);
+ head->start = region->end;
+ head->offset += (region->end - head->start);
+ }
+ /* End eclipsed */
+ if (head->start < region->start && head->end <= region->end) {
+ unmap_page_range(region->start, head->end);
+ remove_cache_range(head, region->start, head->end);
+ head->end = region->start;
+ }
+ /* Total eclipse */
+ if (head->start >= region->start && head->end <= region->end)
+ vm_destroy_region(head);
+ }
+ /* Add to ordered list */
+ for (head = current->vm->regions; head->next; head = head->next)
+ if (head->end <= region->start
+ && head->next->start >= region->end)
+ break;
+ region->next = head->next;
+ region->prev = head;
+ region->prev->next = region;
+ if (region->next)
+ region->next->prev = region;
+
+ return region;
+}
+
+/* Remove a VM Region */
+void
+vm_remove_region(VMRegion *region)
+{
+ /* Remove from list */
+ if (current->vm->regions == region)
+ current->vm->regions = region->next;
+ if (region->prev)
+ region->prev->next = region->next;
+ if (region->next)
+ region->next->prev = region->prev;
+// region->prev = region->next = NULL;
+}
+
+/* Destroy a VM Region */
+void
+vm_destroy_region(VMRegion *region)
+{
+ /* Unlink files */
+ if (region->front)
+ file_put(region->front);
+ if (region->back)
+ file_put(region->back);
+
+ /* Clean page directory */
+ unmap_page_range(region->start, region->end);
+
+ vm_remove_region(region);
+ kfree(region);
+}
+
+/* Clone a set of VM Regions */
+VMRegion *
+vm_clone_regions(VMRegion *head)
+{
+ if (!head)
+ return NULL;
+
+ VMRegion *newhead = NULL, *newcurr, *newprev = NULL;
+ VMRegion *curr = head;
+ off_t i;
+ Page *page;
+ File *file;
+
+ while (curr) {
+ newcurr = kmalloc(sizeof(VMRegion));
+ if (!newhead)
+ newhead = newcurr;
+
+ newcurr->prev = newprev;
+ newcurr->next = NULL;
+ if (newprev)
+ newprev->next = newcurr;
+
+ newcurr->start = curr->start;
+ newcurr->end = curr->end;
+ newcurr->prot = curr->prot;
+ newcurr->flags = curr->flags;
+ newcurr->offset = curr->offset;
+ /* Front (anonymous regions) */
+ if (curr->front && (curr->flags & MAP_PRIVATE)) {
+ /* Copy the file */
+ file = kmalloc(sizeof(File));
+ file->inode = inode_get(kmalloc(sizeof(Inode)));
+ file->ops = &tmpfsFileOps;
+ newcurr->front = file_get(file);
+ for (i = 0; i < curr->end - curr->start; i += 0x1000) {
+ page = page_find(curr->front->inode,
+ i + curr->offset);
+ if (page)
+ page_add(file->inode, page);
+ }
+ } else if (curr->front) {
+ newcurr->front = file_get(curr->front);
+ }
+ /* Back (always a file) */
+ if (curr->back)
+ newcurr->back = file_get(curr->back);
+
+ curr = curr->next;
+ newprev = newcurr;
+ };
+
+ return newhead;
+}
+
+/* Map an object into memory */
+void *
+mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t off)
+{
+ VMRegion *region;
+
+ /* Find gap big enough */
+ if (!addr) {
+ for (region = current->vm->regions;
+ region->next; region = region->next) {
+ if (region->next->start - region->end >= len)
+ break;
+ }
+ addr = (void *) region->end;
+ }
+
+ /* Map anonymous memory */
+ if (flags & MAP_ANONYMOUS) {
+ region = vm_create_region(addr, len, prot, flags, 0, NULL);
+ goto end;
+ }
+
+ /* Map a file */
+ if (fildes < 0 || fildes >= NFILES)
+ return (void *) -EBADF;
+ File *file = current->files->fd[fildes];
+ if (!file)
+ return (void *) -EBADF;
+ region = vm_create_region(addr, len, prot, flags, off, file);
+end:
+ if (!region)
+ return (void *) -ENOMEM;
+ return (void *) region->start;
+}
diff --git a/mem/vm.h b/mem/vm.h
new file mode 100644
index 0000000..eab852d
--- /dev/null
+++ b/mem/vm.h
@@ -0,0 +1,37 @@
+#ifndef KERNEL_MEM_VM_H
+#define KERNEL_MEM_VM_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/mman.h>
+#include "../vfs/vfs.h"
+
+typedef struct VirtualMemory VirtualMemory;
+typedef struct VMRegion VMRegion;
+typedef struct VMObject VMObject;
+
+/* Virtual Memory Namespace */
+struct VirtualMemory {
+ VMRegion *regions;
+ refcount_t usage;
+};
+
+/* Structure for a Virtual Memory Map Entry */
+struct VMRegion {
+ VMRegion *prev, *next;
+ uintptr_t start, end;
+ int prot;
+ int flags;
+ off_t offset;
+ File *front, *back;
+};
+
+VMRegion *vm_create_region(void *addr, size_t len, int prot, int flags,
+ off_t offset, File *back);
+void vm_remove_region(VMRegion *region);
+void vm_destroy_region(VMRegion *region);
+VMRegion *vm_clone_regions(VMRegion *head);
+
+int verify_access(const void *addr, size_t len, int prot);
+
+#endif
diff --git a/multiboot.h b/multiboot.h
new file mode 100644
index 0000000..3fde8d0
--- /dev/null
+++ b/multiboot.h
@@ -0,0 +1,37 @@
+#ifndef MULTIBOOT_H
+#define MULTIBOOT_H
+
+#include <stdint.h>
+
+struct MultibootInfo {
+ uint32_t flags;
+ uint32_t memLower;
+ uint32_t memHigher;
+ uint32_t bootDev;
+ uint32_t cmdline;
+ uint32_t modsCount;
+ uint32_t modsAddr;
+
+ uint32_t symsNum;
+ uint32_t symsSize;
+ uint32_t symsAddr;
+ uint32_t symsShndx;
+
+ uint32_t mmapLen;
+ uint32_t mmapAddr;
+ uint32_t drivesLen;
+ uint32_t drivesAddr;
+
+ uint32_t configTable;
+ uint32_t bootLoader;
+ uint32_t apmTable;
+
+ uint32_t vbeControlInfo;
+ uint32_t vbeModeInfo;
+ uint16_t vbeMode;
+ uint16_t vbeInterfaceSeg;
+ uint16_t vbeInterfaceOff;
+ uint16_t vbeInterfaceLen;
+};
+
+#endif
diff --git a/net/arp.c b/net/arp.c
new file mode 100644
index 0000000..8906abb
--- /dev/null
+++ b/net/arp.c
@@ -0,0 +1,118 @@
+/*
+ * This file contains the implementation of ARP for the Network Stack. It
+ * handles converting IP addresses to MAC addresses, and sits between the
+ * Network layer and the Ethernet layer.
+ */
+
+#include <stdint.h>
+#include <stddef.h>
+#include <string.h>
+#include "net.h"
+#include "../mem/heap.h"
+#include "../screen.h"
+
+typedef struct ARPCacheEntry ARPCacheEntry;
+
+/* Structure for an ARP Cache Entry */
+struct ARPCacheEntry {
+ uint8_t mac[6], ip[4];
+ ARPCacheEntry *next;
+};
+
+/* Structure for an ARP Packet */
+typedef struct Packet {
+ uint16_t hardwareType;
+ uint16_t protocolType;
+ uint8_t hardwareSize;
+ uint8_t protocolSize;
+ uint16_t opcode;
+ uint8_t srcMac[6], srcIp[4];
+ uint8_t dstMac[6], dstIp[4];
+} __attribute__((packed)) Packet;
+
+ARPCacheEntry *arpCache;
+
+/* Request a MAC address from IP with ARP */
+void
+arp_request(NetIF *netif, uint8_t dstIp[4], uint8_t *res)
+{
+ uint8_t dstMac[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
+ Packet packet = {
+ .hardwareType = htons(netif->type),
+ .protocolType = htons(0x0800), /* EtherType IPv4 */
+ .hardwareSize = 6,
+ .protocolSize = 4,
+ .opcode = htons(0x0001), /* ARP Request */
+ };
+ memcpy(packet.srcMac, netif->mac, 6);
+ memcpy(packet.srcIp, netif->ip, 4);
+ memcpy(packet.dstMac, dstMac, 6);
+ memcpy(packet.dstIp, dstIp, 4);
+
+ ethernet_send_frame(netif, dstMac, 0x0806, &packet, sizeof(Packet));
+
+ if (!res)
+ return;
+ /* Wait for result */
+ ARPCacheEntry *ce;
+search:
+ for (ce = arpCache; ce; ce = ce->next)
+ if (!memcmp(ce->ip, dstIp, 4))
+ break;
+ if (!ce)
+ goto search;
+ memcpy(res, ce->mac, 6);
+}
+
+/* Reply to an ARP Request */
+void
+arp_reply(NetIF *netif, Packet *request)
+{
+ Packet reply = {
+ .hardwareType = htons(netif->type),
+ .hardwareSize = request->hardwareSize,
+ .protocolType = htons(request->protocolType),
+ .protocolSize = request->protocolSize,
+ .opcode = htons(0x0002), /* ARP Reply */
+ };
+ memcpy(reply.srcMac, netif->mac, 6);
+ memcpy(reply.srcIp, netif->ip, 4);
+ memcpy(reply.dstMac, request->srcMac, 6);
+ memcpy(reply.dstIp, request->srcIp, 4);
+
+ ethernet_send_frame(netif, request->srcMac, 0x0806, &reply,
+ sizeof(Packet));
+}
+
+/* Handle an incoming ARP packet */
+void
+arp_receive_packet(NetIF *netif, void *data, size_t len)
+{
+ Packet packet;
+ memcpy(&packet, data, sizeof(Packet));
+ packet.hardwareType = ntohs(packet.hardwareType);
+ packet.protocolType = ntohs(packet.protocolType);
+ packet.opcode = ntohs(packet.opcode);
+
+ switch (packet.opcode) {
+ case 0x0001: /* ARP Request */
+ if (!memcmp(packet.dstIp, netif->ip, 4))
+ arp_reply(netif, &packet);
+ /* FALLTHROUGH */
+ case 0x0002: /* ARP Reply */
+ ARPCacheEntry *ce;
+ for (ce = arpCache; ce; ce = ce->next)
+ if (!memcmp(ce->ip, packet.srcIp, 4))
+ break;
+ if (ce) {
+ memcpy(ce->mac, packet.srcMac, 6);
+ break;
+ }
+ ce = kmalloc(sizeof(ARPCacheEntry));
+ memcpy(ce->mac, packet.srcMac, 6);
+ memcpy(ce->ip, packet.srcIp, 4);
+ ce->next = arpCache;
+ arpCache = ce;
+ break;
+ }
+}
diff --git a/net/ethernet.c b/net/ethernet.c
new file mode 100644
index 0000000..98d6692
--- /dev/null
+++ b/net/ethernet.c
@@ -0,0 +1,63 @@
+/*
+ * This file implements the Ethernet layer of the Network Stack. It wraps any
+ * packet sent to it with the Ethernet Header. Equally, it unwraps any packets
+ * before passing them to the next layer, determined by the EtherType in the
+ * header.
+ */
+
+#include <stdint.h>
+#include <stddef.h>
+#include <string.h>
+#include "net.h"
+#include "../mem/heap.h"
+#include "../screen.h"
+
+/* Structure for an Ethernet Header */
+typedef struct Header {
+ uint8_t dst[6];
+ uint8_t src[6];
+ uint16_t ethertype;
+} __attribute__((packed)) Header;
+
+/* Receive a frame of data */
+void
+ethernet_receive_frame(NetIF *netif, void *data, size_t len)
+{
+ Header header;
+ memcpy(&header, data, sizeof(Header));
+ header.ethertype = ntohs(header.ethertype);
+
+ /* Handle appropriately */
+ switch (header.ethertype) {
+ case 0x0800: /* IPv4 */
+ ipv4_receive_packet(netif, data + sizeof(Header),
+ len - sizeof(Header));
+ break;
+ case 0x0806: /* ARP */
+ arp_receive_packet(netif, data + sizeof(Header),
+ len - sizeof(Header));
+ break;
+// default:
+// kprintf("Unhandled type (%04x)", header.ethertype);
+ }
+}
+
+/* Send a frame of data */
+void
+ethernet_send_frame(NetIF *netif, uint8_t dst[6], uint16_t type,
+ void *data, size_t len)
+{
+ Header header;
+ header.ethertype = htons(type);
+ memcpy(header.src, netif->mac, 6);
+ memcpy(header.dst, dst, 6);
+ size_t frameLen = sizeof(Header) + len;
+ if (frameLen < 64)
+ frameLen = 64;
+
+ char *frame = kmalloc(frameLen);
+ memcpy(frame, &header, sizeof(Header));
+ memcpy(frame + sizeof(Header), data, len);
+ netif->transmit(frame, frameLen);
+ kfree(frame);
+}
diff --git a/net/ipv4.c b/net/ipv4.c
new file mode 100644
index 0000000..ce27ba7
--- /dev/null
+++ b/net/ipv4.c
@@ -0,0 +1,97 @@
+/*
+ * This file contains the IPv4 implementation in the Network Stack. It sits
+ * above the Ethernet layer, but makes heavy use of the Address Resolution
+ * Protocol.
+ */
+
+#include <stdint.h>
+#include <stddef.h>
+#include <string.h>
+#include "net.h"
+#include "../mem/heap.h"
+#include "../screen.h"
+
+/* Structure for an IPv4 Packet Header */
+typedef struct Header {
+ /* ihl and version are swapped to work with network byte order */
+ uint8_t ihl : 4;
+ uint8_t version : 4;
+ uint8_t service;
+ uint16_t len;
+ uint16_t id;
+ uint16_t flags;
+ uint8_t ttl;
+ uint8_t proto;
+ uint16_t checksum;
+ uint32_t src, dst;
+} __attribute__((packed)) Header;
+
+uint16_t ipid = 1;
+
+/* Calculate an IPv4 checksum */
+static uint16_t
+ipv4_checksum(void *addr, size_t len)
+{
+ register uint32_t sum = 0;
+ while (len > 1) {
+ sum += *(uint16_t *) addr++;
+ len -= 2;
+ }
+ if (len > 0)
+ sum += *(uint8_t *) addr;
+
+ while (sum >> 16)
+ sum = (sum & 0xFFFF) + (sum >> 16);
+
+ return ~sum;
+}
+
+/* Receive an IPv4 Packet */
+void
+ipv4_receive_packet(NetIF *netif, void *data, size_t len)
+{
+ Header header;
+ memcpy(&header, data, sizeof(Header));
+
+ uint8_t src[4];
+ memcpy(src, &header.src, 4);
+ kprintf("Received IPv4 Packet from %d.%d.%d.%d",
+ src[0], src[1], src[2], src[3]);
+
+ /* Handle packet */
+ switch (header.proto) {
+// case 0x01: /* ICMP */
+// break;
+// case 0x02: /* IGMP */
+// case 0x06: /* TCP */
+// case 0x11: /* UDP */
+ default:
+ kprintf("Unhandled IP packet (proto %d)", header.proto);
+ }
+}
+
+/* Transmit an IPv4 Packet */
+void
+ipv4_send_packet(NetIF *netif, uint8_t dst[4], uint8_t proto, uint16_t flags,
+ void *data, size_t len)
+{
+ Header header;
+ header.ihl = 5;
+ header.version = 4;
+ header.service = 0;
+ header.len = htons(len + sizeof(Header));
+ header.id = htons(ipid++);
+ header.flags = htons(flags);
+ header.ttl = 255;
+ header.proto = proto;
+ memcpy(&header.src, netif->ip, 4);
+ memcpy(&header.dst, dst, 4);
+ header.checksum = ipv4_checksum(&header, sizeof(Header));
+
+ char *packet = kmalloc(len + sizeof(Header));
+ memcpy(packet, &header, sizeof(Header));
+ memcpy(packet + sizeof(Header), data, len);
+ ethernet_send_frame(netif, netif->gatewayMac,
+ 0x800, packet, len + sizeof(Header));
+ kfree(packet);
+}
diff --git a/net/net.c b/net/net.c
new file mode 100644
index 0000000..5c963dc
--- /dev/null
+++ b/net/net.c
@@ -0,0 +1,90 @@
+/*
+ * This file represents the core of the Kernel Networking System. It contains
+ * the outwards facing interface, as well as the internal interface for the
+ * lower levels to make use of.
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include "net.h"
+#include "../mem/heap.h"
+
+NetIF *netifs;
+
+/* Switch the endian of integers */
+static uint16_t
+switch_endian16(uint16_t nb)
+{
+ return (nb>>8) | (nb<<8);
+}
+static uint32_t
+switch_endian32(uint32_t nb)
+{
+ return ((nb>>24)&0xff) |
+ ((nb<<8)&0xff0000) |
+ ((nb>>8)&0xff00) |
+ ((nb<<24)&0xff000000);
+}
+
+/* Host to Network byte order */
+uint16_t
+htons(uint16_t hostshort)
+{
+ return switch_endian16(hostshort);
+}
+uint32_t
+htonl(uint32_t hostlong)
+{
+ return switch_endian32(hostlong);
+}
+
+/* Network to Host byte order */
+uint16_t
+ntohs(uint16_t netshort)
+{
+ return switch_endian16(netshort);
+}
+uint32_t
+ntohl(uint32_t netlong)
+{
+ return switch_endian32(netlong);
+}
+
+/* Intialise Networking */
+void
+init_net(void)
+{
+ char ip[] = {10,0,2,2};
+ memcpy(netifs->gatewayIp, ip, 4);
+ arp_request(netifs, ip, netifs->gatewayMac);
+
+ /* Send ICMP ECHO */
+ char icmp[12] = {
+ 8, 0,
+ 0xF7, 0xFE,
+ 0x00, 0x01,
+ 0, 0
+ };
+ char pip[] = {1,1,1,1};
+ ipv4_send_packet(netifs, pip, 1, 0, icmp, 12);
+}
+
+/* Register a Network Driver */
+NetIF *
+net_create_interface(uint16_t type, void (*transmit)(void *, size_t),
+ void (*get_mac)(char *buf))
+{
+ NetIF *netif = kmalloc(sizeof(NetIF));
+ netif->type = type;
+ netif->transmit = transmit;
+ netif->get_mac = get_mac;
+ netif->get_mac(netif->mac);
+ netif->ip[0] = 10;
+ netif->ip[1] = 0;
+ netif->ip[2] = 2;
+ netif->ip[3] = 15;
+
+ /* Link into list */
+ netif->next = netifs;
+ netifs = netif;
+}
diff --git a/net/net.h b/net/net.h
new file mode 100644
index 0000000..3fd113b
--- /dev/null
+++ b/net/net.h
@@ -0,0 +1,40 @@
+#ifndef KERNEL_NET_H
+#define KERNEL_NET_H
+
+#include <stdint.h>
+#include <stddef.h>
+
+typedef struct NetIF NetIF;
+
+/* Structure for a Network Interface */
+struct NetIF {
+ uint16_t type;
+ void (*transmit)(void *, size_t);
+ void (*get_mac)(char *);
+
+ uint8_t ip[4], mac[6];
+ uint8_t gatewayIp[4], gatewayMac[6];
+ uint8_t dnsIp[4], dnsMac[6];
+ NetIF *next;
+};
+
+uint16_t htons(uint16_t hostshort);
+uint32_t htonl(uint32_t hostlong);
+uint16_t ntohs(uint16_t hostshort);
+uint32_t ntohl(uint32_t hostlong);
+void init_net(void);
+NetIF *net_create_interface(uint16_t type, void (*transmit)(void *, size_t),
+ void (*get_mac)(char *buf));
+
+void ethernet_receive_frame(NetIF *netif, void *data, size_t len);
+void ethernet_send_frame(NetIF *netif, uint8_t dst[6], uint16_t type,
+ void *data, size_t len);
+
+void arp_receive_packet(NetIF *netif, void *data, size_t len);
+void arp_request(NetIF *netif, uint8_t dstIp[4], uint8_t *res);
+
+void ipv4_receive_packet(NetIF *netif, void *data, size_t len);
+void ipv4_send_packet(NetIF *netif, uint8_t dst[4], uint8_t proto,
+ uint16_t flags, void *data, size_t len);
+
+#endif
diff --git a/proc/apic.c b/proc/apic.c
new file mode 100644
index 0000000..a784f50
--- /dev/null
+++ b/proc/apic.c
@@ -0,0 +1,258 @@
+/*
+ * This file contains code dealing with the Advanced Programmable Interrupt
+ * Controller system. It should proved a drop-in replacement for the standard
+ * PIC system, abstracting it in the same way. It should also be able to handle
+ * a non-SMP system, and fall back on the PIC.
+ */
+
+#include <stdint.h>
+#include "proc.h"
+#include "../mem/heap.h"
+#include "../mem/mem.h"
+#include "../task/task.h"
+#include "../io.h"
+#include "../screen.h"
+#include "../spinlock.h"
+
+/* RSDP Descriptor */
+typedef struct RSDPDescriptor {
+ char signature[8];
+ uint8_t checksum;
+ char oem[6];
+ uint8_t revision;
+ uint32_t rsdt;
+} __attribute__((packed)) RSDPDescriptor;
+
+/* System Descriptor Table Header */
+typedef struct SDTHeader {
+ char signature[4];
+ uint32_t length;
+ uint8_t revision;
+ uint8_t checksum;
+ char oem[6];
+ char oemTable[8];
+ uint32_t oemRevision;
+ uint32_t creator;
+ uint32_t creatorRevision;
+} SDTHeader;
+
+/* IOAPIC Interrupt Source Override */
+typedef struct IOAPICOverride {
+ uint8_t type, length;
+ uint8_t bus, irq;
+ uint8_t gsi, flags;
+} IOAPICOverride;
+
+void cpu_load(void);
+extern void ap_trampoline(void);
+
+uint8_t numCores;
+uint8_t bspId;
+uint32_t lapicPtr, ioapicPtr;
+uint8_t lapicIds[MAX_CPUS], lapicNums[MAX_CPUS];
+Spinlock timerLock;
+
+/* Enable APIC */
+static void
+enable_apic(void)
+{
+ LAPIC(0xF0) = 0x1FF;
+ LAPIC(0x80) = 0;
+}
+
+/* Read from the IOAPIC */
+static uint32_t
+read_io_apic(uint32_t reg)
+{
+ uint32_t volatile *ioapic = (uint32_t volatile *) ioapicPtr;
+ ioapic[0] = (reg & 0xFF);
+ return ioapic[4];
+}
+
+/* Write to the IOAPIC */
+static void
+write_io_apic(uint32_t reg, uint32_t value)
+{
+ uint32_t volatile *ioapic = (uint32_t volatile *) ioapicPtr;
+ ioapic[0] = (reg & 0xFF);
+ ioapic[4] = value;
+}
+
+/* Delay in gaps of 100 microseconds */
+static void
+delay(uint32_t time)
+{
+ float calc = (1193182 / 10000) * time;
+ uint32_t divisor = (uint32_t) calc;
+ outb(0x43, 0x30);
+ outb(0x40, divisor & 0xFF);
+ outb(0x40, (divisor >> 8) & 0xFF);
+ do outb(0x43, 0xE2); while (!(inb(0x40) & (1 << 7)));
+}
+
+/* Start the APIC timer */
+static void
+apic_start_timer(void)
+{
+ /* Don't run while another timer is calibrating */
+ acquire(&timerLock);
+
+ /* Start LAPIC countdown from -1 */
+ LAPIC(0x3E0) = 3;
+ LAPIC(0x380) = 0xFFFFFFFF;
+ delay(10);
+
+ /* Stop after PIT fires, count LAPIC ticks */
+ LAPIC(0x320) = 0x10000;
+ uint32_t ticks = 0xFFFFFFFF - LAPIC(0x390);
+
+ /* Tick every 1ms */
+ LAPIC(0x320) = 0x20000 | 32;
+ LAPIC(0x3E0) = 3;
+ LAPIC(0x380) = ticks;
+
+ release(&timerLock);
+}
+
+/* Initialise SMP (RSDT method) */
+void
+init_multicore(void *ebda)
+{
+ numCores = 1;
+ memset(lapicIds, 0, MAX_CPUS);
+ memset(lapicNums, 0, MAX_CPUS);
+
+ init_lock(&timerLock);
+
+ /* Search for SMP tables, start from EBDA start */
+ char *signature = (char *) ebda;
+ uint8_t found;
+ while (signature < (char *) 0x100000) {
+ /* RSDT */
+ if (!memcmp(signature, "RSD PTR ", 8)) {
+ found = 1;
+ break;
+ }
+ signature++;
+ }
+ /* Fall back to PIC */
+ if (!found)
+ return;
+ /* RSDT checksum */
+ uint32_t check = 0, i, j;
+ for (i = 0; i < sizeof(RSDPDescriptor); i++)
+ check += signature[i];
+ if (check % 0x100)
+ return;
+
+ uint8_t *ptr, *ptr2;
+ uint8_t *rsdt, len;
+ uint8_t overrides[16] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
+ IOAPICOverride *iso;
+
+ /* Find APIC table */
+ kprintf("Searching for APIC table");
+ asm volatile("mov $1, %%eax; cpuid; shrl $24, %%ebx" : "=b" (bspId));
+ numCores = 0;
+ rsdt = (uint8_t *) ((RSDPDescriptor *) signature)->rsdt;
+ len = *((uint32_t *) (rsdt + 4));
+ for (ptr2 = rsdt + 36; ptr2 < rsdt + len; ptr2 += 4) {
+ ptr = *((uint8_t **) ptr2);
+ if (!memcmp(ptr, "APIC", 4))
+ break;
+ }
+ if (memcmp(ptr, "APIC", 4))
+ return;
+
+ /* Read APIC table */
+ lapicPtr = *((uint32_t *) (ptr + 0x24));
+ ptr2 = ptr + *((uint32_t *) (ptr + 4));
+ for (ptr += 44; ptr < ptr2; ptr += ptr[1]) {
+ /* Entries */
+ switch (ptr[0]) {
+ case 0: /* Processor Local APIC */
+ if (numCores >= MAX_CPUS)
+ continue;
+ if (ptr[4] & 1) {
+ lapicNums[ptr[3]] = numCores;
+ lapicIds[numCores] = ptr[3];
+ numCores++;
+ }
+ break;
+ case 1: /* IOAPIC */
+ ioapicPtr = *((uint32_t *) (ptr + 4));
+ break;
+ case 2: /* IOAPIC ISO */
+ iso = (void *) ptr;
+ overrides[iso->irq] = iso->gsi;
+ kprintf(" IRQ#%.2d -> GSI#%.2d",
+ iso->irq, iso->gsi);
+ break;
+ }
+ }
+
+ /* Send INT#1 to IRQ#33 on CPU#0 */
+ write_io_apic(0x10 + (2 * overrides[1] + 0), 33);
+ write_io_apic(0x10 + (2 * overrides[1] + 1), bspId);
+
+ kprintf("Detected %d CPU%c", numCores, (numCores>1)?'s':'\0');
+
+ /* Initialise APs */
+ uintptr_t apTrampoline = 0x2000;
+ memcpy((void *) apTrampoline, &ap_trampoline, 0x1000);
+ for (i = 0; i < numCores; i++) {
+ if (lapicIds[i] == bspId)
+ continue;
+ /* Give each CPU a separate stack */
+ *((uint32_t *) (apTrampoline + 0xFF0)) = 0x7A000 - (i * 0x2000);
+ /* Send INIT IPI */
+ LAPIC(0x280) = 0;
+ LAPIC(0x310) = (LAPIC(0x310) & 0x00FFFFFF)
+ | (lapicIds[i] << 24);
+ LAPIC(0x300) = (LAPIC(0x300) & 0xFFF00000)
+ | 0xC500;
+ do {
+ asm("pause" ::: "memory");
+ } while (LAPIC(0x300) & (1 << 12));
+ LAPIC(0x310) = (LAPIC(0x310) & 0x00FFFFFF)
+ | (lapicIds[i] << 24);
+ LAPIC(0x300) = (LAPIC(0x300) & 0xFFF00000)
+ | 0x8500;
+ do {
+ asm("pause" ::: "memory");
+ } while (LAPIC(0x300) & (1 << 12));
+ delay(100);
+ /* Send STARTUP IPI (twice) */
+ for (j = 0; j < 2; j++) {
+ LAPIC(0x280) = 0;
+ LAPIC(0x310) = (LAPIC(0x310) & 0x00FFFFFF)
+ | (lapicIds[i] << 24);
+ LAPIC(0x300) = (LAPIC(0x300) & 0xFFF0F800)
+ | (0x0600 + ((apTrampoline >> 12) & 0xFF));
+ delay(2);
+ do {
+ asm("pause" ::: "memory");
+ } while (LAPIC(0x300) & (1 << 12));
+ }
+ }
+
+ /* Enable APIC */
+ enable_apic();
+ /* Disable PIC */
+ outb(0x21, 0xFF);
+ outb(0xA1, 0xFF);
+
+ apic_start_timer();
+}
+
+/* AP initialisation routine */
+void
+ap_startup(void)
+{
+// enable_apic();
+ cpu_load();
+// apic_start_timer();
+ current = NULL;
+ kprintf("CPU#%d idling", CPUID);
+ while (1) asm volatile("hlt");
+}
diff --git a/proc/gdt.c b/proc/gdt.c
new file mode 100644
index 0000000..6d9a7c7
--- /dev/null
+++ b/proc/gdt.c
@@ -0,0 +1,104 @@
+/*
+ * This file deals with the Global Descriptor Table. It creates a simple GDT,
+ * suitable for use in the Kernel, and attaches a TSS to the end. The TSS,
+ * which can be used to switch from ring 3 to ring 0, allows processes access to
+ * all IO ports.
+ */
+
+#include <stdint.h>
+#include "../mem/heap.h"
+#include "../mem/mem.h"
+#include "../mem/frame.h"
+#include "../proc/proc.h"
+#include "../screen.h"
+
+#define GDT_ENTRIES 6
+
+/* Structure for GDT entry */
+static struct GDTEntry {
+ uint16_t limitLower, baseLower;
+ uint8_t baseMiddle, access, gran, baseHigher;
+} __attribute__((packed)) *GDT;
+
+/* Structure for TSS entry */
+static struct TSSEntry {
+ uint32_t prevTSS;
+ uint32_t esp0, ss0;
+ uint32_t esp1, ss1;
+ uint32_t esp2, ss2;
+ uint32_t cr3, eip, eflags;
+ uint32_t eax, ecx, edx, ebx;
+ uint32_t esp, ebp, esi, edi;
+ uint32_t es, cs, ss, ds, fs, gs;
+ uint32_t ldt;
+ uint16_t trap, iomapBase;
+// uint32_t iomap[2048];
+} __attribute__((packed)) *TSS; /* Per CPU */
+
+void load_gdt(uint32_t gdtPtr);
+void load_tss(void);
+
+/* Set a gate of the GDT */
+static void
+gdt_set_gate(uint8_t num, uint32_t base, uint32_t limit,
+ uint8_t access, uint8_t gran)
+{
+ GDT[num].baseLower = (base & 0xFFFF);
+ GDT[num].baseMiddle = (base >> 16) & 0xFF;
+ GDT[num].baseHigher = (base >> 24) & 0xFF;
+ GDT[num].limitLower = (limit & 0xFFFF);
+ GDT[num].gran = (limit >> 16) & 0x0F;
+ GDT[num].gran |= gran & 0xF0;
+ GDT[num].access = access;
+}
+
+/* Create a TSS */
+static void
+write_tss(uint8_t num, uint32_t esp)
+{
+ uint32_t base = (uint32_t) TSS;
+ uint32_t limit = sizeof(struct TSSEntry) - 1;
+
+ gdt_set_gate(num, base, limit, 0xE9, 0x00);
+ num -= 5;
+ memset(&TSS[num], 0, sizeof(struct TSSEntry));
+ TSS[num].ss0 = 0x10;
+ TSS[num].esp0 = esp;
+ TSS[num].cs = 0x08 | 0;
+ TSS[num].ds = TSS[num].es = TSS[num].ss = 0x10 | 0;
+ TSS[num].fs = TSS[num].gs = 0x10 | 3;
+ TSS[num].iomapBase = 104;
+}
+
+/* Initialise the Kernel GDT */
+void
+init_gdt(void)
+{
+ GDT = (void *) alloc_frames(1);
+ TSS = (void *) (GDT + GDT_ENTRIES);
+ kprintf("Loaded GDT @ %#.8x, TSS @ %#.8x", GDT, TSS);
+}
+
+/* Load the Kernel GDT */
+void
+cpu_load_gdt(void)
+{
+ memset(GDT, 0, sizeof(GDT[0]) * GDT_ENTRIES);
+
+ gdt_set_gate(0, 0x00000000, 0x00000000, 0x00, 0x00); /* Null segment */
+ /* Ring 0 */
+ gdt_set_gate(1, 0x00000000, 0xFFFFFFFF, 0x9A, 0xCF); /* Code segment */
+ gdt_set_gate(2, 0x00000000, 0xFFFFFFFF, 0x92, 0xCF); /* Data segment */
+ /* Ring 3 */
+ gdt_set_gate(3, 0x00000000, 0xFFFFFFFF, 0xFA, 0xCF); /* Code segment */
+ gdt_set_gate(4, 0x00000000, 0xFFFFFFFF, 0xF2, 0xCF); /* Data segment */
+ /* TSS */
+ write_tss(5, 0xF0800000 - sizeof(uintptr_t));
+
+ uint16_t gdtPtr[3];
+ gdtPtr[0] = (sizeof(GDT[0]) * GDT_ENTRIES) - 1;
+ gdtPtr[1] = ((uint32_t) GDT) & 0xFFFF;
+ gdtPtr[2] = ((uint32_t) GDT) >> 16;
+ load_gdt((uint32_t) &gdtPtr);
+ load_tss();
+}
diff --git a/proc/idt.c b/proc/idt.c
new file mode 100644
index 0000000..a1552c2
--- /dev/null
+++ b/proc/idt.c
@@ -0,0 +1,148 @@
+/*
+ * This file deals with the Interrupt Descriptor Table. It creates a simple
+ * IDT, with hard-coded handler functions. It is these handler functions that
+ * can run custom handlers, registered at runtime. This file does not deal with
+ * dispatching messages/signals to processes on an interrupt.
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include "proc.h"
+#include "../mem/heap.h"
+#include "../mem/frame.h"
+#include "../screen.h"
+
+/* Structure for IDT entry */
+static struct IDTEntry {
+ uint16_t offsetLower, selector;
+ uint8_t zero, typeAttr;
+ uint16_t offsetHigher;
+} __attribute__((packed)) *IDT;
+
+void load_idt(uint32_t ptr[2]);
+/* Exceptions */
+void exc0(void);
+void exc1(void);
+void exc2(void);
+void exc3(void);
+void exc4(void);
+void exc5(void);
+void exc6(void);
+void exc7(void);
+void exc8(void);
+void exc9(void);
+void exc10(void);
+void exc11(void);
+void exc12(void);
+void exc13(void);
+void exc14(void);
+void exc15(void);
+void exc16(void);
+void exc17(void);
+void exc18(void);
+void exc19(void);
+void exc20(void);
+void exc30(void);
+void exc128(void); /* syscall */
+/* Interrutps */
+void irq0(void);
+void irq1(void);
+void irq2(void);
+void irq3(void);
+void irq4(void);
+void irq5(void);
+void irq6(void);
+void irq7(void);
+void irq8(void);
+void irq9(void);
+void irq10(void);
+void irq11(void);
+void irq12(void);
+void irq13(void);
+void irq14(void);
+void irq15(void);
+
+extern void (**exceptions)(InterruptFrame *);
+extern void (**interrupts)(InterruptFrame *);
+
+/* Install an IDT entry */
+static void
+install_idt_entry(uint8_t num, uint32_t addr)
+{
+ IDT[num].offsetLower = addr & 0xFFFF;
+ IDT[num].selector = 0x08;
+ IDT[num].zero = 0;
+ IDT[num].typeAttr = 0x8E | 0x60; /* Allowed from ring 3 */
+ IDT[num].offsetHigher = addr >> 16;
+}
+
+/* Initialise the IDT */
+void
+init_idt(void)
+{
+ IDT = (void *) alloc_frames(1);
+
+ /* Install exceptions */
+ install_idt_entry(0, (uint32_t) exc0);
+ install_idt_entry(1, (uint32_t) exc1);
+ install_idt_entry(2, (uint32_t) exc2);
+ install_idt_entry(3, (uint32_t) exc3);
+ install_idt_entry(4, (uint32_t) exc4);
+ install_idt_entry(5, (uint32_t) exc5);
+ install_idt_entry(6, (uint32_t) exc6);
+ install_idt_entry(7, (uint32_t) exc7);
+ install_idt_entry(8, (uint32_t) exc8);
+ install_idt_entry(9, (uint32_t) exc9);
+ install_idt_entry(10, (uint32_t) exc10);
+ install_idt_entry(11, (uint32_t) exc11);
+ install_idt_entry(12, (uint32_t) exc12);
+ install_idt_entry(13, (uint32_t) exc13);
+ install_idt_entry(14, (uint32_t) exc14);
+ install_idt_entry(15, (uint32_t) exc15);
+ install_idt_entry(16, (uint32_t) exc16);
+ install_idt_entry(17, (uint32_t) exc17);
+ install_idt_entry(18, (uint32_t) exc18);
+ install_idt_entry(19, (uint32_t) exc19);
+ install_idt_entry(20, (uint32_t) exc20);
+ install_idt_entry(30, (uint32_t) exc30);
+
+ /* Install interrutps */
+ install_idt_entry(32, (uint32_t) irq0);
+ install_idt_entry(33, (uint32_t) irq1);
+ install_idt_entry(34, (uint32_t) irq2);
+ install_idt_entry(35, (uint32_t) irq3);
+ install_idt_entry(36, (uint32_t) irq4);
+ install_idt_entry(37, (uint32_t) irq5);
+ install_idt_entry(38, (uint32_t) irq6);
+ install_idt_entry(39, (uint32_t) irq7);
+ install_idt_entry(40, (uint32_t) irq8);
+ install_idt_entry(41, (uint32_t) irq9);
+ install_idt_entry(42, (uint32_t) irq10);
+ install_idt_entry(43, (uint32_t) irq11);
+ install_idt_entry(44, (uint32_t) irq12);
+ install_idt_entry(45, (uint32_t) irq13);
+ install_idt_entry(46, (uint32_t) irq14);
+ install_idt_entry(47, (uint32_t) irq15);
+
+ /* Install syscall handler */
+ install_idt_entry(128, (uint32_t) exc128);
+
+ exceptions = (void (**)(InterruptFrame *)) (IDT + 256);
+ interrupts = (void (**)(InterruptFrame *)) (exceptions + 32);
+ memset(exceptions, 0, 1024);
+
+ kprintf("Loaded IDT @ %#.8x, Exceptions @ %#.8x, Interrupts @ %#.8x",
+ IDT, exceptions, interrupts);
+}
+
+/* Load the IDT */
+void
+cpu_load_idt(void)
+{
+ uint32_t idtAddr, idtPtr[2];
+ idtAddr = (uint32_t) IDT;
+ idtPtr[0] = sizeof(struct IDTEntry) * 256;
+ idtPtr[0] += (idtAddr & 0xFFFF) << 16;
+ idtPtr[1] = idtAddr >> 16;
+ load_idt(idtPtr);
+}
diff --git a/proc/irqs.c b/proc/irqs.c
new file mode 100644
index 0000000..f9a523c
--- /dev/null
+++ b/proc/irqs.c
@@ -0,0 +1,80 @@
+/*
+ * This file contains the abstraction for exceptions and interrupts. It
+ * presents a system of registrable exception/interrupt handlers. It also
+ * contains the generic handlers, which pass control to the appropriate
+ * registered handler. This file does not deal with dispatching
+ * messages/signals to processes on an interrupt.
+ */
+
+#include <stdint.h>
+#include "proc.h"
+#include "../proc/proc.h"
+#include "../task/task.h"
+#include "../io.h"
+#include "../screen.h"
+
+void (**exceptions)(InterruptFrame *);
+void (**interrupts)(InterruptFrame *);
+
+/* Register an exception handler */
+void
+register_exception(int num, void (*addr)(InterruptFrame *))
+{
+ if (num >= 0 && num < 32)
+ exceptions[num] = addr;
+}
+
+/* Register an interrupt handler */
+void
+register_interrupt(int num, void (*addr)(InterruptFrame *))
+{
+ if (num >= 0 && num < 224)
+ interrupts[num] = addr;
+}
+
+/* Call the appropriate exception handler */
+uintptr_t
+exc_handler(InterruptFrame frame)
+{
+ uint8_t num = frame.intNo & 0xFF;
+
+ /* System Call */
+ if (num == 0x80)
+ syscall_handler(&frame);
+ /* Other exception */
+ else if (exceptions[num])
+ exceptions[num](&frame);
+ else
+ panic("Unhandled exception %d (%#x) @ %#.8x [CPU#%d]\n"
+ "EFLAGS: %#.8x",
+ num, frame.errCode, frame.eip, CPUID, frame.eflags);
+
+ /* Send EOI */
+ LAPIC(0xB0) = 0;
+
+ if (current)
+ if (current->tls)
+ return current->tls->start + 4;
+ return 0;
+}
+
+/* Call the appropriate interrupt handler */
+uintptr_t
+irq_handler(InterruptFrame frame)
+{
+ uint8_t num = (frame.intNo & 0xFF) - 32;
+
+ if (interrupts[num])
+ interrupts[num](&frame);
+
+ /* Send EOI */
+ outb(0x20, 0x20);
+ if (num >= 8)
+ outb(0xA0, 0x20);
+ LAPIC(0xB0) = 0;
+
+ if (current)
+ if (current->tls)
+ return current->tls->start + 4;
+ return 0;
+}
diff --git a/proc/load.S b/proc/load.S
new file mode 100644
index 0000000..98330a0
--- /dev/null
+++ b/proc/load.S
@@ -0,0 +1,38 @@
+; This file is responsible for loading the tables onto the CPU. The functions
+; will load the GDT, IDT and TSS onto the CPU running the function. They only
+; provide a wrapper for the relevant x86 instruction, and do not do any table
+; creation. See the relevant C files for the table setup routines. The GDT and
+; IDT require a pointer to the table, while the TSS can be loaded from the
+; currently loaded GDT.
+
+[bits 32]
+
+; Load the IDT
+[global load_idt]
+load_idt:
+ mov eax, [esp + 4]
+ lidt [eax]
+ ret
+
+; Load the GDT
+[global load_gdt]
+load_gdt:
+ mov eax, [esp + 4]
+ lgdt [eax]
+
+ mov ax, 0x10
+ mov ds, ax
+ mov es, ax
+ mov fs, ax
+ mov gs, ax
+ mov ss, ax
+ jmp 0x08:.flush
+.flush:
+ ret
+
+; Load the TSS
+[global load_tss]
+load_tss:
+ mov ax, 0x28 | 3
+ ltr ax
+ ret
diff --git a/proc/pic.c b/proc/pic.c
new file mode 100644
index 0000000..e478921
--- /dev/null
+++ b/proc/pic.c
@@ -0,0 +1,40 @@
+/*
+ * This file deals with the Programmable Interrupt Controller. It is usually
+ * disable and replaced by the APIC.
+ */
+
+#include "../io.h"
+
+/* Initialise the PIC to a specified frequency */
+static void
+init_pit(void)
+{
+ uint32_t divisor = 1193182 / 1000;
+ outb(0x43, 0x36);
+ outb(0x40, divisor & 0xFF);
+ outb(0x40, (divisor >> 8) & 0xFF);
+}
+
+/* Initialise the PIC */
+void
+init_pic(void)
+{
+ /*
+ * By default interrupts and exceptions both start at IRQ#0. To avoid
+ * collision we can map interrupts to start at IRQ#32. Since lower
+ * numbered IRQ lines are higher priority, exceptions have priority.
+ */
+ outb(0x20, 0x11); io_wait();
+ outb(0xA0, 0x11); io_wait();
+ outb(0x21, 0x20); io_wait();
+ outb(0xA1, 0x28); io_wait();
+ outb(0x21, 0x04); io_wait();
+ outb(0xA1, 0x02); io_wait();
+ outb(0x21, 0x01); io_wait();
+ outb(0xA1, 0x01); io_wait();
+ outb(0x21, 0x00); io_wait();
+ outb(0xA1, 0x00); io_wait();
+
+ init_pit();
+}
+
diff --git a/proc/proc.h b/proc/proc.h
new file mode 100644
index 0000000..bba44cf
--- /dev/null
+++ b/proc/proc.h
@@ -0,0 +1,32 @@
+#ifndef KERNEL_PROC_H
+#define KERNEL_PROC_H
+
+#include <stdint.h>
+
+/* Structure pushed on interrupt */
+typedef struct InterruptFrame {
+ uint32_t ds, fs, gs;
+ uint32_t edi, esi, ebp, esp, ebx, edx, ecx, eax;
+ uint32_t intNo, errCode;
+ uint32_t eip, cs, eflags, useresp, ss;
+} InterruptFrame;
+
+extern uint8_t numCores;
+extern uint32_t lapicPtr, ioapicPtr;
+extern uint8_t lapicNums[];
+#define LAPIC(off) (*((uint32_t *) ((uint32_t) lapicPtr + (off))))
+#define IOAPIC(off) (*((uint32_t *) ((uint32_t) ioapicPtr + (off))))
+#define CPUID lapicNums[(uint8_t) (LAPIC(0x20) >> 24)]
+#define MAX_CPUS 2
+
+void init_pic(void);
+void init_multicore(void *ebda);
+
+void init_idt(void);
+void init_gdt(void);
+void cpu_load_idt(void);
+void cpu_load_gdt(void);
+void register_exception(int num, void (*handler)(InterruptFrame *));
+void register_interrupt(int num, void (*handler)(InterruptFrame *));
+
+#endif
diff --git a/proc/stub.S b/proc/stub.S
new file mode 100644
index 0000000..a16d014
--- /dev/null
+++ b/proc/stub.S
@@ -0,0 +1,149 @@
+; This file contains the stubs for the interrupt handlers, which can push more
+; useful information on the the stack, before calling a generic handler. Since
+; each exception/interrupt must have it's own handler, macros are used to create
+; a handler for each of them.
+
+[bits 32]
+
+[extern irq_handler]
+[extern exc_handler]
+
+; Exception without error code
+%macro EXC_NOERRCODE 1
+[global exc%1]
+exc%1:
+ cli
+ push byte 0
+ push %1
+ jmp exc_stub
+%endmacro
+
+; Exception with error code
+%macro EXC_ERRCODE 1
+[global exc%1]
+exc%1:
+ cli
+ push %1
+ jmp exc_stub
+%endmacro
+
+; Interrupt
+%macro IRQ 2
+[global irq%1]
+irq%1:
+ cli
+ push byte 0
+ push byte %2
+ jmp irq_stub
+%endmacro
+
+EXC_NOERRCODE 0
+EXC_NOERRCODE 1
+EXC_NOERRCODE 2
+EXC_NOERRCODE 3
+EXC_NOERRCODE 4
+EXC_NOERRCODE 5
+EXC_NOERRCODE 6
+EXC_NOERRCODE 7
+EXC_ERRCODE 8
+EXC_NOERRCODE 9
+EXC_ERRCODE 10
+EXC_ERRCODE 11
+EXC_ERRCODE 12
+EXC_ERRCODE 13
+EXC_ERRCODE 14
+EXC_NOERRCODE 15
+EXC_NOERRCODE 16
+EXC_ERRCODE 17
+EXC_NOERRCODE 18
+EXC_NOERRCODE 19
+EXC_NOERRCODE 20
+EXC_NOERRCODE 30
+EXC_NOERRCODE 128 ; syscall
+
+IRQ 0, 32
+IRQ 1, 33
+IRQ 2, 34
+IRQ 3, 35
+IRQ 4, 36
+IRQ 5, 37
+IRQ 6, 38
+IRQ 7, 39
+IRQ 8, 40
+IRQ 9, 41
+IRQ 10, 42
+IRQ 11, 43
+IRQ 12, 44
+IRQ 13, 45
+IRQ 14, 46
+IRQ 15, 47
+
+; Handle an exception
+exc_stub:
+ pusha
+ mov ax, gs
+ push eax
+ mov ax, fs
+ push eax
+ mov ax, ds
+ push eax
+
+ mov ax, 0x10
+ mov ds, ax
+ mov es, ax
+ mov fs, ax
+ mov gs, ax
+
+ call exc_handler
+
+ pop ebx
+ mov ds, bx
+ mov es, bx
+ pop ebx
+ mov fs, bx
+ pop ebx
+ mov gs, bx
+
+ mov ecx, 0xC0000101
+ xor edx, edx
+ wrmsr
+
+ popa
+ add esp, 8
+ sti
+ iret
+
+; Handle an interrupt
+irq_stub:
+ pusha
+ mov ax, gs
+ push eax
+ mov ax, fs
+ push eax
+ mov ax, ds
+ push eax
+
+ mov ax, 0x10
+ mov ds, ax
+ mov es, ax
+ mov fs, ax
+ mov gs, ax
+
+ call irq_handler
+
+ pop ebx
+ mov ds, bx
+ mov es, bx
+ pop ebx
+ mov fs, bx
+ pop ebx
+ mov gs, bx
+
+ mov ecx, 0xC0000101
+ xor edx, edx
+ wrmsr
+
+ popa
+ add esp, 8
+ sti
+ iret
diff --git a/proc/trampoline.S b/proc/trampoline.S
new file mode 100644
index 0000000..8a8656f
--- /dev/null
+++ b/proc/trampoline.S
@@ -0,0 +1,52 @@
+; This file contains the AP trampoline code to bounce it back into protected
+; mode and run the Kernel on them. The location of the stack is set by the
+; calling code. This code runs initially in real (16-bit) mode, at 0x0000 in
+; physical memory. Modifying this code should be done with care, since it makes
+; use of fixed offsets in memory to jump around.
+
+[bits 16]
+
+[extern ap_startup]
+
+; This is the code run by the APs on startup. They need to initialise
+; themselves, which includes setting up a GDT and switching to protected mode.
+; When they're in protected mode they can just run their main C function.
+[global ap_trampoline]
+ap_trampoline:
+ cli
+ cld
+ jmp 0x0000:0x2040 ; continue
+
+align 16
+.gdt_start:
+ dd 0, 0
+ dd 0x0000FFFF, 0x00CF9A00 ; Flat Code
+ dd 0x0000FFFF, 0x00CF9200 ; Flat Data
+ dd 0x00000068, 0x00CF8900 ; TSS
+.gdt_desc:
+ dw .gdt_desc - .gdt_start - 1
+ dd 0x2010 ; gdt_start
+ dd 0, 0
+
+align 64
+.continue:
+ xor ax, ax
+ mov ds, ax
+ lgdt [0x2030] ; gdt_desc
+ mov eax, cr0
+ or eax, 0x01
+ mov cr0, eax
+ jmp 0x08:0x2060 ; pm
+
+align 32
+[bits 32]
+.pm:
+ mov ax, 0x10
+ mov ds, ax
+ mov ss, ax
+ ; Load passed stack
+ mov eax, [0x2FF0]
+ mov esp, eax
+ mov ebp, esp
+ sti
+ jmp 0x08:ap_startup
diff --git a/screen.c b/screen.c
new file mode 100644
index 0000000..3a722cc
--- /dev/null
+++ b/screen.c
@@ -0,0 +1,356 @@
+/*
+ * This file controls the kernel screen. It has several routines which can
+ * output text to the debug port and the the VGA Text Monitor. Most of these
+ * functions are for logging - except the Kernel Panic routine. Hopefully it's
+ * never called. It should probably also halt execution on all CPUs, not just
+ * the local one. This would need IPIs to be sent, which can be implemented
+ * later.
+ */
+
+#include <stdarg.h>
+#include "mem/mem.h"
+#include "mem/vm.h"
+#include "task/task.h"
+#include "io.h"
+#include "spinlock.h"
+
+#define IS_DIGIT(c) ((c) >= '0' && (c) <= '9')
+
+#define ZEROPAD 1 /* Pad with zero */
+#define SIGN 2 /* Insigned/signed long */
+#define PLUS 4 /* Show plus */
+#define SPACE 8 /* Space if plus */
+#define LEFT 16 /* Left justified */
+#define SPECIAL 32 /* 0x */
+#define SMALL 64 /* Use 'abcdef' instead of 'ABCDEF' */
+
+#define DO_DIV(n,base) ({ \
+ int __res; \
+ __asm__("divl %4":"=a" (n),"=d" (__res):"0" (n),"1" (0),"r" (base)); \
+ __res; \
+})
+
+Spinlock screenLock;
+
+/* Do not convert */
+static int
+skip_atoi(const char **s)
+{
+ int i = 0;
+
+ while (IS_DIGIT(**s))
+ i = i*10 + *((*s)++) - '0';
+
+ return i;
+}
+
+/* Convert a number to ASCII */
+static char *
+number(char *str, int num, int base, int size, int precision, int type)
+{
+ char c, sign, tmp[36];
+ const char *digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ int i;
+
+ if (type & SMALL)
+ digits = "0123456789abcdefghijklmnopqrstuvwxyz";
+ if (type & LEFT)
+ type &= ~ZEROPAD;
+ if (base < 2 || base > 36)
+ return 0;
+ c = (type & ZEROPAD) ? '0' : ' ' ;
+ if (type & SIGN && num < 0) {
+ sign = '-';
+ num = -num;
+ } else {
+ sign = (type & PLUS) ? '+' : ((type & SPACE) ? ' ' : 0);
+ }
+ if (sign)
+ size--;
+ if (type & SPECIAL)
+ if (base == 16)
+ size -= 2;
+ else if (base == 8)
+ size--;
+ i = 0;
+ if (num == 0)
+ tmp[i++] = '0';
+ else while (num != 0)
+ tmp[i++] = digits[DO_DIV(num,base)];
+ if (i > precision)
+ precision = i;
+ size -= precision;
+ if (!(type & (ZEROPAD + LEFT)))
+ while (size-- > 0)
+ *str++ = ' ';
+ if (sign)
+ *str++ = sign;
+ if (type & SPECIAL)
+ if (base == 8) {
+ *str++ = '0';
+ } else if (base == 16) {
+ *str++ = '0';
+ *str++ = digits[33];
+ }
+ if (!(type & LEFT))
+ while (size-- > 0)
+ *str++ = c;
+ while (i < precision--)
+ *str++ = '0';
+ while (i-- > 0)
+ *str++ = tmp[i];
+ while (size-- > 0)
+ *str++ = ' ';
+ return str;
+}
+
+/* Print formatted to a buffer */
+static int
+vsprintf(char *buf, const char *fmt, va_list args)
+{
+ int len, i;
+ char *str, *s, *p;
+ int *ip, flags;
+ int field_width, precision, qualifier;
+
+ for (str = buf; *fmt; fmt++) {
+ if (*fmt != '%') {
+ *str++ = *fmt;
+ continue;
+ }
+
+ /* Process flags */
+ flags = 0;
+repeat:
+ fmt++;
+ switch (*fmt) {
+ case '-':
+ flags |= LEFT;
+ goto repeat;
+ case '+':
+ flags |= PLUS;
+ goto repeat;
+ case ' ':
+ flags |= SPACE;
+ goto repeat;
+ case '#':
+ flags |= SPECIAL;
+ goto repeat;
+ case '0':
+ flags |= ZEROPAD;
+ goto repeat;
+ }
+
+ /* Get field width */
+ field_width = -1;
+ if (IS_DIGIT(*fmt)) {
+ field_width = skip_atoi(&fmt);
+ } else if (*fmt == '*') {
+ field_width = va_arg(args, int);
+ if (field_width < 0) {
+ field_width = -field_width;
+ flags |= LEFT;
+ }
+ }
+
+ /* Get the precision */
+ precision = -1;
+ if (*fmt == '.') {
+ fmt++;
+ if (IS_DIGIT(*fmt)) {
+ precision = skip_atoi(&fmt);
+ } else if (*fmt == '*') {
+ precision = va_arg(args, int);
+ }
+ if (precision < 0)
+ precision = 0;
+ }
+
+ /* Get the conversion qualifier */
+ qualifier = -1;
+ if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') {
+ qualifier = *fmt;
+ fmt++;
+ }
+
+ switch (*fmt) {
+ case 'c':
+ if (!(flags & LEFT))
+ while (--field_width > 0)
+ *str++ = ' ';
+ *str++ = (unsigned char) va_arg(args, int);
+ while (--field_width > 0)
+ *str++ = ' ';
+ break;
+
+ case 's':
+ s = va_arg(args, char *);
+ len = 0;
+ p = s;
+ while (*p++) len++;
+ if (precision < 0)
+ precision = len;
+ else if (len > precision)
+ len = precision;
+
+ if (!(flags & LEFT))
+ while (len < field_width--)
+ *str++ = ' ';
+ for (i = 0; i < len; ++i)
+ *str++ = *s++;
+ while (len < field_width--)
+ *str++ = ' ';
+ break;
+
+ case 'o':
+ str = number(str, va_arg(args, unsigned long), 8,
+ field_width, precision, flags);
+ break;
+
+ case 'p':
+ if (field_width == -1) {
+ field_width = 8;
+ flags |= ZEROPAD;
+ }
+ str = number(str,
+ (unsigned long) va_arg(args, void *), 16,
+ field_width, precision, flags);
+ break;
+
+ case 'x':
+ flags |= SMALL;
+ /* FALLTHROUGH */
+ case 'X':
+ str = number(str, va_arg(args, unsigned long), 16,
+ field_width, precision, flags);
+ break;
+
+ case 'd': /* FALLTHROUGH */
+ case 'i':
+ flags |= SIGN;
+ /* FALLTHROUGH */
+ case 'u':
+ str = number(str, va_arg(args, unsigned long), 10,
+ field_width, precision, flags);
+ break;
+
+ case 'n':
+ ip = va_arg(args, int *);
+ *ip = (str - buf);
+ break;
+
+ default:
+ if (*fmt != '%')
+ *str++ = '%';
+ if (*fmt)
+ *str++ = *fmt;
+ else
+ --fmt;
+ break;
+ }
+ }
+ *str = '\0';
+ return str-buf;
+}
+
+/* Format a string into a buffer */
+int
+sprintf(char *buf, char *fmt, ...)
+{
+ int ret;
+ va_list args;
+ va_start(args, fmt);
+ ret = vsprintf(buf, fmt, args);
+ va_end(args);
+ return ret;
+}
+
+/* Print a character */
+static inline void
+print_char(char c)
+{
+ outb(0xE9, c);
+}
+
+/* Set the output colour */
+static inline void
+set_attribute(char c, char *esc)
+{
+ while (*esc)
+ outb(0xE9, *esc++);
+}
+
+/* Kernel panic */
+void
+panic(char *fmt, ...)
+{
+ char buf[1024], *error = buf;
+
+ /* Print error to serial port */
+ acquire(&screenLock);
+ set_attribute(0x04, "\033[31m");
+ va_list args;
+ va_start(args, fmt);
+ vsprintf(buf, fmt, args);
+ va_end(args);
+ while (*error) print_char(*error++);
+ print_char('\n');
+ release(&screenLock);
+
+ /* Hang */
+ while (1) asm("hlt");
+}
+
+/* Kernel print */
+void
+kprintf(char *fmt, ...)
+{
+ char buf[1024], *error = buf;
+
+ /* Print to debug port */
+ acquire(&screenLock);
+ set_attribute(0x03, "\033[36m");
+ va_list args;
+ va_start(args, fmt);
+ vsprintf(buf, fmt, args);
+ va_end(args);
+ while (*error) print_char(*error++);
+ print_char('\n');
+ release(&screenLock);
+}
+
+/* Debug prefix print */
+static void
+dbgprefix(char *fmt, ...)
+{
+ char buf[1024], *error = buf;
+
+ /* Print to debug port */
+ set_attribute(0x0E, "\033[93;01m");
+ va_list args;
+ va_start(args, fmt);
+ vsprintf(buf, fmt, args);
+ va_end(args);
+ while (*error) print_char(*error++);
+}
+
+/* Debug print */
+void
+dbgprintf(char *msg)
+{
+ size_t len = 1024;
+
+ if (!msg)
+ return;
+
+ if (!verify_access(msg, strnlen(msg, len), PROT_READ))
+ return;
+
+ /* Print to debug port */
+ acquire(&screenLock);
+ dbgprefix("%s(%d:%d): ", current->name, current->tgid, current->tid);
+ set_attribute(0x0F, "\033[0m");
+ while (*msg && len--) print_char(*msg++);
+ print_char('\n');
+ release(&screenLock);
+}
diff --git a/screen.h b/screen.h
new file mode 100644
index 0000000..3fbda71
--- /dev/null
+++ b/screen.h
@@ -0,0 +1,16 @@
+#ifndef KERNEL_SCREEN_H
+#define KERNEL_SCREEN_H
+
+int sprintf(char *buf, char *fmt, ...);
+void panic(char *fmt, ...);
+void kprintf(char *fmt, ...);
+void dbgprintf(char *msg);
+
+#define ASSERT(c) ({ \
+ if (!(c)) \
+ panic("Assertion failed (%s:%d): " #c, \
+ __FILE__, __LINE__); \
+ c; \
+})
+
+#endif
diff --git a/spinlock.c b/spinlock.c
new file mode 100644
index 0000000..059f65e
--- /dev/null
+++ b/spinlock.c
@@ -0,0 +1,70 @@
+/*
+ * This file contains the implementation of spinlocks. It makes heavy use of
+ * GCC's atomic built-ins for synchronization. The spinlocks have some simple
+ * mechanisms for preventing deadlocks. Each spinlock knows which CPU/task is
+ * holding it, and can allow that CPU/task to acquire it multiple times and
+ * safely release it.
+ */
+
+#include <stdint.h>
+#include "task/task.h"
+#include "proc/proc.h"
+#include "spinlock.h"
+#include "screen.h"
+#include "io.h"
+
+/* Check if already holding */
+static uint8_t
+holding(Spinlock *lock)
+{
+ uint8_t r;
+ r = lock->locked;
+ r &= (current ? lock->task == current->tid : lock->cpu == CPUID);
+ return r;
+}
+
+/* Create a new lock */
+void
+init_lock(Spinlock *lock)
+{
+ lock->locked = 0;
+ lock->task = 0;
+ lock->cpu = 0;
+ lock->ref = 0;
+}
+
+/* Acquire a lock */
+void
+acquire(Spinlock *lock)
+{
+ /*
+ * Reference count the lock so it can be safely acquired by the same
+ * holder multiple times. This stops a lock from deadlocking itself.
+ */
+ if (holding(lock)) {
+ lock->ref++;
+ return;
+ }
+ while (!__sync_bool_compare_and_swap(&lock->locked, 0, 1))
+ asm volatile("pause");
+ __sync_synchronize();
+ if (current)
+ lock->task = current->tid;
+ lock->cpu = CPUID;
+}
+
+/* Release a lock */
+void
+release(Spinlock *lock)
+{
+ if (!holding(lock))
+ panic("Cannot release unheld lock");
+ if (lock->ref) {
+ lock->ref--;
+ return;
+ }
+ lock->task = 0;
+ lock->cpu = 0;
+ __sync_lock_release(&lock->locked);
+ __sync_synchronize();
+}
diff --git a/spinlock.h b/spinlock.h
new file mode 100644
index 0000000..75d7154
--- /dev/null
+++ b/spinlock.h
@@ -0,0 +1,19 @@
+#ifndef KERNEL_SPINLOCK_H
+#define KERNEL_SPINLOCK_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+/* Structure for a spinlock */
+typedef struct Spinlock {
+ uint32_t locked;
+ pid_t task;
+ uint8_t cpu;
+ uint8_t ref;
+} Spinlock;
+
+void init_lock(Spinlock *lock);
+void acquire(Spinlock *lock);
+void release(Spinlock *lock);
+
+#endif
diff --git a/task/exec.c b/task/exec.c
new file mode 100644
index 0000000..dffc92b
--- /dev/null
+++ b/task/exec.c
@@ -0,0 +1,321 @@
+/*
+ * This file deals with loading programs from the Kernel File System. The
+ * programs in KernelFS are statically linked, so they just need to be copied
+ * into their address space and run. Since KernelFS is present in memory, this
+ * process is very simple.
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include "../proc/proc.h"
+#include "../task/task.h"
+#include "../mem/heap.h"
+#include "../mem/paging.h"
+#include "../mem/vm.h"
+#include "../vfs/vfs.h"
+
+#define KFS_START ((void *) 0x100000)
+
+/* Structure of a KernelFS file listing */
+typedef struct KFSEntry {
+ char name[27];
+ uint8_t size;
+ uint32_t start;
+} __attribute__((packed)) KFSEntry;
+
+/* ELF File Header */
+typedef struct ELFHeader {
+ char magic[4];
+ uint8_t arch;
+ uint8_t endian;
+ uint8_t headerVersion;
+ uint8_t osAbi;
+ uint8_t reserved[8];
+ uint16_t type;
+ uint16_t isa;
+ uint32_t version;
+ uint32_t entry;
+ uint32_t programHeader;
+ uint32_t sectionHeader;
+ uint32_t flags;
+ uint16_t headerSize;
+ uint16_t programEntrySize;
+ uint16_t numProgramEntries;
+ uint16_t sectionEntrySize;
+ uint16_t numSectionEntries;
+ uint16_t sectionNames;
+} ELFHeader;
+
+/* ELF Section Header */
+typedef struct SectionHeader {
+ uint32_t name;
+ uint32_t type;
+ uint32_t flags;
+ uint32_t address;
+ uint32_t offset;
+ uint32_t size;
+ uint32_t link;
+ uint32_t info;
+ uint32_t align;
+ uint32_t entrySize;
+} SectionHeader;
+
+/* ELF Program Header */
+typedef struct ProgramHeader {
+ uint32_t type;
+ uint32_t offset;
+ uint32_t address;
+ uint32_t reserved;
+ uint32_t filesz;
+ uint32_t memsz;
+ uint32_t flags;
+ uint32_t align;
+} ProgramHeader;
+
+static KFSEntry *
+get_file_by_name(char *name)
+{
+ KFSEntry *index;
+ for (index = KFS_START;
+ index < (KFSEntry *) (KFS_START + 512);
+ index++) {
+ if (!strcmp(name, index->name))
+ return index;
+ }
+ return (KFSEntry *) 0;
+}
+
+/* Get SectionHeader by index */
+static SectionHeader *
+section_header(ELFHeader *header, uint16_t index)
+{
+ SectionHeader *section;
+ section = (void *) ((char *) header + header->sectionHeader +
+ (index * header->sectionEntrySize));
+ return section;
+}
+
+/* Get a Section name */
+static char *
+section_name(ELFHeader *header, uint32_t index)
+{
+ char *data = (char *) header + section_header(header,
+ header->sectionNames)->offset;
+ return data + index;
+}
+
+/* Get ProgramHeader by index */
+static ProgramHeader *
+program_header(ELFHeader *header, uint16_t index)
+{
+ ProgramHeader *program;
+ program = (void *) ((char *) header + header->programHeader +
+ (index * header->programEntrySize));
+ return program;
+}
+
+extern TaskQueue tasks;
+
+/* Execute a program */
+int
+execve(const char *file, char *argv[], char *envp[])
+{
+ if (current->tid != current->tgid) {
+ /*
+ * TODO: This should execute the program to execute in the
+ * "thread group leader" (the Task where tid = current->tgid)
+ * and all other threads in the group should be exited.
+ */
+ return -EFAULT;
+ }
+
+ if (!verify_access(file, strnlen(file, PATH_MAX), PROT_READ))
+ return -EFAULT;
+
+ /* Count argv and envp */
+ int argc, argi, envc, envi;
+ if (argv == NULL) argc = 0;
+ else for (argc = 0; argv[argc] != NULL; argc++);
+ if (envp == NULL) envc = 0;
+ else for (envc = 0; envp[envc] != NULL; envc++);
+ /* Find size of argv and envp strings */
+ size_t ssz = sizeof(int)
+ + (sizeof(uintptr_t) * (argc + 1))
+ + (sizeof(uintptr_t) * (envc + 1));
+ for (argi = 0; argi < argc; argi++) {
+ if (!verify_access(argv[argi], strlen(argv[argi]), PROT_READ))
+ return -EFAULT;
+ ssz += strlen(argv[argi]) + 1;
+ }
+ for (envi = 0; envi < envc; envi++) {
+ if (!verify_access(envp[envi], strlen(envp[envi]), PROT_READ))
+ return -EFAULT;
+ ssz += strlen(envp[envi]) + 1;
+ }
+
+ /* Read ELF header */
+ ELFHeader header;
+ int fd = open(file, O_RDONLY);
+ if (fd < 0)
+ return fd;
+
+ current->inSyscall = 0;
+
+ /* Only execute regular files */
+ if (!S_ISREG(current->files->fd[fd]->mode)) {
+ close(fd);
+ return -EACCES;
+ }
+
+ /* Read file header */
+ read(fd, &header, sizeof(ELFHeader));
+ if (memcmp(header.magic, "\x7F""ELF", 4) || header.isa != 3) {
+ close(fd);
+ return -ENOEXEC;
+ }
+ if (header.type != 2) { /* 1: relocatable, 2: executable */
+ close(fd);
+ return -ENOEXEC;
+ }
+
+ /*
+ * POINT OF NO RETURN
+ */
+
+ /* Set process name */
+ kfree(current->name);
+ current->name = kmalloc(strlen(file)+1);
+ memcpy(current->name, file, strlen(file)+1);
+
+ /* Store everything (pointers adjusted) in temporary buffer */
+ uintptr_t esp = 0xE0000000 - ssz;
+ char *istack = kmalloc(ssz);
+ char *isp = istack + ssz;
+ for (envi = envc - 1; envi >= 0; envi--) {
+ isp -= strlen(envp[envi]) + 1;
+ memcpy(isp, envp[envi], strlen(envp[envi]) + 1);
+ envp[envi] = (char *) (isp - istack) + esp;
+ }
+ if (envp)
+ envp[envc] = NULL;
+ for (argi = argc - 1; argi >= 0; argi--) {
+ isp -= strlen(argv[argi]) + 1;
+ memcpy(isp, argv[argi], strlen(argv[argi]) + 1);
+ argv[argi] = (char *) (isp - istack) + esp;
+ }
+ if (argv)
+ argv[argc] = NULL;
+ isp -= sizeof(uintptr_t);
+ *((uintptr_t *) isp ) = (uintptr_t) NULL;
+ isp -= sizeof(uintptr_t) * envc;
+ memcpy(isp, envp, sizeof(uintptr_t) * envc);
+ isp -= sizeof(uintptr_t);
+ *((uintptr_t *) isp ) = (uintptr_t) NULL;
+ isp -= sizeof(uintptr_t) * argc;
+ memcpy(isp, argv, sizeof(uintptr_t) * argc);
+ isp -= sizeof(int);
+ *((int *) isp) = argc;
+
+ /* Destroy previous executable */
+ VMRegion *head;
+ for (head = current->vm->regions; head; head = head->next)
+ vm_destroy_region(head);
+
+ /* Program headers */
+ size_t p;
+ off_t off;
+ uintptr_t pgbrk, heapEnd;
+ ProgramHeader ph, tlsph;
+ memset(&tlsph, 0, sizeof(ProgramHeader));
+ for (p = 0; p < header.numProgramEntries; p++) {
+ off = header.programHeader + (p * header.programEntrySize);
+ lseek(fd, off, 0);
+ read(fd, &ph, sizeof(ProgramHeader));
+ if (ph.type != 1) {
+ if (ph.type == 7)
+ memcpy(&tlsph, &ph, sizeof(ProgramHeader));
+ continue;
+ }
+
+ /* Map data into region */
+ mmap((void *) ph.address, ph.filesz, ph.flags,
+ MAP_PRIVATE, fd, ph.offset & ~0xFFF);
+ /* Space left before */
+ if (ph.address & 0xFFF) {
+ memset((void *) (ph.address & ~0xFFF), 0,
+ ph.address - (ph.address & ~0xFFF));
+ }
+ /* Unset memory */
+ if (ph.memsz > ph.filesz) {
+ pgbrk = (ph.address + ph.filesz + 0xFFF) & ~0xFFF;
+ memset((void *) (ph.address + ph.filesz), 0,
+ pgbrk - ph.address - ph.filesz);
+ if (ph.memsz > pgbrk - ph.address)
+ mmap((void *) pgbrk,
+ ph.memsz - (pgbrk - ph.address),
+ ph.flags, MAP_PRIVATE | MAP_ANONYMOUS,
+ -1, 0);
+ }
+ if (ph.address + ph.memsz > heapEnd)
+ heapEnd = ph.address + ph.memsz;
+ }
+
+ /* Store executable */
+ current->executable = file_get(current->files->fd[fd]);
+ close(fd);
+
+ /* Thread Local Storage */
+ /* FIXME */
+ if (current->tls)
+ vm_destroy_region(current->tls);
+ if (tlsph.type == 7) {
+ /* should be filesz not memsz */
+ tlsph.flags |= PROT_WRITE;
+ current->tls = vm_create_region((void *) ((heapEnd + 0xFFF) & ~0xFFF),
+ tlsph.memsz + 4, tlsph.flags,
+ MAP_PRIVATE | MAP_ANONYMOUS,
+ 0, NULL);
+// tlsph.offset, current->executable);
+ vm_remove_region(current->tls);
+ *((uint32_t *) current->tls->start + 1) = current->tls->start + 4;
+ if (tlsph.filesz)
+ memcpy((void *) current->tls->start,
+ (void *) tlsph.address, tlsph.filesz);
+ }
+
+ /* Stack area */
+ VMRegion *oldstack = current->stack;
+ current->stack = vm_create_region((void *) 0xDFC00000, 0x400000,
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, 0, NULL);
+ vm_remove_region(current->stack);
+ if (oldstack)
+ vm_destroy_region(oldstack);
+
+ memcpy((void *) esp, istack, ssz);
+ kfree(istack);
+
+ /* Switch to user-mode */
+ asm volatile(
+ "cli;"
+ "mov $0x23, %%ax;"
+ "mov %%ax, %%ds;"
+ "mov %%ax, %%es;"
+ "mov %%ax, %%fs;"
+ "mov %%ax, %%gs;"
+ "mov %%esi, %%eax;" // "movl %%esp, %%eax;"
+ "pushl $0x23;"
+ "pushl %%eax;"
+ "pushf;"
+ "pop %%eax;"
+ "or $0x200, %%eax;" /* Enable interrupts */
+ "push %%eax;"
+ "pushl $0x1B;"
+ "pushl %%ebx;"
+ "iret;"
+ : : "b" (header.entry), "S" (esp)
+ );
+ /* UNREACHED */
+}
diff --git a/task/ipc.c b/task/ipc.c
new file mode 100644
index 0000000..1faafd9
--- /dev/null
+++ b/task/ipc.c
@@ -0,0 +1,131 @@
+/*
+ * This file contains all the functions related to message passing and
+ * inter-process communication.
+ */
+
+#include <stdint.h>
+#include <stddef.h>
+#include <sys/ipc.h>
+#include "task.h"
+#include "../proc/proc.h"
+#include "../mem/mem.h"
+#include "../mem/heap.h"
+#include "../spinlock.h"
+
+extern TaskQueue readyQueue;
+
+/* Block until a message is received */
+static uint8_t
+block_recv(Task *task, uint32_t from)
+{
+// Message *head, *prev;
+// /* Blocking for RECEIVE from any */
+// if (!task->msgQueue)
+// return 0;
+// /* Block for RECEIVE from specific process */
+// if (task->msgQueue->from != from && from != ANY) {
+// for (head = task->msgQueue;
+// head->from != from;
+// prev = head, head = head->next);
+// /* End of list */
+// if (!head) return 0;
+// /* Move message to start of queue */
+// if (head != task->msgQueue) {
+// prev->next = head->next;
+// head->next = task->msgQueue;
+// task->msgQueue = head;
+// }
+// }
+//
+// return 1;
+}
+
+/* Send a message */
+Message *
+nb_send_msg(pid_t to, uint16_t type, MessageContent *msg)
+{
+// Message *item, *msgSearch;
+// Task *taskSearch;
+//
+// item = kmalloc(sizeof(Message));
+// memcpy(&item->msg, msg, sizeof(MessageContent));
+// item->from = current->tid;
+// item->type = type;
+// item->next = 0;
+//
+// /* Find target process */
+// for (taskSearch = readyQueue;
+// taskSearch->tid != to && taskSearch;
+// taskSearch = taskSearch->next);
+// /* Add message to queue */
+// if (taskSearch) {
+// acquire(&taskSearch->lock);
+// if (taskSearch->msgQueue) {
+// for (msgSearch = taskSearch->msgQueue;
+// msgSearch->next;
+// msgSearch = msgSearch->next);
+// msgSearch->next = item;
+// } else {
+// taskSearch->msgQueue = item;
+// }
+// release(&taskSearch->lock);
+// }
+//
+// if (taskSearch)
+// return item;
+// kfree(item);
+// return NULL;
+}
+
+/* Send a message and block until it is delivered */
+Message *
+send_msg(pid_t to, uint16_t type, MessageContent *msg)
+{
+// Message *nb = nb_send_msg(to, type, msg);
+// if (!nb) return NULL;
+// block(block_send, (uint32_t) nb);
+// return nb;
+}
+
+/* Receive a message */
+pid_t
+nb_recv_msg(Message *buf, pid_t from)
+{
+// Message *msg;
+// Task *taskSearch;
+//
+// acquire(¤t->lock);
+// msg = current->msgQueue;
+// if (msg && (msg->from == from || from == ANY)) {
+// current->msgQueue = msg->next;
+// memcpy(buf, msg, sizeof(Message));
+// kfree(msg);
+// }
+// release(¤t->lock);
+//
+// if (msg && (buf->from == from || from == ANY)) {
+// /* Find sending process */
+// for (taskSearch = readyQueue;
+// taskSearch->tid != buf->from && taskSearch;
+// taskSearch = taskSearch->next);
+// if (taskSearch) {
+// if (taskSearch->block.function == block_send
+// && taskSearch->block.condition == (uint32_t) msg)
+// taskSearch->block.function = NULL;
+// }
+// return buf->from;
+// }
+// return 0;
+}
+
+/* Block until a message is received */
+pid_t
+recv_msg(Message *buf, pid_t from)
+{
+// pid_t nb;
+//check:
+// nb = nb_recv_msg(buf, from);
+// if (nb) return nb;
+// block(block_recv, from);
+// goto check;
+}
diff --git a/task/process.S b/task/process.S
new file mode 100644
index 0000000..131481f
--- /dev/null
+++ b/task/process.S
@@ -0,0 +1,23 @@
+; This file contains a few assembly routines related to tasking. These routines
+; should be minimal, and get called from within various functions from the
+; tasking system. See the relevant C source for a better description of how the
+; functions work.
+
+; Get the return address
+[global read_eip]
+read_eip:
+ mov eax, [esp]
+ ret
+
+; Switch to a task's context
+[global context_switch]
+context_switch:
+ cli
+ mov ecx, [esp + 4]
+ mov eax, [esp + 8]
+ mov ebp, [esp + 12]
+ mov esp, [esp + 16]
+ mov cr3, eax
+ mov eax, 0x10032004 ; Magic number
+ sti
+ jmp ecx
diff --git a/task/schedule.c b/task/schedule.c
new file mode 100644
index 0000000..1c00576
--- /dev/null
+++ b/task/schedule.c
@@ -0,0 +1,182 @@
+/*
+ * This file controls the Kernel's scheduler. It decides when a new task must
+ * be scheduled, and which tasks can actually be scheduled.
+ */
+
+#include <stdint.h>
+#include "../proc/proc.h"
+#include "../mem/paging.h"
+#include "task.h"
+
+uintptr_t read_eip(void);
+void context_switch(uintptr_t eip, page_dir_t phys,
+ uintptr_t ebp, uintptr_t esp);
+void handle_signals(void);
+
+TaskQueue readyQueue, tasks;
+Task *currentTask[MAX_CPUS];
+
+/* Switch to a task */
+static void
+switch_to_task(Task *task)
+{
+ uintptr_t esp, ebp, eip;
+ asm volatile("mov %%esp, %0" : "=r" (esp));
+ asm volatile("mov %%ebp, %0" : "=r" (ebp));
+ eip = read_eip();
+ if (eip == 0x10032004) /* Magic number */
+ return;
+
+ acquire(¤t->lock);
+ current->eip = eip;
+ current->esp = esp;
+ current->ebp = ebp;
+ release(¤t->lock);
+ current = task;
+ eip = current->eip;
+ esp = current->esp;
+ ebp = current->ebp;
+
+ context_switch(eip, current->pageDir, ebp, esp);
+ /* UNREACHED */
+
+ /*
+ * This code actually returns to the read_eip() call above. This is due
+ * to how the call works. The context switch jumps to the address
+ * stored in the eip variable. This address is the return address of
+ * the read_eip() call. The context_switch() function sets the return
+ * value before jumping, so it appears as though read_eip() returned
+ * that value. The code uses the magic number 0x10032004 to tell when
+ * this is occurring and just returns early.
+ */
+}
+
+/* Find task by ID */
+Task *
+find_task(pid_t tid)
+{
+ Task *task;
+ for (task = tasks.start; task && task->tid != tid; task = task->tnext);
+ return task;
+}
+
+/* Add a task to a task queue */
+void
+add_to_queue(TaskQueue *queue, Task *task)
+{
+ if (!queue->start) {
+ queue->start = task;
+ queue->end = task;
+ } else {
+ queue->end->next = task;
+ queue->end = task;
+ }
+ task->next = NULL;
+}
+
+/* Remove a task from a task queue */
+void
+remove_from_queue(TaskQueue *queue, Task *task)
+{
+ /* Start of list */
+ if (queue->start == task) {
+ queue->start = task->next;
+ if (!queue->start)
+ queue->end = NULL;
+ return;
+ }
+
+ /* Search */
+ Task *prev;
+ for (prev = queue->start; prev->next; prev = prev->next)
+ if (prev->next == task)
+ break;
+ if (prev->next) {
+ prev->next = task->next;
+ if (queue->end == task)
+ queue->end = prev;
+ }
+}
+
+/* Remove the first task from a task queue */
+Task *
+pop_from_queue(TaskQueue *queue)
+{
+ Task *head = queue->start;
+ queue->start = head->next;
+ if (!queue->start)
+ queue->end = NULL;
+ return head;
+}
+
+/* Block a task */
+void
+block_task(int reason)
+{
+ acquire(¤t->lock);
+ current->state = reason;
+ release(¤t->lock);
+ schedule();
+}
+
+/* Unblock a task */
+void
+unblock_task(Task *task)
+{
+ if (task->state == READY || task->state == RUNNING)
+ return;
+ task->state = READY;
+ if (!readyQueue.start || task->priority > current->priority) {
+ acquire(&readyQueue.lock);
+ task->next = readyQueue.start;
+ readyQueue.start = task;
+ if (!readyQueue.end)
+ readyQueue.end = task;
+ release(&readyQueue.lock);
+ } else {
+ add_to_queue(&readyQueue, task);
+ }
+}
+
+/* Schedule next task */
+void
+schedule(void)
+{
+ Task *task = current;
+
+ /* Next schedulable task */
+ if (readyQueue.start) {
+ acquire(&readyQueue.lock);
+ task = readyQueue.start;
+ readyQueue.start = task->next;
+ if (!readyQueue.start)
+ readyQueue.end = NULL;
+ task->state = RUNNING;
+ task->next = NULL;
+ if (current->state == RUNNING) {
+ current->state = READY;
+ add_to_queue(&readyQueue, current);
+ }
+ release(&readyQueue.lock);
+ switch_to_task(task);
+ /* Idle task */
+ } else if (current->state != RUNNING) {
+ current = NULL;
+ asm volatile("sti");
+ while (!readyQueue.start)
+ asm volatile("hlt");
+ asm volatile("cli");
+ current = task;
+ acquire(&readyQueue.lock);
+ task = readyQueue.start;
+ readyQueue.start = task->next;
+ if (!readyQueue.start)
+ readyQueue.end = NULL;
+ task->state = RUNNING;
+ task->next = NULL;
+ release(&readyQueue.lock);
+ switch_to_task(task);
+ }
+
+ handle_signals();
+}
diff --git a/task/signal.c b/task/signal.c
new file mode 100644
index 0000000..c80c279
--- /dev/null
+++ b/task/signal.c
@@ -0,0 +1,63 @@
+/*
+ * This file handles signals to tasks. It handles blocking signals, and
+ * registering the signal handlers. It send/dispatches signals to tasks and
+ * runs the registered signal handlers when appropriate.
+ */
+
+#include <stdint.h>
+#include <errno.h>
+#include "task.h"
+
+extern TaskQueue tasks;
+
+/* Handle the signals for a task */
+void
+handle_signals(void)
+{
+ if (!(current->sigset & ~current->blockedSignals))
+ return;
+
+ int signal;
+ for (signal = 0; signal < 32; signal++) {
+ if (!(current->sigset & (1 << (signal - 1))))
+ continue;
+ current->status = signal;
+ terminate();
+ }
+}
+
+/* Send a signal to a thread */
+int
+tgkill(pid_t tgid, pid_t tid, int sig)
+{
+ if (sig < 0 || sig > 31)
+ return -EINVAL;
+
+ Task *task = find_task(tid);
+ if (!task || task->tgid != tgid)
+ return -ESRCH;
+ if (sig)
+ task->sigset |= (1 << (sig - 1));
+
+ return 0;
+}
+
+/* Send a signal to a process */
+int
+kill(pid_t pid, int sig)
+{
+ if (sig < 0 || sig > 31)
+ return -EINVAL;
+
+ int sent = 0;
+ Task *task;
+ for (task = tasks.start; task; task = task->tnext) {
+ if (task->tgid != pid)
+ continue;
+ if (sig)
+ task->sigset |= (1 << (sig - 1));
+ sent++;
+ }
+
+ return sent ? 0 : -ESRCH;
+}
diff --git a/task/syscall.c b/task/syscall.c
new file mode 100644
index 0000000..d43542d
--- /dev/null
+++ b/task/syscall.c
@@ -0,0 +1,96 @@
+/*
+ * This file handles system calls. Every system call gets passed to the
+ * syscall_handler() function, which decides which Kernel function to run.
+ * There is an array of syscalls, which are indexed numerically, these are the
+ * syscall numbers.
+ */
+
+#include <sys/syscall.h>
+#include <sys/mman.h>
+#include <errno.h>
+#include "task.h"
+#include "../screen.h"
+
+/* List of syscalls */
+void *syscalls[] = {
+ /* Tasking */
+ [SYSCALL_DBGPRINTF] = dbgprintf,
+ [SYSCALL_CLONE] = clone,
+ [SYSCALL_EXIT] = exit,
+ [SYSCALL_GETPID] = getpid,
+ [SYSCALL_GETUID] = getuid,
+ [SYSCALL_SETUID] = setuid,
+ [SYSCALL_GETEUID] = geteuid,
+ [SYSCALL_SETEUID] = seteuid,
+ [SYSCALL_GETGID] = getgid,
+ [SYSCALL_SETGID] = setgid,
+ [SYSCALL_GETEGID] = getegid,
+ [SYSCALL_SETEGID] = setegid,
+ [SYSCALL_EXECVE] = execve,
+ [SYSCALL_WAITPID] = waitpid,
+ [SYSCALL_TGKILL] = tgkill,
+ [SYSCALL_KILL] = kill,
+ [SYSCALL_TIME] = time,
+ [SYSCALL_TIMES] = times,
+ [SYSCALL_SLEEP] = sleep,
+
+ /* Files */
+ [SYSCALL_OPEN] = open,
+ [SYSCALL_CLOSE] = close,
+ [SYSCALL_READ] = read,
+ [SYSCALL_WRITE] = write,
+ [SYSCALL_IOCTL] = ioctl,
+ [SYSCALL_LSEEK] = lseek,
+ [SYSCALL_STAT] = stat,
+ [SYSCALL_GETDENTS] = getdents,
+ [SYSCALL_MKDIR] = mkdir,
+ [SYSCALL_RMDIR] = rmdir,
+ [SYSCALL_MKNOD] = mknod,
+ [SYSCALL_RENAME] = rename,
+ [SYSCALL_DUP] = dup,
+ [SYSCALL_DUP2] = dup2,
+ [SYSCALL_ISATTY] = isatty,
+
+ /* File System */
+ [SYSCALL_MOUNT] = mount,
+ [SYSCALL_CHDIR] = chdir,
+ [SYSCALL_CHROOT] = chroot,
+ [SYSCALL_GETCWD] = getcwd,
+
+ /* Memory */
+ [SYSCALL_MMAP] = mmap,
+
+ /* Messaging */
+ [SYSCALL_NB_SEND_MSG] = nb_send_msg,
+ [SYSCALL_SEND_MSG] = send_msg,
+ [SYSCALL_NB_RECV_MSG] = nb_recv_msg,
+ [SYSCALL_RECV_MSG] = recv_msg,
+};
+
+/* Handle a syscall */
+void
+syscall_handler(InterruptFrame *frame)
+{
+ if (frame->eax >= sizeof(syscalls)/sizeof(syscalls[0])) {
+ frame->eax = -EINVAL;
+ return;
+ }
+ void *handler = syscalls[frame->eax];
+ int ret;
+ current->inSyscall = 1;
+
+ size_t num;
+ int *val = (int *) frame->esi;
+ if (!verify_access(val, frame->ecx * sizeof(int), PROT_READ)) {
+ frame->eax = -EINVAL;
+ return;
+ }
+ /* Push args onto stack */
+ for (num = frame->ecx; num; num--)
+ asm volatile("pushl %0" :: "r" (val[num - 1]));
+ /* Call handler */
+ asm volatile("call *%1" : "=a" (ret) : "r" (handler));
+
+ frame->eax = ret;
+ current->inSyscall = 0;
+}
diff --git a/task/task.c b/task/task.c
new file mode 100644
index 0000000..294b3cc
--- /dev/null
+++ b/task/task.c
@@ -0,0 +1,420 @@
+/*
+ * This file is responsible for the core functions related to multitasking. It
+ * relies on a decent paging implementation. It contains several routines which
+ * are related to the creation, modification and deletion of tasks.
+ */
+
+#include <stdint.h>
+#include <sys/sched.h>
+#include <errno.h>
+#include "task.h"
+#include "../vfs/vfs.h"
+#include "../vfs/inode.h"
+#include "../vfs/cache.h"
+#include "../vfs/tmpfs/fs.h"
+#include "../mem/mem.h"
+#include "../mem/heap.h"
+#include "../mem/paging.h"
+#include "../proc/proc.h"
+#include "../screen.h"
+
+extern TaskQueue readyQueue, tasks;
+pid_t nextPid = 1;
+
+extern uint32_t initialStack;
+extern page_dir_t kernelDir;
+
+uintptr_t read_eip(void);
+
+/* Move the initial stack to a new position */
+static void
+move_stack(void *start, size_t size)
+{
+ uintptr_t i, tmp, *tmpp;
+ uintptr_t oldStackPointer, oldBasePointer, offset;
+ uintptr_t newStackPointer, newBasePointer;
+
+ for (i = (size_t) start - size; i <= (size_t) start; i += 0x1000) {
+ page_t *pg = get_page((void *) i);
+ alloc_page(pg, PTE_PRESENT | PTE_WRITE | PTE_USER, -1);
+ }
+
+ asm volatile("mov %%esp, %0" : "=r" (oldStackPointer));
+ asm volatile("mov %%ebp, %0" : "=r" (oldBasePointer));
+ offset = (uint32_t) start - initialStack;
+ newStackPointer = oldStackPointer + offset;
+ newBasePointer = oldBasePointer + offset;
+
+ memcpy((void *) newStackPointer, (void *) oldStackPointer,
+ initialStack - oldStackPointer);
+
+ /* Update pointers on stack */
+ for (i = (size_t) start; i > (size_t) start - size; i -= 4) {
+ tmp = *(uint32_t *) i;
+ if ((tmp > oldStackPointer) && (tmp < initialStack)) {
+ tmp += offset;
+ tmpp = (uintptr_t *) i;
+ *tmpp = tmp;
+ }
+ }
+
+ asm volatile("mov %0, %%esp" :: "r" (newStackPointer));
+ asm volatile("mov %0, %%ebp" :: "r" (newBasePointer));
+}
+
+/* Fork a task */
+pid_t
+fork(void)
+{
+ return clone(CLONE_NONE);
+}
+
+/* Clone a task */
+pid_t
+clone(int flags)
+{
+ Task *parent = current, *child = kmalloc(sizeof(Task)), *tmp;
+
+ if (flags & CLONE_THREAD) {
+ flags |= CLONE_PARENT;
+ flags |= CLONE_VM;
+ }
+
+ child->tid = nextPid++;
+ if (flags & CLONE_THREAD)
+ child->tgid = parent->tgid;
+ else
+ child->tgid = child->tid;
+ child->priority = NORMAL;
+ child->state = READY;
+ child->status = 0;
+ child->inSyscall = parent->inSyscall;
+ child->name = kmalloc(strlen(parent->name)+1);
+ memcpy(child->name, parent->name, strlen(parent->name)+1);
+
+ /* Set parent */
+ child->parent = parent;
+ if (flags & CLONE_PARENT)
+ child->parent = parent->parent;
+ child->ppid = child->parent->tgid;
+
+ child->executable = file_get(parent->executable);
+
+ /* Add to list of tasks */
+ tasks.end->tnext = child;
+ tasks.end = child;
+
+ /* Clone parent's file descriptors */
+ int fd;
+ File *file;
+ if (flags & CLONE_FILES) {
+ child->files = parent->files;
+ child->files->usage++;
+ } else {
+ child->files = kmalloc(sizeof(Files));
+ child->files->usage = 1;
+ for (fd = 0; fd < NFILES; fd++) {
+ file = parent->files->fd[fd];
+ if (!file) continue;
+ child->files->fd[fd] = file_get(file);
+ }
+ }
+
+ /* Clone parent's file system context */
+ if (flags & CLONE_FS) {
+ child->fs = parent->fs;
+ child->fs->usage++;
+ } else {
+ child->fs = kmalloc(sizeof(FileSystem));
+ child->fs->usage = 1;
+ child->fs->cwd = parent->fs->cwd;
+ child->fs->cwd->usage++;
+ init_custody_chain(&child->fs->cwdCustody);
+ copy_custody_chain(&parent->fs->cwdCustody,
+ &child->fs->cwdCustody);
+ child->fs->root = parent->fs->root;
+ child->fs->root->usage++;
+ }
+
+ /* Clone page directory */
+ if (flags & CLONE_VM) {
+ child->vm = parent->vm;
+ child->vm->usage++;
+ } else {
+ child->vm = kmalloc(sizeof(VirtualMemory));
+ child->vm->usage = 1;
+ }
+ child->pageDir = clone_dir();
+
+ /* Clone parent's VM Regions in child */
+ VMRegion *head;
+ Page *page;
+ off_t i;
+ if (child->vm != parent->vm)
+ child->vm->regions = vm_clone_regions(parent->vm->regions);
+ child->stack = kmalloc(sizeof(VMRegion));
+ memcpy(child->stack, parent->stack, sizeof(VMRegion));
+ child->stack->next = child->stack->prev = NULL;
+ /* Copy stack */
+ if (parent->stack && parent->stack->front) {
+ file = kmalloc(sizeof(File));
+ file->inode = inode_get(kmalloc(sizeof(Inode)));
+ file->ops = &tmpfsFileOps;
+ child->stack->front = file_get(file);
+ for (i = 0; i < child->stack->end - child->stack->start;
+ i += 0x1000) {
+ page = page_find(parent->stack->front->inode, i);
+ if (page)
+ page_add(file->inode, page);
+ }
+ }
+ /* Copy thread local storage */
+ if (parent->tls) {
+ child->tls = kmalloc(sizeof(VMRegion));
+ memcpy(child->tls, parent->tls, sizeof(VMRegion));
+ child->tls->next = child->tls->prev = NULL;
+ if (parent->tls->front) {
+ file = kmalloc(sizeof(File));
+ file->inode = inode_get(kmalloc(sizeof(Inode)));
+ file->ops = &tmpfsFileOps;
+ child->tls->front = file_get(file);
+ for (i = 0; i < child->tls->end - child->tls->start;
+ i += 0x1000) {
+ page = page_find(parent->tls->front->inode, i);
+ if (page)
+ page_add(file->inode, page);
+ }
+ }
+ if (parent->tls->back)
+ child->tls->back = file_get(parent->tls->back);
+ }
+
+ /* Split tasks here */
+ uintptr_t esp, ebp, eip;
+ eip = read_eip();
+ if (current == parent) {
+ asm volatile("mov %%esp, %0" : "=r" (esp));
+ asm volatile("mov %%ebp, %0" : "=r" (ebp));
+ child->esp = esp;
+ child->ebp = ebp;
+ child->eip = eip;
+ add_to_queue(&readyQueue, child);
+ return child->tid;
+ }
+ return 0;
+}
+
+/* Terminate the current task */
+void
+terminate(void)
+{
+ /* Close files */
+ int fd;
+ if (--current->files->usage == 0) {
+ for (fd = 0; fd < NFILES; fd++)
+ if (current->files->fd[fd])
+ close(fd);
+ kfree(current->files);
+ }
+ if (current->executable)
+ file_put(current->executable);
+
+ /* Clean File System info */
+ if (--current->fs->usage == 0)
+ kfree(current->fs);
+
+ /* Clean VM Regions and unreferenced VM Objects */
+ VMRegion *head;
+ if (--current->vm->usage == 0) {
+ for (head = current->vm->regions; head; head = head->next)
+ vm_destroy_region(head);
+ kfree(current->vm);
+ }
+ vm_destroy_region(current->stack);
+
+ /* Clean unread IPC messages */
+ /* TODO */
+
+ /* Clean signals */
+ /* TODO */
+
+ /* Deschedule */
+ current->state = TERMINATED;
+ acquire(&readyQueue.lock);
+ Task *tmp, *next;
+ for (tmp = current->waiting.start; tmp; tmp = next) {
+ next = tmp->next;
+ add_to_queue(&readyQueue, tmp);
+ }
+ release(&readyQueue.lock);
+ schedule();
+ panic("Unreached");
+
+ /* Clean task - FIXME */
+ kfree(current->name);
+ kfree(current);
+ clean_dir();
+ schedule();
+ /* UNREACHED */
+}
+
+/* Exit the current task */
+void
+exit(int status)
+{
+ if (current->tid == 1)
+ panic("Attempted to exit init! Exit code %d", status);
+ current->status = (1 << 31) | (status & 0x0F);
+ terminate();
+}
+
+/* Wait for a child process to change state */
+pid_t
+waitpid(pid_t pid, int *wstatus, int options)
+{
+ if (!verify_access(wstatus, sizeof(int), PROT_WRITE))
+ return -EFAULT;
+
+ Task *task = find_task(pid);
+ if (!task)
+ return -ECHILD;
+ if (task->ppid != current->tgid && task->tgid != current->tgid)
+ return -ECHILD;
+
+ if (task->state != TERMINATED) {
+ add_to_queue(&task->waiting, current);
+ block_task(WAITING_FOR_CHILD);
+ }
+
+ if (wstatus)
+ *wstatus = task->status;
+ return task->tid;
+}
+
+/* Get current task's PID */
+pid_t
+getpid(void)
+{
+ return current->tgid;
+}
+
+/* Get current task's UID */
+uid_t
+getuid(void)
+{
+ return current->uid;
+}
+
+/* Set current task's (E)UID */
+int
+setuid(uid_t uid)
+{
+ if (uid != current->uid && uid != current->suid && !super_user())
+ return -EPERM;
+ if (super_user()) {
+ current->uid = uid;
+ current->suid = uid;
+ }
+ current->euid = uid;
+ return 0;
+}
+
+/* Get current task's EUID */
+uid_t
+geteuid(void)
+{
+ return current->euid;
+}
+
+/* Set the current task's EUID */
+int
+seteuid(uid_t euid)
+{
+ if (euid != current->uid
+ && euid != current->euid
+ && euid != current->suid
+ && !super_user())
+ return -EPERM;
+ current->euid = euid;
+ return 0;
+}
+
+/* Get current task's GID */
+gid_t
+getgid(void)
+{
+ return current->gid;
+}
+
+/* Set current task's (E)GID */
+int
+setgid(gid_t gid)
+{
+ if (gid != current->gid
+ && gid != current->sgid
+ && !super_user())
+ return -EPERM;
+ if (super_user()) {
+ current->gid = gid;
+ current->sgid = gid;
+ }
+ current->egid = gid;
+ return 0;
+}
+
+/* Get current task's EGID */
+gid_t
+getegid(void)
+{
+ return current->egid;
+}
+
+/* Set the current task's EUID */
+int
+setegid(gid_t egid)
+{
+ if (egid != current->gid
+ && egid != current->egid
+ && egid != current->sgid
+ && !super_user())
+ return -EPERM;
+ current->egid = egid;
+ return 0;
+}
+
+/* Initialse tasking */
+void
+init_tasking(void)
+{
+ move_stack((void *) (0xF0800000 - sizeof(uintptr_t)), 0x2000);
+
+ /* Initialise the Kernel Task */
+ tasks.start = tasks.end = current = kmalloc(sizeof(Task));
+ current->tid = nextPid++;
+ current->tgid = current->tid;
+ current->priority = NORMAL;
+ current->name = kmalloc(7);
+ current->state = RUNNING;
+ memcpy(current->name, "kernel", 7);
+ current->pageDir = kernelDir;
+
+ /* Files Namespace */
+ current->files = kmalloc(sizeof(Files));
+ current->files->usage = 1;
+
+ /* File System Namespace */
+ current->fs = kmalloc(sizeof(FileSystem));
+ init_custody_chain(¤t->fs->cwdCustody);
+ current->fs->usage = 1;
+
+ /* Virtual Memory Namespace */
+ current->vm = kmalloc(sizeof(Files));
+ current->vm->regions = NULL;
+ current->vm->usage = 1;
+
+ /* Inter-Process Communication Namespace */
+
+ /* Signals Namespace */
+
+ register_interrupt(0, timer_handler);
+}
diff --git a/task/task.h b/task/task.h
new file mode 100644
index 0000000..228c3d8
--- /dev/null
+++ b/task/task.h
@@ -0,0 +1,128 @@
+#ifndef KERNEL_TASK_H
+#define KERNEL_TASK_H
+
+#include <stdint.h>
+#include <sys/ipc.h>
+#include <sys/times.h>
+#include <time.h>
+#include <signal.h>
+#include "../mem/paging.h"
+#include "../mem/vm.h"
+#include "../proc/proc.h"
+#include "../vfs/vfs.h"
+#include "../spinlock.h"
+
+typedef struct Task Task;
+typedef struct TaskQueue TaskQueue;
+
+/* Process priorities */
+enum Priority {
+ NONE,
+ LOWEST,
+ LOW,
+ NORMAL,
+ HIGH,
+ HIGHEST,
+};
+
+/* Task states */
+enum States {
+ RUNNING,
+ READY,
+ TERMINATED,
+ WAITING_FOR_CHILD,
+ WAITING_FOR_READ,
+ SLEEP,
+};
+
+/* Structure for a Task Queue */
+struct TaskQueue {
+ Task *start, *end;
+ Spinlock lock;
+};
+
+/* Structure of a Task */
+struct Task {
+ pid_t tid, tgid;
+ uid_t uid, euid, suid;
+ gid_t gid, egid, sgid;
+ uint8_t priority;
+ char *name;
+ uint32_t usertime, systime;
+ int state;
+ uint64_t sleepExpiry;
+ int status;
+ uintptr_t esp, ebp, eip;
+ Task *next, *tnext, *parent;
+ pid_t ppid;
+ page_dir_t pageDir;
+ Spinlock lock;
+ uint8_t inSyscall;
+ File *executable;
+ VMRegion *stack, *tls;
+ TaskQueue waiting;
+ sigset_t sigset;
+ sigset_t blockedSignals;
+ void (*sig_handler[32])(int);
+
+ /* Messages */
+ Message *msgQueue;
+
+ /* Namespaces */
+ FileSystem *fs;
+ Files *files;
+ VirtualMemory *vm;
+// Messages *ipc;
+// Signals *signals;
+};
+
+extern Task *currentTask[];
+#define current currentTask[CPUID]
+
+/* Check if a routine is running as a syscall */
+static inline uint8_t
+in_syscall(void)
+{
+ if (!current)
+ return 0;
+ return (current->inSyscall);
+}
+
+/* Check if super-user */
+static inline int
+super_user(void)
+{
+ return (current->euid == 0);
+}
+
+void init_tasking(void);
+void syscall_handler(InterruptFrame *frame);
+Task *find_task(pid_t tid);
+void add_to_queue(TaskQueue *queue, Task *task);
+void remove_from_queue(TaskQueue *queue, Task *task);
+Task *pop_from_queue(TaskQueue *queue);
+void timer_handler(InterruptFrame *frame);
+void block_task(int reason);
+void unblock_task(Task *task);
+void schedule(void);
+pid_t fork(void);
+pid_t clone(int flags);
+void terminate(void);
+void exit(int status);
+pid_t waitpid(pid_t pid, int *wstatus, int options);
+pid_t getpid(void);
+uid_t getuid(void);
+int setuid(uid_t uid);
+uid_t geteuid(void);
+int seteuid(uid_t euid);
+gid_t getgid(void);
+int setgid(gid_t gid);
+gid_t getegid(void);
+int setegid(gid_t egid);
+int isatty(int fd);
+int execve(const char *file, char *argv[], char *envp[]);
+
+int tgkill(pid_t tgid, pid_t tid, int sig);
+int kill(pid_t pid, int sig);
+
+#endif
diff --git a/task/time.c b/task/time.c
new file mode 100644
index 0000000..f81d496
--- /dev/null
+++ b/task/time.c
@@ -0,0 +1,183 @@
+/*
+ * This file controls the system clock and contains the functions related to
+ * getting and setting the time from various sources. It keeps a monotonic
+ * clock internally, but can also make use of the tasks' clocks, and the RTC.
+ */
+
+#include <stdint.h>
+#include <sys/times.h>
+#include <errno.h>
+#include "task.h"
+#include "../proc/proc.h"
+#include "../io.h"
+
+#define RTC_SECONDS 0x00
+#define RTC_MINUTES 0x02
+#define RTC_HOURS 0x04
+#define RTC_WEEKDAY 0x06
+#define RTC_DAY 0x07
+#define RTC_MONTH 0x08
+#define RTC_YEAR 0x09
+#define RTC_CENTURY 0x32
+
+#define LEAP_YEAR(x) ((((x % 4) == 0) && ((x % 100) != 0)) || ((x % 400) == 0))
+
+extern TaskQueue readyQueue;
+
+uint32_t monotonicClock = 0, millis = 0;
+uint8_t slice[MAX_CPUS] = {0};
+TaskQueue sleepQueue;
+
+/* Read an RTC register */
+static uint8_t
+read_rtc(uint8_t index)
+{
+ outb(0x70, 0x80 | index);
+ io_wait();
+ return inb(0x71);
+}
+
+/* Timer interrupt */
+void
+timer_handler(InterruptFrame *frame)
+{
+ /* Monotonic clock */
+ millis++;
+ if (millis == 1000) {
+ monotonicClock++;
+ millis = 0;
+ }
+
+ /* Sleeping processes */
+ Task *proc = sleepQueue.start, *prev = NULL;
+ for (proc = sleepQueue.start; proc; prev = proc, proc = proc->next) {
+ if (proc->sleepExpiry > (monotonicClock * 1000) + millis)
+ break;
+ if (prev)
+ prev->next = proc->next;
+ if (sleepQueue.start == proc)
+ sleepQueue.start = proc->next;
+ if (sleepQueue.end == proc)
+ sleepQueue.end = NULL;
+ add_to_queue(&readyQueue, proc);
+ }
+
+ /* Account timeslice */
+ slice[CPUID]++;
+ if (!current)
+ return;
+
+ /* Account task time */
+ if (in_syscall())
+ current->systime++;
+ else
+ current->usertime++;
+
+ /* Call scheduler */
+ if (slice[CPUID] < current->priority)
+ return;
+ slice[CPUID] = 0;
+ schedule();
+}
+
+/* Sleep for a specified time (milliseconds) */
+int
+sleep(uint32_t ms)
+{
+ current->sleepExpiry = (monotonicClock * 1000) + millis + ms;
+
+ /* Add to sorted sleep TaskQueue */
+ TaskQueue *q = &sleepQueue;
+ Task *task, *prev = NULL;
+ acquire(&q->lock);
+ if (!q->start) {
+ q->start = current;
+ q->end = current;
+ current->next = NULL;
+ } else {
+ for (task = q->start; task; prev = task, task = task->next)
+ if (task->sleepExpiry > current->sleepExpiry)
+ break;
+ if (!prev) {
+ current->next = q->start;
+ q->start = current;
+ } else {
+ current->next = task;
+ prev->next = current;
+ }
+ }
+ release(&q->lock);
+
+ block_task(SLEEP);
+ return 0;
+}
+
+/* Get epoch time */
+time_t
+time(time_t *tloc)
+{
+ if (!verify_access(tloc, sizeof(time_t), PROT_WRITE))
+ return -EFAULT;
+
+ /* Length of months for normal and leap years */
+ const uint16_t monthLen[2][12] = {
+ {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
+ {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
+ };
+
+ uint16_t i;
+ uint8_t second, minute, hour, day, month, year, cent;
+ time_t time = 0;
+ uint8_t years = 0, leapYears = 0;
+
+ second = read_rtc(RTC_SECONDS);
+ minute = read_rtc(RTC_MINUTES);
+ hour = read_rtc(RTC_HOURS);
+ day = read_rtc(RTC_DAY);
+ month = read_rtc(RTC_MONTH);
+ year = read_rtc(RTC_YEAR);
+ cent = read_rtc(RTC_CENTURY);
+
+ second = (second & 0x0F) + ((second / 16) * 10);
+ minute = (minute & 0x0F) + ((minute / 16) * 10);
+ hour = ((hour & 0x0F) + (((hour & 0x70) / 16) * 10)) | (hour & 0x80);
+ day = (day & 0x0F) + ((day / 16) * 10);
+ month = (month & 0x0F) + ((month / 16) * 10);
+ year = (year & 0x0F) + ((year / 16) * 10);
+ cent = (cent & 0x0F) + ((cent / 16) * 10);
+
+ for (i = 1970; i < (cent * 100) + year; i++)
+ if (LEAP_YEAR(i))
+ leapYears++;
+ else
+ years++;
+ time += ((years * 365) + (leapYears * 366)) * 86400;
+
+ for (i = 0; i < month - 1; i++)
+ time += monthLen[LEAP_YEAR(year)][i] * 86400;
+
+ time += (day - 1) * 86400;
+ time += hour * 3600;
+ time += minute * 60;
+ time += second;
+
+ if (tloc)
+ *tloc = time;
+
+ return time;
+}
+
+/* Get process times */
+clock_t
+times(Times *buf)
+{
+ if (!verify_access(buf, sizeof(Times), PROT_WRITE))
+ return -EFAULT;
+ if (buf) {
+ buf->utime = current->usertime;
+ buf->stime = current->systime;
+ buf->cutime = current->usertime;
+ buf->cstime = current->systime;
+ }
+ return (monotonicClock * 1000) + millis;
+}
diff --git a/vfs/cache.c b/vfs/cache.c
new file mode 100644
index 0000000..54a7495
--- /dev/null
+++ b/vfs/cache.c
@@ -0,0 +1,307 @@
+/*
+ * This file handles the VFS cache. This includes the VFS tree cache and all
+ * the functions for managing the associated structures. One such structure
+ * is the Inode's list of Directory Entries. Every DirEntry has a name, but
+ * also has a hash. This is for fast comparisons - when iterating the list to
+ * search for a name, first check if the hash matches. This saves a lot of time
+ * rather than actually comparing the names. Entries with hash collisions are
+ * placed adjacent in the list.
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include "vfs.h"
+#include "inode.h"
+#include "cache.h"
+#include "../mem/heap.h"
+#include "../mem/paging.h"
+#include "../mem/frame.h"
+#include "../task/task.h"
+
+/* Hash a file name */
+static uint32_t
+name_hash(const char *name)
+{
+ uint32_t digest = 5381;
+ int c;
+ while (c = *name++)
+ digest = ((digest << 5) + digest) ^ c; /* digest*33 ^ c */
+ return digest;
+}
+
+/* Reap any unused branches of the cache tree */
+void
+cache_reaper(void)
+{
+ /*
+ * The Cache Reaper attempts to release all the directory entries
+ * belonging to the root of the current task's file system context.
+ * This will in theory release all the associated inodes. If those
+ * inodes are only held in the cache (not in use, and therefore not
+ * referenced by anything else), they'll be freed and will in turn have
+ * their directory entries released. This recursive process will prune
+ * most of the VFS cache, leaving only in-use inodes.
+ */
+ if (!current)
+ return;
+ if (!current->fs)
+ return;
+ if (!current->fs->root)
+ return;
+ entry_clean(current->fs->root);
+ current->fs->root->dirEntries = NULL;
+}
+
+/* Get a DirEntry */
+DirEntry *
+entry_get(DirEntry *entry)
+{
+ entry->usage++;
+ return entry;
+}
+
+/* Put a DirEntry */
+void
+entry_put(DirEntry *entry)
+{
+ if (--entry->usage)
+ return;
+ if (entry->inode)
+ inode_put(entry->inode);
+ kfree(entry);
+}
+
+/* Search for a Directory Entry in a list by name */
+DirEntry *
+entry_find(Inode *inode, const char *name)
+{
+ if (!inode)
+ return NULL;
+ acquire(&inode->lock);
+ uint32_t search = name_hash(name);
+ DirEntry *item = inode->dirEntries;
+ /* Look for matching hash */
+ while (item) {
+ if (item->hash == search)
+ break;
+ item = item->next;
+ }
+ if (!item) {
+ release(&inode->lock);
+ return NULL;
+ }
+ /* Check all collisions */
+ while (item && item->hash == search) {
+ if (!strcmp(item->name, name))
+ break;
+ item = item->next;
+ }
+ release(&inode->lock);
+ if (!item || item->hash != search)
+ return NULL;
+ return item;
+}
+
+/* Add a Directory Entry to a list */
+void
+entry_add(Inode *inode, DirEntry *insert)
+{
+ if (!inode)
+ return;
+ acquire(&inode->lock);
+ insert->hash = name_hash(insert->name);
+ if (!inode->dirEntries) {
+ inode->dirEntries = entry_get(insert);
+ release(&inode->lock);
+ return;
+ }
+
+ /* Try to find a collision */
+ DirEntry *collision = inode->dirEntries;
+ while (collision->next) {
+ if (collision->hash == insert->hash)
+ break;
+ collision = collision->next;
+ }
+ /* Add into list */
+ insert->next = collision->next;
+ collision->next = entry_get(insert);
+ release(&inode->lock);
+}
+
+/* Remove a Directory Entry from a list */
+void
+entry_remove(Inode *inode, const char *name)
+{
+ if (!inode)
+ return;
+ acquire(&inode->lock);
+ uint32_t search = name_hash(name);
+ DirEntry *item = inode->dirEntries, *prev = NULL;
+ /* Look for matching hash */
+ while (item) {
+ if (item->hash == search)
+ break;
+ prev = item;
+ item = item->next;
+ }
+ if (!item) {
+ release(&inode->lock);
+ return;
+ }
+ /* Check all collisions */
+ while (item && item->hash == search) {
+ if (!strcmp(item->name, name))
+ break;
+ prev = item;
+ item = item->next;
+ }
+ if (!item || item->hash != search) {
+ release(&inode->lock);
+ return;
+ }
+
+ /* Link over item */
+ if (prev)
+ prev->next = item->next;
+ else
+ inode->dirEntries = item;
+ entry_put(item);
+ release(&inode->lock);
+}
+
+/* Clean a list of Directory Entries */
+void
+entry_clean(Inode *inode)
+{
+ if (!inode)
+ return;
+ if (!inode->dirEntries)
+ return;
+ acquire(&inode->lock);
+ DirEntry *de, *den;
+ for (de = inode->dirEntries; de; de = den) {
+ den = de->next;
+ entry_put(de);
+ }
+ release(&inode->lock);
+}
+
+/* Get a Page */
+Page *
+page_get(Page *page)
+{
+ page->usage++;
+ return page;
+}
+
+/* Put a Page */
+void
+page_put(Page *page)
+{
+ if (--page->usage)
+ return;
+ free_frame(PG_ADDR(page->frame));
+ kfree(page);
+}
+
+/* Find a Page by offset */
+Page *
+page_find(Inode *inode, off_t offset)
+{
+ if (!inode)
+ return NULL;
+ acquire(&inode->lock);
+ PageCache *cache;
+ for (cache = inode->pages; cache; cache = cache->next)
+ if (cache->page->offset == PG_ADDR(offset))
+ break;
+ release(&inode->lock);
+ if (!cache)
+ return NULL;
+ return cache->page;
+}
+
+/* Add a page to a list */
+void
+page_add(Inode *inode, Page *page)
+{
+ if (!inode)
+ return;
+ acquire(&inode->lock);
+ PageCache *cache;
+ if (!inode->pages) {
+ inode->pages = kmalloc(sizeof(PageCache));
+ cache = inode->pages;
+ } else {
+ for (cache = inode->pages; cache->next; cache = cache->next);
+ cache->next = kmalloc(sizeof(PageCache));
+ cache = cache->next;
+ }
+ cache->page = page_get(page);
+ release(&inode->lock);
+}
+
+/* Create and add a Page to a list */
+Page *
+page_create(Inode *inode, page_t frame, off_t offset)
+{
+ if (!inode)
+ return NULL;
+ acquire(&inode->lock);
+ Page *prev, *page = kmalloc(sizeof(Page));
+ page->frame = PG_ADDR(frame);
+ page->offset = PG_ADDR(offset);
+ page_add(inode, page);
+ release(&inode->lock);
+ return page;
+}
+
+/* Remove a Page from a list */
+void
+page_remove(Inode *inode, Page *page)
+{
+ if (!inode)
+ return;
+ if (!inode->pages)
+ return;
+ acquire(&inode->lock);
+
+ PageCache *cache, *item;
+ if (inode->pages->page == page) {
+ item = inode->pages;
+ inode->pages = item->next;
+ } else {
+ for (cache = inode->pages;
+ cache->next; cache = cache->next)
+ if (cache->next->page == page)
+ break;
+ if (!cache->next) {
+ release(&inode->lock);
+ return;
+ }
+ item = cache->next;
+ cache->next = item->next;
+ }
+ page_put(page);
+ kfree(item);
+ release(&inode->lock);
+}
+
+/* Clean a list of Pages */
+void
+page_clean(Inode *inode)
+{
+ if (!inode)
+ return;
+ if (!inode->pages)
+ return;
+ acquire(&inode->lock);
+ PageCache *c, *cn;
+ for (c = inode->pages; c; c = cn) {
+ page_put(c->page);
+ cn = c->next;
+ kfree(c);
+ }
+ release(&inode->lock);
+}
diff --git a/vfs/cache.h b/vfs/cache.h
new file mode 100644
index 0000000..158d2ab
--- /dev/null
+++ b/vfs/cache.h
@@ -0,0 +1,22 @@
+#ifndef KERNEL_VFS_CACHE_H
+#define KERNEL_VFS_CACHE_H
+
+#include "vfs.h"
+
+void cache_reaper(void);
+
+DirEntry *entry_get(DirEntry *entry);
+void entry_put(DirEntry *entry);
+DirEntry *entry_find(Inode *inode, const char *name);
+void entry_add(Inode *inode, DirEntry *insert);
+void entry_remove(Inode *inode, const char *name);
+void entry_clean(Inode *inode);
+
+Page *page_get(Page *page);
+Page *page_find(Inode *inode, off_t offset);
+void page_add(Inode *inode, Page *page);
+Page *page_create(Inode *inode, page_t frame, off_t offset);
+void page_remove(Inode *inode, Page *page);
+void page_clean(Inode *inode);
+
+#endif
diff --git a/vfs/custody.c b/vfs/custody.c
new file mode 100644
index 0000000..1200f59
--- /dev/null
+++ b/vfs/custody.c
@@ -0,0 +1,117 @@
+/*
+ * This file handles chains of custody for files. It implements a generic set
+ * of operations for acting on CustodyChain objects. A CustodyChain holds a
+ * linked list of Custody objects, each of which refers to a directory entry.
+ * With this chain, the VFS is able to find what path was used to access a
+ * resources and go back up the chain (for implementing ..) and avoid going
+ * beyond the root.
+ */
+
+#include <string.h>
+#include "vfs.h"
+#include "cache.h"
+#include "../mem/heap.h"
+
+/* Initialise a custody chain */
+void
+init_custody_chain(CustodyChain *chain)
+{
+ Custody *root = kmalloc(sizeof(Custody));
+ chain->start = chain->end = root;
+ chain->size = 0;
+ init_lock(&chain->lock);
+ root->prev = root->next = NULL;
+ root->chain = chain;
+ root->entry = NULL;
+}
+
+/* Create a new custody chain */
+CustodyChain *
+create_custody_chain(void)
+{
+ CustodyChain *chain = kmalloc(sizeof(CustodyChain));
+ init_custody_chain(chain);
+ return chain;
+}
+
+/* Clean a custody chain */
+void
+clean_custody_chain(CustodyChain *chain)
+{
+ acquire(&chain->lock);
+ while (chain->size)
+ remove_custody(chain);
+ kfree(chain->start);
+ release(&chain->lock);
+}
+
+/* Destroy a custody chain */
+void
+destroy_custody_chain(CustodyChain *chain)
+{
+ clean_custody_chain(chain);
+ kfree(chain);
+}
+
+/* Copy custody chain */
+void
+copy_custody_chain(CustodyChain *chain, CustodyChain *newchain)
+{
+ acquire(&chain->lock);
+ acquire(&newchain->lock);
+ Custody *c;
+ for (c = chain->start->next; c; c = c->next)
+ add_custody(newchain, c->entry);
+ release(&newchain->lock);
+ release(&chain->lock);
+}
+
+/* Add entry */
+void
+add_custody(CustodyChain *chain, DirEntry *entry)
+{
+ acquire(&chain->lock);
+ chain->end->next = kmalloc(sizeof(Custody));
+ chain->end->next->prev= chain->end;
+ chain->end = chain->end->next;
+ chain->end->chain = chain;
+ chain->end->entry = entry_get(entry);
+ chain->size++;
+ release(&chain->lock);
+}
+
+/* Remove an entry */
+DirEntry *
+remove_custody(CustodyChain *chain)
+{
+ if (!chain->size)
+ return NULL;
+ acquire(&chain->lock);
+ entry_put(chain->end->entry);
+ chain->end = chain->end->prev;
+ kfree(chain->end->next);
+ chain->end->next = NULL;
+ chain->size--;
+ release(&chain->lock);
+ return chain->end->entry;
+}
+
+/* Convert a chain to a path string */
+char *
+custody_path(CustodyChain *chain, char *buf, size_t size)
+{
+ char *ptr = buf;
+ Custody *curr;
+ if (!chain->start->next) {
+ *ptr++ = '/';
+ *ptr = '\0';
+ return buf;
+ }
+ for (curr = chain->start->next; curr; curr = curr->next) {
+ *ptr++ = '/';
+ strcpy(ptr, curr->entry->name);
+ ptr += strlen(curr->entry->name);
+ }
+ *ptr = '\0';
+ return buf;
+}
diff --git a/vfs/devfs/file.c b/vfs/devfs/file.c
new file mode 100644
index 0000000..d2da4e4
--- /dev/null
+++ b/vfs/devfs/file.c
@@ -0,0 +1,84 @@
+/*
+ * This file controls access to DevFS Files. It contains the functions called
+ * by the VFS for any operation on a File struct belonging to DevFS.
+ */
+
+#include <stddef.h>
+#include <string.h>
+#include <errno.h>
+#include <dirent.h>
+#include "fs.h"
+#include "../vfs.h"
+#include "../cache.h"
+#include "../../mem/paging.h"
+#include "../../mem/frame.h"
+#include "../../drivers/drivers.h"
+
+int devfs_open(File *file);
+int devfs_readdir(File *file, DirEnt *dent, off_t index);
+
+FileOps devfsFileOps = {
+ .open = devfs_open,
+ .readdir = devfs_readdir,
+};
+
+/* Open a device node */
+int
+devfs_open(File *file)
+{
+ if (S_ISREG(file->inode->mode) || S_ISDIR(file->inode->mode))
+ return 0;
+
+ /* Use major number to find relevant driver */
+ Driver *driver;
+ for (driver = drivers; driver; driver = driver->next)
+ if (driver->major == MAJOR(file->inode->dev))
+ break;
+ if (!driver)
+ return -ENXIO;
+
+ file->ops = driver->ops;
+ if (!file->ops)
+ return 0;
+ if (!file->ops->open)
+ return 0;
+ return file->ops->open(file);
+ /* For checking the minor internally */
+}
+
+/* Read a directory entry */
+int
+devfs_readdir(File *file, DirEnt *dent, off_t index)
+{
+ DirEntry *de;
+
+ if (!index--) {
+ dent->ino = file->inode->ino;
+ dent->type = DT_DIR;
+ dent->namelen = 2;
+ strncpy(dent->name, ".", dent->namelen);
+ return 0;
+ }
+
+ for (de = file->inode->dirEntries; de && index; de = de->next, index--);
+ if (!de)
+ return -ENOENT;
+ dent->ino = de->inode->ino;
+ if (S_ISBLK(de->inode->mode))
+ dent->type = DT_BLK;
+ if (S_ISCHR(de->inode->mode))
+ dent->type = DT_CHR;
+ if (S_ISDIR(de->inode->mode))
+ dent->type = DT_DIR;
+ if (S_ISFIFO(de->inode->mode))
+ dent->type = DT_FIFO;
+ if (S_ISLNK(de->inode->mode))
+ dent->type = DT_LNK;
+ if (S_ISREG(de->inode->mode))
+ dent->type = DT_REG;
+ if (S_ISSOCK(de->inode->mode))
+ dent->type = DT_SOCK;
+ dent->namelen = strnlen(de->name, NAME_MAX) + 1;
+ strncpy(dent->name, de->name, NAME_MAX);
+ return 0;
+}
diff --git a/vfs/devfs/fs.h b/vfs/devfs/fs.h
new file mode 100644
index 0000000..f972b67
--- /dev/null
+++ b/vfs/devfs/fs.h
@@ -0,0 +1,13 @@
+#ifndef KERNEL_VFS_DEVFS_H
+#define KERNEL_VFS_DEVFS_H
+
+#include "../vfs.h"
+
+/* Operations */
+extern SuperOps devfsSuperOps;
+extern InodeOps devfsInodeOps;
+extern FileOps devfsFileOps;
+
+extern FileSystemType devfsType;
+
+#endif
diff --git a/vfs/devfs/inode.c b/vfs/devfs/inode.c
new file mode 100644
index 0000000..2208263
--- /dev/null
+++ b/vfs/devfs/inode.c
@@ -0,0 +1,69 @@
+/*
+ * This file contains the functions dealing with DevFS inodes. The VFS will
+ * call these when it performs operations on DevFS Inodes, or is dealing with
+ * the DevFS hierarchy.
+ */
+
+#include <string.h>
+#include "fs.h"
+#include "../vfs.h"
+#include "../../mem/heap.h"
+
+int devfs_create(Inode *inode, DirEntry *entry, mode_t mode);
+Inode *devfs_lookup(Inode *inode, const char *name);
+int devfs_mkdir(Inode *inode, DirEntry *entry, mode_t mode);
+int devfs_rmdir(Inode *inode, DirEntry *entry);
+int devfs_mknod(Inode *inode, DirEntry *entry, mode_t mode, dev_t dev);
+int devfs_rename(Inode *si, DirEntry *sde, Inode *di, DirEntry *dde);
+
+InodeOps devfsInodeOps = {
+ .create = devfs_create,
+ .lookup = devfs_lookup,
+ .mkdir = devfs_mkdir,
+ .rmdir = devfs_rmdir,
+ .mknod = devfs_mknod,
+ .rename = devfs_rename,
+};
+
+/* Create a file */
+int
+devfs_create(Inode *inode, DirEntry *entry, mode_t mode)
+{
+ return 0;
+}
+
+
+/* Look up a file */
+Inode *
+devfs_lookup(Inode *inode, const char *name)
+{
+ return NULL;
+}
+
+/* Make a directory */
+int
+devfs_mkdir(Inode *inode, DirEntry *entry, mode_t mode)
+{
+ return 0;
+}
+
+/* Remove a directory */
+int
+devfs_rmdir(Inode *inode, DirEntry *entry)
+{
+ return 0;
+}
+
+/* Make a node */
+int
+devfs_mknod(Inode *inode, DirEntry *entry, mode_t mode, dev_t dev)
+{
+ return 0;
+}
+
+/* Rename/mode a directory entry */
+int
+devfs_rename(Inode *si, DirEntry *sde, Inode *di, DirEntry *dde)
+{
+ return 0;
+}
diff --git a/vfs/devfs/super.c b/vfs/devfs/super.c
new file mode 100644
index 0000000..e57d60b
--- /dev/null
+++ b/vfs/devfs/super.c
@@ -0,0 +1,85 @@
+/*
+ * This file controls the superblock for DevFS. It supports mounting new DevFS
+ * filesystems. The VFS will use the calls here when dealing directly with the
+ * filesystem structure.
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include "fs.h"
+#include "../vfs.h"
+#include "../super.h"
+#include "../inode.h"
+#include "../../mem/heap.h"
+
+Inode *devfs_mount(FileSystemType *type, int flags,
+ const char *dev, void *data);
+Inode *devfs_alloc_inode(SuperBlock *sb);
+
+FileSystemType devfsType = {
+ .name = "DevFS",
+ .mount = devfs_mount,
+// .kill_sb = devfs_kill_sb,
+};
+
+SuperOps devfsSuperOps = {
+ .alloc_inode = devfs_alloc_inode,
+// .free_inode = devfs_free_inode,
+// .write_inode = devfs_write_inode,
+// .delete_inode = devfs_delete_inode,
+};
+
+Inode devfsRoot = {
+ .mode = S_IFDIR | 0755,
+ .ops = &devfsInodeOps,
+ .fileOps = &devfsFileOps,
+ .size = 4096,
+};
+
+/* Mount a DevFS instance */
+Inode *
+devfs_mount(FileSystemType *type, int flags, const char *dev, void *data)
+{
+ if (type != &devfsType)
+ return NULL;
+
+ SuperBlock *super = kmalloc(sizeof(SuperBlock));
+
+ super->type = type;
+ super->ops = &devfsSuperOps;
+ init_lock(&super->lock);
+
+ /*
+ * DevFS is special - it has a globally unique root. This means that no
+ * matter where a DevFS instance is mounted, it will be mounted with
+ * this inode, meaning the contents are the same. It basically causes
+ * the mountpoint to act as a hard-link. This means you can mount a new
+ * DevFS instance in a chroot() environment, and not have to remake all
+ * the device nodes.
+ */
+ Inode *inode = &devfsRoot;
+ inode->nlink++;
+ inode->super = super;
+ /* FIXME: need an inode per super, or only one super */
+ super->root = inode;
+ /* Never free */
+ if (!inode->usage)
+ inode_get(inode);
+
+ return inode;
+}
+
+/* Allocate an inode */
+Inode *
+devfs_alloc_inode(SuperBlock *sb)
+{
+ Inode *inode = kmalloc(sizeof(Inode));
+ init_lock(&inode->lock);
+ inode->ops = &devfsInodeOps;
+ inode->fileOps = &devfsFileOps;
+ inode->super = sb;
+ inode->nlink = 1;
+ return inode_get(inode); /* This ensures that the inode is never free */
+}
diff --git a/vfs/ext2fs/block.c b/vfs/ext2fs/block.c
new file mode 100644
index 0000000..28cf480
--- /dev/null
+++ b/vfs/ext2fs/block.c
@@ -0,0 +1,32 @@
+/*
+ * This file contains the Ext2 block implementation. It reads a backing file
+ * from the super block in the intervals specified by the superblock. It also
+ * contains functions for reading blocks from files.
+ */
+
+#include <stdint.h>
+#include "fs.h"
+
+/* Read a block of data */
+void
+ext2_read_block(SuperBlock *super, uint32_t index, char *buf)
+{
+ Ext2Super *rsuper = super->data;
+
+ super->back->pos = index * (1024 << rsuper->blockSize);
+ file_read(super->back, buf, 1024 << rsuper->blockSize);
+}
+
+/* Get the data block address from inode block index */
+uint32_t
+ext2_get_data_addr(SuperBlock *super, Ext2Inode *node, uint32_t index)
+{
+ uint32_t tmp;
+ char block[4096];
+
+ /* Main blocks */
+ if (index < 12)
+ return node->directBlock[index];
+ index -= 12;
+ return 0;
+}
diff --git a/vfs/ext2fs/file.c b/vfs/ext2fs/file.c
new file mode 100644
index 0000000..d0caa35
--- /dev/null
+++ b/vfs/ext2fs/file.c
@@ -0,0 +1,86 @@
+/*
+ * This file controls access to Ext2FS Files. It contains the functions called
+ * by the VFS for any operation on a Ext2FS File struct.
+ */
+
+#include <stddef.h>
+#include <string.h>
+#include <dirent.h>
+#include <errno.h>
+#include "fs.h"
+#include "../vfs.h"
+
+size_t ext2fs_read(File *file, char *buf, size_t size, off_t offset);
+int ext2fs_readdir(File *file, DirEnt *dent, off_t index);
+int ext2fs_open(File *file);
+
+FileOps ext2fsFileOps = {
+ .read = ext2fs_read,
+ .readdir = ext2fs_readdir,
+ .open = ext2fs_open,
+};
+
+/* Read a file */
+size_t
+ext2fs_read(File *file, char *buf, size_t size, off_t offset)
+{
+ Ext2Inode inode;
+ uint16_t min;
+ size_t count = 0, i = offset / 4096;
+ uint32_t blk;
+ char ebuf[4096];
+ ext2_read_inode(file->inode->super, file->inode->ino, &inode);
+ if (offset > inode.lsize)
+ return 0;
+ if (size + offset > inode.lsize)
+ size = inode.lsize - offset;
+ while (size) {
+ min = (size > 0x1000) ? 0x1000 : size;
+ blk = ext2_get_data_addr(file->inode->super, &inode, i);
+ ext2_read_block(file->inode->super, blk, ebuf);
+ memcpy(buf + count, ebuf + (offset % 4096), min);
+ size -= min;
+ count += min;
+ i++;
+ }
+ if (count >= 0x1000)
+ return count;
+}
+
+/* Read a directory entry */
+int
+ext2fs_readdir(File *file, DirEnt *dent, off_t index)
+{
+ char buf[4096];
+ uint32_t block, blk = 0;
+ Ext2DirEntry *de;
+ Ext2Inode inode;
+ ext2_read_inode(file->inode->super, file->inode->ino, &inode);
+ for (blk = 0; blk < 0xFFFF; blk++) {
+ block = ext2_get_data_addr(file->inode->super, &inode, blk);
+ if (!block)
+ return -ENOENT;
+ ext2_read_block(file->inode->super, block, buf);
+ for (de = (Ext2DirEntry *) buf;
+ index && de < (Ext2DirEntry *) (buf + 4096);
+ de = (void *) ((char *) de + de->size), index--);
+ if (de >= (Ext2DirEntry *) (buf + 4096))
+ return -ENOENT;
+ if (!index)
+ break;
+ }
+ if (!de->ino)
+ return -ENOENT;
+ dent->ino = de->ino;
+ dent->type = de->type;
+ dent->namelen = de->nameLen + 1;
+ strncpy(dent->name, de->name, de->size);
+ return 0;
+}
+
+/* Open a file */
+int
+ext2fs_open(File *file)
+{
+ return 0;
+}
diff --git a/vfs/ext2fs/fs.h b/vfs/ext2fs/fs.h
new file mode 100644
index 0000000..0e5cadb
--- /dev/null
+++ b/vfs/ext2fs/fs.h
@@ -0,0 +1,116 @@
+#ifndef KERNEL_VFS_EXT2FS_H
+#define KERNEL_VFS_EXT2FS_H
+
+#include "../vfs.h"
+
+typedef struct Ext2Super Ext2Super;
+typedef struct Ext2BlockGroupDesc Ext2BlockGroupDesc;
+typedef struct Ext2Inode Ext2Inode;
+typedef struct Ext2DirEntry Ext2DirEntry;
+
+/* Structure of the Ext2 SuperBlock */
+struct Ext2Super {
+ uint32_t numInodes;
+ uint32_t numBlocks;
+ uint32_t reservedBlocks;
+ uint32_t unallocBlocks;
+ uint32_t unallocInodes;
+ uint32_t superBlock;
+ uint32_t blockSize;
+ uint32_t fragSize;
+ uint32_t blocksPerGroup;
+ uint32_t fragsPerGroup;
+ uint32_t inodesPerGroup;
+ uint32_t lastMountTime;
+ uint32_t lastWriteTime;
+ uint16_t lastCheck;
+ uint16_t mustCheck;
+ uint16_t signature;
+ uint16_t state;
+ uint16_t error;
+ uint16_t verMinor;
+ uint32_t lastCheckTime;
+ uint32_t checkInterval;
+ uint32_t creator;
+ uint32_t verMajor;
+ uint16_t uid, gid;
+ /* Extended fields */
+ uint32_t firstAvailInode;
+ uint16_t inodeSize;
+ uint16_t blockGroup;
+ uint32_t optionalFeatures;
+ uint32_t requiredFeatures;
+ uint32_t writableFeatures;
+ char fsId[16];
+ char volumeName[16];
+ char lastPath[64];
+ uint32_t compression;
+ uint8_t preallocFileBlocks;
+ uint8_t preallocDirBlocks;
+ uint16_t unused;
+ char journalId[16];
+ uint32_t journalInode;
+ uint32_t journalDev;
+ uint32_t orphanHead;
+} __attribute__((packed));
+
+/* Structure of the Ext2 Block Group Descriptor */
+struct Ext2BlockGroupDesc {
+ uint32_t blockUsage;
+ uint32_t inodeUsage;
+ uint32_t inodeTable;
+ uint16_t unallocBlocks;
+ uint16_t unallocInodes;
+ uint16_t numDirs;
+} __attribute__((packed));
+
+/* Structure of the Ext2 Inode */
+struct Ext2Inode {
+ uint16_t type;
+ uint16_t uid;
+ uint32_t lsize;
+ uint32_t lastAccessTime;
+ uint32_t creationTime;
+ uint32_t lastWriteTime;
+ uint32_t deletionTime;
+ uint16_t gid;
+ uint16_t numHardLinks;
+ uint32_t numSectors;
+ uint32_t flags;
+ uint32_t osA;
+ uint32_t directBlock[12];
+ uint32_t singleBlock;
+ uint32_t doubleBlock;
+ uint32_t tripleBlock;
+ uint32_t gen;
+ uint32_t attr;
+ union {
+ uint32_t usize;
+ uint32_t dirACL;
+ };
+ uint32_t fragment;
+ uint32_t osB[3];
+} __attribute__((packed));
+
+/* Structure of the Ext2 Directory Entry */
+struct Ext2DirEntry {
+ uint32_t ino;
+ uint16_t size;
+ uint8_t nameLen;
+ uint8_t type;
+ char name[];
+} __attribute__((packed));
+
+/* Operations */
+extern SuperOps ext2fsSuperOps;
+extern InodeOps ext2fsInodeOps;
+extern FileOps ext2fsFileOps;
+
+extern FileSystemType ext2fsType;
+
+void ext2_read_vnode(SuperBlock *super, uint32_t index, Inode *res);
+void ext2_read_inode(SuperBlock *super, uint32_t index, Ext2Inode *res);
+void ext2_read_block(SuperBlock *super, uint32_t index, char *buf);
+uint32_t ext2_get_data_addr(SuperBlock *super, Ext2Inode *node, uint32_t index);
+
+#endif
diff --git a/vfs/ext2fs/inode.c b/vfs/ext2fs/inode.c
new file mode 100644
index 0000000..3437220
--- /dev/null
+++ b/vfs/ext2fs/inode.c
@@ -0,0 +1,94 @@
+/*
+ * This file contains the Ext2 inode implementation. It contains the functions
+ * for the inode operations, as well as several internal operations relating to
+ * inodes required for the full Ext2 implementation.
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include <fcntl.h>
+#include "fs.h"
+#include "../vfs.h"
+#include "../super.h"
+#include "../../mem/heap.h"
+
+Inode *ext2fs_lookup(Inode *inode, const char *name);
+
+InodeOps ext2fsInodeOps = {
+ .lookup = ext2fs_lookup,
+};
+
+/* Read an Ext2 inode as a VFS inode */
+void
+ext2_read_vnode(SuperBlock *super, uint32_t index, Inode *res)
+{
+ Ext2Inode inode;
+ ext2_read_inode(super, index, &inode);
+
+ res->ino = index;
+ res->usage = 0;
+ res->uid = inode.uid;
+ res->gid = inode.gid;
+ res->mode = inode.type;
+ res->nlink = inode.numHardLinks;
+ res->size = inode.lsize;
+ res->ops = &ext2fsInodeOps;
+ res->fileOps = &ext2fsFileOps;
+ res->super = super;
+}
+
+/* Read an Ext2 inode */
+void
+ext2_read_inode(SuperBlock *super, uint32_t index, Ext2Inode *res)
+{
+ Ext2Super *rsuper = super->data;
+
+ /* Find the Block Group Descriptor */
+ Ext2BlockGroupDesc bgd;
+ uint32_t groups = rsuper->numBlocks / rsuper->blocksPerGroup;
+ uint32_t group = (index - 1) / rsuper->inodesPerGroup;
+ index = (index - 1) % rsuper->inodesPerGroup;
+ if (rsuper->numBlocks % rsuper->blocksPerGroup)
+ groups++;
+ super->back->pos = (rsuper->blockSize ? 1 : 2)
+ * (1024 << rsuper->blockSize);
+ super->back->pos += sizeof(Ext2BlockGroupDesc) * group;
+ file_read(super->back, (char *) &bgd, sizeof(Ext2BlockGroupDesc));
+ /* Read Inode */
+ super->back->pos = bgd.inodeTable * (1024 << rsuper->blockSize);
+ super->back->pos += rsuper->inodeSize * index;
+ file_read(super->back, (char *) res, sizeof(Ext2Inode));
+}
+
+/* Look up a file */
+Inode *
+ext2fs_lookup(Inode *vnode, const char *name)
+{
+ char buf[4096];
+ uint32_t block, blk = 0;
+ Ext2DirEntry *de;
+ Ext2Inode inode;
+ ext2_read_inode(vnode->super, vnode->ino, &inode);
+ for (blk = 0; blk < 0xFFFF; blk++) {
+ block = ext2_get_data_addr(vnode->super, &inode, blk);
+ if (!block)
+ return NULL;
+ ext2_read_block(vnode->super, block, buf);
+ for (de = (Ext2DirEntry *) buf;
+ strncmp(de->name, name, de->nameLen)
+ && de < (Ext2DirEntry *) (buf + 4096);
+ de = (void *) ((char *) de + de->size));
+ if (de >= (Ext2DirEntry *) (buf + 4096))
+ return NULL;
+ if (!strncmp(de->name, name, de->nameLen))
+ break;
+ }
+ if (!de->ino)
+ return NULL;
+ Inode *res = super_find_inode(vnode->super, de->ino);
+ if (res)
+ return res;
+ res = kmalloc(sizeof(Inode));
+ ext2_read_vnode(vnode->super, de->ino, res);
+ return res;
+}
diff --git a/vfs/ext2fs/super.c b/vfs/ext2fs/super.c
new file mode 100644
index 0000000..98ca800
--- /dev/null
+++ b/vfs/ext2fs/super.c
@@ -0,0 +1,89 @@
+/*
+ * This file controls the superblock for Ext2FS. It supports mounting new
+ * Ext2FS filesystems. The VFS will use the calls here when dealing directly
+ * with the filesystem structure.
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/mount.h>
+#include "fs.h"
+#include "../vfs.h"
+#include "../inode.h"
+#include "../super.h"
+#include "../../mem/heap.h"
+#include "../../task/task.h"
+#include "../../screen.h"
+
+Inode *ext2fs_mount(FileSystemType *type, int flags,
+ const char *dev, void *data);
+Inode *ext2fs_alloc_inode(SuperBlock *sb);
+
+FileSystemType ext2fsType = {
+ .name = "Ext2FS",
+ .mount = ext2fs_mount,
+};
+
+SuperOps ext2fsSuperOps = {
+ .alloc_inode = ext2fs_alloc_inode,
+};
+
+/* Mount a Ext2FS instance */
+Inode *
+ext2fs_mount(FileSystemType *type, int flags, const char *dev, void *data)
+{
+ if (type != &ext2fsType)
+ return NULL;
+
+ int fd = open(dev, O_RDONLY);
+ if (fd < 0)
+ return (void *) fd;
+ File *back = file_get(current->files->fd[fd]);
+ close(fd);
+ if (!S_ISBLK(back->mode)) {
+ file_put(back);
+ return (void *) -ENOTBLK;
+ }
+
+ Ext2Super *rsuper = kmalloc(sizeof(Ext2Super));
+ back->pos = 1024;
+ file_read(back, (char *) rsuper, sizeof(Ext2Super));
+ if (rsuper->signature != 0xEF53) {
+ kprintf("Error while mounting %s: Not an Ext2FS drive", dev);
+ file_put(back);
+ kfree(rsuper);
+ return (void *) -EINVAL;
+ }
+ if (rsuper->requiredFeatures & 0x01) {
+ kprintf("Cannot mount %s: Compression required", dev);
+ file_put(back);
+ kfree(rsuper);
+ return (void *) -EINVAL;
+ }
+ if ((rsuper->writableFeatures & 0x02) && !(flags & MS_RDONLY)) {
+ kprintf("Cannot mount %s: 64-bit File System required", dev);
+ file_put(back);
+ kfree(rsuper);
+ return (void *) -EINVAL;
+ }
+
+ SuperBlock *super = kmalloc(sizeof(SuperBlock));
+ super->type = type;
+ super->ops = &ext2fsSuperOps;
+ init_lock(&super->lock);
+ super->back = back;
+ super->data = rsuper;
+ super->root = kmalloc(sizeof(Inode));
+ ext2_read_vnode(super, 2, super->root);
+
+ return super->root;
+}
+
+/* Allocate an inode */
+Inode *
+ext2fs_alloc_inode(SuperBlock *sb)
+{
+ return NULL;
+}
diff --git a/vfs/file.c b/vfs/file.c
new file mode 100644
index 0000000..d3dfdc6
--- /dev/null
+++ b/vfs/file.c
@@ -0,0 +1,181 @@
+/*
+ * This file handles the file operations. Most of the functions here are just
+ * wrapper around the file operations. They generally check if the operation
+ * exists and use it if it does. If it doesn't they may implement a generic
+ * action, or just simply return. They also perform the necessary validation to
+ * ensure the operation can be called on the particular file.
+ */
+
+#include <string.h>
+#include <errno.h>
+#include "vfs.h"
+#include "inode.h"
+#include "cache.h"
+#include "../mem/heap.h"
+#include "../mem/frame.h"
+#include "../screen.h"
+
+/* Get a File */
+File *
+file_get(File *file)
+{
+ file->usage++;
+ return file;
+}
+
+/* Put a File */
+void
+file_put(File *file)
+{
+ ASSERT(file->usage);
+ if (--file->usage)
+ return;
+ if (file->inode)
+ inode_put(file->inode);
+ if (file->path)
+ destroy_custody_chain(file->path);
+ kfree(file);
+}
+
+/* Read a file */
+size_t
+file_read(File *file, char *buf, size_t size)
+{
+ size_t count = 0;
+ if (!S_ISREG(file->inode->mode)) {
+ if (!file->ops)
+ return count;
+ if (!file->ops->read)
+ return count;
+ count = file->ops->read(file, buf, size, file->pos);
+ file->pos += count;
+ return count;
+ }
+ if (file->pos > file->inode->size)
+ return count;
+ if (size + file->pos > file->inode->size)
+ size = file->inode->size - file->pos;
+
+ uint16_t min, offset = file->pos & 0xFFF;
+ Page *page;
+ page_t oldPage;
+ acquire(&file->lock);
+ while (size) {
+ min = (size > 0x1000) ? 0x1000 : size;
+ page = page_find(file->inode, file->pos + (count & ~0xFFF));
+ if (!page) {
+ if (!file->ops)
+ goto end;
+ if (!file->ops->read)
+ goto end;
+ file->ops->read(file, buf + count, min,
+ file->pos + count);
+ } else {
+ acquire(&quickPageLock);
+ oldPage = quick_page(page->frame);
+ memcpy(buf + count, QUICK_PAGE + offset, min);
+ quick_page(oldPage);
+ release(&quickPageLock);
+ }
+ offset = 0;
+end:
+ size -= min;
+ count += min;
+ }
+ file->pos += count;
+ release(&file->lock);
+ return count;
+}
+
+/* Write a file */
+size_t
+file_write(File *file, char *buf, size_t size)
+{
+ size_t count = 0;
+ if (!S_ISREG(file->inode->mode)) {
+ if (!file->ops)
+ return count;
+ if (!file->ops->write)
+ return count;
+ count = file->ops->write(file, buf, size, file->pos);
+ file->pos += count;
+ return count;
+ }
+ if (size + file->pos > file->inode->size)
+ file->inode->size = size + file->pos;
+
+ uint16_t min, offset = file->pos & 0xFFF;
+ Page *page;
+ page_t oldPage;
+ acquire(&file->lock);
+ while (size) {
+ min = (size > 0x1000) ? 0x1000 : size;
+ page = page_find(file->inode, file->pos + (count & ~0xFFF));
+ if (!page) {
+ if (!file->ops)
+ goto end;
+ if (!file->ops->write)
+ goto end;
+ file->ops->write(file, buf + count, min,
+ file->pos + count);
+ } else {
+ acquire(&quickPageLock);
+ oldPage = quick_page(page->frame);
+ memcpy(QUICK_PAGE + offset, buf + count, min);
+ quick_page(oldPage);
+ release(&quickPageLock);
+ }
+ offset = 0;
+end:
+ size -= min;
+ count += min;
+ }
+ file->pos += count;
+ release(&file->lock);
+ return count;
+}
+
+/* I/O Control */
+int
+file_ioctl(File *file, unsigned long request, uintptr_t argp)
+{
+ if (!file->ops)
+ return -EINVAL;
+ if (!file->ops->ioctl)
+ return -ENOTTY;
+ return file->ops->ioctl(file, request, argp);
+}
+
+/* Read a directory entry (DirEnt) from a directory */
+int
+file_readdir(File *file, DirEnt *dent, off_t index)
+{
+ if (!file->ops)
+ return -EINVAL;
+ if (!file->ops->readdir)
+ return -EINVAL;
+ return file->ops->readdir(file, dent, index);
+}
+
+/* Open a file */
+int
+file_open(File *file)
+{
+ if (!file->ops)
+ return -EINVAL;
+ if (!file->ops->open)
+ return -EINVAL;
+ return file->ops->open(file);
+}
+
+/* Map a file into memory */
+void
+file_mmap(File *file, void *addr, size_t len, off_t offset)
+{
+ if (!file->ops)
+ return;
+ if (file->ops->mmap)
+ return file->ops->mmap(file, addr, len, offset);
+ if (S_ISREG(file->mode))
+ file->ops->read(file, addr, len, offset);
+}
diff --git a/vfs/inode.c b/vfs/inode.c
new file mode 100644
index 0000000..34b5c65
--- /dev/null
+++ b/vfs/inode.c
@@ -0,0 +1,204 @@
+/*
+ * This file deals with inode operations. Most of the functions here are just
+ * wrappers for the inode operations. They generally check if the operation
+ * exists and use it if it does. If it doesn't they may implement a generic
+ * action, or just simply return. They also perform the necessary validation to
+ * ensure the operation can be called on the particular inode.
+ */
+
+#include <string.h>
+#include <errno.h>
+#include "vfs.h"
+#include "cache.h"
+#include "inode.h"
+#include "super.h"
+#include "../mem/heap.h"
+#include "../screen.h"
+
+/* Get an Inode */
+Inode *
+inode_get(Inode *inode)
+{
+ /* Add the inode to the Inode List */
+ if (!inode->usage && inode->super) {
+ if (!inode->super->inodes) {
+ inode->super->inodes = inode;
+ } else {
+ inode->lnext = inode->super->inodes;
+ inode->super->inodes = inode;
+ }
+ inode->super->cachedInodes++;
+ }
+
+ inode->usage++;
+ return inode;
+}
+
+/* Put an Inode */
+void
+inode_put(Inode *inode)
+{
+ ASSERT(inode->usage);
+ if (--inode->usage)
+ return;
+
+ /* Remove the inode from the Inode List */
+ Inode *search, *prev = NULL;
+ if (inode->super) {
+ for (search = inode->super->inodes; search;
+ prev = search, search = search->lnext)
+ if (search == inode)
+ break;
+ if (search) {
+ if (prev)
+ prev->lnext = search->lnext;
+ else
+ inode->super->inodes = search->lnext;
+ }
+ }
+
+ /* Clean up */
+ if (S_ISDIR(inode->mode))
+ entry_clean(inode);
+ else
+ page_clean(inode);
+
+ kfree(inode);
+}
+
+/* Create an inode */
+int
+inode_create(Inode *inode, DirEntry *entry, mode_t mode)
+{
+ if (!inode->ops)
+ return -EINVAL;
+ if (!inode->ops->create)
+ return -EINVAL;
+ acquire(&inode->lock);
+ int err = inode->ops->create(inode, entry, mode);
+ if (!err) {
+ entry_add(inode, entry);
+ entry->inode = super_alloc_inode(inode->super);
+ entry->inode->mode = mode | S_IFREG;
+ }
+ release(&inode->lock);
+ return err;
+}
+
+/* Find a child inode in a directory inode */
+DirEntry *
+inode_lookup(Inode *inode, const char *name)
+{
+ if (!S_ISDIR(inode->mode))
+ return NULL;
+
+ acquire(&inode->lock);
+
+ /* Check cache first */
+ DirEntry *de = entry_find(inode, name);
+ if (de) {
+ entry_get(de);
+ if (de->inode) {
+ release(&inode->lock);
+ return de;
+ }
+ }
+ /* Try file system lookup */
+ if (!inode->ops) {
+ release(&inode->lock);
+ return NULL;
+ }
+ if (!inode->ops->lookup) {
+ release(&inode->lock);
+ return NULL;
+ }
+ Inode *child = inode->ops->lookup(inode, name);
+
+ /* The file doesn't exist */
+ if (!child) {
+ if (de)
+ entry_remove(inode, name);
+ release(&inode->lock);
+ return NULL;
+ }
+
+ /* Fill in DirEntry */
+ if (!de) {
+ de = kmalloc(sizeof(DirEntry));
+ strncpy(de->name, name, NAME_MAX);
+ de->super = inode->super;
+ entry_add(inode, de);
+ }
+ de->inode = inode_get(child);
+ de->mnt = NULL;
+ release(&inode->lock);
+
+ return entry_get(de);
+}
+
+/* Make a directory */
+int
+inode_mkdir(Inode *inode, DirEntry *entry, mode_t mode)
+{
+ if (!inode->ops)
+ return -EINVAL;
+ if (!inode->ops->mkdir)
+ return -EINVAL;
+ acquire(&inode->lock);
+ int err = inode->ops->mkdir(inode, entry, mode);
+ if (!err) {
+ entry_add(inode, entry);
+ entry->inode = super_alloc_inode(inode->super);
+ entry->inode->mode = mode | S_IFDIR;
+ }
+ release(&inode->lock);
+ return err;
+}
+
+/* Remove a directory */
+int
+inode_rmdir(Inode *inode, DirEntry *entry)
+{
+ if (!inode->ops)
+ return -EINVAL;
+ if (!inode->ops->rmdir)
+ return -EINVAL;
+ acquire(&inode->lock);
+ int err = inode->ops->rmdir(inode, entry);
+ if (!err)
+ entry_remove(inode, entry->name);
+ release(&inode->lock);
+ return err;
+}
+
+/* Make a node */
+int
+inode_mknod(Inode *inode, DirEntry *entry, mode_t mode, dev_t dev)
+{
+ if (!inode->ops)
+ return -EINVAL;
+ if (!inode->ops->mknod)
+ return -EINVAL;
+ acquire(&inode->lock);
+
+ int err = inode->ops->mknod(inode, entry, mode, dev);
+ if (!err) {
+ entry_add(inode, entry);
+ entry->inode = super_alloc_inode(inode->super);
+ entry->inode->mode = mode; /* FIXME */
+ entry->inode->dev = dev;
+ }
+ release(&inode->lock);
+ return err;
+}
+
+/* Rename/mode a directory entry */
+int
+inode_rename(Inode *si, DirEntry *sde, Inode *di, DirEntry *dde)
+{
+ if (!si->ops)
+ return -EINVAL;
+ if (!si->ops->rename)
+ return -EINVAL;
+ return 0;
+}
diff --git a/vfs/inode.h b/vfs/inode.h
new file mode 100644
index 0000000..adf5b3b
--- /dev/null
+++ b/vfs/inode.h
@@ -0,0 +1,15 @@
+#ifndef KERNEL_VFS_INODE_H
+#define KERNEL_VFS_INODE_H
+
+#include "vfs.h"
+
+Inode *inode_get(Inode *inode);
+void inode_put(Inode *inode);
+int inode_create(Inode *inode, DirEntry *entry, mode_t mode);
+DirEntry *inode_lookup(Inode *inode, const char *name);
+int inode_mkdir(Inode *inode, DirEntry *entry, mode_t mode);
+int inode_rmdir(Inode *inode, DirEntry *entry);
+int inode_mknod(Inode *inode, DirEntry *entry, mode_t mode, dev_t dev);
+int inode_rename(Inode *si, DirEntry *sde, Inode *di, DirEntry *dde);
+
+#endif
diff --git a/vfs/procfs/file.c b/vfs/procfs/file.c
new file mode 100644
index 0000000..b292cf4
--- /dev/null
+++ b/vfs/procfs/file.c
@@ -0,0 +1,126 @@
+/*
+ * This file controls access to ProcFS Files. It contains the functions called
+ * by the VFS for any operation on a File struct belonging to ProcFS.
+ */
+
+#include <stddef.h>
+#include <string.h>
+#include <errno.h>
+#include "fs.h"
+#include "../vfs.h"
+#include "../cache.h"
+#include "../../mem/paging.h"
+#include "../../mem/frame.h"
+
+size_t procfs_read(File *file, char *buf, size_t size, off_t offset);
+size_t procfs_write(File *file, char *buf, size_t size, off_t offset);
+int procfs_readdir(File *file, DirEnt *dent, off_t index);
+int procfs_open(File *file);
+
+FileOps procfsFileOps = {
+ .read = procfs_read,
+ .write = procfs_write,
+ .readdir = procfs_readdir,
+ .open = procfs_open,
+};
+
+/* Read a file */
+size_t
+procfs_read(File *file, char *buf, size_t size, off_t offset)
+{
+ if (offset > file->inode->size)
+ return 0;
+ if (size + offset > file->inode->size)
+ size = file->inode->size - offset;
+
+ uint16_t min;
+ size_t count = 0;
+ Page *page;
+ page_t oldPage;
+ while (size) {
+ min = (size > 0x1000) ? 0x1000 : size;
+ page = page_find(file->inode, offset);
+ if (!page)
+ break;
+ acquire(&quickPageLock);
+ oldPage = quick_page(page->frame);
+ memcpy(buf + count, QUICK_PAGE, min);
+ quick_page(oldPage);
+ release(&quickPageLock);
+ size -= min;
+ count += min;
+ }
+ return count;
+}
+
+/* Write a file */
+size_t
+procfs_write(File *file, char *buf, size_t size, off_t offset)
+{
+ if (size + offset > file->inode->size)
+ file->inode->size = size + offset;
+
+ uint16_t min;
+ size_t count = 0;
+ Page *page;
+ page_t oldPage;
+ while (size) {
+ min = (size > 0x1000) ? 0x1000 : size;
+ page = page_find(file->inode, offset);
+ if (!page)
+ page = page_create(file->inode, alloc_frames(1),
+ offset);
+ acquire(&quickPageLock);
+ oldPage = quick_page(page->frame);
+ memcpy(QUICK_PAGE, buf + count, min);
+ quick_page(oldPage);
+ release(&quickPageLock);
+ size -= min;
+ count += min;
+ }
+ return count;
+}
+
+/* Read a directory entry */
+int
+procfs_readdir(File *file, DirEnt *dent, off_t index)
+{
+ DirEntry *de;
+
+ if (!index--) {
+ dent->ino = file->inode->ino;
+ dent->type = DT_DIR;
+ dent->namelen = 2;
+ strncpy(dent->name, ".", dent->namelen);
+ return 0;
+ }
+
+ for (de = file->inode->dirEntries; de && index; de = de->next, index--);
+ if (!de)
+ return -ENOENT;
+ dent->ino = de->inode->ino;
+ if (S_ISBLK(de->inode->mode))
+ dent->type = DT_BLK;
+ if (S_ISCHR(de->inode->mode))
+ dent->type = DT_CHR;
+ if (S_ISDIR(de->inode->mode))
+ dent->type = DT_DIR;
+ if (S_ISFIFO(de->inode->mode))
+ dent->type = DT_FIFO;
+ if (S_ISLNK(de->inode->mode))
+ dent->type = DT_LNK;
+ if (S_ISREG(de->inode->mode))
+ dent->type = DT_REG;
+ if (S_ISSOCK(de->inode->mode))
+ dent->type = DT_SOCK;
+ dent->namelen = strnlen(de->name, NAME_MAX) + 1;
+ strncpy(dent->name, de->name, NAME_MAX);
+ return 0;
+}
+
+/* Open a file */
+int
+procfs_open(File *file)
+{
+ return 0;
+}
diff --git a/vfs/procfs/fs.h b/vfs/procfs/fs.h
new file mode 100644
index 0000000..346c1b3
--- /dev/null
+++ b/vfs/procfs/fs.h
@@ -0,0 +1,13 @@
+#ifndef KERNEL_VFS_PROCFS_H
+#define KERNEL_VFS_PROCFS_H
+
+#include "../vfs.h"
+
+/* Operations */
+extern SuperOps procfsSuperOps;
+extern InodeOps procfsInodeOps;
+extern FileOps procfsFileOps;
+
+extern FileSystemType procfsType;
+
+#endif
diff --git a/vfs/procfs/inode.c b/vfs/procfs/inode.c
new file mode 100644
index 0000000..a81c3fc
--- /dev/null
+++ b/vfs/procfs/inode.c
@@ -0,0 +1,69 @@
+/*
+ * This file contains the functions dealing with ProcFS inodes. The VFS will
+ * call these when it performs operations on ProcFS Inodes, or is dealing with
+ * the ProcFS hierarchy.
+ */
+
+#include <string.h>
+#include "fs.h"
+#include "../vfs.h"
+#include "../../mem/heap.h"
+
+int procfs_create(Inode *inode, DirEntry *entry, mode_t mode);
+Inode *procfs_lookup(Inode *inode, const char *name);
+int procfs_mkdir(Inode *inode, DirEntry *entry, mode_t mode);
+int procfs_rmdir(Inode *inode, DirEntry *entry);
+int procfs_mknod(Inode *inode, DirEntry *entry, mode_t mode, dev_t dev);
+int procfs_rename(Inode *si, DirEntry *sde, Inode *di, DirEntry *dde);
+
+InodeOps procfsInodeOps = {
+ .create = procfs_create,
+ .lookup = procfs_lookup,
+ .mkdir = procfs_mkdir,
+ .rmdir = procfs_rmdir,
+ .mknod = procfs_mknod,
+ .rename = procfs_rename,
+};
+
+/* Create a file */
+int
+procfs_create(Inode *inode, DirEntry *entry, mode_t mode)
+{
+ return 0;
+}
+
+
+/* Look up a file */
+Inode *
+procfs_lookup(Inode *inode, const char *name)
+{
+ return NULL;
+}
+
+/* Make a directory */
+int
+procfs_mkdir(Inode *inode, DirEntry *entry, mode_t mode)
+{
+ return 0;
+}
+
+/* Remove a directory */
+int
+procfs_rmdir(Inode *inode, DirEntry *entry)
+{
+ return 0;
+}
+
+/* Make a node */
+int
+procfs_mknod(Inode *inode, DirEntry *entry, mode_t mode, dev_t dev)
+{
+ return 0;
+}
+
+/* Rename/mode a directory entry */
+int
+procfs_rename(Inode *si, DirEntry *sde, Inode *di, DirEntry *dde)
+{
+ return 0;
+}
diff --git a/vfs/procfs/super.c b/vfs/procfs/super.c
new file mode 100644
index 0000000..dcfa871
--- /dev/null
+++ b/vfs/procfs/super.c
@@ -0,0 +1,77 @@
+/*
+ * This file controls the superblock for ProcFS. It supports mounting new ProcFS
+ * filesystems. The VFS will use the calls here when dealing directly with the
+ * filesystem structure.
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include "fs.h"
+#include "../vfs.h"
+#include "../super.h"
+#include "../inode.h"
+#include "../../mem/heap.h"
+
+Inode *procfs_mount(FileSystemType *type, int flags,
+ const char *dev, void *data);
+Inode *procfs_alloc_inode(SuperBlock *sb);
+
+FileSystemType procfsType = {
+ .name = "ProcFS",
+ .mount = procfs_mount,
+// .kill_sb = procfs_kill_sb,
+};
+
+SuperOps procfsSuperOps = {
+ .alloc_inode = procfs_alloc_inode,
+// .free_inode = procfs_free_inode,
+// .write_inode = procfs_write_inode,
+// .delete_inode = procfs_delete_inode,
+};
+
+Inode procfsRoot = {
+ .mode = S_IFDIR | 0755,
+ .ops = &procfsInodeOps,
+ .fileOps = &procfsFileOps,
+ .size = 4096,
+};
+
+/* Mount a ProcFS instance */
+Inode *
+procfs_mount(FileSystemType *type, int flags, const char *dev, void *data)
+{
+ if (type != &procfsType)
+ return NULL;
+
+ SuperBlock *super = kmalloc(sizeof(SuperBlock));
+
+ super->type = type;
+ super->ops = &procfsSuperOps;
+ init_lock(&super->lock);
+
+ Inode *inode = &procfsRoot;
+ inode->nlink++;
+ inode->super = super;
+ /* FIXME: need an inode per super, or only one super */
+ super->root = inode;
+ /* Never free */
+ if (!inode->usage)
+ inode_get(inode);
+
+ return inode;
+}
+
+/* Allocate an inode */
+Inode *
+procfs_alloc_inode(SuperBlock *sb)
+{
+ Inode *inode = kmalloc(sizeof(Inode));
+ init_lock(&inode->lock);
+ inode->ops = &procfsInodeOps;
+ inode->fileOps = &procfsFileOps;
+ inode->super = sb;
+ inode->nlink = 1;
+ return inode_get(inode); /* This ensures that the inode is never free */
+}
diff --git a/vfs/super.c b/vfs/super.c
new file mode 100644
index 0000000..5201110
--- /dev/null
+++ b/vfs/super.c
@@ -0,0 +1,42 @@
+/*
+ * This file contains the wrapper functions for the Super Block Operations. It
+ * just calls the relevant internal functions for the file system, since most of
+ * them need to know driver specific values, like where to find the file system
+ * operations. There usually isn't a generic way to handle the functions.
+ */
+
+#include <stdint.h>
+#include "vfs.h"
+
+/* Allocate an inode */
+Inode *
+super_alloc_inode(SuperBlock *sb)
+{
+ if (!sb->ops)
+ return NULL;
+ if (!sb->ops->alloc_inode)
+ return NULL;
+ return sb->ops->alloc_inode(sb);
+}
+
+/* Search for an Inode in a Super Block's Inode list */
+Inode *
+super_find_inode(SuperBlock *sb, ino_t ino)
+{
+ Inode *inode;
+ for (inode = sb->inodes; inode; inode = inode->lnext)
+ if (inode->ino == ino)
+ break;
+ return inode;
+}
+
+/* Write an inode to disk */
+int
+super_write_inode(SuperBlock *sb, Inode *inode)
+{
+ if (!sb->ops)
+ return 0;
+ if (!sb->ops->write_inode)
+ return 0;
+ return sb->ops->write_inode(inode);
+}
diff --git a/vfs/super.h b/vfs/super.h
new file mode 100644
index 0000000..917b7c1
--- /dev/null
+++ b/vfs/super.h
@@ -0,0 +1,10 @@
+#ifndef KERNEL_VFS_SUPER_H
+#define KERNEL_VFS_SUPER_H
+
+#include "vfs.h"
+
+Inode *super_alloc_inode(SuperBlock *sb);
+Inode *super_find_inode(SuperBlock *sb, ino_t ino);
+int super_write_inode(SuperBlock *sb, Inode *inode);
+
+#endif
diff --git a/vfs/tmpfs/file.c b/vfs/tmpfs/file.c
new file mode 100644
index 0000000..252c88f
--- /dev/null
+++ b/vfs/tmpfs/file.c
@@ -0,0 +1,126 @@
+/*
+ * This file controls access to TmpFS Files. It contains the functions called
+ * by the VFS for any operation on a File struct belonging to TmpFS.
+ */
+
+#include <stddef.h>
+#include <string.h>
+#include <errno.h>
+#include "fs.h"
+#include "../vfs.h"
+#include "../cache.h"
+#include "../../mem/paging.h"
+#include "../../mem/frame.h"
+
+size_t tmpfs_read(File *file, char *buf, size_t size, off_t offset);
+size_t tmpfs_write(File *file, char *buf, size_t size, off_t offset);
+int tmpfs_readdir(File *file, DirEnt *dent, off_t index);
+int tmpfs_open(File *file);
+
+FileOps tmpfsFileOps = {
+ .read = tmpfs_read,
+ .write = tmpfs_write,
+ .readdir = tmpfs_readdir,
+ .open = tmpfs_open,
+};
+
+/* Read a file */
+size_t
+tmpfs_read(File *file, char *buf, size_t size, off_t offset)
+{
+ if (offset > file->inode->size)
+ return 0;
+ if (size + offset > file->inode->size)
+ size = file->inode->size - offset;
+
+ uint16_t min;
+ size_t count = 0;
+ Page *page;
+ page_t oldPage;
+ while (size) {
+ min = (size > 0x1000) ? 0x1000 : size;
+ page = page_find(file->inode, offset);
+ if (!page)
+ break;
+ acquire(&quickPageLock);
+ oldPage = quick_page(page->frame);
+ memcpy(buf + count, QUICK_PAGE, min);
+ quick_page(oldPage);
+ release(&quickPageLock);
+ size -= min;
+ count += min;
+ }
+ return count;
+}
+
+/* Write a file */
+size_t
+tmpfs_write(File *file, char *buf, size_t size, off_t offset)
+{
+ if (size + offset > file->inode->size)
+ file->inode->size = size + offset;
+
+ uint16_t min;
+ size_t count = 0;
+ Page *page;
+ page_t oldPage;
+ while (size) {
+ min = (size > 0x1000) ? 0x1000 : size;
+ page = page_find(file->inode, offset);
+ if (!page)
+ page = page_create(file->inode, alloc_frames(1),
+ offset);
+ acquire(&quickPageLock);
+ oldPage = quick_page(page->frame);
+ memcpy(QUICK_PAGE, buf + count, min);
+ quick_page(oldPage);
+ release(&quickPageLock);
+ size -= min;
+ count += min;
+ }
+ return count;
+}
+
+/* Read a directory entry */
+int
+tmpfs_readdir(File *file, DirEnt *dent, off_t index)
+{
+ DirEntry *de;
+
+ if (!index--) {
+ dent->ino = file->inode->ino;
+ dent->type = DT_DIR;
+ dent->namelen = 2;
+ strncpy(dent->name, ".", dent->namelen);
+ return 0;
+ }
+
+ for (de = file->inode->dirEntries; de && index; de = de->next, index--);
+ if (!de)
+ return -ENOENT;
+ dent->ino = de->inode->ino;
+ if (S_ISBLK(de->inode->mode))
+ dent->type = DT_BLK;
+ if (S_ISCHR(de->inode->mode))
+ dent->type = DT_CHR;
+ if (S_ISDIR(de->inode->mode))
+ dent->type = DT_DIR;
+ if (S_ISFIFO(de->inode->mode))
+ dent->type = DT_FIFO;
+ if (S_ISLNK(de->inode->mode))
+ dent->type = DT_LNK;
+ if (S_ISREG(de->inode->mode))
+ dent->type = DT_REG;
+ if (S_ISSOCK(de->inode->mode))
+ dent->type = DT_SOCK;
+ dent->namelen = strnlen(de->name, NAME_MAX) + 1;
+ strncpy(dent->name, de->name, NAME_MAX);
+ return 0;
+}
+
+/* Open a file */
+int
+tmpfs_open(File *file)
+{
+ return 0;
+}
diff --git a/vfs/tmpfs/fs.h b/vfs/tmpfs/fs.h
new file mode 100644
index 0000000..104f64d
--- /dev/null
+++ b/vfs/tmpfs/fs.h
@@ -0,0 +1,13 @@
+#ifndef KERNEL_VFS_TMPFS_H
+#define KERNEL_VFS_TMPFS_H
+
+#include "../vfs.h"
+
+/* Operations */
+extern SuperOps tmpfsSuperOps;
+extern InodeOps tmpfsInodeOps;
+extern FileOps tmpfsFileOps;
+
+extern FileSystemType tmpfsType;
+
+#endif
diff --git a/vfs/tmpfs/inode.c b/vfs/tmpfs/inode.c
new file mode 100644
index 0000000..5da5408
--- /dev/null
+++ b/vfs/tmpfs/inode.c
@@ -0,0 +1,69 @@
+/*
+ * This file contains the functions dealing with TmpFS inodes. The VFS will
+ * call these when it performs operations on TmpFS Inodes, or is dealing with
+ * the TmpFS hierarchy.
+ */
+
+#include <string.h>
+#include "fs.h"
+#include "../vfs.h"
+#include "../../mem/heap.h"
+
+int tmpfs_create(Inode *inode, DirEntry *entry, mode_t mode);
+Inode *tmpfs_lookup(Inode *inode, const char *name);
+int tmpfs_mkdir(Inode *inode, DirEntry *entry, mode_t mode);
+int tmpfs_rmdir(Inode *inode, DirEntry *entry);
+int tmpfs_mknod(Inode *inode, DirEntry *entry, mode_t mode, dev_t dev);
+int tmpfs_rename(Inode *si, DirEntry *sde, Inode *di, DirEntry *dde);
+
+InodeOps tmpfsInodeOps = {
+ .create = tmpfs_create,
+ .lookup = tmpfs_lookup,
+ .mkdir = tmpfs_mkdir,
+ .rmdir = tmpfs_rmdir,
+ .mknod = tmpfs_mknod,
+ .rename = tmpfs_rename,
+};
+
+/* Create a file */
+int
+tmpfs_create(Inode *inode, DirEntry *entry, mode_t mode)
+{
+ return 0;
+}
+
+
+/* Look up a file */
+Inode *
+tmpfs_lookup(Inode *inode, const char *name)
+{
+ return NULL;
+}
+
+/* Make a directory */
+int
+tmpfs_mkdir(Inode *inode, DirEntry *entry, mode_t mode)
+{
+ return 0;
+}
+
+/* Remove a directory */
+int
+tmpfs_rmdir(Inode *inode, DirEntry *entry)
+{
+ return 0;
+}
+
+/* Make a node */
+int
+tmpfs_mknod(Inode *inode, DirEntry *entry, mode_t mode, dev_t dev)
+{
+ return 0;
+}
+
+/* Rename/mode a directory entry */
+int
+tmpfs_rename(Inode *si, DirEntry *sde, Inode *di, DirEntry *dde)
+{
+ return 0;
+}
diff --git a/vfs/tmpfs/super.c b/vfs/tmpfs/super.c
new file mode 100644
index 0000000..ede1961
--- /dev/null
+++ b/vfs/tmpfs/super.c
@@ -0,0 +1,65 @@
+/*
+ * This file controls the superblock for TmpFS. It supports mounting new TmpFS
+ * filesystems. The VFS will use the calls here when dealing directly with the
+ * filesystem structure.
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include "fs.h"
+#include "../vfs.h"
+#include "../super.h"
+#include "../inode.h"
+#include "../../mem/heap.h"
+
+Inode *tmpfs_mount(FileSystemType *type, int flags,
+ const char *dev, void *data);
+Inode *tmpfs_alloc_inode(SuperBlock *sb);
+
+FileSystemType tmpfsType = {
+ .name = "TmpFS",
+ .mount = tmpfs_mount,
+// .kill_sb = tmpfs_kill_sb,
+};
+
+SuperOps tmpfsSuperOps = {
+ .alloc_inode = tmpfs_alloc_inode,
+// .free_inode = tmpfs_free_inode,
+// .write_inode = tmpfs_write_inode,
+// .delete_inode = tmpfs_delete_inode,
+};
+
+/* Mount a TmpFS instance */
+Inode *
+tmpfs_mount(FileSystemType *type, int flags, const char *dev, void *data)
+{
+ if (type != &tmpfsType)
+ return NULL;
+
+ SuperBlock *super = kmalloc(sizeof(SuperBlock));
+
+ super->type = type;
+ super->ops = &tmpfsSuperOps;
+ init_lock(&super->lock);
+
+ Inode *inode = super_alloc_inode(super);
+ inode->mode = S_IFDIR | 0755;
+ super->root = inode;
+
+ return inode;
+}
+
+/* Allocate an inode */
+Inode *
+tmpfs_alloc_inode(SuperBlock *sb)
+{
+ Inode *inode = kmalloc(sizeof(Inode));
+ init_lock(&inode->lock);
+ inode->ops = &tmpfsInodeOps;
+ inode->fileOps = &tmpfsFileOps;
+ inode->super = sb;
+ inode->nlink = 1;
+ return inode_get(inode); /* This ensures that the inode is never free */
+}
diff --git a/vfs/vfs.c b/vfs/vfs.c
new file mode 100644
index 0000000..68f4b34
--- /dev/null
+++ b/vfs/vfs.c
@@ -0,0 +1,680 @@
+/*
+ * This file controls the Virtual File System for the Kernel. It implements a
+ * generic File System, which can hook other File Systems. This forms a key
+ * part of the Kernel, and is used for loading programs and some higher-level
+ * IPC. This system will dispatch messages to the actual File System tasks in
+ * user-space.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include "tmpfs/fs.h"
+#include "devfs/fs.h"
+#include "procfs/fs.h"
+#include "ext2fs/fs.h"
+#include "vfs.h"
+#include "cache.h"
+#include "inode.h"
+#include "../mem/heap.h"
+#include "../task/task.h"
+
+FileSystemType *fsTypes;
+
+/* Register a File System Type */
+void
+register_fstype(FileSystemType *fstype)
+{
+ if (!fsTypes) {
+ fsTypes = fstype;
+ return;
+ }
+ FileSystemType *prev;
+ for (prev = fsTypes; prev->next; prev = prev->next);
+ prev->next = fstype;
+}
+
+/* Initialise the Virtual File System */
+void
+init_vfs(void)
+{
+ register_fstype(&tmpfsType);
+ register_fstype(&devfsType);
+ register_fstype(&procfsType);
+ register_fstype(&ext2fsType);
+
+ mount("tmpfs", NULL, "TmpFS", 0, NULL);
+ mkdir("dev", 0);
+ mount("devfs", "/dev", "DevFS", 0, NULL);
+ mkdir("proc", 0);
+ mount("procfs", "/proc", "ProcFS", 0, NULL);
+}
+
+/* Check if a process has permission for a file */
+int
+permission(Inode *inode, int mask)
+{
+ if (!inode)
+ return 0;
+
+ int mode = inode->mode;
+
+ if (current->euid == inode->uid)
+ mode >>= 6;
+ else if (current->egid == inode->gid)
+ mode >>= 3;
+
+ if (((mode & mask & 0007) == mask) || super_user())
+ return 1;
+ return 0;
+}
+
+/* Lookup a file path */
+Inode *
+lookup(const char *path, CustodyChain *newchain)
+{
+ if (!current->fs->root)
+ return NULL;
+
+ char buf[PATH_MAX], *p = buf;
+ strncpy(buf, path, PATH_MAX);
+ Inode *ino;
+ DirEntry *de = NULL;
+ CustodyChain *chain = create_custody_chain();
+
+ /* Resolve to absolute/relative root */
+ if (*p == '/') {
+ ino = current->fs->root;
+ while (*++p == '/');
+ } else {
+ ino = current->fs->cwd;
+ copy_custody_chain(¤t->fs->cwdCustody, chain);
+ }
+
+ /* Drop through directories */
+ char *curr, *prev = p;
+ for (curr = p; *curr; curr++) {
+ if (*curr != '/')
+ continue;
+ *curr = '\0';
+ if (!strcmp(prev, ".")) {
+ de = NULL;
+ } else if (!strcmp(prev, "..")) {
+ de = NULL;
+ if (ino != current->fs->root) {
+ de = remove_custody(chain);
+ if (!de)
+ ino = current->fs->root;
+ }
+ } else {
+ de = inode_lookup(ino, prev);
+ if (!de) {
+ ino = NULL;
+ goto end;
+ }
+ add_custody(chain, de);
+ entry_put(de);
+ }
+ if (de) {
+ ino = de->inode;
+ if (de->mnt)
+ ino = de->mnt;
+ }
+ while (*++curr == '/');
+ prev = curr;
+ }
+ /* Basename */
+ if (!strcmp(prev, ".") || !strcmp(prev, "")) {
+ if (!S_ISDIR(ino->mode)) {
+ ino = NULL;
+ de = NULL;
+ }
+ } else if (!strcmp(prev, "..")) {
+ if (ino != current->fs->root) {
+ de = remove_custody(chain);
+ if (!de)
+ ino = current->fs->root;
+ }
+ } else {
+ de = inode_lookup(ino, prev);
+ if (!de) {
+ de = kmalloc(sizeof(DirEntry));
+ init_lock(&de->lock);
+ strncpy(de->name, prev, NAME_MAX);
+ de->super = ino->super;
+ de->usage = 1;
+ }
+ add_custody(chain, de);
+ entry_put(de);
+ }
+ if (de) {
+ ino = de->inode;
+ if (de->mnt)
+ ino = de->mnt;
+ if (ino)
+ inode_get(ino);
+ }
+
+ /* Copy chain */
+ if (newchain)
+ copy_custody_chain(chain, newchain);
+end:
+ destroy_custody_chain(chain);
+ return ino;
+}
+
+/* Mount a file system */
+int
+mount(const char *src, const char *target, const char *type,
+ unsigned long flags, void *data)
+{
+ if (!verify_access(src, strnlen(src, PATH_MAX), PROT_READ))
+ return -EFAULT;
+ if (!verify_access(target, strnlen(target, PATH_MAX), PROT_READ))
+ return -EFAULT;
+ if (!verify_access(type, strnlen(type, 16), PROT_READ))
+ return -EFAULT;
+
+ FileSystemType *fsType;
+ for (fsType = fsTypes; fsType; fsType = fsType->next)
+ if (!strcmp(fsType->name, type))
+ break;
+ if (!fsType)
+ return -ENODEV;
+
+ Inode *mnt = fsType->mount(fsType, flags, src, data);
+ if ((int) mnt < 0)
+ return (int) mnt;
+ if (!target && !current->fs->root) {
+ current->fs->root = inode_get(mnt);
+ current->fs->cwd = inode_get(mnt);
+ return 0;
+ }
+ /* Find target's DirEntry */
+ CustodyChain tmp;
+ init_custody_chain(&tmp);
+ Inode *root = lookup(target, &tmp);
+ if (!root) {
+ clean_custody_chain(&tmp);
+ return -ENOENT;
+ }
+ if (!S_ISDIR(root->mode)) {
+ inode_put(root);
+ clean_custody_chain(&tmp);
+ return -ENOTDIR;
+ }
+ DirEntry *de = entry_get(tmp.end->entry);
+ de->mnt = inode_get(mnt);
+ inode_put(root);
+ clean_custody_chain(&tmp);
+ return 0;
+}
+
+/* Open a file */
+int
+open(const char *name, int flags, ...)
+{
+ if (!verify_access(name, strnlen(name, PATH_MAX), PROT_READ))
+ return -EFAULT;
+
+ /* Allocate file descriptor */
+ int fd;
+ for (fd = 0; fd < NFILES; fd++)
+ if (!current->files->fd[fd])
+ break;
+ if (fd == NFILES)
+ return -EMFILE;
+
+ /* Find inode */
+ CustodyChain chain;
+ init_custody_chain(&chain);
+ Inode *ino = lookup(name, &chain);
+ va_list args;
+ va_start(args, flags);
+ if (!ino) {
+ if (!(flags & O_CREATE)) {
+ clean_custody_chain(&chain);
+ return -ENOENT;
+ }
+ /* Create file */
+ if (!chain.size) {
+ clean_custody_chain(&chain);
+ return -ENOENT;
+ }
+ if (chain.end->prev->entry) {
+ ino = chain.end->prev->entry->inode;
+ if (chain.end->prev->entry->mnt)
+ ino = chain.end->prev->entry->mnt;
+ } else {
+ ino = current->fs->root;
+ }
+ if (!permission(ino, MAY_WRITE)) {
+ clean_custody_chain(&chain);
+ return -EACCES;
+ }
+ inode_create(ino, chain.end->entry, va_arg(args, mode_t));
+ ino = lookup(name, NULL);
+ }
+ va_end(args);
+
+ /* Check permissions */
+ int perm = 0;
+ if ((flags & O_ACCMODE) == O_RDONLY)
+ perm = MAY_READ;
+ else if ((flags & O_ACCMODE) == O_WRONLY)
+ perm = MAY_WRITE;
+ else if ((flags & O_ACCMODE) == O_RDWR)
+ perm = MAY_READ | MAY_WRITE;
+ if (!permission(ino, perm)) {
+ inode_put(ino);
+ clean_custody_chain(&chain);
+ return -EACCES;
+ }
+
+ /* Open file */
+ File *file = kmalloc(sizeof(File));
+ file->inode = inode_get(ino);
+ file->uid = ino->uid;
+ file->gid = ino->gid;
+ file->flags = flags;
+ file->mode = ino->mode;
+ file->pos = 0;
+ init_lock(&file->lock);
+ file->ops = ino->fileOps;
+ file->path = create_custody_chain();
+ file->usage = 1;
+
+ copy_custody_chain(&chain, file->path);
+ clean_custody_chain(&chain);
+
+ int err = file_open(file);
+ if (err) {
+ file_put(file);
+ inode_put(ino);
+ return err;
+ }
+
+ current->files->fd[fd] = file;
+ inode_put(ino);
+ return fd;
+}
+
+/* Close a file */
+int
+close(int fd)
+{
+ if (fd < 0 || fd >= NFILES)
+ return -EBADF;
+ File *file = current->files->fd[fd];
+ current->files->fd[fd] = NULL;
+ if (!file)
+ return -EBADF;
+
+ file_put(file);
+ return 0;
+}
+
+/* Read a file */
+size_t
+read(int fd, void *buf, size_t count)
+{
+ if (!verify_access(buf, count, PROT_WRITE))
+ return -EFAULT;
+ if (fd < 0 || fd >= NFILES)
+ return -EBADF;
+ File *file = current->files->fd[fd];
+ if (!file)
+ return -EBADF;
+
+ if (S_ISDIR(file->mode))
+ return -EISDIR;
+
+ return file_read(file, buf, count);
+}
+
+/* Write a file */
+size_t
+write(int fd, void *buf, size_t count)
+{
+ if (!verify_access(buf, count, PROT_READ))
+ return -EFAULT;
+ if (fd < 0 || fd >= NFILES)
+ return -EBADF;
+ File *file = current->files->fd[fd];
+ if (!file)
+ return -EBADF;
+
+ if (S_ISDIR(file->mode))
+ return -EISDIR;
+
+ return file_write(file, buf, count);
+}
+
+/* I/O Control */
+int
+ioctl(int fd, unsigned long request, ...)
+{
+ if (fd < 0 || fd >= NFILES)
+ return -EBADF;
+ File *file = current->files->fd[fd];
+ if (!file)
+ return -EBADF;
+ va_list args;
+ va_start(args, request);
+ uintptr_t argp = va_arg(args, uintptr_t);
+ va_end(args);
+ return file_ioctl(file, request, argp);
+}
+
+/* Move a file's position */
+off_t
+lseek(int fd, off_t offset, int whence)
+{
+ if (fd < 0 || fd >= NFILES)
+ return -EBADF;
+ File *file = current->files->fd[fd];
+ if (!file)
+ return -EBADF;
+ acquire(&file->lock);
+
+ switch (whence) {
+ case SEEK_SET:
+ file->pos = offset;
+ break;
+ case SEEK_CUR:
+ file->pos += offset;
+ break;
+ case SEEK_END:
+ file->pos = file->inode->size + offset;
+ break;
+ default:
+ release(&file->lock);
+ return -EINVAL;
+ }
+ release(&file->lock);
+ return file->pos;
+}
+
+/* Get file status */
+int
+stat(const char *pathname, struct stat *statbuf)
+{
+ if (!verify_access(pathname, strnlen(pathname, PATH_MAX), PROT_READ))
+ return -EFAULT;
+ if (!verify_access(statbuf, sizeof(struct stat), PROT_WRITE))
+ return -EFAULT;
+ Inode *inode = lookup(pathname, NULL);
+ if (!inode)
+ return -ENOENT;
+ if (statbuf) {
+ statbuf->inode = inode->ino;
+ statbuf->mode = inode->mode;
+ statbuf->nlink = inode->nlink;
+ statbuf->uid = inode->uid;
+ statbuf->gid = inode->gid;
+ statbuf->size = inode->size;
+ }
+ inode_put(inode);
+ return 0;
+}
+
+/* Get file status by file descriptor */
+int
+fstat(int fd, struct stat *statbuf)
+{
+ if (!verify_access(statbuf, sizeof(struct stat), PROT_WRITE))
+ return -EFAULT;
+ if (fd < 0 || fd >= NFILES)
+ return -EBADF;
+ File *file = current->files->fd[fd];
+ if (!file)
+ return -EBADF;
+ Inode *inode = file->inode;
+ statbuf->inode = inode->ino;
+ statbuf->mode = inode->mode;
+ statbuf->size = inode->size;
+ return 0;
+}
+
+/* Get directory entries */
+size_t
+getdents(int fd, void *buf, size_t count)
+{
+ if (!verify_access(buf, count, PROT_WRITE))
+ return -EFAULT;
+ if (count < sizeof(DirEnt))
+ return -EINVAL;
+
+ int err;
+ size_t i, size = 0;
+ DirEnt *dent = buf;
+ File *file = current->files->fd[fd];
+ if (!file)
+ return -EBADF;
+ if (!S_ISDIR(file->inode->mode))
+ return -ENOTDIR;
+
+ /* Read as many entries as possible */
+ for (i = 0; i < count / sizeof(DirEnt); i++) {
+ err = file_readdir(file, dent, i);
+ if (err < 0)
+ goto out;
+ size += sizeof(DirEnt) + dent->namelen;
+ *((char *) buf + size - 1) = '\0';
+ dent = (void *) ((char *) buf + size);
+ }
+out:
+ if (!i)
+ return err;
+ return size;
+}
+
+/* Make a directory */
+int
+mkdir(const char *pathname, mode_t mode)
+{
+ if (!verify_access(pathname, strnlen(pathname, PATH_MAX), PROT_READ))
+ return -EFAULT;
+ int err;
+ CustodyChain chain;
+ init_custody_chain(&chain);
+ Inode *inode = lookup(pathname, &chain);
+ if (inode) {
+ err = -EEXIST;
+ goto end;
+ }
+ if (!chain.size) {
+ err = -ENOENT;
+ goto end;
+ }
+ /* Check write permission */
+ if (chain.end->prev->entry) {
+ inode = chain.end->prev->entry->inode;
+ if (chain.end->prev->entry->mnt)
+ inode = chain.end->prev->entry->mnt;
+ } else {
+ inode = current->fs->root;
+ }
+ if (!permission(inode, MAY_WRITE)) {
+ err = -EACCES;
+ goto end;
+ }
+
+ /* Create directory */
+ err = inode_mkdir(inode, entry_get(chain.end->entry), mode);
+end:
+ clean_custody_chain(&chain);
+// if (inode)
+// inode_put(inode);
+ return err;
+}
+
+/* Remove a directory */
+int
+rmdir(const char *pathname)
+{
+ return 0;
+}
+
+/* Make a special node */
+int
+mknod(const char *pathname, mode_t mode, dev_t dev)
+{
+ if (!verify_access(pathname, strnlen(pathname, PATH_MAX), PROT_READ))
+ return -EFAULT;
+ int err;
+ CustodyChain chain;
+ init_custody_chain(&chain);
+ Inode *inode = lookup(pathname, &chain);
+ if (inode) {
+ err = -EEXIST;
+ goto end;
+ }
+ if (!chain.size) {
+ err = -ENOENT;
+ goto end;
+ }
+ /* Check write permission */
+ if (chain.end->prev->entry) {
+ inode = chain.end->prev->entry->inode;
+ if (chain.end->prev->entry->mnt)
+ inode = chain.end->prev->entry->mnt;
+ } else {
+ inode = current->fs->root;
+ }
+ if (!permission(inode, MAY_WRITE)) {
+ err = -EACCES;
+ goto end;
+ }
+
+ /* Create node */
+ err = inode_mknod(inode, entry_get(chain.end->entry), mode, dev);
+end:
+ clean_custody_chain(&chain);
+// if (inode)
+// inode_put(inode);
+ return err;
+}
+
+/* Rename/move a file */
+int
+rename(const char *oldpath, const char *newpath)
+{
+ return 0;
+}
+
+/* Duplicate a file descriptor */
+int
+dup(int oldfd)
+{
+ /* Check file descriptor */
+ if (oldfd < 0 || oldfd >= NFILES)
+ return -EBADF;
+ if (!current->files->fd[oldfd])
+ return -EBADF;
+
+ /* Allocate file descriptor */
+ int fd;
+ for (fd = 0; fd < NFILES; fd++)
+ if (!current->files->fd[fd])
+ break;
+ if (fd == NFILES)
+ return -EMFILE;
+ dup2(oldfd, fd);
+}
+
+/* Duplicate a file descriptor to a new file descriptor */
+int
+dup2(int oldfd, int newfd)
+{
+ /* Check file descriptors */
+ if (oldfd < 0 || oldfd >= NFILES)
+ return -EBADF;
+ if (!current->files->fd[oldfd])
+ return -EBADF;
+ if (newfd < 0 || newfd >= NFILES)
+ return -EBADF;
+
+ if (newfd == oldfd)
+ return newfd;
+
+ /* Close old file in newfd */
+ if (current->files->fd[newfd])
+ close(newfd);
+ current->files->fd[newfd] = file_get(current->files->fd[oldfd]);
+ return newfd;
+}
+
+/* Check if a file descriptor refers to a terminal */
+int
+isatty(int fd)
+{
+ if (fd < 0 || fd >= NFILES)
+ return -EBADF;
+ File *file = current->files->fd[fd];
+ if (!file)
+ return -EBADF;
+ if (S_ISCHR(file->mode))
+ return 1;
+ return 0;
+}
+
+/* Change the current working directory */
+int
+chdir(const char *path)
+{
+ if (!verify_access(path, strnlen(path, PATH_MAX), PROT_READ))
+ return -EFAULT;
+ CustodyChain tmp;
+ init_custody_chain(&tmp);
+ Inode *node = lookup(path, &tmp);
+ if (!node)
+ return -ENOENT;
+ if (!S_ISDIR(node->mode))
+ return -ENOTDIR;
+ if (!permission(node, MAY_READ))
+ return -EACCES;
+ inode_put(current->fs->cwd);
+ current->fs->cwd = inode_get(node);
+ while (current->fs->cwdCustody.size)
+ remove_custody(¤t->fs->cwdCustody);
+ copy_custody_chain(&tmp, ¤t->fs->cwdCustody);
+ clean_custody_chain(&tmp);
+// inode_put(node);
+ return 0;
+}
+
+/* Change the current root */
+int
+chroot(const char *path)
+{
+ if (!verify_access(path, strnlen(path, PATH_MAX), PROT_READ))
+ return -EFAULT;
+ Inode *node = lookup(path, NULL);
+ if (!node)
+ return -ENOENT;
+ if (!S_ISDIR(node->mode))
+ return -ENOTDIR;
+ if (!permission(node, MAY_READ))
+ return -EACCES;
+ inode_put(current->fs->root);
+ current->fs->root = inode_get(node);
+// inode_put(node);
+ return 0;
+}
+
+/* Get the current working directory's path */
+char *
+getcwd(char *buf, size_t size)
+{
+ if (!verify_access(buf, size, PROT_WRITE))
+ return (char *) -EFAULT;
+ return custody_path(¤t->fs->cwdCustody, buf, size);
+}
diff --git a/vfs/vfs.h b/vfs/vfs.h
new file mode 100644
index 0000000..ece5679
--- /dev/null
+++ b/vfs/vfs.h
@@ -0,0 +1,202 @@
+#ifndef KERNEL_VFS_H
+#define KERNEL_VFS_H
+
+#include <stdint.h>
+#include <stddef.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include "../spinlock.h"
+#include "../mem/paging.h"
+
+#define NFILES 32
+#define NAME_MAX 255
+#define PATH_MAX 1024
+
+#define MAY_READ 4
+#define MAY_WRITE 2
+#define MAY_EXECUTE 1
+
+typedef struct FileSystemType FileSystemType;
+typedef struct SuperBlock SuperBlock;
+typedef struct SuperOps SuperOps;
+typedef struct Inode Inode;
+typedef struct InodeOps InodeOps;
+typedef struct File File;
+typedef struct FileOps FileOps;
+typedef struct DirEntry DirEntry;
+typedef struct Page Page;
+typedef struct PageCache PageCache;
+typedef struct Custody Custody;
+typedef struct CustodyChain CustodyChain;
+
+/* Structure for a File System Type */
+struct FileSystemType {
+ const char *name;
+ Inode *(*mount)(FileSystemType *, int, const char *, void *);
+ void (*kill_sb)(SuperBlock *);
+ pid_t owner;
+ FileSystemType *next;
+};
+
+/* Structure for a Super Block */
+struct SuperBlock {
+ FileSystemType *type;
+ SuperOps *ops;
+ Spinlock lock;
+ Inode *root;
+ Inode *inodes; /* List of cached inodes */
+ size_t cachedInodes;
+ File *back;
+ void *data;
+};
+struct SuperOps {
+ Inode *(*alloc_inode)(SuperBlock *);
+ int (*write_inode)(Inode *);
+ void (*delete_inode)(Inode *);
+};
+
+/* Structure for an Inode */
+struct Inode {
+ ino_t ino;
+ refcount_t usage;
+ unsigned short uid, gid;
+ mode_t mode;
+ nlink_t nlink;
+ uint32_t flags;
+ size_t size;
+ dev_t dev;
+ Spinlock lock;
+ InodeOps *ops;
+ FileOps *fileOps;
+ SuperBlock *super;
+ union {
+ DirEntry *dirEntries; /* List of Directory Entries */
+ PageCache *pages; /* List of Pages */
+ };
+ Inode *lnext; /* Next inode in super list */
+};
+struct InodeOps {
+ int (*create)(Inode *, DirEntry *, mode_t);
+ Inode *(*lookup)(Inode *, const char *);
+ int (*mkdir)(Inode *, DirEntry *, mode_t);
+ int (*rmdir)(Inode *, DirEntry *);
+ int (*mknod)(Inode *, DirEntry *, mode_t, dev_t);
+ int (*rename) (Inode *, DirEntry *, Inode *, DirEntry *);
+};
+
+/* Structure for an open File */
+struct File {
+ Inode *inode;
+ unsigned short uid, gid;
+ int flags;
+ mode_t mode;
+ off_t pos;
+ Spinlock lock;
+ FileOps *ops;
+ CustodyChain *path;
+ refcount_t usage;
+};
+struct FileOps {
+ off_t (*lseek)(File *, off_t, int);
+ size_t (*read)(File *, char *, size_t, off_t);
+ size_t (*write)(File *, char *, size_t, off_t);
+ int (*ioctl)(File *, unsigned long, uintptr_t);
+ int (*readdir)(File *, DirEnt *, off_t);
+ int (*open)(File *);
+ int (*flush)(File *);
+ int (*release)(File *);
+ void (*mmap)(File *, void *, size_t, off_t);
+};
+
+/* Structure for a Directory Entry */
+struct DirEntry {
+ DirEntry *next;
+ Inode *inode, *mnt;
+ uint32_t hash;
+ char name[NAME_MAX];
+ Spinlock lock;
+ SuperBlock *super;
+ refcount_t usage;
+ DirEntry *lnext; /* Next directory entry in global list */
+};
+
+/* Structure for a Page in a block cache */
+struct Page {
+ page_t frame;
+ off_t offset;
+ refcount_t usage;
+};
+struct PageCache {
+ Page *page;
+ PageCache *next;
+};
+
+/* Custody Chains */
+struct Custody {
+ Custody *prev, *next;
+ CustodyChain *chain;
+ DirEntry *entry;
+};
+struct CustodyChain {
+ Custody *start, *end;
+ size_t size;
+ Spinlock lock;
+};
+
+/* File System Namespace */
+typedef struct FileSystem {
+ Inode *cwd;
+ CustodyChain cwdCustody;
+ Inode *root;
+ refcount_t usage;
+} FileSystem;
+
+/* Files Namespace */
+typedef struct Files {
+ File *fd[NFILES];
+ refcount_t usage;
+} Files;
+
+void init_vfs(void);
+void register_fstype(FileSystemType *fstype);
+Inode *lookup(const char *name, CustodyChain *chain);
+
+int open(const char *name, int flags, ...); /* mode_t mode */
+int close(int fd);
+size_t read(int fd, void *buf, size_t count);
+size_t write(int fd, void *buf, size_t count);
+int ioctl(int fd, unsigned long request, ...);
+off_t lseek(int fd, off_t offset, int whence);
+int stat(const char *pathname, struct stat *statbuf);
+size_t getdents(int fd, void *buf, size_t count);
+int mkdir(const char *pathname, mode_t mode);
+int rmdir(const char *pathname);
+int mknod(const char *pathname, mode_t mode, dev_t dev);
+int rename(const char *oldpath, const char *newpath);
+int dup(int oldfd);
+int dup2(int oldfd, int newfd);
+int mount(const char *src, const char *target, const char *type,
+ unsigned long flags, void *data);
+int chdir(const char *path);
+int chroot(const char *path);
+char *getcwd(char *buf, size_t size);
+
+void init_custody_chain(CustodyChain *chain);
+CustodyChain *create_custody_chain(void);
+void clean_custody_chain(CustodyChain *chain);
+void destroy_custody_chain(CustodyChain *chain);
+void copy_custody_chain(CustodyChain *chain, CustodyChain *newchain);
+void add_custody(CustodyChain *chain, DirEntry *entry);
+DirEntry *remove_custody(CustodyChain *chain);
+char *custody_path(CustodyChain *chain, char *buf, size_t size);
+
+File *file_get(File *file);
+void file_put(File *file);
+size_t file_read(File *file, char *buf, size_t size);
+size_t file_write(File *file, char *buf, size_t size);
+int file_ioctl(File *file, unsigned long reqeust, uintptr_t argp);
+int file_readdir(File *file, DirEnt *dent, off_t index);
+int file_open(File *file);
+void file_mmap(File *file, void *buf, size_t len, off_t offset);
+
+#endif