Compare commits

...
5 Commits
Author SHA1 Message Date
freywar 532d8e757b Fix reading past array end in FAT16 driver 2026-08-25 22:53:53 +03:00
freywar b5215d5327 Fix xHCI endpoint ring loopback issue 2026-08-25 22:42:46 +03:00
freywar dccb93fb82 Add the most important part of README 2026-08-25 21:44:01 +03:00
freywar b78ef69da8 Fix minor issues in user apps
Uninitialized variables, reading past array end, etc.
2026-08-25 21:41:25 +03:00
freywar d72169473f Finalize xHCI and USB modules
Scratchpad buffers allocation added to xHCI. Missing initialization steps
for keyboards - set configuration and set idle - added to USB. (Even
though they are specific to HID protocol, they go through USB command
endpoint, and with current module contents it's cleaner to have them
in USB module.)
2026-08-25 21:20:53 +03:00
10 changed files with 69 additions and 23 deletions
+1
View File
@@ -0,0 +1 @@
![It works!](./WATCHME.png)
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 949 KiB

+1 -1
View File
@@ -357,7 +357,7 @@ fs_node_t *fat16_open_at(const fs_node_t *directory, uint16_t index, uint64_t fl
ei++; ei++;
} }
fs_node_t *result = open_entry(dir_cluster, entries, ei); fs_node_t *result = ei < entries_count ? open_entry(dir_cluster, entries, ei) : NUL;
memory_free(entries); memory_free(entries);
+1
View File
@@ -170,6 +170,7 @@ static uint64_t stream_read(__attribute__((unused)) stream_t *self, uint64_t max
} }
buffer_length -= size; buffer_length -= size;
buffer_offset = (buffer_offset + size) % BUFFER_SIZE; buffer_offset = (buffer_offset + size) % BUFFER_SIZE;
return size; return size;
} }
+18 -8
View File
@@ -35,6 +35,8 @@ typedef struct __attribute__((packed)) {
#define USB_SETUP_RECIPIENT_OTHER 3 #define USB_SETUP_RECIPIENT_OTHER 3
#define USB_SETUP_GET_DESCRIPTOR 0x06 #define USB_SETUP_GET_DESCRIPTOR 0x06
#define USB_SETUP_SET_CONFIGURATION 0x09
#define USB_SETUP_SET_IDLE 0x0A
#define USB_SETUP_SET_PROTOCOL 0x0B #define USB_SETUP_SET_PROTOCOL 0x0B
#define USB_SETUP_DESCRIPTOR_TYPE_R(s) BITS_R((s)->w_value, 15, 8) #define USB_SETUP_DESCRIPTOR_TYPE_R(s) BITS_R((s)->w_value, 15, 8)
@@ -52,11 +54,6 @@ typedef struct __attribute__((packed)) {
#define USB_DESCRIPTOR_TYPE_CONFIGURATION 0x02 #define USB_DESCRIPTOR_TYPE_CONFIGURATION 0x02
#define USB_DESCRIPTOR_TYPE_INTERFACE 0x04 #define USB_DESCRIPTOR_TYPE_INTERFACE 0x04
#define USB_DESCRIPTOR_TYPE_ENDPOINT 0x05 #define USB_DESCRIPTOR_TYPE_ENDPOINT 0x05
#define USB_DESCRIPTOR_TYPE_HID 0x21
#define USB_INTERFACE_HID_KEYBOARD_CLASS 0x3
#define USB_INTERFACE_HID_KEYBOARD_SUBCLASS 0x1
#define USB_INTERFACE_HID_KEYBOARD_PROTOCOL 0x1
typedef struct __attribute__((packed)) { typedef struct __attribute__((packed)) {
usb_descriptor_base_t base; usb_descriptor_base_t base;
@@ -176,7 +173,6 @@ void usb_enumerate(stream_t *out) {
} }
xhci_slot_t slot = xhci_attach(port); xhci_slot_t slot = xhci_attach(port);
if (slot == (xhci_slot_t)-1) { if (slot == (xhci_slot_t)-1) {
continue; continue;
} }
@@ -206,7 +202,6 @@ xhci_port_t usb_find_by_interface(uint8_t class, uint8_t subclass, uint8_t proto
} }
xhci_slot_t slot = xhci_attach(port); xhci_slot_t slot = xhci_attach(port);
if (slot == (xhci_slot_t)-1) { if (slot == (xhci_slot_t)-1) {
continue; continue;
} }
@@ -239,6 +234,14 @@ xhci_endpoint_t usb_open_endpoint(xhci_slot_t slot, uint8_t type, uint16_t max_p
scan_device(slot, &device, &configuration, &interface, &endpoint); scan_device(slot, &device, &configuration, &interface, &endpoint);
usb_setup_t setup; usb_setup_t setup;
memory_set(0, sizeof(setup), &setup);
USB_SETUP_DIRECTION_W(&setup, USB_SETUP_DIRECTION_H2D);
USB_SETUP_TYPE_W(&setup, USB_SETUP_TYPE_STANDARD);
USB_SETUP_RECIPIENT_W(&setup, USB_SETUP_RECIPIENT_DEVICE);
setup.b_request = USB_SETUP_SET_CONFIGURATION;
setup.w_value = configuration.configuration_value;
xhci_control_transfer(slot, *(uint64_t *)&setup, NUL, 0);
memory_set(0, sizeof(setup), &setup); memory_set(0, sizeof(setup), &setup);
USB_SETUP_DIRECTION_W(&setup, USB_SETUP_DIRECTION_H2D); USB_SETUP_DIRECTION_W(&setup, USB_SETUP_DIRECTION_H2D);
USB_SETUP_TYPE_W(&setup, USB_SETUP_TYPE_CLASS); USB_SETUP_TYPE_W(&setup, USB_SETUP_TYPE_CLASS);
@@ -246,9 +249,16 @@ xhci_endpoint_t usb_open_endpoint(xhci_slot_t slot, uint8_t type, uint16_t max_p
setup.b_request = USB_SETUP_SET_PROTOCOL; setup.b_request = USB_SETUP_SET_PROTOCOL;
xhci_control_transfer(slot, *(uint64_t *)&setup, NUL, 0); xhci_control_transfer(slot, *(uint64_t *)&setup, NUL, 0);
memory_set(0, sizeof(setup), &setup);
USB_SETUP_DIRECTION_W(&setup, USB_SETUP_DIRECTION_H2D);
USB_SETUP_TYPE_W(&setup, USB_SETUP_TYPE_CLASS);
USB_SETUP_RECIPIENT_W(&setup, USB_SETUP_RECIPIENT_INTERFACE);
setup.b_request = USB_SETUP_SET_IDLE;
xhci_control_transfer(slot, *(uint64_t *)&setup, NUL, 0);
ASSERT(endpoint.max_packet_size == max_packet_size, "usb_open_endpoint: Unexpected max packet size."); ASSERT(endpoint.max_packet_size == max_packet_size, "usb_open_endpoint: Unexpected max packet size.");
return xhci_open_endpoint(slot, endpoint.endpoint_address, type, endpoint.max_packet_size, endpoint.interval); return xhci_open_endpoint(slot, endpoint.endpoint_address, type, endpoint.max_packet_size, 7);
} }
uint16_t usb_read_endpoint(xhci_slot_t slot, xhci_endpoint_t endpoint, uint16_t max, void *to) { uint16_t usb_read_endpoint(xhci_slot_t slot, xhci_endpoint_t endpoint, uint16_t max, void *to) {
+38 -8
View File
@@ -1,12 +1,12 @@
#include "src/kernel/xhci.h" #include "src/kernel/xhci.h"
#include "src/kernel/log.h" #include "src/kernel/log.h"
#include "src/kernel/memory.h"
#include "src/kernel/panic.h" #include "src/kernel/panic.h"
#include "src/kernel/pci.h" #include "src/kernel/pci.h"
#include "src/kernel/stream.h" #include "src/kernel/stream.h"
#include "src/lib/layout.h" #include "src/lib/layout.h"
#include "src/lib/memory.h" #include "src/lib/memory.h"
#include "src/lib/util.h" #include "src/lib/util.h"
#include <stdint.h>
#define XHCI_PCI_CLASS 0x0C #define XHCI_PCI_CLASS 0x0C
#define XHCI_PCI_SUBCLASS 0x03 #define XHCI_PCI_SUBCLASS 0x03
@@ -29,6 +29,7 @@ typedef volatile struct __attribute__((packed)) {
#define XHCI_CAP_REGS_MAX_INTRS(c) BITS_R((c)->hcsparams1, 18, 8) #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_PORTS(c) BITS_R((c)->hcsparams1, 31, 24)
#define XHCI_CAP_REGS_MAX_ERSTS(c) BITS_R((c)->hcsparams2, 7, 4) #define XHCI_CAP_REGS_MAX_ERSTS(c) BITS_R((c)->hcsparams2, 7, 4)
#define XHCI_CAP_REGS_MAX_SCRATCHPAD_BUFFERS(c) (BITS_R((c)->hcsparams2, 31, 27) << 5 | BITS_R((c)->hcsparams2, 4, 0))
typedef volatile struct __attribute__((packed)) { typedef volatile struct __attribute__((packed)) {
uint32_t usbcmd; uint32_t usbcmd;
@@ -96,6 +97,8 @@ static xhci_db_regs *db_regs;
#define MAX_SLOTS 32 #define MAX_SLOTS 32
static void *scratchpads[32];
static void *dcbaa[MAX_SLOTS + 1] __attribute__((aligned(PAGE_SIZE))); static void *dcbaa[MAX_SLOTS + 1] __attribute__((aligned(PAGE_SIZE)));
typedef struct __attribute__((packed)) { typedef struct __attribute__((packed)) {
@@ -408,6 +411,13 @@ void xhci_init() {
RING_WRAP(cmd); RING_WRAP(cmd);
XHCI_TRB_CYCLE_W(&cmd_r[RING_LENGTH], cmd_r_cycle); XHCI_TRB_CYCLE_W(&cmd_r[RING_LENGTH], cmd_r_cycle);
LOG_LN_INFO("Setting up scratchpad buffers...");
ASSERT(XHCI_CAP_REGS_MAX_SCRATCHPAD_BUFFERS(cap_regs) <= sizeof(scratchpads), "Too many scratchpad buffers expected.");
for (uint32_t i = 0; i < XHCI_CAP_REGS_MAX_SCRATCHPAD_BUFFERS(cap_regs); i++) {
scratchpads[i] = memory_page_allocate();
}
dcbaa[0] = VIRT_TO_PHYS(scratchpads);
LOG_LN_INFO("Configuring controller..."); LOG_LN_INFO("Configuring controller...");
XHCI_OP_REGS_MAX_SLOTS_EN_W(op_regs, MAX_SLOTS); XHCI_OP_REGS_MAX_SLOTS_EN_W(op_regs, MAX_SLOTS);
op_regs->dcbaap = (uint64_t)VIRT_TO_PHYS(dcbaa); op_regs->dcbaap = (uint64_t)VIRT_TO_PHYS(dcbaa);
@@ -475,13 +485,17 @@ xhci_slot_t xhci_attach(xhci_port_t port) {
RING_WRAP(tsf); RING_WRAP(tsf);
LOG_LN_STEP("Resetting device..."); LOG_LN_STEP("Resetting device...");
LOG_VAL_TRACE(port_regs[port].portsc, "%x");
port_regs[port].portsc = (port_regs[port].portsc & ~(uint32_t)0x00FE0000) | 0x00FE0000;
LOG_VAL_TRACE(port_regs[port].portsc, "%x");
port_regs[port].portsc = (port_regs[port].portsc & ~(uint32_t)0x00FE0000) | (1 << 4); port_regs[port].portsc = (port_regs[port].portsc & ~(uint32_t)0x00FE0000) | (1 << 4);
LOG_VAL_TRACE(port_regs[port].portsc, "%x");
while (XHCI_PORT_REGS_PR_R(&port_regs[port])) while (XHCI_PORT_REGS_PR_R(&port_regs[port]))
; ;
LOG_VAL_TRACE(&port_regs[port].portsc, "%x"); LOG_VAL_TRACE(port_regs[port].portsc, "%x");
while (!XHCI_PORT_REGS_PED(&port_regs[port]) && XHCI_PORT_REGS_PLL(&port_regs[port]) != 0) while (!XHCI_PORT_REGS_PED(&port_regs[port]) && XHCI_PORT_REGS_PLL(&port_regs[port]) != 0)
; ;
LOG_VAL_TRACE(&port_regs[port].portsc, "%x"); LOG_VAL_TRACE(port_regs[port].portsc, "%x");
LOG_LN_STEP("Enabling a slot..."); LOG_LN_STEP("Enabling a slot...");
xhci_trb_t cmd_es; xhci_trb_t cmd_es;
@@ -524,7 +538,7 @@ uint64_t xhci_control_transfer(xhci_slot_t slot, uint64_t setup, void *data, uin
} }
xhci_endpoint_t xhci_open_endpoint(xhci_slot_t slot, uint8_t address, uint8_t type, uint16_t max_packet_size, uint8_t interval) { xhci_endpoint_t xhci_open_endpoint(xhci_slot_t slot, uint8_t address, uint8_t type, uint16_t max_packet_size, uint8_t interval) {
LOG_LN_INFO("Opening endpoint slot %d address %hhx...", slot, address); LOG_LN_STEP("Opening endpoint slot %d address %hhx...", slot, address);
ASSERT(slot < MAX_SLOTS, "xhci_control_transfer: Slot does not exist."); ASSERT(slot < MAX_SLOTS, "xhci_control_transfer: Slot does not exist.");
ASSERT(tsf_r_attached_slot == slot, "xhci_open_endpoint: Slot is not attached."); ASSERT(tsf_r_attached_slot == slot, "xhci_open_endpoint: Slot is not attached.");
@@ -535,11 +549,11 @@ xhci_endpoint_t xhci_open_endpoint(xhci_slot_t slot, uint8_t address, uint8_t ty
uint8_t ep_dir = (address >> 7) & 1; uint8_t ep_dir = (address >> 7) & 1;
uint8_t dci = ep_num * 2 + ep_dir; uint8_t dci = ep_num * 2 + ep_dir;
LOG_LN_INFO("Setting up endpoint ring..."); LOG_LN_STEP("Setting up endpoint ring...");
RING_WRAP(ep); RING_WRAP(ep);
XHCI_TRB_CYCLE_W(&ep_r[RING_LENGTH], ep_r_cycle); XHCI_TRB_CYCLE_W(&ep_r[RING_LENGTH], ep_r_cycle);
LOG_LN_INFO("Configuring endpoint..."); LOG_LN_STEP("Configuring endpoint...");
memory_set(0, sizeof(xhci_input_ctx_t), &input_ctx); memory_set(0, sizeof(xhci_input_ctx_t), &input_ctx);
input_ctx.slot = device_ctx.slot; input_ctx.slot = device_ctx.slot;
input_ctx.control.add_flags = (1 << 0) | (1 << dci); input_ctx.control.add_flags = (1 << 0) | (1 << dci);
@@ -583,9 +597,10 @@ uint16_t xhci_read_endpoint(xhci_slot_t slot, xhci_endpoint_t endpoint, uint16_t
XHCI_TRB_TRB_TYPE_W(ere, XHCI_TRB_TYPE_TRANSFER_NORMAL); XHCI_TRB_TRB_TYPE_W(ere, XHCI_TRB_TYPE_TRANSFER_NORMAL);
XHCI_TRB_IOC_W(ere, 1); XHCI_TRB_IOC_W(ere, 1);
XHCI_TRB_CYCLE_W(ere, ep_r_cycle); XHCI_TRB_CYCLE_W(ere, ep_r_cycle);
RING_ADVANCE(ep);
} }
ep_r_cycle ^= 1;
db_regs[tsf_r_attached_slot] = ep_r_attached_endpoint; db_regs[tsf_r_attached_slot] = ep_r_attached_endpoint;
return 0; return 0;
@@ -593,6 +608,14 @@ uint16_t xhci_read_endpoint(xhci_slot_t slot, xhci_endpoint_t endpoint, uint16_t
uint16_t transferred = 0; uint16_t transferred = 0;
while (XHCI_TRB_CYCLE_R(&evt_r[evt_r_i]) == evt_r_cycle && !transferred) { while (XHCI_TRB_CYCLE_R(&evt_r[evt_r_i]) == evt_r_cycle && !transferred) {
if (ep_processed_events == 0) {
// A bit of a hack: flip link TRB cycle only after the first event is processed. There is some delay between controller
// posting the last success event of the batch, and consuming the link TRB. Flipping the link cycle together with the batch
// might happen before the controller got a chance to check the link cycle, and stall the consumption. If we received the
// first event of the next batch, we're certain the link TRB has just been processed, and there's plenty of time before the next loop.
XHCI_TRB_CYCLE_W(&ep_r[RING_LENGTH], !ep_r_cycle);
}
xhci_trb_t *cmp = &evt_r[evt_r_i]; xhci_trb_t *cmp = &evt_r[evt_r_i];
if (XHCI_TRB_TRB_TYPE_R(cmp) != XHCI_TRB_TYPE_EVENT_TRANSFER_COMPLETION) { if (XHCI_TRB_TRB_TYPE_R(cmp) != XHCI_TRB_TYPE_EVENT_TRANSFER_COMPLETION) {
@@ -612,10 +635,17 @@ uint16_t xhci_read_endpoint(xhci_slot_t slot, xhci_endpoint_t endpoint, uint16_t
LOG_LN_STEP("Received transfer event, returning..."); LOG_LN_STEP("Received transfer event, returning...");
ep_processed_events++; ep_processed_events++;
LOG_VAL_TRACE(cmp->parameter, "%lx");
LOG_VAL_TRACE(cmp->status, "%x");
LOG_VAL_TRACE(cmp->control, "%x");
xhci_trb_t *cmd = PHYS_TO_VIRT(cmp->parameter); xhci_trb_t *cmd = PHYS_TO_VIRT(cmp->parameter);
uint16_t size = max < cmd->status ? max : (uint16_t)cmd->status; uint16_t size = max < cmd->status ? max : (uint16_t)cmd->status;
memory_copy(PHYS_TO_VIRT(cmd->parameter), size, to); memory_copy(PHYS_TO_VIRT(cmd->parameter), size, to);
LOG_VAL_TRACE(size, "%d");
LOG_VAL_TRACE(*(uint64_t *)to, "%lx");
transferred = size; transferred = size;
} }
@@ -627,7 +657,7 @@ uint16_t xhci_read_endpoint(xhci_slot_t slot, xhci_endpoint_t endpoint, uint16_t
} }
void xhci_close_endpoint(xhci_slot_t slot, xhci_endpoint_t endpoint) { void xhci_close_endpoint(xhci_slot_t slot, xhci_endpoint_t endpoint) {
LOG_LN_INFO("Closing endpoind %hhx...", endpoint); LOG_LN_STEP("Closing endpoind %hhx...", endpoint);
ASSERT(slot < MAX_SLOTS, "xhci_control_transfer: Slot does not exist."); ASSERT(slot < MAX_SLOTS, "xhci_control_transfer: Slot does not exist.");
ASSERT(tsf_r_attached_slot == slot, "xhci_close_endpoint: Slot is not attached."); ASSERT(tsf_r_attached_slot == slot, "xhci_close_endpoint: Slot is not attached.");
+1 -1
View File
@@ -5,7 +5,7 @@
#define BLOCK_SIZE 65536 #define BLOCK_SIZE 65536
exit_code_t pass(uint64_t fd) { exit_code_t pass(uint64_t fd) {
static char buffer[BLOCK_SIZE]; char buffer[BLOCK_SIZE];
uint64_t bytes; uint64_t bytes;
while ((bytes = read(fd, BLOCK_SIZE, buffer))) { while ((bytes = read(fd, BLOCK_SIZE, buffer))) {
if (bytes == (uint64_t)-1) { if (bytes == (uint64_t)-1) {
+1 -2
View File
@@ -10,8 +10,7 @@ exit_code_t main(uint64_t argc, const char **argv) {
uint64_t code = EXIT_CODE_OK; uint64_t code = EXIT_CODE_OK;
for (uint64_t i = 1; i < argc; i++) { for (uint64_t i = 1; i < argc; i++) {
uint64_t removed = remove(argv[i]); if (remove(argv[i]) == (uint64_t)-1) {
if (removed == (uint64_t)-1) {
ERR_S("rm: could not remove file\n"); ERR_S("rm: could not remove file\n");
code = EXIT_CODE_GENERAL_FAILURE; code = EXIT_CODE_GENERAL_FAILURE;
} }
+7 -2
View File
@@ -96,7 +96,12 @@ static void execute(char *command) {
uint8_t pids_count = 0; uint8_t pids_count = 0;
for (uint8_t i = 0; i < subcommands_count; i++) { for (uint8_t i = 0; i < subcommands_count; i++) {
string_trim(subcommands[i], ' ', &subcommands[i]); if (!string_trim(subcommands[i], ' ', &subcommands[i])) {
if (subcommands_count > 1) {
ERR_S("Syntax error.\n");
}
break;
}
char *argv[16]; char *argv[16];
uint8_t argc = (uint8_t)string_split(subcommands[i], ' ', 16, argv); uint8_t argc = (uint8_t)string_split(subcommands[i], ' ', 16, argv);
@@ -166,7 +171,7 @@ exit_code_t main() {
print_prompt(); print_prompt();
char line[256]; char line[256];
uint64_t offset; uint64_t offset = 0;
char chunk[64]; char chunk[64];
uint64_t received; uint64_t received;
+1 -1
View File
@@ -6,7 +6,7 @@
#define BLOCK_SIZE 65536 #define BLOCK_SIZE 65536
exit_code_t pass(uint64_t fd) { exit_code_t pass(uint64_t fd) {
static char buffer[BLOCK_SIZE]; char buffer[BLOCK_SIZE];
uint64_t bytes; uint64_t bytes;
uint64_t total = 0; uint64_t total = 0;
while ((bytes = read(fd, BLOCK_SIZE, buffer))) { while ((bytes = read(fd, BLOCK_SIZE, buffer))) {