Orion
Barry Importing existing Orion kernel d41a53c (2 years, 4 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