diff options
Diffstat (limited to 'kernel/include/drivers')
-rw-r--r-- | kernel/include/drivers/blockdev.h | 99 | ||||
-rw-r--r-- | kernel/include/drivers/chardev.h | 51 | ||||
-rw-r--r-- | kernel/include/drivers/cmos.h | 40 | ||||
-rw-r--r-- | kernel/include/drivers/dev.h | 49 | ||||
-rw-r--r-- | kernel/include/drivers/disk/ahci.h | 325 | ||||
-rw-r--r-- | kernel/include/drivers/disk/sata.h | 14 | ||||
-rw-r--r-- | kernel/include/drivers/keyboard.h | 43 | ||||
-rw-r--r-- | kernel/include/drivers/memdevs.h | 6 | ||||
-rw-r--r-- | kernel/include/drivers/pcie.h | 112 | ||||
-rw-r--r-- | kernel/include/drivers/screen.h | 72 | ||||
-rw-r--r-- | kernel/include/drivers/tty/ldisc.h | 68 | ||||
-rw-r--r-- | kernel/include/drivers/tty/tty.h | 21 | ||||
-rw-r--r-- | kernel/include/drivers/tty/vterminal.h | 249 |
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); |