Add basic xHCI driver and USB devices enumeration
This commit is contained in:
@@ -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(
|
||||
|
||||
@@ -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;
|
||||
|
||||
+19
-15
@@ -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);
|
||||
|
||||
+15
-38
@@ -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) {
|
||||
|
||||
+94
-38
@@ -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));
|
||||
}
|
||||
|
||||
+7
-26
@@ -1,34 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "src/kernel/stream.h"
|
||||
#include <stdint.h>
|
||||
|
||||
void usb_init();
|
||||
|
||||
void usb_enumerate(stream_t *out);
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include "src/kernel/stream.h"
|
||||
#include <stdint.h>
|
||||
|
||||
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);
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user