From 4813725cf30da7244058b67f07b397e381369012 Mon Sep 17 00:00:00 2001 From: Freywar Ulvnaudgari Date: Fri, 7 Aug 2026 21:11:08 +0300 Subject: [PATCH] Add basic xHCI driver and USB devices enumeration --- meson.build | 2 + src/kernel/kernel.c | 14 ++ src/kernel/log.h | 34 +-- src/kernel/nvme.c | 53 ++--- src/kernel/pci.c | 132 ++++++++---- src/kernel/pci.h | 33 +-- src/kernel/usb.c | 44 ++++ src/kernel/usb.h | 8 + src/kernel/xhci.c | 490 ++++++++++++++++++++++++++++++++++++++++++++ src/kernel/xhci.h | 20 ++ src/lib/layout.h | 5 + vm.sh | 3 + 12 files changed, 721 insertions(+), 117 deletions(-) create mode 100644 src/kernel/usb.c create mode 100644 src/kernel/usb.h create mode 100644 src/kernel/xhci.c create mode 100644 src/kernel/xhci.h diff --git a/meson.build b/meson.build index d65e234..502b62d 100644 --- a/meson.build +++ b/meson.build @@ -86,7 +86,9 @@ kernel_sources = files([ 'src/kernel/syscall.c', 'src/kernel/timer.c', 'src/kernel/tss.c', + 'src/kernel/usb.c', 'src/kernel/vga.c', + 'src/kernel/xhci.c', ]) kernel_elf = executable( diff --git a/src/kernel/kernel.c b/src/kernel/kernel.c index 29fbf25..09535b2 100644 --- a/src/kernel/kernel.c +++ b/src/kernel/kernel.c @@ -14,8 +14,10 @@ #include "src/kernel/syscall.h" #include "src/kernel/timer.h" #include "src/kernel/tss.h" +#include "src/kernel/usb.h" #include "src/kernel/util.h" #include "src/kernel/vga.h" +#include "src/kernel/xhci.h" #include "src/lib/layout.h" #include "src/lib/memory.h" #include "src/lib/syscall.h" @@ -26,6 +28,15 @@ __attribute__((interrupt)) void isr_divide_by_zero(__attribute__((unused)) struc ; } +__attribute__((interrupt)) void isr_invalid_opcode(struct interrupt_frame *frame) { + char msg[80]; + string_format("INVALID OPCODE: ip=%x cs=%x flags=%x sp=%x", 80, msg, frame->ip, frame->cs, frame->flags, frame->sp); + + vga_set_string(VGA_HEIGHT - 1, 0, msg, 0x4F); + while (1) + ; +} + __attribute__((interrupt)) void isr_page_fault(__attribute__((unused)) struct interrupt_frame *frame, uint64_t error_code) { uint64_t cr2; __asm__ volatile("mov %%cr2, %0" : "=r"(cr2)); @@ -57,6 +68,7 @@ void kernel_main() { idt_init(); outb(0x21, inb(0x21) | 0x01); // mask out timer interrupt idt_set_entry(0, isr_divide_by_zero, 0x8E); + idt_set_entry(0x06, isr_invalid_opcode, 0x8E); idt_set_entry_ec(0x0E, isr_page_fault, 0x8E); idt_set_entry(0x0D, isr_general_violation, 0x8E); idt_set_entry(46, isr_ata_primary, 0x8E); @@ -65,6 +77,8 @@ void kernel_main() { syscall_init(); pci_init(); nvme_init(); + xhci_init(); + usb_init(); process_t *kernel = memory_allocate(sizeof(process_t)); kernel->pml4 = (uint64_t *)KERNEL_VIRTUAL_PML4; diff --git a/src/kernel/log.h b/src/kernel/log.h index 9a7b8d3..ae32ab4 100644 --- a/src/kernel/log.h +++ b/src/kernel/log.h @@ -6,37 +6,41 @@ extern stream_t *kernel_log; -#define LOG(fmt, ...) \ +#define PRINT(stream, fmt, ...) \ do { \ uint64_t _size = string_length(fmt) * 10; \ char *_string = memory_allocate(_size); \ _size = string_format(fmt, _size, _string, ##__VA_ARGS__); \ - kernel_log->write(kernel_log, _string, _size); \ + stream->write(stream, _string, _size); \ memory_free(_string); \ } while (0) +#define PRINT_LN(stream, fmt, ...) \ + do { \ + uint64_t _size = string_length(fmt) * 10; \ + char *_string = memory_allocate(_size); \ + _size = string_format(fmt, _size, _string, ##__VA_ARGS__); \ + stream->write(stream, __func__, string_length(__func__)); \ + stream->write(stream, ": ", 2); \ + stream->write(stream, _string, _size); \ + stream->write(stream, "\n", 1); \ + memory_free(_string); \ + } while (0) + +#define LOG(fmt, ...) PRINT(kernel_log, fmt, ##__VA_ARGS__) + #define TRACE(fmt, ...) -#define LOG_LN(fmt, ...) \ - do { \ - uint64_t _size = string_length(fmt) * 10; \ - char *_string = memory_allocate(_size); \ - _size = string_format(fmt, _size, _string, ##__VA_ARGS__); \ - kernel_log->write(kernel_log, __func__, string_length(__func__)); \ - kernel_log->write(kernel_log, ": ", 2); \ - kernel_log->write(kernel_log, _string, _size); \ - kernel_log->write(kernel_log, "\n", 1); \ - memory_free(_string); \ - } while (0) +#define LOG_LN(fmt, ...) PRINT_LN(kernel_log, fmt, ##__VA_ARGS__) #define LOG_VAL(v) LOG_LN(#v "=%x") -#define LOG_VAL_F(v, f) LOG_LN(#v "=" f) +#define LOG_VAL_F(v, f) LOG_LN(#v "=" f, v) #define TRACE_LN(fmt, ...) #define TRACE_VAL(v) TRACE_LN(#v "=%lx") -#define TRACE_VAL_F(v, f) TRACE_LN(#v "=" f) +#define TRACE_VAL_F(v, f) TRACE_LN(#v "=" f, v) void log_init(stream_t *log); diff --git a/src/kernel/nvme.c b/src/kernel/nvme.c index 419ee04..f812eff 100644 --- a/src/kernel/nvme.c +++ b/src/kernel/nvme.c @@ -1,10 +1,10 @@ #include "src/kernel/nvme.h" #include "src/kernel/log.h" -#include "src/kernel/memory.h" #include "src/kernel/panic.h" #include "src/kernel/pci.h" #include "src/lib/layout.h" #include "src/lib/memory.h" +#include "src/lib/util.h" #define SECTOR_SIZE 512 @@ -41,8 +41,8 @@ typedef volatile struct __attribute__((packed)) { #define NVME_REGS_ASQS_R(r) BITS_R(r->aqa, 11, 0) #define NVME_REGS_ASQS_W(r, v) BITS_W(r->aqa, 11, 0, v) -static nvme_regs_t *regs = (nvme_regs_t *)(KERNEL_VIRTUAL_BASE + MEMORY_SIZE); -static volatile uint32_t *db_regs = (uint32_t *)(KERNEL_VIRTUAL_BASE + MEMORY_SIZE + 0x1000); // TODO Get stride from caps. +static nvme_regs_t *regs = (nvme_regs_t *)KERNEL_VIRTUAL_NVME; +static volatile uint32_t *db_regs = (uint32_t *)(KERNEL_VIRTUAL_NVME + 0x1000); // TODO Get stride from caps. #define QUEUE_DEPTH 2 @@ -140,7 +140,7 @@ static void io_exec_sync(nvme_sqe_t *cmd) { memory_copy(cmd, sizeof(nvme_sqe_t), &io_sq[io_sq_tail]); TRACE_LN("Advancing submission doorbell..."); - db_regs[2] = io_sq_tail = (io_sq_tail + 1) % QUEUE_DEPTH; // TODO Get offset from caps. + db_regs[2] = io_sq_tail = (io_sq_tail + 1) % QUEUE_DEPTH; TRACE_LN("Waiting for completion..."); nvme_cqe_t *cmp = &io_cq[io_cq_head]; @@ -165,46 +165,23 @@ static void io_exec_sync(nvme_sqe_t *cmd) { } void nvme_init() { - LOG_LN("Searching for suitable device..."); - uint8_t found = 0; - for (uint16_t bus = 0; bus < PCI_MAX_BUSES && !found; bus++) { - for (uint8_t device = 0; device < PCI_MAX_DEVICES && !found; device++) { - for (uint8_t function = 0; function < PCI_MAX_FUNCTIONS && !found; function++) { - uint32_t id = pci_read((uint8_t)bus, device, function, PCI_OFFSET_DEVICE_VENDOR); - if (PCI_DEVICE(id) == PCI_DEVICE_EMPTY) { - continue; - } - uint32_t class = pci_read((uint8_t)bus, device, function, PCI_OFFSET_CLASS_REVISION); - if (PCI_CLASS(class) != NVME_PCI_CLASS || PCI_CLASS_SUB(class) != NVME_PCI_SUBCLASS) { - continue; - } + TRACE_LN("Searching for suitable device..."); + pci_bdf_t bdf = pci_find_by_class(NVME_PCI_CLASS, NVME_PCI_SUBCLASS, NUL); + ASSERT(bdf, "nvme_init: No suitable devices found."); - LOG_LN("Found device, %hx:%hx.", PCI_VENDOR(id), PCI_DEVICE(id)); - - uint64_t bar = pci_bar((uint8_t)bus, device, function); - LOG_LN("Mapping device to memory, phys %lx <-> virt %lx...", bar, regs); - for (uint8_t i = 0; i < 4; i++) { - memory_page_map((void *)KERNEL_VIRTUAL_PML4, (void *)((uint8_t *)regs + i * PAGE_SIZE), (void *)((uint8_t *)bar + i * PAGE_SIZE), - PAGE_WRITABLE | PAGE_PCD | PAGE_USER); - } - - found = 1; - } - } - } - - ASSERT(found, "nvme_init: No suitable devices found."); + TRACE_LN("Mapping device to virtual memory..."); + pci_map(bdf, regs, 4); ASSERT(NVME_REGS_NVME_CS_SUPPORTED(regs), "nvme_init: NVM command set not supported."); ASSERT(QUEUE_DEPTH <= NVME_REGS_MQES(regs), "nvme_init: Command queues are too big.") - LOG_LN("Disabling device..."); + TRACE_LN("Disabling device..."); regs->cc = 0; TRACE_VAL_F(regs->cc, "%lx"); while (NVME_REGS_RDY(regs)) ; TRACE_VAL_F(regs->cc, "%lx"); - LOG_LN("Configuring device..."); + TRACE_LN("Configuring device..."); NVME_REGS_ASQS_W(regs, QUEUE_DEPTH - 1); regs->asq = (uint64_t)(VIRT_TO_PHYS(admin_sq)); NVME_REGS_ACQS_W(regs, QUEUE_DEPTH - 1); @@ -213,7 +190,7 @@ void nvme_init() { TRACE_VAL_F(regs->acq, "%lx"); TRACE_VAL_F(regs->aqa, "%lx"); - LOG_LN("Enabling device..."); + TRACE_LN("Enabling device..."); NVME_REGS_IOCQES_W(regs, 4); NVME_REGS_IOSQES_W(regs, 6); NVME_REGS_CSS_W(regs, 0); @@ -223,7 +200,7 @@ void nvme_init() { ; TRACE_VAL_F(regs->cc, "%lx"); - LOG_LN("Creating I/O completion queue..."); + TRACE_LN("Creating I/O completion queue..."); nvme_sqe_t create_io_cq = { .opc = 0x05, .prp1 = (uint64_t)VIRT_TO_PHYS(io_cq), @@ -232,7 +209,7 @@ void nvme_init() { }; admin_exec_sync(&create_io_cq); - LOG_LN("Creating I/O submission queue..."); + TRACE_LN("Creating I/O submission queue..."); nvme_sqe_t create_io_sq = { .opc = 0x01, .prp1 = (uint64_t)VIRT_TO_PHYS(io_sq), @@ -241,7 +218,7 @@ void nvme_init() { }; admin_exec_sync(&create_io_sq); - LOG_LN("Done."); + TRACE_LN("Done."); } void nvme_read_sectors(uint32_t index, uint8_t count, void *to) { diff --git a/src/kernel/pci.c b/src/kernel/pci.c index 2343e49..59e6cd9 100644 --- a/src/kernel/pci.c +++ b/src/kernel/pci.c @@ -1,55 +1,111 @@ #include "src/kernel/pci.h" #include "src/kernel/log.h" -#include "src/kernel/panic.h" +#include "src/kernel/memory.h" #include "src/kernel/util.h" +#include "src/lib/layout.h" +#include "src/lib/util.h" + +#define PCI_MAX_BUSES 256 +#define PCI_MAX_DEVICES 32 +#define PCI_MAX_FUNCTIONS 8 #define PCI_PORT_ADDRESS 0xCF8 #define PCI_PORT_DATA 0xCFC -void pci_init() { - LOG_LN("Scanning devices..."); - for (uint16_t bus = 0; bus < PCI_MAX_BUSES; bus++) { - for (uint8_t device = 0; device < PCI_MAX_DEVICES; device++) { - for (uint8_t function = 0; function < PCI_MAX_FUNCTIONS; function++) { - uint32_t id = pci_read((uint8_t)bus, device, function, PCI_OFFSET_DEVICE_VENDOR); - if (PCI_DEVICE(id) == PCI_DEVICE_EMPTY) { - continue; - } - uint32_t class = pci_read((uint8_t)bus, device, function, PCI_OFFSET_CLASS_REVISION); - LOG_LN("%hx:%hx class=%hhx subclass=%hhx, iface=%hhx.", PCI_VENDOR(id), PCI_DEVICE(id), PCI_CLASS(class), PCI_CLASS_SUB(class), - PCI_CLASS_IFACE(class)); - } - } - } - LOG_LN("Done."); -} +#define PCI_BDF(b, d, f) (pci_bdf_t)(((b) << 8) | ((d) << 3) | (f)) +#define PCI_B(bdf) (((bdf) >> 8) & 0xFF) +#define PCI_D(bdf) (((bdf) >> 3) & 0x1F) +#define PCI_F(bdf) ((bdf) & 0x07) -uint32_t pci_read(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset) { - // ASSERT(bus < PCI_MAX_BUSES, "pci_read: Malformed address."); - ASSERT(device < PCI_MAX_DEVICES, "pci_read: Malformed address."); - ASSERT(function < PCI_MAX_FUNCTIONS, "pci_read: Malformed address."); - outl(PCI_PORT_ADDRESS, - ((uint32_t)1 << 31) | ((uint32_t)bus << 16) | ((uint32_t)device << 11) | ((uint32_t)function << 8) | (offset & 0xFC)); +#define PCI_OFFSET_DEVICE_VENDOR 0x00 +#define PCI_OFFSET_STATUS_COMMAND 0x04 +#define PCI_OFFSET_CLASS_REVISION 0x08 +#define PCI_OFFSET_BAR_0 0x10 +#define PCI_OFFSET_BAR_1 0x14 + +#define PCI_VENDOR(i) BITS_R(i, 15, 0) +#define PCI_DEVICE(i) BITS_R(i, 31, 16) + +#define PCI_DEVICE_EMPTY 0xFFFF + +#define PCI_CLASS(c) BITS_R(c, 31, 24) +#define PCI_SUBCLASS(c) BITS_R(c, 23, 16) +#define PCI_INTERFACE(c) BITS_R(c, 15, 8) + +#define PCI_COMMAND_MEMORY_SPACE 0x02 +#define PCI_COMMAND_BUS_MASTER 0x04 + +static uint32_t pci_read(pci_bdf_t bdf, uint8_t offset) { + outl(PCI_PORT_ADDRESS, ((uint32_t)1 << 31) | ((uint32_t)bdf << 8) | (offset & 0xFC)); return inl(PCI_PORT_DATA); } -void pci_write(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset, uint32_t value) { - // ASSERT(bus < PCI_MAX_BUSES, "pci_write: Malformed address."); - ASSERT(device < PCI_MAX_DEVICES, "pci_write: Malformed address."); - ASSERT(function < PCI_MAX_FUNCTIONS, "pci_write: Malformed address."); - outl(PCI_PORT_ADDRESS, - ((uint32_t)1 << 31) | ((uint32_t)bus << 16) | ((uint32_t)device << 11) | ((uint32_t)function << 8) | (offset & 0xFC)); +static void pci_write(pci_bdf_t bdf, uint8_t offset, uint32_t value) { + outl(PCI_PORT_ADDRESS, ((uint32_t)1 << 31) | ((uint32_t)bdf << 8) | (offset & 0xFC)); outl(PCI_PORT_DATA, value); } -uint64_t pci_bar(uint8_t bus, uint8_t device, uint8_t function) { - // ASSERT(bus < PCI_MAX_BUSES, "pci_bar: Malformed address."); - ASSERT(device < PCI_MAX_DEVICES, "pci_bar: Malformed address."); - ASSERT(function < PCI_MAX_FUNCTIONS, "pci_bar: Malformed address."); +void pci_init() { + LOG_LN("Scanning devices..."); + pci_enumerate(kernel_log); + LOG_LN("Done."); +} - pci_write(bus, device, function, PCI_OFFSET_STATUS_COMMAND, - pci_read(bus, device, function, PCI_OFFSET_STATUS_COMMAND) | PCI_COMMAND_MEMORY_SPACE | PCI_COMMAND_BUS_MASTER); +void pci_enumerate(stream_t *out) { + for (uint16_t b = 0; b < PCI_MAX_BUSES; b++) { + for (uint8_t d = 0; d < PCI_MAX_DEVICES; d++) { + for (uint8_t f = 0; f < PCI_MAX_FUNCTIONS; f++) { + uint32_t id = pci_read(PCI_BDF(b, d, f), PCI_OFFSET_DEVICE_VENDOR); + if (PCI_DEVICE(id) == PCI_DEVICE_EMPTY) { + continue; + } + uint32_t class = pci_read(PCI_BDF(b, d, f), PCI_OFFSET_CLASS_REVISION); + PRINT_LN(out, "%hx:%hx class=%hhx subclass=%hhx iface=%hhx.", PCI_VENDOR(id), PCI_DEVICE(id), PCI_CLASS(class), + PCI_SUBCLASS(class), PCI_INTERFACE(class)); + } + } + } +} - return ((uint64_t)pci_read(bus, device, function, PCI_OFFSET_BAR_1) << 32) | - (pci_read(bus, device, function, PCI_OFFSET_BAR_0) & 0xFFFFFFF0); +pci_bdf_t pci_find_by_class(uint8_t class, uint8_t subclass, uint8_t iface) { + for (uint16_t b = 0; b < PCI_MAX_BUSES; b++) { + for (uint8_t d = 0; d < PCI_MAX_DEVICES; d++) { + for (uint8_t f = 0; PCI_MAX_FUNCTIONS; f++) { + uint32_t id = pci_read(PCI_BDF(b, d, f), PCI_OFFSET_DEVICE_VENDOR); + if (PCI_DEVICE(id) == PCI_DEVICE_EMPTY) { + continue; + } + uint32_t csi = pci_read(PCI_BDF(b, d, f), PCI_OFFSET_CLASS_REVISION); + if ((class != NUL && PCI_CLASS(csi) != class) || (subclass != NUL && PCI_SUBCLASS(csi) != subclass) || + (iface != NUL && PCI_INTERFACE(csi) != iface)) { + continue; + } + + return PCI_BDF(b, d, f); + } + } + } + return NUL; +} + +void pci_map(pci_bdf_t bdf, volatile void *virt, uint8_t pages) { + pci_write(bdf, PCI_OFFSET_STATUS_COMMAND, pci_read(bdf, PCI_OFFSET_STATUS_COMMAND) | PCI_COMMAND_MEMORY_SPACE | PCI_COMMAND_BUS_MASTER); + + uint64_t bar = ((uint64_t)pci_read(bdf, PCI_OFFSET_BAR_1) << 32) | (pci_read(bdf, PCI_OFFSET_BAR_0) & 0xFFFFFFF0); + + LOG_LN("Mapping BAR to memory, phys %lx <-> virt %lx...", bar, virt); + for (uint8_t i = 0; i < pages; i++) { + memory_page_map((void *)KERNEL_VIRTUAL_PML4, (void *)((uint8_t *)virt + i * PAGE_SIZE), (void *)((uint8_t *)bar + i * PAGE_SIZE), + PAGE_WRITABLE | PAGE_PCD | PAGE_USER); + } +} + +void pci_unmap(pci_bdf_t bdf, volatile void *virt, uint8_t pages) { + LOG_LN("Unmapping BAR from memory, virt %lx...", virt); + for (uint8_t i = 0; i < pages; i++) { + memory_page_unmap((void *)KERNEL_VIRTUAL_PML4, (void *)((uint8_t *)virt + i * PAGE_SIZE)); + } + + pci_write(bdf, PCI_OFFSET_STATUS_COMMAND, + pci_read(bdf, PCI_OFFSET_STATUS_COMMAND) & ~(uint32_t)(PCI_COMMAND_MEMORY_SPACE | PCI_COMMAND_BUS_MASTER)); } diff --git a/src/kernel/pci.h b/src/kernel/pci.h index 635fa18..5fb3dbe 100644 --- a/src/kernel/pci.h +++ b/src/kernel/pci.h @@ -1,34 +1,15 @@ #pragma once -#include -#include "src/lib/util.h" +#include "src/kernel/stream.h" -#define PCI_MAX_BUSES 256 -#define PCI_MAX_DEVICES 32 -#define PCI_MAX_FUNCTIONS 8 - -#define PCI_OFFSET_DEVICE_VENDOR 0x00 -#define PCI_OFFSET_STATUS_COMMAND 0x04 -#define PCI_OFFSET_CLASS_REVISION 0x08 -#define PCI_OFFSET_BAR_0 0x10 -#define PCI_OFFSET_BAR_1 0x14 - -#define PCI_VENDOR(i) BITS_R(i, 15, 0) -#define PCI_DEVICE(i) BITS_R(i, 31, 16) - -#define PCI_DEVICE_EMPTY 0xFFFF - -#define PCI_CLASS(c) BITS_R(c, 31, 24) -#define PCI_CLASS_SUB(c) BITS_R(c, 23, 16) -#define PCI_CLASS_IFACE(c) BITS_R(c, 15, 8) - -#define PCI_COMMAND_MEMORY_SPACE 0x02 -#define PCI_COMMAND_BUS_MASTER 0x04 +typedef uint16_t pci_bdf_t; void pci_init(); -uint32_t pci_read(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset); +void pci_enumerate(stream_t *out); -void pci_write(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset, uint32_t value); +pci_bdf_t pci_find_by_class(uint8_t class, uint8_t subclass, uint8_t iface); -uint64_t pci_bar(uint8_t bus, uint8_t device, uint8_t function); +void pci_map(pci_bdf_t bdf, volatile void *virt, uint8_t pages); + +void pci_unmap(pci_bdf_t bdf, volatile void *virt, uint8_t pages); diff --git a/src/kernel/usb.c b/src/kernel/usb.c new file mode 100644 index 0000000..6bfb4e0 --- /dev/null +++ b/src/kernel/usb.c @@ -0,0 +1,44 @@ +#include "src/kernel/usb.h" +#include "src/kernel/log.h" +#include "src/kernel/stream.h" +#include "src/kernel/xhci.h" + +typedef struct __attribute__((packed)) { + uint8_t length; + uint8_t descriptor_type; // 0x01 for device descriptor + uint16_t usb_version; // BCD, e.g. 0x0200 for USB 2.0 + uint8_t device_class; + uint8_t device_subclass; + uint8_t device_protocol; + uint8_t max_packet_size; // for ep0 + uint16_t vendor_id; + uint16_t product_id; + uint16_t device_version; + uint8_t manufacturer_str; + uint8_t product_str; + uint8_t serial_str; + uint8_t num_configurations; +} usb_device_descriptor_t; + +static usb_device_descriptor_t device_descriptor __attribute__((aligned(64))); + +void usb_init() { + LOG_LN("Enumerating devices..."); + usb_enumerate(kernel_log); + LOG_LN("Done."); +} + +void usb_enumerate(stream_t *out) { + for (uint8_t i = 0; i < xhci_port_count(); i++) { + if (!xhci_port_connected(i)) { + continue; + } + + xhci_slot_t slot = xhci_attach(i); + + xhci_control_transfer(slot, 0x0012000001000680ULL, &device_descriptor, sizeof(usb_device_descriptor_t)); + + PRINT_LN(out, "%hx:%hx class=%hx subclass=%hx protocol=%hx", device_descriptor.vendor_id, device_descriptor.product_id, + device_descriptor.device_class, device_descriptor.device_subclass, device_descriptor.device_protocol); + } +} diff --git a/src/kernel/usb.h b/src/kernel/usb.h new file mode 100644 index 0000000..be99494 --- /dev/null +++ b/src/kernel/usb.h @@ -0,0 +1,8 @@ +#pragma once + +#include "src/kernel/stream.h" +#include + +void usb_init(); + +void usb_enumerate(stream_t *out); diff --git a/src/kernel/xhci.c b/src/kernel/xhci.c new file mode 100644 index 0000000..c4501d5 --- /dev/null +++ b/src/kernel/xhci.c @@ -0,0 +1,490 @@ +#include "src/kernel/xhci.h" +#include "src/kernel/log.h" +#include "src/kernel/panic.h" +#include "src/kernel/pci.h" +#include "src/kernel/stream.h" +#include "src/lib/layout.h" +#include "src/lib/memory.h" +#include "src/lib/util.h" + +#define XHCI_PCI_CLASS 0x0C +#define XHCI_PCI_SUBCLASS 0x03 +#define XHCI_PCI_INTERFACE 0x30 + +typedef volatile struct __attribute__((packed)) { + uint8_t caplength; + uint8_t reserved; + uint16_t hciversion; + uint32_t hcsparams1; + uint32_t hcsparams2; + uint32_t hcsparams3; + uint32_t hccparams1; + uint32_t dboff; + uint32_t rtsoff; + uint32_t hccparams2; +} xhci_cap_regs_t; + +#define XHCI_CAP_REGS_MAX_SLOTS(c) BITS_R((c)->hcsparams1, 7, 0) +#define XHCI_CAP_REGS_MAX_INTRS(c) BITS_R((c)->hcsparams1, 18, 8) +#define XHCI_CAP_REGS_MAX_PORTS(c) BITS_R((c)->hcsparams1, 31, 24) +#define XHCI_CAP_REGS_MAX_ERSTS(c) BITS_R((c)->hcsparams2, 7, 4) + +typedef volatile struct __attribute__((packed)) { + uint32_t usbcmd; + uint32_t usbsts; + uint32_t pagesize; + uint32_t reserved[2]; + uint32_t dnctrl; + uint64_t crcr; + uint32_t reserved2[4]; + uint64_t dcbaap; + uint32_t config; +} xhci_op_regs_t; + +#define XHCI_OP_REGS_RS_R(o) BITS_R((o)->usbcmd, 0, 0) +#define XHCI_OP_REGS_RS_W(o, v) BITS_W((o)->usbcmd, 0, 0, v) +#define XHCI_OP_REGS_HCRST_R(o) BITS_R((o)->usbcmd, 1, 1) +#define XHCI_OP_REGS_HCRST_W(o, v) BITS_W((o)->usbcmd, 1, 1, v) +#define XHCI_OP_REGS_HCH(o) BITS_R((o)->usbsts, 0, 0) +#define XHCI_OP_REGS_HSE(o) BITS_R((o)->usbsts, 2, 2) +#define XHCI_OP_REGS_EINT_R(o) BITS_R((o)->usbsts, 3, 3) +#define XHCI_OP_REGS_EINT_W(o, v) BITS_W((o)->usbsts, 3, 3, v) +#define XHCI_OP_REGS_CNR(o) BITS_R((o)->usbsts, 11, 11) +#define XHCI_OP_REGS_HCE(o) BITS_R((o)->usbsts, 12, 12) +#define XHCI_OP_REGS_RCS(o) BITS_R((o)->crcr, 0, 0) +#define XHCI_OP_REGS_CRR(o) BITS_R((o)->crcr, 3, 3) +#define XHCI_OP_REGS_CRP(o) BITS_R((o)->crcr, 63, 6) +#define XHCI_OP_REGS_MAX_SLOTS_EN_R(o) BITS_R((o)->config, 7, 0) +#define XHCI_OP_REGS_MAX_SLOTS_EN_W(o, v) BITS_W((o)->config, 7, 0, v) + +typedef volatile struct __attribute__((packed)) { + uint32_t portsc; + uint32_t portpmsc; + uint32_t portli; + uint32_t porthlpmc; +} xhci_port_regs_t; + +#define XHCI_PORT_REGS_CCS(p) BITS_R((p)->portsc, 0, 0) +#define XHCI_PORT_REGS_PED(p) BITS_R((p)->portsc, 1, 1) +#define XHCI_PORT_REGS_PR_R(p) BITS_R((p)->portsc, 4, 4) +#define XHCI_PORT_REGS_PR_W(p, v) BITS_W((p)->portsc, 4, 4, v) +#define XHCI_PORT_REGS_PP(p) BITS_R((p)->portsc, 9, 9) +#define XHCI_PORT_REGS_PS(p) BITS_R((p)->portsc, 13, 10) + +typedef volatile struct __attribute__((packed)) { + uint32_t iman; + uint32_t imod; + uint32_t erstsz; + uint32_t reserved; + uint64_t erstba; + uint64_t erdp; +} xhci_intr_regs_t; + +static xhci_cap_regs_t *cap_regs = (xhci_cap_regs_t *)KERNEL_VIRTUAL_XHCI; +static xhci_op_regs_t *op_regs; +static xhci_port_regs_t *port_regs; +static xhci_intr_regs_t *intr_regs; + +typedef volatile uint32_t xhci_db_regs; + +#define XHCI_DB_REGS_TASK_ID(d) BITS_R((d), 31, 16) +#define XHCI_DB_REGS_TARGET(d) BITS_R((d), 7, 0) + +static xhci_db_regs *db_regs; + +#define MAX_SLOTS 32 + +static void *dcbaa[MAX_SLOTS + 1] __attribute__((aligned(PAGE_SIZE))); + +typedef struct __attribute__((packed)) { + uint64_t base; + uint32_t size; + uint32_t reserved; +} xchi_erst_entry_t; + +typedef struct __attribute__((packed)) { + uint64_t parameter; + uint32_t status; + uint32_t control; +} xhci_trb_t; + +#define XHCI_TRB_CYCLE_R(t) BITS_R((t)->control, 0, 0) +#define XHCI_TRB_CYCLE_W(t, v) BITS_W((t)->control, 0, 0, v) +#define XHCI_TRB_TOGGLE_CYCLE_R(t) BITS_R((t)->control, 1, 1) +#define XHCI_TRB_TOGGLE_CYCLE_W(t, v) BITS_W((t)->control, 1, 1, v) +#define XHCI_TRB_TRB_TYPE_R(t) BITS_R((t)->control, 15, 10) +#define XHCI_TRB_TRB_TYPE_W(t, v) BITS_W((t)->control, 15, 10, v) +#define XHCI_TRB_SLOT_TYPE_R(t) BITS_R((t)->control, 20, 16) +#define XHCI_TRB_SLOT_TYPE_W(t, v) BITS_W((t)->control, 20, 16, v) +#define XHCI_TRB_COMPLETION_CODE(t) BITS_R((t)->status, 31, 24) +#define XHCI_TRB_SLOT_ID_R(t) BITS_R((t)->control, 31, 24) +#define XHCI_TRB_SLOT_ID_W(t, v) BITS_W((t)->control, 31, 24, v) + +#define XHCI_TRB_TYPE_TRANSFER_SETUP_STAGE 2 +#define XHCI_TRB_TYPE_TRANSFER_DATA_STAGE 3 +#define XHCI_TRB_TYPE_TRANSFER_STATUS_STAGE 4 +#define XHCI_TRB_TYPE_LINK 6 +#define XHCI_TRB_TYPE_COMMAND_ENABLE_SLOT 9 +#define XHCI_TRB_TYPE_COMMAND_DISABLE_SLOT 10 +#define XHCI_TRB_TYPE_COMMAND_ADDRESS_DEVICE 11 +#define XHCI_TRB_TYPE_COMMAND_NOOP 23 +#define XHCI_TRB_TYPE_EVENT_TRANSFER_COMPLETION 32 +#define XHCI_TRB_TYPE_EVENT_COMMAND_COMPLETION 33 +#define XHCI_TRB_TYPE_EVENT_PORT_STATUS_CHANGE 34 + +static xchi_erst_entry_t erst[1] __attribute__((aligned(PAGE_SIZE))); + +#define RING_LENGTH 32 + +static uint8_t cr_cycle = 1; +static uint8_t cr_i = 0; +static xhci_trb_t cr[RING_LENGTH + 1] __attribute__((aligned(PAGE_SIZE))); + +static uint8_t er_cycle = 1; +static uint8_t er_i = 0; +static xhci_trb_t er[RING_LENGTH] __attribute__((aligned(PAGE_SIZE))); + +typedef struct __attribute__((packed)) { + uint32_t drop_flags; + uint32_t add_flags; + uint32_t reserved[6]; +} xhci_input_control_ctx_t; + +typedef struct __attribute__((packed)) { + uint32_t dw0; + uint32_t dw1; + uint32_t dw2; + uint32_t dw3; + uint32_t reserved[4]; +} xhci_slot_ctx_t; + +#define XHCI_SLOT_CTX_SPEED_R(s) BITS_R((s)->dw0, 23, 20) +#define XHCI_SLOT_CTX_SPEED_W(s, v) BITS_W((s)->dw0, 23, 20, v) +#define XHCI_SLOT_CTX_CTX_ENTRIES_R(s) BITS_R((s)->dw0, 31, 27) +#define XHCI_SLOT_CTX_CTX_ENTRIES_W(s, v) BITS_W((s)->dw0, 31, 27, v) +#define XHCI_SLOT_CTX_ROOT_HUB_PORT_R(s) BITS_R((s)->dw1, 23, 16) +#define XHCI_SLOT_CTX_ROOT_HUB_PORT_W(s, v) BITS_W((s)->dw1, 23, 16, v) + +typedef struct __attribute__((packed)) { + uint32_t dw0; + uint32_t dw1; + uint64_t dequeue; + uint32_t dw4; + uint32_t reserved[3]; +} xhci_ep_ctx_t; + +#define USB_EP_CTX_EP_TYPE_R(e) BITS_R((e)->dw1, 5, 3) +#define USB_EP_CTX_EP_TYPE_W(e, v) BITS_W((e)->dw1, 5, 3, v) +#define USB_EP_CTX_MAX_PACKET_SIZE_R(e) BITS_R((e)->dw1, 31, 16) +#define USB_EP_CTX_MAX_PACKET_SIZE_W(e, v) BITS_W((e)->dw1, 31, 16, v) + +typedef struct __attribute__((packed)) { + xhci_input_control_ctx_t control; + xhci_slot_ctx_t slot; + xhci_ep_ctx_t ep[31]; +} xhci_input_ctx_t; + +static xhci_input_ctx_t input_ctx __attribute__((aligned(64))); + +typedef struct __attribute__((packed)) { + xhci_slot_ctx_t slot; + xhci_ep_ctx_t endpoints[31]; +} xchi_device_ctx_t; + +static xchi_device_ctx_t device_ctx __attribute__((aligned(64))); + +static uint8_t tr_attached_slot = 0; +static uint8_t tr_cycle = 1; +static uint8_t tr_i = 0; +static xhci_trb_t tr[RING_LENGTH + 1] __attribute__((aligned(PAGE_SIZE))); + +static const xhci_trb_t *command_exec_sync(const xhci_trb_t *cmd) { + LOG_LN("Queueing command TRB..."); + LOG_VAL_F(cmd->parameter, "%lx"); + LOG_VAL_F(cmd->status, "%x"); + LOG_VAL_F(cmd->control, "%x"); + + xhci_trb_t *cre = &cr[cr_i]; + + memory_copy(cmd, sizeof(xhci_trb_t), cre); + XHCI_TRB_CYCLE_W(cre, cr_cycle); + + cr_i = (cr_i + 1) % RING_LENGTH; + XHCI_TRB_CYCLE_W((&cr[RING_LENGTH]), cr_cycle); + cr_cycle ^= (cr_i == 0); + + LOG_LN("Ringing command doorbell..."); + LOG_VAL_F(op_regs->usbsts, "%x"); + db_regs[0] = 0; + LOG_VAL_F(op_regs->usbsts, "%x"); + + LOG_LN("Waiting for completion..."); + while (1) { + xhci_trb_t *cmp = &er[er_i]; + while (XHCI_TRB_CYCLE_R(cmp) != er_cycle) + ; + + er_i = (er_i + 1) % RING_LENGTH; + intr_regs->erdp = (uint64_t)(VIRT_TO_PHYS(&er[er_i])); + er_cycle ^= (er_i == 0); + + if (XHCI_TRB_TRB_TYPE_R(cmp) != XHCI_TRB_TYPE_EVENT_COMMAND_COMPLETION || (void *)cmp->parameter != VIRT_TO_PHYS(cre)) { + LOG_LN("Received irrelevant event, skipping..."); + } else { + LOG_LN("Received command completion event..."); + + LOG_VAL_F(cmp->parameter, "%lx"); + LOG_VAL_F(cmp->status, "%x"); + LOG_VAL_F(cmp->control, "%x"); + + ASSERT(XHCI_TRB_COMPLETION_CODE(cmp) == 1, "command_exec_sync: Command failed."); + + return cmp; + } + } +} + +void xhci_init() { + TRACE_LN("Searching for suitable controller..."); + pci_bdf_t bdf = pci_find_by_class(XHCI_PCI_CLASS, XHCI_PCI_SUBCLASS, XHCI_PCI_INTERFACE); + ASSERT(bdf, "xhci_init: No suitable controllers found."); + + TRACE_LN("Mapping controller to virtual memory..."); + pci_map(bdf, cap_regs, 4); + op_regs = (xhci_op_regs_t *)((uint8_t *)cap_regs + cap_regs->caplength); + port_regs = (xhci_port_regs_t *)((uint8_t *)cap_regs + cap_regs->caplength + 0x400); + intr_regs = (xhci_intr_regs_t *)((uint8_t *)cap_regs + cap_regs->rtsoff + 0x20); + db_regs = (uint32_t *)((uint8_t *)cap_regs + cap_regs->dboff); + + TRACE_VAL_F(cap_regs->caplength, "%hhx"); + TRACE_VAL_F(cap_regs->hciversion, "%hx"); + TRACE_VAL_F(cap_regs->hcsparams1, "%x"); + TRACE_VAL_F(cap_regs->hcsparams2, "%x"); + TRACE_VAL_F(cap_regs->hcsparams3, "%x"); + TRACE_VAL_F(cap_regs->hccparams1, "%x"); + TRACE_VAL_F(cap_regs->dboff, "%x"); + TRACE_VAL_F(cap_regs->rtsoff, "%x"); + TRACE_VAL_F(cap_regs->hccparams2, "%x"); + + TRACE_VAL_F(op_regs->usbcmd, "%x"); + TRACE_VAL_F(op_regs->usbsts, "%x"); + TRACE_VAL_F(op_regs->pagesize, "%x"); + TRACE_VAL_F(op_regs->dnctrl, "%x"); + TRACE_VAL_F(op_regs->crcr, "%lx"); + TRACE_VAL_F(op_regs->dcbaap, "%lx"); + TRACE_VAL_F(op_regs->config, "%x"); + + TRACE_LN("Stopping controller..."); + XHCI_OP_REGS_RS_W(op_regs, 0); + TRACE_VAL_F(op_regs->usbcmd, "%x"); + TRACE_VAL_F(op_regs->usbsts, "%x"); + while (!XHCI_OP_REGS_HCH(op_regs)) + ; + TRACE_VAL_F(op_regs->usbcmd, "%x"); + TRACE_VAL_F(op_regs->usbsts, "%x"); + + TRACE_LN("Resetting controller..."); + XHCI_OP_REGS_EINT_W(op_regs, 0); + XHCI_OP_REGS_HCRST_W(op_regs, 1); + TRACE_VAL_F(op_regs->usbcmd, "%x"); + TRACE_VAL_F(op_regs->usbsts, "%x"); + while (XHCI_OP_REGS_HCRST_R(op_regs)) + ; + TRACE_VAL_F(op_regs->usbcmd, "%x"); + TRACE_VAL_F(op_regs->usbsts, "%x"); + while (XHCI_OP_REGS_CNR(op_regs)) + ; + TRACE_VAL_F(op_regs->usbcmd, "%x"); + TRACE_VAL_F(op_regs->usbsts, "%x"); + + TRACE_LN("Setting up command ring..."); + xhci_trb_t *wrapper = &cr[RING_LENGTH]; + wrapper->parameter = (uint64_t)VIRT_TO_PHYS(cr); + wrapper->status = 0; + XHCI_TRB_TRB_TYPE_W(wrapper, XHCI_TRB_TYPE_LINK); + XHCI_TRB_TOGGLE_CYCLE_W(wrapper, 1); + XHCI_TRB_CYCLE_W(wrapper, cr_cycle); + + TRACE_LN("Configuring controller..."); + XHCI_OP_REGS_MAX_SLOTS_EN_W(op_regs, MAX_SLOTS); + op_regs->dcbaap = (uint64_t)VIRT_TO_PHYS(dcbaa); + op_regs->crcr = (uint64_t)VIRT_TO_PHYS(cr) | cr_cycle; + erst[0].size = RING_LENGTH; + erst[0].base = (uint64_t)VIRT_TO_PHYS(er); + intr_regs->erstsz = sizeof(erst) / sizeof(xchi_erst_entry_t); + intr_regs->erstba = (uint64_t)VIRT_TO_PHYS(erst); + intr_regs->erdp = erst[0].base; + + TRACE_VAL_F(op_regs->config, "%x"); + TRACE_VAL_F(op_regs->dcbaap, "%lx"); + TRACE_VAL_F(op_regs->crcr, "%lx"); + TRACE_VAL_F(intr_regs->erstba, "%lx"); + TRACE_VAL_F(intr_regs->erstsz, "%x"); + TRACE_VAL_F(intr_regs->erdp, "%lx"); + TRACE_VAL_F(erst[0].size, "%hx"); + TRACE_VAL_F(erst[0].base, "%lx"); + + TRACE_LN("Starting controller..."); + TRACE_VAL_F(op_regs->usbcmd, "%x"); + TRACE_VAL_F(op_regs->usbsts, "%x"); + XHCI_OP_REGS_RS_W(op_regs, 1); + TRACE_VAL_F(op_regs->usbcmd, "%x"); + TRACE_VAL_F(op_regs->usbsts, "%x"); + while (XHCI_OP_REGS_HCH(op_regs)) + ; + TRACE_VAL_F(op_regs->usbcmd, "%x"); + TRACE_VAL_F(op_regs->usbsts, "%x"); + + LOG_LN("Enumerating ports..."); + xhci_enumerate(kernel_log); + + LOG_LN("Done."); +} + +void xhci_enumerate(stream_t *out) { + for (uint8_t i = 0; i < XHCI_CAP_REGS_MAX_PORTS(cap_regs); i++) { + PRINT_LN(out, "port=%d CCS=%d PED=%d PP=%d PS=%d", i, XHCI_PORT_REGS_CCS(&port_regs[i]), XHCI_PORT_REGS_PED(&port_regs[i]), + XHCI_PORT_REGS_PP(&port_regs[i]), XHCI_PORT_REGS_PS(&port_regs[i])); + } +} + +uint8_t xhci_port_count() { + return XHCI_CAP_REGS_MAX_PORTS(cap_regs); +} + +uint8_t xhci_port_connected(uint8_t port) { + return XHCI_PORT_REGS_CCS(&port_regs[port]); +} + +xhci_slot_t xhci_attach(uint8_t port) { + ASSERT(!tr_attached_slot, "xhci_attach: Can only attach one port at a time.") + ASSERT(XHCI_PORT_REGS_CCS(&port_regs[port]), "xhci_attach: Port is not connected."); + + memory_set(0, sizeof(tr), tr); + tr_i = 0; + tr_cycle = 1; + + LOG_LN("Resetting device..."); + port_regs[port].portsc = 1 << 4; + while (XHCI_PORT_REGS_PR_R(&port_regs[port])) + ; + while (!XHCI_PORT_REGS_PED(&port_regs[port])) + ; + TRACE_VAL_F(XHCI_PORT_REGS_PED(&port_regs[port]), "%hhx"); + + LOG_LN("Enabling a slot..."); + xhci_trb_t cmd_es; + memory_set(0, sizeof(xhci_trb_t), &cmd_es); + XHCI_TRB_TRB_TYPE_W(&cmd_es, XHCI_TRB_TYPE_COMMAND_ENABLE_SLOT); + xhci_slot_t slot = XHCI_TRB_SLOT_ID_R(command_exec_sync(&cmd_es)); + + LOG_LN("Addressing device..."); + memory_set(0, sizeof(input_ctx), &input_ctx); + input_ctx.control.add_flags = 0b11; + XHCI_SLOT_CTX_CTX_ENTRIES_W(&input_ctx.slot, 1); + XHCI_SLOT_CTX_ROOT_HUB_PORT_W(&input_ctx.slot, port + 1); + XHCI_SLOT_CTX_SPEED_W(&input_ctx.slot, XHCI_PORT_REGS_PS((&port_regs[port]))); + USB_EP_CTX_EP_TYPE_W(&input_ctx.ep[0], 4); + USB_EP_CTX_MAX_PACKET_SIZE_W(&input_ctx.ep[0], 64); + input_ctx.ep[0].dequeue = (uint64_t)VIRT_TO_PHYS(tr) | tr_cycle; + + dcbaa[slot] = VIRT_TO_PHYS(&device_ctx); + + xhci_trb_t cmd_ad; + memory_set(0, sizeof(xhci_trb_t), &cmd_ad); + cmd_ad.parameter = (uint64_t)VIRT_TO_PHYS(&input_ctx); + XHCI_TRB_TRB_TYPE_W(&cmd_ad, XHCI_TRB_TYPE_COMMAND_ADDRESS_DEVICE); + XHCI_TRB_SLOT_ID_W(&cmd_ad, slot); + command_exec_sync(&cmd_ad); + + tr_attached_slot = slot; + + return slot; +} + +uint64_t xhci_control_transfer(xhci_slot_t slot, uint64_t setup, void *data, uint16_t length) { + ASSERT(tr_attached_slot == slot, "xhci_control_transfer: Can only attach one port at a time."); + + LOG_LN("Queueing transfer TRBs..."); + + tr[tr_i].parameter = setup; + tr[tr_i].status = sizeof(setup); + XHCI_TRB_TRB_TYPE_W(&tr[tr_i], XHCI_TRB_TYPE_TRANSFER_SETUP_STAGE); + BITS_W(tr[tr_i].control, 17, 16, 3); + BITS_W(tr[tr_i].control, 6, 6, 1); + XHCI_TRB_CYCLE_W(&tr[tr_i], tr_cycle); + + LOG_VAL_F(tr[tr_i].parameter, "%lx"); + LOG_VAL_F(tr[tr_i].status, "%x"); + LOG_VAL_F(tr[tr_i].control, "%x"); + + tr_i = (tr_i + 1) % RING_LENGTH; + XHCI_TRB_CYCLE_W(&tr[RING_LENGTH], tr_cycle); + tr_cycle ^= (tr_i == 0); + + tr[tr_i].parameter = (uint64_t)VIRT_TO_PHYS(data); + tr[tr_i].status = length; + XHCI_TRB_TRB_TYPE_W(&tr[tr_i], XHCI_TRB_TYPE_TRANSFER_DATA_STAGE); + BITS_W(tr[tr_i].control, 16, 16, 1); + XHCI_TRB_CYCLE_W(&tr[tr_i], tr_cycle); + + LOG_VAL_F(tr[tr_i].parameter, "%lx"); + LOG_VAL_F(tr[tr_i].status, "%x"); + LOG_VAL_F(tr[tr_i].control, "%x"); + + tr_i = (tr_i + 1) % RING_LENGTH; + XHCI_TRB_CYCLE_W(&tr[RING_LENGTH], tr_cycle); + tr_cycle ^= (tr_i == 0); + + xhci_trb_t *tre = &tr[tr_i]; + + XHCI_TRB_TRB_TYPE_W(&tr[tr_i], XHCI_TRB_TYPE_TRANSFER_STATUS_STAGE); + BITS_W(tr[tr_i].control, 16, 16, 0); + BITS_W(tr[tr_i].control, 5, 5, 1); + XHCI_TRB_CYCLE_W(&tr[tr_i], tr_cycle); + + tr_i = (tr_i + 1) % RING_LENGTH; + XHCI_TRB_CYCLE_W(&tr[RING_LENGTH], tr_cycle); + tr_cycle ^= (tr_i == 0); + + LOG_LN("Ringing slot doorbell..."); + db_regs[tr_attached_slot] = 1; + + LOG_LN("Waiting for completion..."); + while (1) { + xhci_trb_t *cmp = &er[er_i]; + while (XHCI_TRB_CYCLE_R(cmp) != er_cycle) + ; + + er_i = (er_i + 1) % RING_LENGTH; + intr_regs->erdp = (uint64_t)(VIRT_TO_PHYS(&er[er_i])); + er_cycle ^= (er_i == 0); + + if (XHCI_TRB_TRB_TYPE_R(cmp) != XHCI_TRB_TYPE_EVENT_TRANSFER_COMPLETION || (void *)cmp->parameter != VIRT_TO_PHYS(tre)) { + LOG_LN("Received irrelevant event, skipping..."); + } else { + LOG_LN("Received transfer completion event..."); + + LOG_VAL_F(cmp->parameter, "%lx"); + LOG_VAL_F(cmp->status, "%x"); + LOG_VAL_F(cmp->control, "%x"); + + ASSERT(XHCI_TRB_COMPLETION_CODE(cmp) == 1, "xhci_control_transfer: Transfer failed."); + + return length - (cmp->status & 0xFFFFFF); + } + } +} + +void xhci_detach(xhci_slot_t slot) { + ASSERT(tr_attached_slot == slot, "xhci_detach: Slot not attached."); + + xhci_trb_t cmd_ds; + memory_set(0, sizeof(xhci_trb_t), &cmd_ds); + XHCI_TRB_TRB_TYPE_W(&cmd_ds, XHCI_TRB_TYPE_COMMAND_DISABLE_SLOT); + XHCI_TRB_SLOT_ID_W(&cmd_ds, slot); + command_exec_sync(&cmd_ds); + + dcbaa[slot] = 0; + tr_attached_slot = 0; +} diff --git a/src/kernel/xhci.h b/src/kernel/xhci.h new file mode 100644 index 0000000..7a9f805 --- /dev/null +++ b/src/kernel/xhci.h @@ -0,0 +1,20 @@ +#pragma once + +#include "src/kernel/stream.h" +#include + +typedef uint8_t xhci_slot_t; + +void xhci_init(); // Assuming one and only one xHCI controller. + +void xhci_enumerate(stream_t *out); + +uint8_t xhci_port_count(); + +uint8_t xhci_port_connected(uint8_t port); + +xhci_slot_t xhci_attach(uint8_t port); + +uint64_t xhci_control_transfer(xhci_slot_t slot, uint64_t setup, void *data, uint16_t length); + +void xhci_detach(xhci_slot_t slot); diff --git a/src/lib/layout.h b/src/lib/layout.h index 0f8b2c5..09ef9ef 100644 --- a/src/lib/layout.h +++ b/src/lib/layout.h @@ -11,12 +11,17 @@ #define PHYS_TO_VIRT(phys) ((void *)((uint64_t)(phys) + KERNEL_VIRTUAL_BASE)) #define VIRT_TO_PHYS(virt) ((void *)((uint64_t)(virt) - KERNEL_VIRTUAL_BASE)) +#define NVME_PAGE_COUNT 4 +#define USB_PAGE_COUNT 4 + #define KERNEL_VIRTUAL_PML4 (KERNEL_VIRTUAL_BASE + 0x10000) #define KERNEL_VIRTUAL_CODE (KERNEL_VIRTUAL_PML4 + 0x10000) #define KERNEL_VIRTUAL_HEAP (KERNEL_VIRTUAL_CODE + 0x100000) #define KERNEL_VIRTUAL_UNUSED (KERNEL_VIRTUAL_HEAP + HEAP_SIZE) #define KERNEL_VIRTUAL_STACK (KERNEL_VIRTUAL_BASE + 0xF00000 - PAGE_SIZE) #define KERNEL_VIRTUAL_STACK_TOP (KERNEL_VIRTUAL_STACK + PAGE_SIZE) +#define KERNEL_VIRTUAL_NVME (KERNEL_VIRTUAL_BASE + MEMORY_SIZE) +#define KERNEL_VIRTUAL_XHCI (KERNEL_VIRTUAL_NVME + NVME_PAGE_COUNT * PAGE_SIZE) #define USER_VIRTUAL_BASE 0x0000000000000000ULL #define USER_VIRTUAL_CODE (USER_VIRTUAL_BASE + 0x400000) diff --git a/vm.sh b/vm.sh index 4d823eb..e9d0b6c 100755 --- a/vm.sh +++ b/vm.sh @@ -11,5 +11,8 @@ qemu-system-x86_64 \ -monitor stdio \ -drive file=build/os.img,format=raw,if=none,id=nvme0 \ -device nvme,drive=nvme0,serial=foo \ + -device qemu-xhci,id=xhci,msi=off,msix=off \ + -device usb-kbd,bus=xhci.0 \ -no-reboot \ + -trace "*xhci*" \ -d int