summaryrefslogtreecommitdiff
path: root/kernel/drivers
diff options
context:
space:
mode:
authorAnton Kling <anton@kling.gg>2023-10-30 22:12:14 +0100
committerAnton Kling <anton@kling.gg>2023-10-31 00:18:38 +0100
commit8a9208612eec8ddae4c418485d848ecfa0613699 (patch)
tree2f4b29200c2f0c19ae52f45bdb9b38a41b356e30 /kernel/drivers
parentca76600acc8bf7a02346efa5bd8f17072210ec01 (diff)
Meta: Move kernel and userland to their own folders.
This is to allow both the kernel and the userland to share certain header files and to make the folder structure a bit more clear.
Diffstat (limited to 'kernel/drivers')
-rw-r--r--kernel/drivers/ata.c253
-rw-r--r--kernel/drivers/ata.h16
-rw-r--r--kernel/drivers/keyboard.c188
-rw-r--r--kernel/drivers/keyboard.h11
-rw-r--r--kernel/drivers/mouse.c144
-rw-r--r--kernel/drivers/mouse.h5
-rw-r--r--kernel/drivers/pci.c69
-rw-r--r--kernel/drivers/pci.h26
-rw-r--r--kernel/drivers/pit.c55
-rw-r--r--kernel/drivers/pit.h12
-rw-r--r--kernel/drivers/pst.c17
-rw-r--r--kernel/drivers/pst.h7
-rw-r--r--kernel/drivers/rtl8139.c182
-rw-r--r--kernel/drivers/rtl8139.h4
-rw-r--r--kernel/drivers/serial.c35
-rw-r--r--kernel/drivers/serial.h2
-rw-r--r--kernel/drivers/vbe.c73
-rw-r--r--kernel/drivers/vbe.h7
18 files changed, 1106 insertions, 0 deletions
diff --git a/kernel/drivers/ata.c b/kernel/drivers/ata.c
new file mode 100644
index 0000000..fd9b504
--- /dev/null
+++ b/kernel/drivers/ata.c
@@ -0,0 +1,253 @@
+#include <assert.h>
+#include <cpu/io.h>
+#include <drivers/ata.h>
+#include <drivers/pit.h>
+
+#define PRIMARY_BUS_BASEPORT 0x1F0
+#define SECONDAY_BUS_BASEPORT 0x170
+
+#define PRIMARY_BUS_IRQ 14
+#define SECONDAY_BUS_IRQ 15
+
+#define STATUS_PORT 7
+#define COMMAND_PORT 7
+#define DRIVE_SELECT 6
+#define LBAhi 5
+#define LBAmid 4
+#define LBAlo 3
+#define SECTOR_COUNT 2
+#define DATA_PORT 0
+
+#define IDENTIFY 0xEC
+#define READ_SECTORS 0x20
+#define WRITE_SECTORS 0x30
+#define CACHE_FLUSH 0xE7
+
+#define STATUS_BSY ((1 << 7))
+#define STATUS_DF ((1 << 5))
+#define STATUS_DRQ ((1 << 3))
+#define STATUS_ERR ((1 << 0))
+
+uint32_t io_base;
+
+unsigned char read_buffer[SECTOR_SIZE];
+
+void select_drive(uint8_t master_slave) {
+ outb(io_base + DRIVE_SELECT, (master_slave) ? 0xA0 : 0xB0);
+}
+
+int identify(int master_slave) {
+ select_drive(master_slave);
+ outb(io_base + SECTOR_COUNT, 0);
+ outb(io_base + LBAlo, 0);
+ outb(io_base + LBAmid, 0);
+ outb(io_base + LBAhi, 0);
+ outb(io_base + COMMAND_PORT, IDENTIFY);
+ if (0 == inb(io_base + STATUS_PORT))
+ return 0; // Drive does not exist
+
+ for (; 0 != (inb(io_base + STATUS_PORT) & STATUS_BSY);)
+ ;
+
+ // Because of some ATAPI drives that do not
+ // follow spec, at this point we need to check
+ // the LBAmid and LBAhi ports to see if they are
+ // non-zero. If so, the drive is not ATA, and we
+ // should stop polling.
+ if (0 != inb(io_base + LBAmid) || 0 != inb(io_base + LBAhi)) {
+ klog("Drive is not ATA.", LOG_ERROR);
+ return -1;
+ }
+
+ for (uint16_t status;;) {
+ status = inb(io_base + STATUS_PORT);
+
+ if (1 == (status & STATUS_ERR)) {
+ klog("Drive ERR set.", LOG_ERROR);
+ return -2;
+ }
+
+ if ((status & STATUS_DRQ))
+ break;
+ }
+
+ // The data is ready to read from the Data
+ // port (0x1F0). Read 256 16-bit values,
+ // and store them.
+ // TODO: This returns some intreasting information.
+ // https://wiki.osdev.org/ATA_PIO_Mode#Interesting_information_returned_by_IDENTIFY
+ uint16_t array[256];
+ rep_insw(1 * SECTOR_SIZE / 16, io_base + DATA_PORT, array);
+ return 1;
+}
+
+int poll_status(void) {
+ for (uint16_t status;;) {
+ // Read the Regular Status port until...
+ // We read this 15 times to give some
+ // time for the drive to catch up.
+ for (int n = 0; n < 15; n++)
+ status = inb(io_base + STATUS_PORT);
+
+ // ERR or
+ // DF sets
+ if ((status & STATUS_ERR) || (status & STATUS_DF)) {
+ klog("Drive error set.", LOG_ERROR);
+ return 0;
+ }
+
+ // BSY clears
+ // DRQ sets
+ if (0 == (status & STATUS_BSY) && 0 != (status & STATUS_DRQ))
+ break;
+ }
+ return 1;
+}
+
+// Instructions from: https://wiki.osdev.org/ATA_PIO_Mode#28_bit_PIO
+void __attribute__((optimize("O0")))
+setup_drive_for_command(uint32_t lba, uint32_t sector_count) {
+ // 1. Send 0xE0 for the "master" or 0xF0 for
+ // the "slave", ORed with the highest 4 bits
+ // of the LBA to port 0x1F6
+ outb(io_base + DRIVE_SELECT, 0xE0 | (0 << 4) | ((lba >> 24) & 0x0F));
+
+ // 2. Send a NULL byte to port 0x1F1, if you
+ // like (it is ignored and wastes
+ // lots of CPU time)
+
+ // NOP
+
+ // 3. Send the sectorcount to port 0x1F2
+ outb(io_base + SECTOR_COUNT, sector_count);
+
+ // 4. Send the low 8 bits of the LBA to port 0x1F3
+ outb(io_base + LBAlo, (lba >> 0) & 0xFF);
+
+ // 5. Send the next 8 bits of the LBA to port 0x1F4
+ outb(io_base + LBAmid, (lba >> 8) & 0xFF);
+
+ // 6. Send the next 8 bits of the LBA to port 0x1F5
+ outb(io_base + LBAhi, (lba >> 16) & 0xFF);
+}
+
+void delayed_rep_outsw(size_t n, uint16_t port, volatile uint8_t *buffer) {
+ for (volatile size_t i = 0; i < n; i++) {
+ outsw(port, (uint32_t)buffer);
+ buffer += 2;
+ // outsw(port, buffer);
+ }
+}
+
+void __attribute__((optimize("O0")))
+ata_write_lba28(uint32_t lba, uint32_t sector_count,
+ volatile const uint8_t *buffer) {
+ setup_drive_for_command(lba, sector_count);
+
+ outb(io_base + COMMAND_PORT, WRITE_SECTORS);
+
+ for (volatile uint32_t i = 0; i < sector_count; i++) {
+ if (!poll_status()) {
+ // FIXME: Fail properly
+ for (;;)
+ ;
+ }
+
+ delayed_rep_outsw(256, io_base + DATA_PORT,
+ (void *)((uint32_t)buffer + i * 256));
+ }
+
+ // Cache flush
+ outb(io_base + COMMAND_PORT, CACHE_FLUSH);
+
+ // Wait for BSY to clear
+ for (;;) {
+ uint16_t status = inb(io_base + STATUS_PORT);
+ if (!(status & STATUS_BSY))
+ break;
+ }
+}
+
+// Instructions from: https://wiki.osdev.org/ATA_PIO_Mode#28_bit_PIO
+void __attribute__((optimize("O0")))
+ata_read_lba28(uint32_t lba, uint32_t sector_count, volatile void *address) {
+ // Step 1-6 is done in this function.
+ setup_drive_for_command(lba, sector_count);
+
+ // 7. Send the "READ SECTORS" command to port 0x17F
+ outb(io_base + COMMAND_PORT, READ_SECTORS);
+
+ // 8. Wait for an IRQ or poll.
+
+ // This step can be found in the for loop
+
+ // 9. Transfer 256 16-bit values, a uint16_t at a time,
+ // into your buffer from I/O port 0x1F0
+ for (volatile uint32_t i = 0; i < sector_count; i++) {
+ // 10. Then loop back to waiting for the next IRQ
+ // or poll again for each successive sector.
+ // 8. Wait for an IRQ or poll.
+ if (!poll_status()) {
+ // FIXME: Fail properly
+ for (;;)
+ ;
+ }
+ rep_insw(256, io_base + DATA_PORT, (void *)((uint32_t)address + i * 256));
+ }
+}
+
+void ata_init(void) {
+ io_base = PRIMARY_BUS_BASEPORT;
+
+ // Before sending any data to the IO ports,
+ // read the Regular Status byte. The value
+ // 0xFF is an illegal status value, and
+ // indicates that the bus has no drives
+ if (0xFF == inb(io_base + STATUS_PORT)) {
+ klog("Bus has no drives", LOG_ERROR);
+ }
+
+ // Issue IDENTIFY command
+ select_drive(1);
+}
+
+void __attribute__((optimize("O0")))
+read_lba(uint32_t lba, void *address, size_t size, size_t offset) {
+ uintptr_t ptr = (uintptr_t)address;
+ lba += offset / SECTOR_SIZE;
+ offset = offset % SECTOR_SIZE;
+ asm("cli");
+ size_t total_read = 0;
+ for (int i = 0; size > 0; i++) {
+ uint32_t read_len =
+ (SECTOR_SIZE < (size + offset)) ? (SECTOR_SIZE - offset) : size;
+ ata_read_lba28(lba + i, 1, read_buffer);
+ memcpy((void *)ptr, read_buffer + offset, read_len);
+ size -= read_len;
+ total_read += read_len;
+ ptr += read_len;
+ offset = 0;
+ }
+}
+
+void write_lba(uint32_t lba, volatile void *address, size_t size,
+ size_t offset) {
+ uintptr_t ptr = (uintptr_t)address;
+ lba += offset / SECTOR_SIZE;
+ offset = offset % SECTOR_SIZE;
+ size_t total_write = 0;
+ uint8_t sector_buffer[512];
+ for (int i = 0; size > 0; i++) {
+ ata_read_lba28(lba + i, 1, sector_buffer);
+ uint32_t write_len =
+ (SECTOR_SIZE < (size + offset)) ? (SECTOR_SIZE - offset) : size;
+
+ memcpy(sector_buffer + offset, (void *)ptr, write_len);
+ ata_write_lba28(lba + i, 1, sector_buffer);
+
+ size -= write_len;
+ total_write += write_len;
+ ptr += write_len;
+ offset = 0;
+ }
+}
diff --git a/kernel/drivers/ata.h b/kernel/drivers/ata.h
new file mode 100644
index 0000000..3e88d7f
--- /dev/null
+++ b/kernel/drivers/ata.h
@@ -0,0 +1,16 @@
+#include "../cpu/idt.h"
+#include <stddef.h>
+
+#define SECTOR_SIZE 512
+
+void ata_init(void);
+
+void read_drive_chs(uint8_t head_index, uint8_t sector_count,
+ uint8_t sector_index, uint8_t cylinder_low_value,
+ uint8_t cylinder_high_value, void *address);
+void read_drive_lba(uint32_t lba, uint8_t sector_count, void *address);
+void read_lba(uint32_t lba, void *address, size_t size, size_t offset);
+void write_lba(uint32_t lba, volatile void *address, size_t size,
+ size_t offset);
+void ata_write_lba28(uint32_t lba, uint32_t sector_count,
+ volatile const uint8_t *buffer);
diff --git a/kernel/drivers/keyboard.c b/kernel/drivers/keyboard.c
new file mode 100644
index 0000000..8c96606
--- /dev/null
+++ b/kernel/drivers/keyboard.c
@@ -0,0 +1,188 @@
+#include <assert.h>
+#include <drivers/keyboard.h>
+#include <errno.h>
+#include <fs/devfs.h>
+#include <fs/fifo.h>
+#include <fs/vfs.h>
+#include <sched/scheduler.h>
+#include <stdint.h>
+
+#define PS2_REG_DATA 0x60
+#define PS2_REG_STATUS 0x64
+#define PS2_REG_COMMAND 0x64
+
+#define PS2_CMD_ENABLE_FIRST_PORT 0xAE // no rsp
+
+#define PS2_CMD_TEST_CONTROLLER 0xAA // has rsp
+
+#define PS2_RSP_TEST_PASSED 0x55
+#define PS2_RSP_TEST_FAILED 0xFC
+
+#define PS2_CMD_SET_SCANCODE 0xF0 // has rsp
+#define PS2_KB_ACK 0xFA
+#define PS2_KB_RESEND 0xFE
+
+#define PS2_CMD_SET_MAKE_RELEASE 0xF8 // has rsp
+
+uint8_t kb_scancodes[3] = {0x43, 0x41, 0x3f};
+
+FIFO_FILE *keyboard_fifo;
+
+uint8_t ascii_table[] = {
+ 'e', '\x1B', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', 8,
+ '\t',
+
+ 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p',
+ // 0, // [
+ // 0, // ]
+ // 0,
+ // 0, // ?
+ '[', ']',
+ '\n', // ENTER
+ 'C',
+
+ 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l',
+ ';', // ;
+ '\'', // ;
+ '`', // ;
+ 'D', // LEFT SHIFT
+ '\\', // ;
+ 'z', 'x', 'c', 'v', 'b', 'n', 'm',
+ ',', // ;
+ '.', // ;
+ '/', // ;
+ 'U', // ;
+ 'U', // ;
+ 'U', // ;
+ ' ', // ;
+};
+
+uint8_t capital_ascii_table[] = {
+ 'e', '\x1B', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', 8,
+ '\t',
+
+ 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P',
+ // 0, // [
+ // 0, // ]
+ // 0,
+ // 0, // ?
+ '{', '}',
+ '\n', // ENTER
+ 'C',
+
+ 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L',
+ ':', // ;
+ '\"', // ;
+ '~', // ;
+ 'D', // LEFT SHIFT
+ '\\', // ;
+ 'Z', 'X', 'C', 'V', 'B', 'N', 'M',
+ '<', // ;
+ '>', // ;
+ '?', // ;
+ 'U', // ;
+ 'U', // ;
+ 'U', // ;
+ ' ', // ;
+};
+
+vfs_inode_t *kb_inode;
+
+uint8_t keyboard_to_ascii(uint16_t key, uint8_t capital) {
+ if ((key & 0xFF) > sizeof(ascii_table))
+ return 'U';
+ if (capital)
+ return capital_ascii_table[key & 0xFF];
+ else
+ return ascii_table[key & 0xFF];
+}
+
+uint8_t is_shift_down = 0;
+uint8_t is_alt_down = 0;
+
+struct KEY_EVENT {
+ char c;
+ uint8_t mode; // (shift (0 bit)) (alt (1 bit))
+ uint8_t release; // 0 pressed, 1 released
+};
+
+extern process_t *ready_queue;
+__attribute__((interrupt)) void
+int_keyboard(__attribute__((unused)) struct interrupt_frame *frame) {
+ outb(0x20, 0x20);
+ uint16_t c;
+ c = inb(PS2_REG_DATA);
+ int released = 0;
+ if (c & 0x80) {
+ switch ((c & ~(0x80)) & 0xFF) {
+ case 0x2A: // Left shift
+ case 0x36: // Right shift
+ is_shift_down = 0;
+ return;
+ case 0x38:
+ is_alt_down = 0;
+ return;
+ }
+ released = 1;
+ } else {
+ switch (c & 0xFF) {
+ case 0x2A: // Left shift
+ case 0x36: // Right shift
+ is_shift_down = 1;
+ return;
+ case 0x38:
+ is_alt_down = 1;
+ return;
+ }
+ released = 0;
+ }
+ unsigned char a = keyboard_to_ascii((c & ~(0x80)) & 0xFF, is_shift_down);
+ struct KEY_EVENT ev;
+ ev.c = a;
+ ev.release = released;
+ ev.mode = 0;
+ ev.mode |= is_shift_down << 0;
+ ev.mode |= is_alt_down << 1;
+ fifo_object_write((uint8_t *)&ev, 0, sizeof(ev), keyboard_fifo);
+ kb_inode->has_data = keyboard_fifo->has_data;
+}
+
+#define PS2_WAIT_RECV \
+ { \
+ for (;;) { \
+ uint8_t status = inb(PS2_REG_STATUS); \
+ if (status & 0x1) \
+ break; \
+ } \
+ }
+
+#define PS2_WAIT_SEND \
+ { \
+ for (;;) { \
+ uint8_t status = inb(PS2_REG_STATUS); \
+ if (!(status & (0x1 << 1))) \
+ break; \
+ } \
+ }
+
+void install_keyboard(void) {
+ keyboard_fifo = create_fifo_object();
+ install_handler(int_keyboard, INT_32_INTERRUPT_GATE(0x3), 0x21);
+}
+
+int keyboard_read(uint8_t *buffer, uint64_t offset, uint64_t len,
+ vfs_fd_t *fd) {
+ (void)offset;
+
+ if (0 == fd->inode->has_data) {
+ return -EAGAIN;
+ }
+ int rc = fifo_object_read(buffer, 0, len, keyboard_fifo);
+ fd->inode->has_data = keyboard_fifo->has_data;
+ return rc;
+}
+
+void add_keyboard(void) {
+ kb_inode = devfs_add_file("/keyboard", keyboard_read, NULL, NULL, 0, 0,
+ FS_TYPE_CHAR_DEVICE);
+}
diff --git a/kernel/drivers/keyboard.h b/kernel/drivers/keyboard.h
new file mode 100644
index 0000000..4b76f22
--- /dev/null
+++ b/kernel/drivers/keyboard.h
@@ -0,0 +1,11 @@
+#ifndef KEYBOARD_H
+#define KEYBOARD_H
+
+#include <stdint.h>
+#include <cpu/io.h>
+#include <cpu/idt.h>
+
+void install_keyboard(void);
+void add_keyboard(void);
+
+#endif
diff --git a/kernel/drivers/mouse.c b/kernel/drivers/mouse.c
new file mode 100644
index 0000000..23619a0
--- /dev/null
+++ b/kernel/drivers/mouse.c
@@ -0,0 +1,144 @@
+#include <cpu/idt.h>
+#include <drivers/mouse.h>
+#include <fs/devfs.h>
+#include <fs/fifo.h>
+#include <fs/vfs.h>
+#include <stdint.h>
+
+uint8_t mouse_cycle = 0; // unsigned char
+uint8_t mouse_uint8_t[3]; // signed char
+uint8_t mouse_x = 0; // signed char
+uint8_t mouse_y = 0; // signed char
+vfs_inode_t *mouse_inode;
+vfs_fd_t *mouse_fd;
+
+struct mouse_event {
+ uint8_t buttons;
+ uint8_t x;
+ uint8_t y;
+};
+
+int fs_mouse_write(uint8_t *buffer, uint64_t offset, uint64_t len,
+ vfs_fd_t *fd) {
+ int rc = fifo_object_write(buffer, offset, len, fd->inode->internal_object);
+ FIFO_FILE *f = fd->inode->internal_object;
+ mouse_inode->has_data = f->has_data;
+ return rc;
+}
+
+int fs_mouse_read(uint8_t *buffer, uint64_t offset, uint64_t len,
+ vfs_fd_t *fd) {
+ FIFO_FILE *f = fd->inode->internal_object;
+ if (!mouse_inode->has_data)
+ return 0;
+ int rc = fifo_object_read(buffer, offset, len, f);
+ mouse_inode->has_data = f->has_data;
+ return rc;
+}
+
+void add_mouse(void) {
+ mouse_inode = devfs_add_file("/mouse", fs_mouse_read, fs_mouse_write, NULL, 0,
+ 0, FS_TYPE_CHAR_DEVICE);
+ mouse_inode->internal_object = create_fifo_object();
+ // Don't look at this
+ int fd = vfs_open("/dev/mouse", O_RDWR, 0);
+ mouse_fd = get_vfs_fd(fd);
+ get_current_task()->file_descriptors[fd] = NULL;
+}
+
+__attribute__((interrupt)) void what(registers_t *r) { EOI(0xe); }
+
+__attribute__((interrupt)) void int_mouse(registers_t *r) {
+ (void)r;
+ EOI(12);
+ switch (mouse_cycle) {
+ case 0:
+ mouse_uint8_t[0] = inb(0x60);
+ if(!(mouse_uint8_t[0] & (1 << 3))) {
+ mouse_cycle = 0;
+ return;
+ }
+ mouse_cycle++;
+ break;
+ case 1:
+ mouse_uint8_t[1] = inb(0x60);
+ mouse_cycle++;
+ break;
+ case 2:
+ mouse_uint8_t[2] = inb(0x60);
+ mouse_x = mouse_uint8_t[1];
+ mouse_y = mouse_uint8_t[2];
+ mouse_cycle = 0;
+ struct mouse_event e;
+ e.buttons = mouse_uint8_t[0];
+ e.x = mouse_x;
+ e.y = mouse_y;
+ raw_vfs_pwrite(mouse_fd, &e, sizeof(e), 0);
+ break;
+ }
+}
+
+void mouse_wait(uint8_t a_type) {
+ uint32_t _time_out = 100000;
+ if (a_type == 0) {
+ while (_time_out--) {
+ if ((inb(0x64) & 1) == 1) {
+ return;
+ }
+ }
+ return;
+ } else {
+ while (_time_out--) {
+ if ((inb(0x64) & 2) == 0) {
+ return;
+ }
+ }
+ return;
+ }
+}
+
+void mouse_write(uint8_t a_write) {
+ // Wait to be able to send a command
+ mouse_wait(1);
+ // Tell the mouse we are sending a command
+ outb(0x64, 0xD4);
+ // Wait for the final part
+ mouse_wait(1);
+ // Finally write
+ outb(0x60, a_write);
+}
+
+uint8_t mouse_read() {
+ // Get's response from mouse
+ mouse_wait(0);
+ return inb(0x60);
+}
+
+void install_mouse(void) {
+ uint8_t _status; // unsigned char
+ asm("cli");
+ // Enable the auxiliary mouse device
+ mouse_wait(1);
+ outb(0x64, 0xA8);
+
+ // Enable the interrupts
+ mouse_wait(1);
+ outb(0x64, 0x20);
+ mouse_wait(0);
+ _status = (inb(0x60) | 2);
+ mouse_wait(1);
+ outb(0x64, 0x60);
+ mouse_wait(1);
+ outb(0x60, _status);
+
+ // Tell the mouse to use default settings
+ mouse_write(0xF6);
+ mouse_read(); // Acknowledge
+
+ // Enable the mouse
+ mouse_write(0xF4);
+ mouse_read(); // Acknowledge
+
+ install_handler(int_mouse, INT_32_INTERRUPT_GATE(0x3), 12 + 0x20);
+ install_handler(what, INT_32_INTERRUPT_GATE(0x3), 0xe + 0x20);
+}
diff --git a/kernel/drivers/mouse.h b/kernel/drivers/mouse.h
new file mode 100644
index 0000000..5248143
--- /dev/null
+++ b/kernel/drivers/mouse.h
@@ -0,0 +1,5 @@
+#ifndef MOUSE_H
+#define MOUSE_H
+void install_mouse(void);
+void add_mouse(void);
+#endif
diff --git a/kernel/drivers/pci.c b/kernel/drivers/pci.c
new file mode 100644
index 0000000..5fe14bd
--- /dev/null
+++ b/kernel/drivers/pci.c
@@ -0,0 +1,69 @@
+#include <assert.h>
+#include <cpu/io.h>
+#include <drivers/pci.h>
+#include <stdio.h>
+
+#define CONFIG_ADDRESS 0xCF8
+#define CONFIG_DATA 0xCFC
+
+void pci_config_write32(const struct PCI_DEVICE *device, uint8_t func,
+ uint8_t offset, uint32_t data) {
+ uint32_t address;
+ uint32_t lbus = (uint32_t)device->bus;
+ uint32_t lslot = (uint32_t)device->slot;
+ uint32_t lfunc = (uint32_t)func;
+
+ // Create configuration address as per Figure 1
+ address = (uint32_t)((lbus << 16) | (lslot << 11) | (lfunc << 8) |
+ (offset & 0xFC) | ((uint32_t)0x80000000));
+
+ // Write out the address
+ outl(CONFIG_ADDRESS, address);
+ outl(CONFIG_DATA, data);
+}
+
+uint32_t pci_config_read32(const struct PCI_DEVICE *device, uint8_t func,
+ uint8_t offset) {
+ uint32_t address;
+ uint32_t lbus = (uint32_t)device->bus;
+ uint32_t lslot = (uint32_t)device->slot;
+ uint32_t lfunc = (uint32_t)func;
+
+ // Create configuration address as per Figure 1
+ address = (uint32_t)((lbus << 16) | (lslot << 11) | (lfunc << 8) |
+ (offset & 0xFC) | ((uint32_t)0x80000000));
+
+ // Write out the address
+ outl(CONFIG_ADDRESS, address);
+ return inl(CONFIG_DATA);
+}
+
+int pci_populate_device_struct(uint16_t vendor, uint16_t device,
+ struct PCI_DEVICE *pci_device) {
+ pci_device->vendor = vendor;
+ pci_device->device = device;
+
+ for (int bus = 0; bus < 256; bus++) {
+ for (int slot = 0; slot < 256; slot++) {
+ struct PCI_DEVICE tmp;
+ tmp.bus = bus;
+ tmp.slot = slot;
+ uint32_t device_vendor = pci_config_read32(&tmp, 0, 0);
+ if (vendor != (device_vendor & 0xFFFF))
+ continue;
+ if (device != (device_vendor >> 16))
+ continue;
+ pci_device->bus = bus;
+ pci_device->slot = slot;
+ uint32_t bar0 = pci_config_read32(pci_device, 0, 0x10);
+ assert(bar0 & 0x1 && "Only support memory IO");
+ pci_device->gen.base_mem_io = bar0 & (~0x3);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+uint8_t pci_get_interrupt_line(const struct PCI_DEVICE *device) {
+ return pci_config_read32(device, 0, 0x3C) & 0xFF;
+}
diff --git a/kernel/drivers/pci.h b/kernel/drivers/pci.h
new file mode 100644
index 0000000..7511cee
--- /dev/null
+++ b/kernel/drivers/pci.h
@@ -0,0 +1,26 @@
+#include <stdint.h>
+
+struct GENERAL_DEVICE {
+ uint32_t base_mem_io;
+ uint8_t interrupt_line;
+};
+
+struct PCI_DEVICE {
+ uint16_t vendor;
+ uint16_t device;
+ uint8_t bus;
+ uint8_t slot;
+ union {
+ struct GENERAL_DEVICE gen;
+ };
+};
+
+uint32_t pci_config_read32(const struct PCI_DEVICE *device, uint8_t func,
+ uint8_t offset);
+void pci_config_write32(const struct PCI_DEVICE *device, uint8_t func,
+ uint8_t offset, uint32_t data);
+
+int pci_populate_device_struct(uint16_t vendor, uint16_t device,
+ struct PCI_DEVICE *pci_device);
+
+uint8_t pci_get_interrupt_line(const struct PCI_DEVICE *device);
diff --git a/kernel/drivers/pit.c b/kernel/drivers/pit.c
new file mode 100644
index 0000000..711883c
--- /dev/null
+++ b/kernel/drivers/pit.c
@@ -0,0 +1,55 @@
+#include "pit.h"
+
+#define PIT_IO_CHANNEL_0 0x40
+#define PIT_IO_MODE_COMMAND 0x43
+
+uint64_t clock_num_ms_ticks = 0;
+uint64_t pit_counter = 0;
+uint16_t hertz;
+
+uint64_t pit_num_ms(void) { return clock_num_ms_ticks; }
+
+uint16_t read_pit_count(void) {
+ uint16_t count = 0;
+
+ outb(PIT_IO_MODE_COMMAND, 0x0 /*0b00000000*/);
+
+ count = inb(PIT_IO_CHANNEL_0);
+ count |= inb(PIT_IO_CHANNEL_0) << 8;
+
+ return count;
+}
+
+void set_pit_count(uint16_t hertz) {
+ uint16_t divisor = 1193180 / hertz;
+
+ /*
+ * 0b00110110
+ * ^^
+ * channel - 0
+ * ^^
+ * r/w mode - LSB then MSB
+ * ^^^
+ * mode - 3 Square Wave Mode
+ * ^
+ * BCD - no
+ */
+ outb(PIT_IO_MODE_COMMAND, 0x36 /*0b00110110*/);
+ outb(PIT_IO_CHANNEL_0, divisor & 0xFF);
+ outb(PIT_IO_CHANNEL_0, (divisor & 0xFF00) >> 8);
+}
+
+__attribute__((interrupt)) void
+int_clock(__attribute__((unused)) struct interrupt_frame *frame) {
+ outb(0x20, 0x20);
+ pit_counter++;
+ if (pit_counter >= hertz / 1000) {
+ pit_counter = 0;
+ clock_num_ms_ticks++;
+ }
+ switch_task();
+}
+
+void pit_install(void) {
+ install_handler(int_clock, INT_32_INTERRUPT_GATE(0x0), 0x20);
+}
diff --git a/kernel/drivers/pit.h b/kernel/drivers/pit.h
new file mode 100644
index 0000000..8d9ce98
--- /dev/null
+++ b/kernel/drivers/pit.h
@@ -0,0 +1,12 @@
+#ifndef PIT_H
+#define PIT_H
+#include <cpu/idt.h>
+#include <cpu/io.h>
+#include <sched/scheduler.h>
+#include <stdint.h>
+#include <stdio.h>
+
+void pit_install(void);
+void set_pit_count(uint16_t hertz);
+uint64_t pit_num_ms(void);
+#endif
diff --git a/kernel/drivers/pst.c b/kernel/drivers/pst.c
new file mode 100644
index 0000000..d063ec9
--- /dev/null
+++ b/kernel/drivers/pst.c
@@ -0,0 +1,17 @@
+#include <drivers/pst.h>
+#include <fs/tmpfs.h>
+
+int openpty(int *amaster, int *aslave, char *name,
+ /*const struct termios*/ void *termp,
+ /*const struct winsize*/ void *winp) {
+ (void)name;
+ (void)termp;
+ (void) winp;
+ int fd[2];
+ pipe(fd); // This depends upon that pipe will support read and write
+ // through the same fd. In reality this should not be the
+ // case.
+ *amaster = fd[0];
+ *aslave = fd[1];
+ return 0;
+}
diff --git a/kernel/drivers/pst.h b/kernel/drivers/pst.h
new file mode 100644
index 0000000..e8fdfaa
--- /dev/null
+++ b/kernel/drivers/pst.h
@@ -0,0 +1,7 @@
+#ifndef PST_H
+#define PST_H
+#include "../fs/vfs.h"
+
+int openpty(int *amaster, int *aslave, char *name, /*const struct termios*/ void *termp,
+ /*const struct winsize*/ void *winp);
+#endif
diff --git a/kernel/drivers/rtl8139.c b/kernel/drivers/rtl8139.c
new file mode 100644
index 0000000..3853eab
--- /dev/null
+++ b/kernel/drivers/rtl8139.c
@@ -0,0 +1,182 @@
+#include <assert.h>
+#include <cpu/idt.h>
+#include <cpu/io.h>
+#include <drivers/pci.h>
+#include <drivers/rtl8139.h>
+#include <mmu.h>
+#include <network/arp.h>
+#include <network/ethernet.h>
+
+#define RBSTART 0x30
+#define CMD 0x37
+#define IMR 0x3C
+
+#define TSD0 0x10 // transmit status
+#define TSAD0 0x20 // transmit start address
+
+struct PCI_DEVICE rtl8139;
+uint8_t *device_buffer;
+
+uint8_t *send_buffers[4];
+
+struct _INT_PACKET_HEADER {
+ uint8_t ROK : 1;
+ uint8_t FAE : 1;
+ uint8_t CRC : 1;
+ uint8_t LONG : 1;
+ uint8_t RUNT : 1;
+ uint8_t ISE : 1;
+ uint8_t reserved : 5;
+ uint8_t BAR : 1;
+ uint8_t PAM : 1;
+ uint8_t MAR : 1;
+};
+
+struct PACKET_HEADER {
+ union {
+ uint16_t raw;
+ struct _INT_PACKET_HEADER data;
+ };
+};
+
+uint16_t current_packet_read = 0;
+
+void handle_packet(void) {
+ assert(sizeof(struct _INT_PACKET_HEADER) == sizeof(uint16_t));
+
+ uint16_t *buf = (uint16_t *)(device_buffer + current_packet_read);
+ struct PACKET_HEADER packet_header;
+ packet_header.raw = *buf;
+ assert(packet_header.data.ROK);
+ kprintf("packet_header.raw: %x\n", packet_header.raw);
+ uint16_t packet_length = *(buf + 1);
+ kprintf("packet_length: %x\n", packet_length);
+
+ uint8_t packet_buffer[8192 + 16];
+ if (current_packet_read + packet_length >= 8192 + 16) {
+ uint32_t first_run = ((uint8_t *)buf + (8192 + 16)) - device_buffer;
+ memcpy(packet_buffer, buf, first_run);
+ memcpy(packet_buffer, device_buffer, packet_length - first_run);
+ } else {
+ memcpy(packet_buffer, buf, packet_length);
+ }
+
+ handle_ethernet((uint8_t *)packet_buffer + 4, packet_length);
+
+ // Thanks to exscape
+ // https://github.com/exscape/exscapeOS/blob/master/src/kernel/net/rtl8139.c
+ // and the programmers guide
+ // https://www.cs.usfca.edu/~cruse/cs326f04/RTL8139_ProgrammersGuide.pdf I
+ // have no clue what this calculation, I can't find anything possibly relating
+ // to this in the manual, but it does work I guess.
+ current_packet_read = (current_packet_read + packet_length + 4 + 3) & (~3);
+ current_packet_read %= 8192 + 16;
+ outw(rtl8139.gen.base_mem_io + 0x38, current_packet_read - 0x10);
+}
+
+__attribute__((interrupt)) void rtl8139_handler(void *regs) {
+ (void)regs;
+ uint16_t status = inw(rtl8139.gen.base_mem_io + 0x3e);
+ kprintf("status: %x\n", status);
+
+ outw(rtl8139.gen.base_mem_io + 0x3E, 0x5);
+ if (status & (1 << 2)) {
+ kprintf("Packet sent\n");
+ }
+ if (status & (1 << 0)) {
+ kprintf("Received packet\n");
+ handle_packet();
+ }
+
+ EOI(0xB);
+}
+
+int rtl8139_send_data(uint8_t *data, uint16_t data_size) {
+ const struct PCI_DEVICE *device = &rtl8139;
+ // FIXME: It should block or fail if there is too little space for the
+ // buffer
+ if (data_size > 0x1000)
+ return 0;
+ static int loop = 0;
+ if (loop > 3) {
+ loop = 0;
+ }
+ memcpy(send_buffers[loop], data, data_size);
+ outl(device->gen.base_mem_io + 0x20 + loop * 4,
+ (uint32_t)virtual_to_physical(send_buffers[loop], NULL));
+ outl(device->gen.base_mem_io + 0x10 + loop * 4, data_size);
+ loop += 1;
+ return 1;
+}
+
+void get_mac_address(uint8_t mac[6]) {
+ uint32_t base_address = rtl8139.gen.base_mem_io;
+ // Read the MAC address
+ uint64_t mac_address;
+ {
+ uint32_t low_mac = inl(base_address);
+ uint16_t high_mac = inw(base_address + 0x4);
+ mac_address = ((uint64_t)high_mac << 32) | low_mac;
+ }
+ kprintf("mac_address: %x\n", mac_address);
+ memcpy(mac, &mac_address, sizeof(uint8_t[6]));
+}
+
+uint8_t rtl8139_get_transmit_status(uint32_t base_address) {
+ uint32_t status_register = inl(base_address + 0x3E);
+ if ((status_register >> 3) & 0x1)
+ kprintf("transmit error :(\n");
+ uint8_t status = (status_register >> 2) & 0x1;
+ outl(base_address + 0x3E, 0x5);
+ return status;
+}
+
+void rtl8139_init(void) {
+ if (!pci_populate_device_struct(0x10EC, 0x8139, &rtl8139)) {
+ kprintf("RTL8139 not found :(\n");
+ return;
+ }
+ kprintf("RTL8139 found at bus: %x slot: %x\n", rtl8139.bus, rtl8139.slot);
+
+ uint8_t header_type = (pci_config_read32(&rtl8139, 0, 0xC) >> 16) & 0xFF;
+ assert(0 == header_type);
+
+ uint32_t base_address = rtl8139.gen.base_mem_io;
+ uint8_t interrupt_line = pci_get_interrupt_line(&rtl8139);
+
+ // Enable bus mastering
+ uint32_t register1 = pci_config_read32(&rtl8139, 0, 0x4);
+ register1 |= (1 << 2);
+ pci_config_write32(&rtl8139, 0, 0x4, register1);
+
+ // Turning on the device
+ outb(base_address + 0x52, 0x0);
+
+ // Reset the device and clear the RX and TX buffers
+ outb(base_address + CMD, 0x10);
+ for (; 0 != (inb(base_address + CMD) & 0x10);)
+ ;
+ device_buffer = ksbrk(8192 + 16);
+ memset(device_buffer, 0, 8192 + 16);
+ // Setupt the recieve buffer
+ uint32_t rx_buffer = (uint32_t)virtual_to_physical(device_buffer, NULL);
+ outl(base_address + RBSTART, rx_buffer);
+
+ // Set IMR + ISR
+ outw(base_address + IMR, (1 << 2) | (1 << 3) | (1 << 0));
+
+ // Set transmit and reciever enable
+ outb(base_address + 0x37, (1 << 2) | (1 << 3));
+
+ // Configure the recieve buffer
+ outl(base_address + 0x44,
+ 0xf); // 0xf is AB+AM+APM+AAP
+
+ install_handler(rtl8139_handler, INT_32_INTERRUPT_GATE(0x0),
+ 0x20 + interrupt_line);
+
+ // ksbrk() seems to have the magical ability of disabling interrupts?
+ // I have no fucking clue why that happens and it was a pain to debug.
+ for (int i = 0; i < 4; i++)
+ send_buffers[i] = ksbrk(0x1000);
+}
diff --git a/kernel/drivers/rtl8139.h b/kernel/drivers/rtl8139.h
new file mode 100644
index 0000000..6e13fdd
--- /dev/null
+++ b/kernel/drivers/rtl8139.h
@@ -0,0 +1,4 @@
+#include <stdint.h>
+void get_mac_address(uint8_t mac[6]);
+void rtl8139_init(void);
+int rtl8139_send_data(uint8_t *data, uint16_t data_size);
diff --git a/kernel/drivers/serial.c b/kernel/drivers/serial.c
new file mode 100644
index 0000000..549d852
--- /dev/null
+++ b/kernel/drivers/serial.c
@@ -0,0 +1,35 @@
+#include "cpu/io.h"
+
+#define PORT 0x3f8 // COM1
+
+int serial_init(void) {
+ outb(PORT + 1, 0x00); // Disable all interrupts
+ outb(PORT + 3, 0x80); // Enable DLAB (set baud rate divisor)
+ outb(PORT + 0, 0x03); // Set divisor to 3 (lo byte) 38400 baud
+ outb(PORT + 1, 0x00); // (hi byte)
+ outb(PORT + 3, 0x03); // 8 bits, no parity, one stop bit
+ outb(PORT + 2, 0xC7); // Enable FIFO, clear them, with 14-byte threshold
+ outb(PORT + 4, 0x0B); // IRQs enabled, RTS/DSR set
+ outb(PORT + 4, 0x1E); // Set in loopback mode, test the serial chip
+ outb(PORT + 0, 0xAE); // Test serial chip (send byte 0xAE and check if serial
+ // returns same byte)
+
+ // Check if serial is faulty (i.e: not same byte as sent)
+ if (inb(PORT + 0) != 0xAE) {
+ return 1;
+ }
+
+ // If serial is not faulty set it in normal operation mode
+ // (not-loopback with IRQs enabled and OUT#1 and OUT#2 bits enabled)
+ outb(PORT + 4, 0x0F);
+ return 0;
+}
+
+int is_transmit_empty() { return inb(PORT + 5) & 0x20; }
+
+void write_serial(char a) {
+ while (is_transmit_empty() == 0)
+ ;
+
+ outb(PORT, a);
+}
diff --git a/kernel/drivers/serial.h b/kernel/drivers/serial.h
new file mode 100644
index 0000000..327765b
--- /dev/null
+++ b/kernel/drivers/serial.h
@@ -0,0 +1,2 @@
+int serial_init(void);
+void write_serial(char a);
diff --git a/kernel/drivers/vbe.c b/kernel/drivers/vbe.c
new file mode 100644
index 0000000..c67b7b4
--- /dev/null
+++ b/kernel/drivers/vbe.c
@@ -0,0 +1,73 @@
+#include <assert.h>
+#include <drivers/vbe.h>
+#include <fs/devfs.h>
+#include <fs/vfs.h>
+#include <mmu.h>
+#include <stdio.h>
+
+uint8_t *framebuffer;
+uint32_t framebuffer_physical;
+uint32_t framebuffer_width;
+uint32_t framebuffer_height;
+uint64_t framebuffer_size;
+
+vfs_vm_object_t vbe_vm_object;
+
+#define CHECK_FLAG(flags, bit) ((flags) & (1 << (bit)))
+#define min(_a, _b) (((_a) > (_b)) ? (_b) : (_a))
+
+struct DISPLAY_INFO {
+ uint32_t width;
+ uint32_t height;
+ uint8_t bpp;
+};
+
+struct DISPLAY_INFO vbe_info;
+
+void display_driver_init(multiboot_info_t *mbi) {
+ assert(CHECK_FLAG(mbi->flags, 12));
+ framebuffer_width = mbi->framebuffer_width;
+ framebuffer_height = mbi->framebuffer_height;
+
+ uint32_t bits_pp = mbi->framebuffer_bpp;
+ uint32_t bytes_pp = (bits_pp / 8) + (8 - (bits_pp % 8));
+
+ framebuffer_size = bytes_pp * framebuffer_width * framebuffer_height;
+
+ framebuffer_physical = mbi->framebuffer_addr;
+ framebuffer =
+ mmu_map_frames((void *)(uint32_t)mbi->framebuffer_addr, framebuffer_size);
+
+ vbe_info.width = framebuffer_width;
+ vbe_info.height = framebuffer_height;
+ vbe_info.bpp = mbi->framebuffer_bpp;
+}
+
+vfs_vm_object_t *vbe_get_vm_object(uint64_t length, uint64_t offset,
+ vfs_fd_t *fd) {
+ (void)fd;
+ (void)length;
+ (void)offset;
+ vbe_vm_object.size = framebuffer_size;
+ int n = (uintptr_t)align_page((void *)(uint32_t)framebuffer_size) / 0x1000;
+ vbe_vm_object.object = kmalloc(sizeof(void *) * n);
+ for (int i = 0; i < n; i++) {
+ vbe_vm_object.object[i] = (void *)framebuffer_physical + (i * 0x1000);
+ }
+ return &vbe_vm_object;
+}
+
+int display_info_read(uint8_t *buffer, uint64_t offset, uint64_t len,
+ vfs_fd_t *fd) {
+ (void)offset;
+ int read_len = min(sizeof(struct DISPLAY_INFO), len);
+ memcpy(buffer, &vbe_info, read_len);
+ return read_len;
+}
+
+void add_vbe_device(void) {
+ devfs_add_file("/vbe", NULL, NULL, vbe_get_vm_object, 1, 1,
+ FS_TYPE_BLOCK_DEVICE);
+ devfs_add_file("/display_info", display_info_read, NULL, NULL, 1, 0,
+ FS_TYPE_BLOCK_DEVICE);
+}
diff --git a/kernel/drivers/vbe.h b/kernel/drivers/vbe.h
new file mode 100644
index 0000000..4df2221
--- /dev/null
+++ b/kernel/drivers/vbe.h
@@ -0,0 +1,7 @@
+#ifndef VBE_H
+#define VBE_H
+#include <multiboot.h>
+void display_driver_init(multiboot_info_t *mb);
+void display_driver_cross(multiboot_info_t *mbi);
+void add_vbe_device(void);
+#endif // VBE_H