aboutsummaryrefslogtreecommitdiff
path: root/kernel/include/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/include/drivers')
-rw-r--r--kernel/include/drivers/blockdev.h99
-rw-r--r--kernel/include/drivers/chardev.h51
-rw-r--r--kernel/include/drivers/cmos.h40
-rw-r--r--kernel/include/drivers/dev.h49
-rw-r--r--kernel/include/drivers/disk/ahci.h325
-rw-r--r--kernel/include/drivers/disk/sata.h14
-rw-r--r--kernel/include/drivers/keyboard.h43
-rw-r--r--kernel/include/drivers/memdevs.h6
-rw-r--r--kernel/include/drivers/pcie.h112
-rw-r--r--kernel/include/drivers/screen.h72
-rw-r--r--kernel/include/drivers/tty/ldisc.h68
-rw-r--r--kernel/include/drivers/tty/tty.h21
-rw-r--r--kernel/include/drivers/tty/vterminal.h249
13 files changed, 1149 insertions, 0 deletions
diff --git a/kernel/include/drivers/blockdev.h b/kernel/include/drivers/blockdev.h
new file mode 100644
index 0000000..d1b3062
--- /dev/null
+++ b/kernel/include/drivers/blockdev.h
@@ -0,0 +1,99 @@
+/*
+ * FILE: dev_byte.h
+ * DESCR: device management: block-oriented devices
+ */
+
+#pragma once
+
+#include "types.h"
+
+#include "drivers/dev.h"
+#include "util/list.h"
+
+#include "mm/mobj.h"
+#include "mm/page.h"
+
+#define BLOCK_SIZE PAGE_SIZE
+
+struct blockdev_ops;
+
+/*
+ * Represents a Weenix block device.
+ */
+typedef struct blockdev
+{
+ /* Fields that should be initialized by drivers: */
+ devid_t bd_id;
+
+ struct blockdev_ops *bd_ops;
+
+#ifdef NO
+ /* Fields that should be ignored by drivers: */
+ mobj_t bd_mobj;
+#endif
+
+ /* Link on the list of block-oriented devices */
+ list_link_t bd_link;
+} blockdev_t;
+
+typedef struct blockdev_ops
+{
+ /**
+ * Reads a block from the block device. This call will block.
+ *
+ * @param bdev the block device
+ * @param buf the memory into which to read the block (must be
+ * page-aligned)
+ * @param loc the number of the block to start reading from
+ * @param count the number of blocks to read
+ * @return 0 on success, -errno on failure
+ */
+ long (*read_block)(blockdev_t *bdev, char *buf, blocknum_t loc,
+ size_t block_count);
+
+ /**
+ * Writes a block to the block device. This call will block.
+ *
+ * @param bdev the block device
+ * @param buf the memory from which to write the block (must be
+ * page-aligned)
+ * @param loc the number of the block to start writing at
+ * @param count the number of blocks to write
+ * @return 0 on success, -errno on failure
+ */
+ long (*write_block)(blockdev_t *bdev, const char *buf, blocknum_t loc,
+ size_t block_count);
+} blockdev_ops_t;
+
+/**
+ * Initializes the block device subsystem.
+ */
+void blockdev_init(void);
+
+/**
+ * Registers a given block device.
+ *
+ * @param dev the block device to register
+ */
+long blockdev_register(blockdev_t *dev);
+
+/**
+ * Finds a block device with a given device id.
+ *
+ * @param id the device id of the block device to find
+ * @return the block device with the given id if it exists, or NULL if
+ * it cannot be found
+ */
+blockdev_t *blockdev_lookup(devid_t id);
+
+/**
+ * Cleans and frees all resident pages belonging to a given block
+ * device.
+ *
+ * @param dev the block device to flush
+ */
+void blockdev_flush_all(blockdev_t *dev);
+
+// restructure, perhaps, so that these don't have to be exported
+long blockdev_fill_pframe(mobj_t *mobj, pframe_t *pf);
+long blockdev_flush_pframe(mobj_t *mobj, pframe_t *pf); \ No newline at end of file
diff --git a/kernel/include/drivers/chardev.h b/kernel/include/drivers/chardev.h
new file mode 100644
index 0000000..f6083d8
--- /dev/null
+++ b/kernel/include/drivers/chardev.h
@@ -0,0 +1,51 @@
+#pragma once
+
+#include "drivers/dev.h"
+#include "util/list.h"
+
+struct vnode;
+struct pframe;
+
+struct chardev_ops;
+struct mobj;
+
+typedef struct chardev
+{
+ devid_t cd_id;
+ struct chardev_ops *cd_ops;
+ list_link_t cd_link;
+} chardev_t;
+
+typedef struct chardev_ops
+{
+ ssize_t (*read)(chardev_t *dev, size_t pos, void *buf, size_t count);
+
+ ssize_t (*write)(chardev_t *dev, size_t pos, const void *buf, size_t count);
+
+ long (*mmap)(struct vnode *file, struct mobj **ret);
+
+ long (*fill_pframe)(struct vnode *file, struct pframe *pf);
+
+ long (*flush_pframe)(struct vnode *file, struct pframe *pf);
+} chardev_ops_t;
+
+/**
+ * Initializes the byte device subsystem.
+ */
+void chardev_init(void);
+
+/**
+ * Registers the given byte device.
+ *
+ * @param dev the byte device to register
+ */
+long chardev_register(chardev_t *dev);
+
+/**
+ * Finds a byte device with a given device id.
+ *
+ * @param id the device id of the byte device to find
+ * @return the byte device with the given id if it exists, or NULL if
+ * it cannot be found
+ */
+chardev_t *chardev_lookup(devid_t id);
diff --git a/kernel/include/drivers/cmos.h b/kernel/include/drivers/cmos.h
new file mode 100644
index 0000000..bbbc282
--- /dev/null
+++ b/kernel/include/drivers/cmos.h
@@ -0,0 +1,40 @@
+#ifndef CMOS_H
+#define CMOS_H
+
+#include "main/io.h"
+
+// See: https://wiki.osdev.org/CMOS
+#define CMOS_ADDR 0x70
+#define CMOS_DATA 0x71
+
+#define CMOS_REG_SECOND 0x00
+#define CMOS_REG_MINUTE 0x02
+#define CMOS_REG_HOUR 0x04
+#define CMOS_REG_DAY 0x07
+#define CMOS_REG_MONTH 0x08
+#define CMOS_REG_YEAR 0x09
+
+// We're on a modern computer. It'll have a century register.
+#define CMOS_REG_CENTURY 0x32
+#define CMOS_REG_STAT_A 0x0A
+#define CMOS_REG_STAT_B 0x0B
+
+typedef struct rtc_time_t
+{
+ unsigned char second;
+ unsigned char minute;
+ unsigned char hour;
+ unsigned char day;
+ unsigned char month;
+ unsigned int year;
+
+ // Internal use ONLY
+ unsigned int __century;
+} rtc_time_t;
+
+unsigned char cmos_read_register(int reg);
+
+/* Get the time from the CMOS RTC */
+rtc_time_t rtc_get_time();
+
+#endif \ No newline at end of file
diff --git a/kernel/include/drivers/dev.h b/kernel/include/drivers/dev.h
new file mode 100644
index 0000000..883dcba
--- /dev/null
+++ b/kernel/include/drivers/dev.h
@@ -0,0 +1,49 @@
+#pragma once
+
+#include "types.h"
+
+/*
+ * A Weenix "device identifier" is the concatenation of:
+ * - a "driver number" or "device type" (major number)
+ * - a "device number" (minor number)
+ *
+ * The device identifiers for block devices and character devices are
+ * independent. That is, you could have both a block device and a char device
+ * with major 3, minor 5 (for example). They would be distinct.
+ *
+ * Weenix's device number allocation/assignment scheme is as follows:
+ *
+ * - major 0 (byte or block), minor 0: reserved as an analogue of NULL
+ * for device id's
+ *
+ * - char major 1: Memory devices (mem)
+ * - minor 0: /dev/null The null device
+ * - minor 1: /dev/zero The zero device
+ *
+ * - char major 2: TTY devices (tty)
+ * - minor 0: /dev/tty0 First TTY device
+ * - minor 1: /dev/tty1 Second TTY device
+ * - and so on...
+ *
+ * - block major 1: Disk devices
+ * - minor 0: first disk device
+ * - minor 1: second disk device
+ * - and so on...
+ */
+
+#define MINOR_BITS 8
+#define MINOR_MASK ((1U << MINOR_BITS) - 1)
+#define MAJOR(devid) ((unsigned)((devid) >> MINOR_BITS))
+#define MINOR(devid) ((unsigned)((devid)&MINOR_MASK))
+#define MKDEVID(major, minor) ((devid_t)(((major) << MINOR_BITS) | (minor)))
+
+/* convenience definition: the NULL device id: */
+#define NULL_DEVID (MKDEVID(0, 0))
+#define MEM_NULL_DEVID (MKDEVID(1, 0))
+#define MEM_ZERO_DEVID (MKDEVID(1, 1))
+
+#define DISK_MAJOR 1
+
+#define MEM_MAJOR 1
+#define MEM_NULL_MINOR 0
+#define MEM_ZERO_MINOR 1
diff --git a/kernel/include/drivers/disk/ahci.h b/kernel/include/drivers/disk/ahci.h
new file mode 100644
index 0000000..1c7acf6
--- /dev/null
+++ b/kernel/include/drivers/disk/ahci.h
@@ -0,0 +1,325 @@
+#pragma once
+
+#include <types.h>
+
+/* Documents referenced:
+ * ATA Command Set 4:
+ * http://www.t13.org/Documents/UploadedDocuments/docs2016/di529r14-ATAATAPI_Command_Set_-_4.pdf
+ * AHCI SATA 1.3.1:
+ * https://www.intel.com/content/www/us/en/io/serial-ata/serial-ata-ahci-spec-rev1-3-1.html
+ * Serial ATA Revision 2.6:
+ * http://read.pudn.com/downloads157/doc/project/697017/SerialATA_Revision_2_6_Gold.pdf
+ */
+
+/* Macros for working with physical region descriptors. */
+#define AHCI_PRDT_DBC_WIDTH 22
+#define AHCI_MAX_PRDT_SIZE (1 << AHCI_PRDT_DBC_WIDTH)
+#define ATA_SECTOR_SIZE 512
+#define AHCI_SECTORS_PER_PRDT (AHCI_MAX_PRDT_SIZE / ATA_SECTOR_SIZE)
+#define AHCI_MAX_SECTORS_PER_COMMAND \
+ (1 << 16) /* FLAG: Where does this come from? */
+#define ACHI_NUM_PRDTS_PER_COMMAND_TABLE \
+ (AHCI_MAX_SECTORS_PER_COMMAND / AHCI_SECTORS_PER_PRDT)
+
+#define AHCI_MAX_NUM_PORTS 32
+#define AHCI_COMMAND_HEADERS_PER_LIST 32
+
+#define AHCI_COMMAND_LIST_ARRAY_BASE(ahci_base) (ahci_base)
+#define AHCI_COMMAND_LIST_ARRAY_SIZE \
+ (AHCI_MAX_NUM_PORTS * sizeof(command_list_t))
+
+#define AHCI_RECEIVED_FIS_ARRAY_BASE(ahci_base) \
+ ((ahci_base) + AHCI_COMMAND_LIST_ARRAY_SIZE)
+#define AHCI_RECEIVED_FIS_ARRAY_SIZE \
+ (AHCI_MAX_NUM_PORTS * sizeof(received_fis_t))
+
+#define AHCI_COMMAND_TABLE_ARRAY_BASE(ahci_base) \
+ (AHCI_RECEIVED_FIS_ARRAY_BASE(ahci_base) + AHCI_RECEIVED_FIS_ARRAY_SIZE)
+#define AHCI_COMMAND_TABLE_ARRAY_SIZE \
+ (AHCI_MAX_NUM_PORTS * AHCI_COMMAND_HEADERS_PER_LIST * \
+ sizeof(command_table_t))
+
+#define AHCI_SIZE \
+ (AHCI_COMMAND_LIST_ARRAY_SIZE + AHCI_RECEIVED_FIS_ARRAY_SIZE + \
+ AHCI_COMMAND_TABLE_ARRAY_SIZE)
+#define AHCI_SIZE_PAGES ((uintptr_t)PAGE_ALIGN_UP(AHCI_SIZE) / PAGE_SIZE)
+
+#define ALIGN_DOWN_POW_2(x, align) ((x) & -(align))
+#define ALIGN_UP_POW_2(x, align) (ALIGN_DOWN_POW_2((x)-1, align) + (align))
+
+/*=============================
+ * Frame Information Structures
+ *============================*/
+
+/* fis_type_t - FIS types are recognized by an ID.
+ * For more info, see section 10.3 (FIS Types) of Serial ATA Revision 2.6. */
+typedef enum fis_type
+{
+ fis_type_h2d_register = 0x27
+} packed fis_type_t;
+
+/* Command codes used when forming the host-to-device FIS (see: ATA Command Set
+ * 4). The first two are standard commands. The second two are for NCQ commands.
+ */
+#define ATA_READ_DMA_EXT_COMMAND 0x25
+#define ATA_WRITE_DMA_EXT_COMMAND 0x35
+#define ATA_READ_FPDMA_QUEUED_COMMAND 0x60
+#define ATA_WRITE_FPDMA_QUEUED_COMMAND 0x61
+
+/* 8-bit device setting for host-to-device FIS.
+ * Bit 6 is specified as either obsolete or "shall be set to one" for all
+ * commands used in Weenix. So, we can safely just default to this value for all
+ * commands. More info in sections 7.20, 7.21, 7.55, and 7.57 of ATA Command
+ * Set 4. */
+#define ATA_DEVICE_LBA_MODE 0x40
+
+/* h2d_register_fis - Register Host to Device FIS.
+ * This is the only FIS used in Weenix.
+ */
+typedef struct h2d_register_fis
+{
+ uint8_t fis_type; /* Must be set to fis_type_h2d_register. */
+ uint8_t : 7;
+ uint8_t c : 1; /* When set, indicates that this is an FIS for a command.
+ * This is always the case in Weenix. */
+ uint8_t command; /* See command codes further up. */
+ uint8_t
+ features; /* For regular read/write, no use.
+ * For NCQ commands, features and features_exp form the lower
+ * and upper 8 bits of sector count, respectively. */
+ uint32_t lba : 24; /* lba and lba_exp form the lower and upper 24 bits of
+ the first logical block address, respectively. */
+ uint8_t device; /* Device register.
+ * For Weenix's purposes, this should always be set to
+ * ATA_DEVICE_LBA_MODE. */
+ uint32_t lba_exp : 24;
+ uint8_t features_exp;
+ uint16_t sector_count; /* For regular read/write, specifies number of
+ * sectors to read/write.
+ * For NCQ commands, bits 7:3 specify NCQ tag. */
+ uint16_t : 16;
+ uint32_t : 32;
+} packed h2d_register_fis_t;
+
+/*========================
+ * Command List Structures
+ *=======================*/
+
+/* command_fis_t - Represents a software-constructed FIS stored in a
+ * command_table_t. */
+typedef union command_fis {
+ h2d_register_fis_t h2d_register_fis;
+ /* Must occupy 64 bytes in its corresponding command_table_t.
+ * Recall that unions conform to the size of the largest member. */
+ struct
+ {
+ uint8_t size[64];
+ };
+} packed command_fis_t;
+
+/* received_fis_t - Per-port structure that contains information on received
+ * FISes. More info in section 4.2.1 of the 1.3.1 spec. */
+typedef struct received_fis
+{
+ uint8_t _omit[256]; /* Weenix does not make use of any received FIS from the
+ device. */
+} packed received_fis_t;
+
+/* prd_t - Physical Region Descriptor.
+ * Represents an entry in the PRD table in a command table
+ * (command_table_t->prdt). Points to a chunk of system memory for the device to
+ * use according to whatever command it is executing.
+ */
+typedef struct prd
+{
+ uint64_t dba; /* Data Base Address. */
+ uint32_t : 32;
+ uint32_t
+ dbc : 22; /* Data Byte Count: Indicates length of data block in bytes,
+ * but starts counting from 0. Ex: Length 1 is 0x0. Length 2
+ * is 0x1. Length 3 is 0x10. And so on... Must be even. Due to
+ * counting from 0, this means least-significant bit MUST
+ * be 1. Max length is 4MB (all bits set). */
+ uint16_t : 9;
+ uint8_t i : 1; /* Interrupt on Completion: When set, then upon processing
+ * all PRDs in the current FIS, the port will try to generate
+ * an interrupt by setting PxIS.DPS.
+ *
+ * Whether or not this actually behaves as expected, or ever
+ * is even used, is unclear.
+ */
+} packed prd_t;
+
+/* command_table_t - Structure detailing a command and associated data / memory.
+ * More info in section 4.2.3 of SATA AHCI 1.3.1.
+ */
+typedef struct command_table
+{
+ command_fis_t
+ cfis; /* Command FIS: The actual software constructed command. */
+ uint8_t _omit[64];
+ prd_t prdt[ACHI_NUM_PRDTS_PER_COMMAND_TABLE]; /* Physical Region Descriptor
+ * Table: A list of,
+ * theoretically, up to 2^16
+ * entries of PRDs.
+ * Number of actual usable
+ * entries is indicated by
+ * command_header_t->prdtl. */
+} packed command_table_t;
+
+/* command_header_t - Structure detailing command details. Stored in a
+ * command_list_t. More info in section 4.2.2 of the SATA AHCI 1.3.1 spec. */
+typedef struct command_header
+{
+ uint8_t cfl : 5; /* Command FIS length in DW (4 bytes). Max value is 0x10
+ (16). */
+ uint8_t : 1;
+ uint8_t write : 1; /* Write: Set indicates write, clear indicates read. */
+ uint16_t : 9;
+ uint16_t prdtl; /* Physical Region Descriptor Table Length: Number of PRD
+ entries. */
+ uint32_t : 32;
+ uint64_t ctba; /* Command Table Descriptor Base Address: Pointer to the
+ command table. */
+ uint64_t : 64;
+ uint64_t : 64;
+} packed command_header_t;
+
+/* command_list_t - Per-port command list.
+ * More info in section 4.2.2 of the SATA AHCI 1.3.1 spec.
+ * See also: Figure 5: Port System Memory Structures. */
+typedef struct command_list
+{
+ command_header_t command_headers[AHCI_COMMAND_HEADERS_PER_LIST];
+} packed command_list_t;
+
+/*=================
+ * Host Bus Adapter
+ *================*/
+
+/* px_interrupt_status - Per-port bitmap indicating that a corresponding
+ * interrupt has occurred on the port. Observe that this is a union, making
+ * initialization a little easier. */
+typedef union px_interrupt_status {
+ struct
+ {
+ uint8_t dhrs : 1; /* Interrupt requested by a device-to-host FIS.
+ * Used by normal read/write commands, see 5.6.2
+ * in 1.3.1. */
+ uint8_t : 2;
+ uint8_t
+ sdbs : 1; /* Interrupt requested by a set device bits FIS.
+ * Used by NCQ read/write commands, see 5.6.4 in 1.3.1. */
+ uint8_t : 1;
+ uint8_t dps : 1; /* Interrupt set upon completing an FIS that requested
+ * an interrupt upon completion.
+ * Currently doesn't seem to be working... */
+ uint32_t : 26;
+ } bits;
+ uint32_t value;
+} packed px_interrupt_status_t;
+
+/* Observe that, to clear interrupt status, must set to 1. */
+static px_interrupt_status_t px_interrupt_status_clear = {.value =
+ (uint32_t)-1};
+
+/* Port x Interrupt Enable - Bitwise register controlling generation of various
+ * interrupts. */
+typedef union px_interrupt_enable {
+ uint32_t value;
+} packed px_interrupt_enable_t;
+
+/* Weenix uses this to initialize all ports to enable all interrupts by default.
+ */
+static px_interrupt_enable_t px_interrupt_enable_all_enabled = {
+ .value = (uint32_t)-1};
+
+/* hba_ghc_t - Generic Host Control: Information and control registers
+ * pertaining to the entire HBA. More info in section 3.1 of 1.3.1.
+ */
+typedef struct hba_ghc
+{
+ struct
+ {
+ uint32_t : 30;
+ uint8_t sncq : 1; /* Supports Native Command Queueing. */
+ uint8_t : 1;
+ } packed cap;
+ struct
+ {
+ uint8_t : 1;
+ uint8_t ie : 1; /* Interrupt Enable: Enables/disables interrupts from
+ HBA. */
+ uint32_t : 29;
+ uint8_t ae : 1; /* AHCI Enable: Indicates software adheres to AHCI
+ specification. */
+ } packed ghc;
+ uint32_t is; /* Interrupt Status: If bit x is set, then port x has a pending
+ interrupt. */
+ uint32_t pi; /* Ports Implemented: If bit x is set, then port x is available
+ for use. */
+ uint32_t _omit[7];
+} packed hba_ghc_t;
+
+/* Signature for SATA devices. Compare this against hba_port_t->px_sig to
+ * determine if a SATA device is sitting behind a given port. */
+#define SATA_SIG_ATA 0x00000101
+
+/* hba_port - A per-port structure storing port information.
+ * Each port represents a device that the HBA is communicating with (e.g. a
+ * SATA device!). Details not relevant to Weenix have been omitted. More info in
+ * section 3.3 of the SATA AHCI 1.3.1 spec.
+ */
+typedef struct hba_port
+{
+ uint64_t px_clb; /* 1K-byte aligned base physical address of this port's
+ * command list. This is a pointer to a command_list_t. */
+ uint64_t px_fb; /* Base physical address for received FISes.
+ * Weenix never uses received FIS, but we allocate and set
+ * up memory to make the HBA happy. */
+ px_interrupt_status_t px_is; /* Interrupt Status. */
+ px_interrupt_enable_t px_ie; /* Interrupt Enable. */
+ struct
+ {
+ uint8_t st : 1; /* Start: Allows the HBA to process the command list. */
+ uint8_t : 3;
+ uint8_t fre : 1; /* FIS Receive Enable: Allows HBA to post received
+ FISes in px_fb. */
+ uint16_t : 9;
+ uint8_t fr : 1; /* FIS Receive Running: Read-only indicating if FIS
+ Receive DMA is running. */
+ uint8_t cr : 1; /* Command List Running: Read-only indicating if command
+ list DMA is running. */
+ uint16_t : 16;
+ } packed px_cmd; /* Port Command and Status. */
+ uint64_t : 64;
+ uint32_t px_sig; /* Signature: Contains attached device's signature.
+ * SATA devices should have signature SATA_SIG_ATA, defined
+ * above. */
+ uint64_t : 64;
+ uint32_t px_serr; /* SATA Error: Unclear how Weenix is actually making use
+ of this register. */
+ uint32_t px_sact; /* SATA Active: Used for NCQ.
+ * Each bit corresponds to TAG and command slot of an NCQ
+ * command. Must be set by software before issuing a NCQ
+ * for a command slot.
+ */
+ uint32_t px_ci; /* Commands Issued: Software sets bit x if a command x is
+ * ready to be sent. Each bit corresponds to a command slot.
+ * HBA clears bit upon completing a command.
+ */
+ uint32_t _omit[17];
+} packed hba_port_t;
+
+/* Host Bus Adapter - Control block for the device that actually interfaces
+ * between the OS and the SATA disk device. For more info, see section 3 of
+ * the 1.3.1 spec.
+ */
+typedef struct hba
+{
+ hba_ghc_t ghc; /* Generic Host Control. */
+ uint32_t _omit[53];
+ hba_port_t ports[32]; /* Static array of port descriptors. */
+} packed hba_t;
+
+#define PORT_INDEX(hba, port) ((port) - (hba)->ports)
diff --git a/kernel/include/drivers/disk/sata.h b/kernel/include/drivers/disk/sata.h
new file mode 100644
index 0000000..6bdb573
--- /dev/null
+++ b/kernel/include/drivers/disk/sata.h
@@ -0,0 +1,14 @@
+#pragma once
+
+#define SATA_BLOCK_SIZE 4096
+
+#include <drivers/blockdev.h>
+#include <drivers/disk/ahci.h>
+
+void sata_init();
+
+typedef struct ata_disk
+{
+ hba_port_t *port;
+ blockdev_t bdev;
+} ata_disk_t;
diff --git a/kernel/include/drivers/keyboard.h b/kernel/include/drivers/keyboard.h
new file mode 100644
index 0000000..8ac3762
--- /dev/null
+++ b/kernel/include/drivers/keyboard.h
@@ -0,0 +1,43 @@
+#pragma once
+
+#include <types.h>
+
+#define BS 0x08
+#define DEL 0x7F
+#define ESC 0x1B
+#define LF 0x0A
+#define CR 0x0D
+#define SPACE 0x20
+
+// CTRL-D
+#define EOT 0x04
+
+// CTRL-C
+#define ETX 0x03
+
+/* Special stuff for scrolling (note that these only work when ctrl is held) */
+#define SCROLL_UP 0x0e
+#define SCROLL_DOWN 0x1c
+#define SCROLL_UP_PAGE 0x0f
+#define SCROLL_DOWN_PAGE 0x1d
+
+// pretty arbitrarily chosen, just the first extended ASCII code point and on...
+#define F1 ((uint8_t)128)
+#define F2 ((uint8_t)(F1 + 1))
+#define F3 ((uint8_t)(F1 + 2))
+#define F4 ((uint8_t)(F1 + 3))
+#define F5 ((uint8_t)(F1 + 4))
+#define F6 ((uint8_t)(F1 + 5))
+#define F7 ((uint8_t)(F1 + 6))
+#define F8 ((uint8_t)(F1 + 7))
+#define F9 ((uint8_t)(F1 + 8))
+#define F10 ((uint8_t)(F1 + 9))
+#define F11 ((uint8_t)(F1 + 10))
+#define F12 ((uint8_t)(F1 + 11))
+
+typedef void (*keyboard_char_handler_t)(uint8_t);
+
+/**
+ * Initializes the keyboard subsystem.
+ */
+void keyboard_init(keyboard_char_handler_t handler);
diff --git a/kernel/include/drivers/memdevs.h b/kernel/include/drivers/memdevs.h
new file mode 100644
index 0000000..420c5d0
--- /dev/null
+++ b/kernel/include/drivers/memdevs.h
@@ -0,0 +1,6 @@
+#pragma once
+
+/**
+ * Initializes the memdevs subsystem.
+ */
+void memdevs_init(void);
diff --git a/kernel/include/drivers/pcie.h b/kernel/include/drivers/pcie.h
new file mode 100644
index 0000000..83d182f
--- /dev/null
+++ b/kernel/include/drivers/pcie.h
@@ -0,0 +1,112 @@
+#pragma once
+
+#include <util/list.h>
+
+#define PCI_NUM_BUSES 256
+#define PCI_NUM_DEVICES_PER_BUS 32
+#define PCI_NUM_FUNCTIONS_PER_DEVICE 8
+#define PCI_DEVICE_FUNCTION_SIZE 4096
+#define PCI_CAPABILITY_PTR_MASK (0b11111100)
+#define PCI_MSI_CAPABILITY_ID 0x5
+
+// Intel Vol 3A 10.11.1
+//#define MSI_BASE_ADDRESS 0x0FEE0000
+#define MSI_ADDRESS_FOR(destination) \
+ ((uint32_t)((0x0FEE << 20) | ((destination) << 12) | (0b1100)))
+#define MSI_DATA_FOR(vector) ((uint16_t)(0b00000001 << 8) | (vector))
+
+typedef struct pci_capability
+{
+ uint8_t id;
+ uint8_t next_cap;
+ uint16_t control;
+} packed pci_capability_t;
+
+typedef struct msi_capability
+{
+ uint8_t id;
+ uint8_t next_cap;
+ struct
+ {
+ uint8_t msie : 1; // MSI Enable
+ uint8_t mmc : 3; // Multiple Message Capable
+ uint8_t mme : 3; // Multiple Message Enable
+ uint8_t c64 : 1; // 64 Bit Address Capable
+ uint8_t _reserved;
+ } control;
+ union {
+ struct
+ {
+ uint32_t addr;
+ uint16_t data;
+ } ad32;
+ struct
+ {
+ uint64_t addr;
+ uint16_t data;
+ } ad64;
+ } address_data;
+} packed msi_capability_t;
+
+typedef union pcie_device {
+ struct
+ {
+ char data[PCI_DEVICE_FUNCTION_SIZE];
+ } raw;
+ struct
+ {
+ uint16_t vendor_id;
+ uint16_t device_id;
+ uint16_t command;
+ uint16_t status;
+ uint8_t revision_id;
+ uint8_t prog_if;
+ uint8_t subclass;
+ uint8_t class;
+ uint8_t cache_line_size;
+ uint8_t latency_type;
+ uint8_t header_type;
+ uint8_t bist;
+ uint32_t bar[6];
+ uint32_t cardbus_cis_pointer;
+ uint16_t subsystem_vendor_id;
+ uint16_t subsystem_id;
+ uint32_t expansion_rom_base_addr;
+ uint8_t capabilities_ptr;
+ uint8_t _reserved1[7];
+ uint8_t interrupt_line;
+ uint8_t interrupt_pin;
+ uint8_t min_grant;
+ uint8_t max_latency;
+ pci_capability_t pm_capability;
+ uint16_t pmcsr;
+ uint8_t bse;
+ uint8_t data;
+ pci_capability_t msi_capability;
+ uint64_t message_address;
+ uint16_t message_data;
+ uint8_t _reserved2[2];
+ pci_capability_t pe_capability;
+ uint32_t pcie_device_capabilities;
+ uint16_t device_control;
+ uint16_t device_status;
+ uint32_t pcie_link_capabilities;
+ uint16_t link_control;
+ uint16_t link_status;
+ } standard;
+} packed pcie_device_t;
+
+#define PCI_LOOKUP_WILDCARD 0xff
+
+typedef struct pcie_device_wrapper
+{
+ uint8_t class;
+ uint8_t subclass;
+ uint8_t interface;
+ pcie_device_t *dev;
+ list_link_t link;
+} pcie_device_wrapper_t;
+
+void pci_init(void);
+
+pcie_device_t *pcie_lookup(uint8_t class, uint8_t subclass, uint8_t interface);
diff --git a/kernel/include/drivers/screen.h b/kernel/include/drivers/screen.h
new file mode 100644
index 0000000..97f7e2a
--- /dev/null
+++ b/kernel/include/drivers/screen.h
@@ -0,0 +1,72 @@
+#pragma once
+
+#include "types.h"
+
+#ifdef __VGABUF___
+
+#define SCREEN_CHARACTER_WIDTH 9
+#define SCREEN_CHARACTER_HEIGHT 15
+
+typedef union color {
+ struct
+ {
+ uint8_t blue;
+ uint8_t green;
+ uint8_t red;
+ uint8_t alpha;
+ } channels;
+ uint32_t value;
+} packed color_t;
+
+void screen_init();
+
+size_t screen_get_width();
+
+size_t screen_get_height();
+
+size_t screen_get_character_width();
+
+size_t screen_get_character_height();
+
+void screen_draw_string(size_t x, size_t y, const char *s, size_t len,
+ color_t color);
+
+void screen_fill(color_t color);
+
+void screen_fill_rect(size_t x, size_t y, size_t width, size_t height,
+ color_t color);
+
+void screen_draw_rect(size_t x, size_t y, size_t width, size_t height,
+ color_t color);
+
+void screen_copy_rect(size_t fromx, size_t fromy, size_t width, size_t height,
+ size_t tox, size_t toy);
+
+void screen_flush();
+
+void screen_print_shutdown();
+
+#else
+
+#define VGA_WIDTH ((uint16_t)80)
+#define VGA_HEIGHT ((uint16_t)25)
+#define VGA_LINE_SIZE ((size_t)(VGA_WIDTH * sizeof(uint16_t)))
+#define VGA_AREA ((uint16_t)(VGA_WIDTH * VGA_HEIGHT))
+#define VGA_BUFFER_SIZE ((uint16_t)(VGA_WIDTH * VGA_HEIGHT))
+#define VGA_DEFAULT_ATTRIB 0xF
+
+void vga_init();
+
+void vga_write_char_at(size_t row, size_t col, uint16_t v);
+
+void vga_set_cursor(size_t row, size_t col);
+
+void vga_clear_screen();
+
+void screen_print_shutdown();
+
+void vga_enable_cursor();
+
+void vga_disable_cursor();
+
+#endif \ No newline at end of file
diff --git a/kernel/include/drivers/tty/ldisc.h b/kernel/include/drivers/tty/ldisc.h
new file mode 100644
index 0000000..920c816
--- /dev/null
+++ b/kernel/include/drivers/tty/ldisc.h
@@ -0,0 +1,68 @@
+#pragma once
+
+#include "types.h"
+#include <proc/kmutex.h>
+
+#define LDISC_BUFFER_SIZE 128
+
+/**
+ * The line discipline is implemented as a circular buffer containing two
+ * sections: cooked and raw. These sections are tracked by three indices:
+ * ldisc_cooked, ldisc_tail, and ldisc_head.
+ *
+ * New characters (via ldisc_key_pressed) are put at the head position (and the
+ * head is incremented). If a newline is received, cooked is moved up to the head.
+ * Characters are read from tail up until cooked, and the tail is updated
+ * to equal cooked.
+ *
+ * The cooked portion (ready for reading) runs from ldisc_tail (inclusive) to
+ * ldisc_cooked (exclusive). The raw portion (subject to editing) runs from
+ * ldisc_cooked (inclusive) to ldisc_head (exclusive).
+ *
+ * e.g.
+ * [..........t........c...h.......]
+ * (cooked) ^^^^^^^^^
+ * ^^^^ (raw)
+ *
+ * Bear in mind that the buffer is circular, so another possible configuration
+ * might be
+ *
+ * [....h............t......c......]
+ * (cooked) ^^^^^^^
+ * ^^^^ ^^^^^^^ (raw)
+ *
+ * When incrementing the indices, make sure that you take the circularity of
+ * the buffer into account! (Hint: using LDISC_BUFFER_SIZE macro will be helpful.)
+ *
+ * The field ldisc_full is used to indicate when the circular buffer has been
+ * completely filled. This is necessary because there are two possible states
+ * in which cooked == tail == head:
+ *
+ * 1) The buffer is empty, or
+ *
+ * 2) The buffer is full: head has wrapped around and is equal to tail.
+ *
+ * ldisc_full is used to differentiate between these two states.
+ */
+typedef struct ldisc
+{
+ size_t ldisc_cooked; // Cooked is the index after the most last or most recent '\n' in the buffer.
+ size_t ldisc_tail; // Tail is the index from which characters are read by processes
+ size_t ldisc_head; // Head is the index from which new characters are placed
+ char ldisc_full; // Full identifies if the buffer is full
+ // 1 -> full
+ // 0 -> not full
+
+ ktqueue_t ldisc_read_queue; // Queue for threads waiting for data to be read
+ char ldisc_buffer[LDISC_BUFFER_SIZE];
+} ldisc_t;
+
+void ldisc_init(ldisc_t *ldisc);
+
+long ldisc_wait_read(ldisc_t *ldisc);
+
+size_t ldisc_read(ldisc_t *ldisc, char *buf, size_t count);
+
+void ldisc_key_pressed(ldisc_t *ldisc, char c);
+
+size_t ldisc_get_current_line_raw(ldisc_t *ldisc, char *s); \ No newline at end of file
diff --git a/kernel/include/drivers/tty/tty.h b/kernel/include/drivers/tty/tty.h
new file mode 100644
index 0000000..ec22b68
--- /dev/null
+++ b/kernel/include/drivers/tty/tty.h
@@ -0,0 +1,21 @@
+#pragma once
+
+#include "drivers/chardev.h"
+#include "ldisc.h"
+#include "vterminal.h"
+
+#define TTY_MAJOR 2
+#define cd_to_tty(bd) \
+ CONTAINER_OF((bd), tty_t, tty_cdev) // Should this be cd, for chardev?
+
+typedef struct tty
+{
+ vterminal_t tty_vterminal; // the virtual terminal, where the characters will be displayed
+ ldisc_t tty_ldisc; // the line discipline for the tty
+ chardev_t tty_cdev; // the super struct for the tty
+ kmutex_t tty_read_mutex;
+ kmutex_t tty_write_mutex;
+} tty_t;
+
+void tty_init(void);
+
diff --git a/kernel/include/drivers/tty/vterminal.h b/kernel/include/drivers/tty/vterminal.h
new file mode 100644
index 0000000..99123a7
--- /dev/null
+++ b/kernel/include/drivers/tty/vterminal.h
@@ -0,0 +1,249 @@
+#pragma once
+
+#include <drivers/screen.h>
+#include <mm/page.h>
+#include <types.h>
+#include <util/list.h>
+//
+//
+//#define VGA_WIDTH ((uint16_t) 80)
+//#define VGA_HEIGHT ((uint16_t) 25)
+//#define VGA_AREA ((uint16_t) (VGA_WIDTH * VGA_HEIGHT))
+//#define VGA_BUFFER_COUNT ((uint16_t) (1024 * 16))
+//#define VGA_BUFFER_SIZE ((uint16_t) (VGA_BUFFER_COUNT * sizeof(short)))
+//
+//
+//#define SCREEN_GET_FOREGROUND(x) ((uint8_t) (x & 0b00001111))
+//#define SCREEN_GET_BACKGROUND(x) ((uint8_t) (x & 0b01110000))
+//#define SCREEN_MAKE_COLOR(b, f) ((uint8_t) (b << 4) | f)
+//
+//#define SCREEN_DEFAULT_FOREGROUND ((uint8_t) 0xF)
+//#define SCREEN_DEFAULT_BACKGROUND ((uint8_t) 0x0)
+//#define SCREEN_DEFAULT_COLOR SCREEN_MAKE_COLOR(SCREEN_DEFAULT_BACKGROUND,
+//SCREEN_DEFAULT_FOREGROUND)
+
+// typedef struct screen {
+// uint16_t screen_cursor_pos;
+// uint16_t screen_buffer_pos;
+// uint16_t screen_visible_pos;
+// uint8_t screen_current_color;
+//
+// uint16_t *screen_buffer;
+// uint16_t screen_inactive_buffer[VGA_BUFFER_COUNT];
+//} screen_t;
+
+// typedef struct vterminal_char {
+// char c;
+//// color_t foreground;
+//// color_t background;
+//} vterminal_char_t;
+
+#ifdef __VGABUF___
+
+#define VT_PAGES_PER_HISTORY_CHUNK 1
+#define VT_CHARS_PER_HISTORY_CHUNK \
+ (VT_PAGES_PER_HISTORY_CHUNK * PAGE_SIZE - sizeof(list_link_t))
+
+typedef struct vterminal_history_chunk
+{
+ char chars[VT_CHARS_PER_HISTORY_CHUNK];
+ list_link_t link;
+} vterminal_history_chunk_t;
+
+typedef struct vterminal
+{
+ size_t vt_width;
+ size_t vt_height;
+
+ size_t vt_len;
+ list_t vt_history_chunks;
+
+ size_t *vt_line_positions;
+
+ off_t vt_line_offset;
+
+ size_t *vt_line_widths;
+
+ size_t vt_input_pos;
+ size_t vt_cursor_pos;
+} vterminal_t;
+
+void vterminal_init(vterminal_t *vt);
+
+void vterminal_make_active(vterminal_t *vt);
+
+void vterminal_scroll(vterminal_t *vt, long count);
+
+void vterminal_scroll_to_bottom(vterminal_t *t);
+
+void vterminal_clear(vterminal_t *vt);
+
+size_t vterminal_write(vterminal_t *vt, const char *buf, size_t len);
+
+void vterminal_key_pressed(vterminal_t *vt);
+
+#elif 0
+
+struct vt_cursor
+{
+ int y;
+ int x;
+};
+
+struct vt_attributes
+{
+ int underline : 1;
+ int bold : 1;
+ int blink : 1;
+ uint16_t fg;
+ uint16_t bg;
+};
+
+struct vt_char
+{
+ int codepoint;
+ struct vt_attributes attribs;
+};
+
+struct vt_buffer
+{
+ struct vt_char screen[VGA_HEIGHT][VGA_WIDTH];
+ size_t input_position;
+};
+
+typedef struct vterminal
+{
+ size_t height;
+ size_t width;
+ struct vt_cursor cursor;
+ struct vt_cursor saved_cursor;
+ struct vt_attributes current_attribs;
+ struct vt_buffer *active_buffer;
+ struct vt_buffer pri_buffer;
+ struct vt_buffer alt_buffer;
+} vterminal_t;
+
+void vterminal_init(vterminal_t *vt);
+
+void vterminal_make_active(vterminal_t *vt);
+
+void vterminal_scroll(vterminal_t *vt, long count);
+
+void vterminal_clear(vterminal_t *vt);
+
+size_t vterminal_write(vterminal_t *vt, const char *buf, size_t len);
+
+size_t vterminal_echo_input(vterminal_t *vt, const char *buf, size_t len);
+
+void vterminal_key_pressed(vterminal_t *vt);
+
+void vterminal_scroll_to_bottom(vterminal_t *vt);
+
+#endif
+
+#define VTC_DEFAULT_FOREGROUND VTCOLOR_GREY
+#define VTC_DEFAULT_BACKGROUND VTCOLOR_BLACK
+#define VTC_DEFAULT_ATTR \
+ (vtattr_t) { 0, VTC_DEFAULT_FOREGROUND, VTC_DEFAULT_BACKGROUND }
+#define VTC_ANSI_PARSER_STACK_SIZE 8
+
+struct vtconsole;
+
+typedef enum
+{
+ VTCOLOR_BLACK,
+ VTCOLOR_RED,
+ VTCOLOR_GREEN,
+ VTCOLOR_YELLOW,
+ VTCOLOR_BLUE,
+ VTCOLOR_MAGENTA,
+ VTCOLOR_CYAN,
+ VTCOLOR_GREY,
+} vtcolor_t;
+
+typedef enum
+{
+ VTSTATE_ESC,
+ VTSTATE_BRACKET,
+ VTSTATE_ATTR,
+ VTSTATE_ENDVAL,
+} vtansi_parser_state_t;
+
+typedef struct
+{
+ int value;
+ int empty;
+} vtansi_arg_t;
+
+typedef struct
+{
+ vtansi_parser_state_t state;
+ vtansi_arg_t stack[VTC_ANSI_PARSER_STACK_SIZE];
+ int index;
+} vtansi_parser_t;
+
+typedef struct
+{
+ int bright;
+ vtcolor_t fg;
+ vtcolor_t bg;
+} vtattr_t;
+
+typedef struct
+{
+ char c;
+ vtattr_t attr;
+} vtcell_t;
+
+typedef struct
+{
+ int x;
+ int y;
+} vtcursor_t;
+
+typedef void (*vtc_paint_handler_t)(struct vtconsole *vtc, vtcell_t *cell,
+ int x, int y);
+typedef void (*vtc_cursor_handler_t)(struct vtconsole *vtc, vtcursor_t *cur);
+
+typedef struct vtconsole
+{
+ int width;
+ int height;
+
+ vtattr_t attr;
+ vtansi_parser_t ansiparser;
+
+ vtcell_t *buffer;
+ int *tabs;
+ int tab_index;
+ vtcursor_t cursor;
+
+ vtc_paint_handler_t on_paint;
+ vtc_cursor_handler_t on_move;
+} vtconsole_t;
+
+typedef vtconsole_t vterminal_t;
+
+vtconsole_t *vtconsole(vtconsole_t *vtc, int width, int height,
+ vtc_paint_handler_t on_paint,
+ vtc_cursor_handler_t on_move);
+void vtconsole_delete(vtconsole_t *c);
+
+void vtconsole_clear(vtconsole_t *vtc, int fromx, int fromy, int tox, int toy);
+void vtconsole_scroll(vtconsole_t *vtc, int lines);
+void vtconsole_newline(vtconsole_t *vtc);
+
+void vtconsole_putchar(vtconsole_t *vtc, char c);
+void vtconsole_write(vtconsole_t *vtc, const char *buffer, uint32_t size);
+
+size_t vterminal_write(vterminal_t *vt, const char *buf, size_t len);
+
+size_t vterminal_echo_input(vterminal_t *vt, const char *buf, size_t len);
+
+void vterminal_key_pressed(vterminal_t *vt);
+
+void vterminal_scroll_to_bottom(vterminal_t *vt);
+
+void vterminal_init(vterminal_t *vt);
+
+void vterminal_make_active(vterminal_t *vt);