diff options
author | Anton Kling <anton@kling.gg> | 2023-10-30 22:12:14 +0100 |
---|---|---|
committer | Anton Kling <anton@kling.gg> | 2023-10-31 00:18:38 +0100 |
commit | 8a9208612eec8ddae4c418485d848ecfa0613699 (patch) | |
tree | 2f4b29200c2f0c19ae52f45bdb9b38a41b356e30 /kernel/drivers | |
parent | ca76600acc8bf7a02346efa5bd8f17072210ec01 (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.c | 253 | ||||
-rw-r--r-- | kernel/drivers/ata.h | 16 | ||||
-rw-r--r-- | kernel/drivers/keyboard.c | 188 | ||||
-rw-r--r-- | kernel/drivers/keyboard.h | 11 | ||||
-rw-r--r-- | kernel/drivers/mouse.c | 144 | ||||
-rw-r--r-- | kernel/drivers/mouse.h | 5 | ||||
-rw-r--r-- | kernel/drivers/pci.c | 69 | ||||
-rw-r--r-- | kernel/drivers/pci.h | 26 | ||||
-rw-r--r-- | kernel/drivers/pit.c | 55 | ||||
-rw-r--r-- | kernel/drivers/pit.h | 12 | ||||
-rw-r--r-- | kernel/drivers/pst.c | 17 | ||||
-rw-r--r-- | kernel/drivers/pst.h | 7 | ||||
-rw-r--r-- | kernel/drivers/rtl8139.c | 182 | ||||
-rw-r--r-- | kernel/drivers/rtl8139.h | 4 | ||||
-rw-r--r-- | kernel/drivers/serial.c | 35 | ||||
-rw-r--r-- | kernel/drivers/serial.h | 2 | ||||
-rw-r--r-- | kernel/drivers/vbe.c | 73 | ||||
-rw-r--r-- | kernel/drivers/vbe.h | 7 |
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 |