Compare commits

...
10 Commits
Author SHA1 Message Date
freywar ae7af6e6cf Fix cat and wc
Trying to fit 64K onto stack was a bad idea.
2026-08-25 23:07:54 +03:00
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
freywar c58605b56d Fix LOG_X usage crashing the system
Each `LOG_X` call declared a rather huge variable on the stack,
and using it to dump a whole structure often led to a page fault.
Now the temporary variables are `static`, which is a bit wasteful
on the kernel binary size, but easier to maintain.

Additionally bootloader updated to load kernels bigger than 64K.
2026-08-25 21:15:16 +03:00
freywar 63c1720e60 Improve installation process
`meson.build` rewritten with DRY principle. `deploy.sh` added,
which deploys the image on a installation USB stick borrowed from
another project. Image name aligned with the installer's expectations.
2026-08-25 21:11:15 +03:00
freywar bbb9988bcf Fix xHCI event consumption 2026-08-14 20:53:39 +03:00
freywar 0cb2169c29 Add basic HID keyboard support 2026-08-14 20:51:07 +03:00
25 changed files with 930 additions and 555 deletions
+1
View File
@@ -0,0 +1 @@
![It works!](./WATCHME.png)
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 949 KiB

+16 -16
View File
@@ -18,19 +18,19 @@ meson compile -v -C build
sector_size=512
partition_offset=2048
dd if=/dev/zero of=build/os.img bs="${sector_size}" count="$((partition_offset + 20480))"
dd if=build/mbr.bin of=build/os.img bs="${sector_size}" count=1 conv=notrunc
dd if=build/bootloader.bin of=build/os.img bs="${sector_size}" seek=1 count="$((partition_offset - 1))" conv=notrunc
mkfs.fat -F 16 --offset "${partition_offset}" build/os.img
mcopy -i build/os.img@@"$((partition_offset * sector_size))" -s src ::src
mcopy -i build/os.img@@"$((partition_offset * sector_size))" build/kernel.bin ::kernel.bin
mmd -i build/os.img@@"$((partition_offset * sector_size))" ::bin
mcopy -i build/os.img@@"$((partition_offset * sector_size))" build/terminal ::bin/terminal
mcopy -i build/os.img@@"$((partition_offset * sector_size))" build/shell ::bin/shell
mcopy -i build/os.img@@"$((partition_offset * sector_size))" build/echo ::bin/echo
mcopy -i build/os.img@@"$((partition_offset * sector_size))" build/ls ::bin/ls
mcopy -i build/os.img@@"$((partition_offset * sector_size))" build/cat ::bin/cat
mcopy -i build/os.img@@"$((partition_offset * sector_size))" build/cp ::bin/cp
mcopy -i build/os.img@@"$((partition_offset * sector_size))" build/mkdir ::bin/mkdir
mcopy -i build/os.img@@"$((partition_offset * sector_size))" build/rm ::bin/rm
mcopy -i build/os.img@@"$((partition_offset * sector_size))" build/wc ::bin/wc
dd if=/dev/zero of=build/os.raw bs="${sector_size}" count="$((partition_offset + 20480))"
dd if=build/mbr.bin of=build/os.raw bs="${sector_size}" count=1 conv=notrunc
dd if=build/bootloader.bin of=build/os.raw bs="${sector_size}" seek=1 count="$((partition_offset - 1))" conv=notrunc
mkfs.fat -F 16 --offset "${partition_offset}" build/os.raw
mcopy -i build/os.raw@@"$((partition_offset * sector_size))" -s src ::src
mcopy -i build/os.raw@@"$((partition_offset * sector_size))" build/kernel.bin ::kernel.bin
mmd -i build/os.raw@@"$((partition_offset * sector_size))" ::bin
mcopy -i build/os.raw@@"$((partition_offset * sector_size))" build/terminal ::bin/terminal
mcopy -i build/os.raw@@"$((partition_offset * sector_size))" build/shell ::bin/shell
mcopy -i build/os.raw@@"$((partition_offset * sector_size))" build/echo ::bin/echo
mcopy -i build/os.raw@@"$((partition_offset * sector_size))" build/ls ::bin/ls
mcopy -i build/os.raw@@"$((partition_offset * sector_size))" build/cat ::bin/cat
mcopy -i build/os.raw@@"$((partition_offset * sector_size))" build/cp ::bin/cp
mcopy -i build/os.raw@@"$((partition_offset * sector_size))" build/mkdir ::bin/mkdir
mcopy -i build/os.raw@@"$((partition_offset * sector_size))" build/rm ::bin/rm
mcopy -i build/os.raw@@"$((partition_offset * sector_size))" build/wc ::bin/wc
Executable
+19
View File
@@ -0,0 +1,19 @@
#!/usr/bin/env bash
set -euo pipefail
device=${1:-}
if [[ ! -b ${device} ]]; then
echo "ERROR: \"${device}\" is not a block device" >&2
fi
if [[ ! -f build/os.raw ]]; then
echo "ERROR: \"build/os.raw\" does not appear to be built, run \"build.sh\"" >&2
fi
echo 'Copying the image...' >&2
mnt=$(mktemp --directory)
mount "${device}2" "${mnt}"
cp --update "build/os.raw" "${mnt}"
umount "${mnt}"
rm -rf "${mnt}"
echo 'Done.' >&2
+40 -339
View File
@@ -71,9 +71,9 @@ kernel_sources = files([
'src/kernel/fat16.c',
'src/kernel/fs.c',
'src/kernel/gdt.c',
'src/kernel/hid-keyboard.c',
'src/kernel/idt.c',
'src/kernel/kernel.c',
'src/kernel/keyboard.c',
'src/kernel/log.c',
'src/kernel/memory.c',
'src/kernel/nvme.c',
@@ -83,6 +83,7 @@ kernel_sources = files([
'src/kernel/pic.c',
'src/kernel/pipe.c',
'src/kernel/process.c',
'src/kernel/ps2-keyboard.c',
'src/kernel/syscall.c',
'src/kernel/timer.c',
'src/kernel/tss.c',
@@ -91,20 +92,25 @@ kernel_sources = files([
'src/kernel/xhci.c',
])
kernel_elf = executable(
'kernel.elf',
sources: [kernel_entry_o, timer_o, process_o, syscall_o, lib_sources, kernel_sources],
c_args: [
c_args = [
'-ffreestanding',
'-nostdlib',
'-nostartfiles',
'-mno-red-zone',
'-mgeneral-regs-only',
'-fno-stack-protector',
'-DVERSION="' + meson.project_version() + '"',
]
kernel_elf = executable(
'kernel.elf',
sources: [kernel_entry_o, timer_o, process_o, syscall_o, lib_sources, kernel_sources],
c_args: [
c_args,
'-mcmodel=kernel',
'-Wno-unused-command-line-argument',
'-Wconversion',
'-DKERNEL="yes"',
'-DVERSION="' + meson.project_version() + '"',
],
link_args: [
'-T', meson.project_source_root() / 'src/kernel/linker.ld',
@@ -121,353 +127,48 @@ custom_target(
build_by_default: true,
)
terminal_start = custom_target(
'terminal_start',
foreach app : ['terminal', 'shell', 'echo', 'ls', 'cat', 'cp', 'mkdir', 'rm', 'wc']
start = custom_target(
f'@app@_start',
input: 'src/user/start.asm',
output: 'terminal_start.o',
output: f'@app@_start.o',
command: [nasm, '-f', 'elf64', '-o', '@OUTPUT@', '@INPUT@'],
)
)
terminal_syscall = custom_target(
'terminal_syscall',
syscall = custom_target(
f'@app@_syscall',
input: 'src/user/syscall.asm',
output: 'terminal_syscall.o',
output: f'@app@_syscall.o',
command: [nasm, '-f', 'elf64', '-o', '@OUTPUT@', '@INPUT@'],
)
)
terminal_elf = executable(
'terminal.elf',
sources: [terminal_start, terminal_syscall, lib_sources, 'src/user/app/terminal/terminal.c'],
elf = executable(
f'@app@.elf',
sources: [start, syscall, lib_sources, f'src/user/app/@app@/@app@.c'],
c_args: [
'-ffreestanding',
'-nostdlib',
'-fno-stack-protector',
c_args,
'-mcmodel=large',
'-DVERSION="' + meson.project_version() + '"',
],
link_args: [
'-T', meson.project_source_root() / 'src/user/linker.ld',
'-nostdlib',
],
link_depends: 'src/user/linker.ld',
)
)
custom_target(
'terminal',
input: terminal_elf,
output: 'terminal',
elf_data = custom_target(
f'@app@_data.elf',
input: elf,
output: f'@app@_data.elf',
command: ['objcopy', '--set-section-flags', '.data=alloc,load,contents', '@INPUT@', '@OUTPUT@'],
build_by_default: true,
)
custom_target(
app,
input: elf_data,
output: app,
command: ['objcopy', '-O', 'binary', '@INPUT@', '@OUTPUT@'],
build_by_default: true,
)
shell_start = custom_target(
'shell_start',
input: 'src/user/start.asm',
output: 'shell_start.o',
command: [nasm, '-f', 'elf64', '-o', '@OUTPUT@', '@INPUT@'],
)
shell_syscall = custom_target(
'shell_syscall',
input: 'src/user/syscall.asm',
output: 'shell_syscall.o',
command: [nasm, '-f', 'elf64', '-o', '@OUTPUT@', '@INPUT@'],
)
shell_elf = executable(
'shell.elf',
sources: [shell_start, shell_syscall, lib_sources, 'src/user/app/shell/shell.c'],
c_args: [
'-ffreestanding',
'-nostdlib',
'-fno-stack-protector',
'-mcmodel=large',
'-DVERSION="' + meson.project_version() + '"',
],
link_args: [
'-T', meson.project_source_root() / 'src/user/linker.ld',
'-nostdlib',
],
link_depends: 'src/user/linker.ld',
)
custom_target(
'shell',
input: shell_elf,
output: 'shell',
command: ['objcopy', '-O', 'binary', '@INPUT@', '@OUTPUT@'],
build_by_default: true,
)
echo_start = custom_target(
'echo_start',
input: 'src/user/start.asm',
output: 'echo_start.o',
command: [nasm, '-f', 'elf64', '-o', '@OUTPUT@', '@INPUT@'],
)
echo_syscall = custom_target(
'echo_syscall',
input: 'src/user/syscall.asm',
output: 'echo_syscall.o',
command: [nasm, '-f', 'elf64', '-o', '@OUTPUT@', '@INPUT@'],
)
echo_elf = executable(
'echo.elf',
sources: [echo_start, echo_syscall, lib_sources, 'src/user/app/echo/echo.c'],
c_args: [
'-ffreestanding',
'-nostdlib',
'-fno-stack-protector',
'-mcmodel=large',
'-DVERSION="' + meson.project_version() + '"',
],
link_args: [
'-T', meson.project_source_root() / 'src/user/linker.ld',
'-nostdlib',
],
link_depends: 'src/user/linker.ld',
)
custom_target(
'echo',
input: echo_elf,
output: 'echo',
command: ['objcopy', '-O', 'binary', '@INPUT@', '@OUTPUT@'],
build_by_default: true,
)
ls_start = custom_target(
'ls_start',
input: 'src/user/start.asm',
output: 'ls_start.o',
command: [nasm, '-f', 'elf64', '-o', '@OUTPUT@', '@INPUT@'],
)
ls_syscall = custom_target(
'ls_syscall',
input: 'src/user/syscall.asm',
output: 'ls_syscall.o',
command: [nasm, '-f', 'elf64', '-o', '@OUTPUT@', '@INPUT@'],
)
ls_elf = executable(
'ls.elf',
sources: [ls_start, ls_syscall, lib_sources, 'src/user/app/ls/ls.c'],
c_args: [
'-ffreestanding',
'-nostdlib',
'-fno-stack-protector',
'-mcmodel=large',
'-DVERSION="' + meson.project_version() + '"',
],
link_args: [
'-T', meson.project_source_root() / 'src/user/linker.ld',
'-nostdlib',
],
link_depends: 'src/user/linker.ld',
)
custom_target(
'ls',
input: ls_elf,
output: 'ls',
command: ['objcopy', '-O', 'binary', '@INPUT@', '@OUTPUT@'],
build_by_default: true,
)
cat_start = custom_target(
'cat_start',
input: 'src/user/start.asm',
output: 'cat_start.o',
command: [nasm, '-f', 'elf64', '-o', '@OUTPUT@', '@INPUT@'],
)
cat_syscall = custom_target(
'cat_syscall',
input: 'src/user/syscall.asm',
output: 'cat_syscall.o',
command: [nasm, '-f', 'elf64', '-o', '@OUTPUT@', '@INPUT@'],
)
cat_elf = executable(
'cat.elf',
sources: [cat_start, cat_syscall, lib_sources, 'src/user/app/cat/cat.c'],
c_args: [
'-ffreestanding',
'-nostdlib',
'-fno-stack-protector',
'-mcmodel=large',
'-DVERSION="' + meson.project_version() + '"',
],
link_args: [
'-T', meson.project_source_root() / 'src/user/linker.ld',
'-nostdlib',
],
link_depends: 'src/user/linker.ld',
)
custom_target(
'cat',
input: cat_elf,
output: 'cat',
command: ['objcopy', '-O', 'binary', '@INPUT@', '@OUTPUT@'],
build_by_default: true,
)
cp_start = custom_target(
'cp_start',
input: 'src/user/start.asm',
output: 'cp_start.o',
command: [nasm, '-f', 'elf64', '-o', '@OUTPUT@', '@INPUT@'],
)
cp_syscall = custom_target(
'cp_syscall',
input: 'src/user/syscall.asm',
output: 'cp_syscall.o',
command: [nasm, '-f', 'elf64', '-o', '@OUTPUT@', '@INPUT@'],
)
cp_elf = executable(
'cp.elf',
sources: [cp_start, cp_syscall, lib_sources, 'src/user/app/cp/cp.c'],
c_args: [
'-ffreestanding',
'-nostdlib',
'-fno-stack-protector',
'-mcmodel=large',
'-DVERSION="' + meson.project_version() + '"',
],
link_args: [
'-T', meson.project_source_root() / 'src/user/linker.ld',
'-nostdlib',
],
link_depends: 'src/user/linker.ld',
)
custom_target(
'cp',
input: cp_elf,
output: 'cp',
command: ['objcopy', '-O', 'binary', '@INPUT@', '@OUTPUT@'],
build_by_default: true,
)
mkdir_start = custom_target(
'mkdir_start',
input: 'src/user/start.asm',
output: 'mkdir_start.o',
command: [nasm, '-f', 'elf64', '-o', '@OUTPUT@', '@INPUT@'],
)
mkdir_syscall = custom_target(
'mkdir_syscall',
input: 'src/user/syscall.asm',
output: 'mkdir_syscall.o',
command: [nasm, '-f', 'elf64', '-o', '@OUTPUT@', '@INPUT@'],
)
mkdir_elf = executable(
'mkdir.elf',
sources: [mkdir_start, mkdir_syscall, lib_sources, 'src/user/app/mkdir/mkdir.c'],
c_args: [
'-ffreestanding',
'-nostdlib',
'-fno-stack-protector',
'-mcmodel=large',
'-DVERSION="' + meson.project_version() + '"',
],
link_args: [
'-T', meson.project_source_root() / 'src/user/linker.ld',
'-nostdlib',
],
link_depends: 'src/user/linker.ld',
)
custom_target(
'mkdir',
input: mkdir_elf,
output: 'mkdir',
command: ['objcopy', '-O', 'binary', '@INPUT@', '@OUTPUT@'],
build_by_default: true,
)
rm_start = custom_target(
'rm_start',
input: 'src/user/start.asm',
output: 'rm_start.o',
command: [nasm, '-f', 'elf64', '-o', '@OUTPUT@', '@INPUT@'],
)
rm_syscall = custom_target(
'rm_syscall',
input: 'src/user/syscall.asm',
output: 'rm_syscall.o',
command: [nasm, '-f', 'elf64', '-o', '@OUTPUT@', '@INPUT@'],
)
rm_elf = executable(
'rm.elf',
sources: [rm_start, rm_syscall, lib_sources, 'src/user/app/rm/rm.c'],
c_args: [
'-ffreestanding',
'-nostdlib',
'-fno-stack-protector',
'-mcmodel=large',
'-DVERSION="' + meson.project_version() + '"',
],
link_args: [
'-T', meson.project_source_root() / 'src/user/linker.ld',
'-nostdlib',
],
link_depends: 'src/user/linker.ld',
)
custom_target(
'rm',
input: rm_elf,
output: 'rm',
command: ['objcopy', '-O', 'binary', '@INPUT@', '@OUTPUT@'],
build_by_default: true,
)
wc_start = custom_target(
'wc_start',
input: 'src/user/start.asm',
output: 'wc_start.o',
command: [nasm, '-f', 'elf64', '-o', '@OUTPUT@', '@INPUT@'],
)
wc_syscall = custom_target(
'wc_syscall',
input: 'src/user/syscall.asm',
output: 'wc_syscall.o',
command: [nasm, '-f', 'elf64', '-o', '@OUTPUT@', '@INPUT@'],
)
wc_elf = executable(
'wc.elf',
sources: [wc_start, wc_syscall, lib_sources, 'src/user/app/wc/wc.c'],
c_args: [
'-ffreestanding',
'-nostdlib',
'-fno-stack-protector',
'-mcmodel=large',
'-DVERSION="' + meson.project_version() + '"',
],
link_args: [
'-T', meson.project_source_root() / 'src/user/linker.ld',
'-nostdlib',
],
link_depends: 'src/user/linker.ld',
)
custom_target(
'wc',
input: wc_elf,
output: 'wc',
command: ['objcopy', '-O', 'binary', '@INPUT@', '@OUTPUT@'],
build_by_default: true,
)
)
endforeach
+4 -2
View File
@@ -10,7 +10,6 @@ mov sp, 0x7000
FIRST_PARTITION_SECTOR equ 2048 ; TODO Read MBR.
STAGE_BUFFER_SEGMENT equ 0x0900
STAGE_BUFFER_OFFSET equ 0x0000
KERNEL_BUFFER_SEGMENT equ 0x2000
jmp prepare_kernel
@@ -41,6 +40,7 @@ data_start: dw 0
kernel_filename: db 'KERNEL BIN'
kernel_cluster: dw 0
kernel_buffer_segment: dw 0x2000
kernel_buffer_offset: dw 0
read_sectors:
@@ -192,7 +192,7 @@ load_kernel_cluster:
add ax, [data_start]
xor cx, cx
mov cl, [bpb.sectors_per_cluster]
mov bx, KERNEL_BUFFER_SEGMENT
mov bx, [kernel_buffer_segment]
mov es, bx
mov di, [kernel_buffer_offset]
call read_sectors
@@ -201,6 +201,8 @@ load_kernel_cluster:
shl ax, 9 ; * 512
add ax, [kernel_buffer_offset]
mov [kernel_buffer_offset], ax
jnc find_next_kernel_cluster
add word [kernel_buffer_segment], 0x1000
find_next_kernel_cluster:
xor bx, bx
+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++;
}
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);
+201
View File
@@ -0,0 +1,201 @@
#include "src/kernel/log.h"
#include "src/kernel/panic.h"
#include "src/kernel/process.h"
#include "src/kernel/stream.h"
#include "src/kernel/usb.h"
#include "src/kernel/xhci.h"
#include "src/lib/memory.h"
#define USB_INTERFACE_HID_KEYBOARD_CLASS 0x3
#define USB_INTERFACE_HID_KEYBOARD_SUBCLASS 0x1
#define USB_INTERFACE_HID_KEYBOARD_PROTOCOL 0x1
#define USB_HID_BOOT_PACKET_SIZE 8
#define HID_MOD_LCTRL 0b00000001
#define HID_MOD_LSHIFT 0b00000010
#define HID_MOD_LALT 0b00000100
#define HID_MOD_RCTRL 0b00010000
#define HID_MOD_RSHIFT 0b00100000
#define HID_MOD_RALT 0b01000000
#define HID_KEY_ENTER 0x28
#define HID_KEY_BACKSPACE 0x2A
#define HID_KEY_TAB 0x2B
#define HID_KEY_HOME 0x4A
#define HID_KEY_END 0x4D
#define HID_KEY_DELETE 0x4C
#define HID_KEY_RIGHT 0x4F
#define HID_KEY_LEFT 0x50
#define HID_KEY_DOWN 0x51
#define HID_KEY_UP 0x52
// HID keycode to ASCII, index 0 = keycode 4 ('a')
static const char hid_normal[128] = {
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
'\n', 0, '\b', '\t', ' ', '-', '=', '[', ']', '\\', 0, ';', '\'', '`', ',', '.', '/',
};
static const char hid_shifted[128] = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')',
'\n', 0, '\b', '\t', ' ', '_', '+', '{', '}', '|', 0, ':', '"', '~', '<', '>', '?',
};
static xhci_port_t keyboard_port;
static xhci_slot_t keyboard_slot;
static xhci_endpoint_t keyboard_endpoint;
typedef uint64_t hid_report;
static hid_report prev;
#define BUFFER_SIZE 256
static char buffer[BUFFER_SIZE];
static uint16_t buffer_offset = 0;
static uint16_t buffer_length = 0;
static void append(char c) {
buffer[(buffer_offset + buffer_length) % BUFFER_SIZE] = c;
if (buffer_length < BUFFER_SIZE) {
buffer_length++;
}
}
static void append_sequence(const char *s) {
while (*s) {
append(*s);
s++;
}
}
static void on_key_pressed(uint8_t code, uint8_t modifiers) {
code -= 4;
if (code >= 128) {
return;
}
switch (code + 4) {
case HID_KEY_ENTER:
append('\n');
return;
case HID_KEY_BACKSPACE:
append('\b');
return;
case HID_KEY_TAB:
append('\t');
return;
case HID_KEY_HOME:
append_sequence(STREAM_SEQ_HOME);
return;
case HID_KEY_END:
append_sequence(STREAM_SEQ_END);
return;
case HID_KEY_DELETE:
append_sequence(STREAM_SEQ_DELETE);
return;
case HID_KEY_RIGHT:
append_sequence(STREAM_SEQ_RIGHT);
return;
case HID_KEY_LEFT:
append_sequence(STREAM_SEQ_LEFT);
return;
case HID_KEY_DOWN:
append_sequence(STREAM_SEQ_DOWN);
return;
case HID_KEY_UP:
append_sequence(STREAM_SEQ_UP);
return;
}
if (modifiers & (HID_MOD_LCTRL | HID_MOD_RCTRL)) {
if ((hid_normal[code] >= '0' && hid_normal[code] <= '9') || (hid_normal[code] >= 'a' && hid_normal[code] <= 'z')) {
append(hid_normal[code] - 'a' + 1);
}
} else if (modifiers & (HID_MOD_LALT | HID_MOD_RALT)) {
// TODO Alt combinations.
} else {
append(((modifiers & (HID_MOD_LSHIFT | HID_MOD_RSHIFT)) ? hid_shifted : hid_normal)[code]);
}
}
static uint64_t stream_write(__attribute__((unused)) stream_t *self, __attribute__((unused)) const char *from,
__attribute__((unused)) uint64_t bytes) {
return 0;
}
static uint64_t stream_read(__attribute__((unused)) stream_t *self, uint64_t max, char *to) {
while (!buffer_length) {
hid_report next;
while (xhci_read_endpoint(keyboard_slot, keyboard_endpoint, sizeof(next), &next)) {
uint8_t *prev_bytes = (uint8_t *)&prev;
uint8_t *next_bytes = (uint8_t *)&next;
if (next == prev) { // Assuming new report without changes means automatic repetition.
for (uint8_t i = 2; i < 8; i++) {
if (next_bytes[i]) {
on_key_pressed(next_bytes[i], next_bytes[0]);
}
}
} else {
for (uint8_t i = 2; i < 8; i++) {
uint8_t found = 0;
for (uint8_t j = 2; j < 8; j++) {
if (prev_bytes[j] == next_bytes[i]) {
found = 1;
break;
}
}
if (next_bytes[i] && !found) {
on_key_pressed(next_bytes[i], next_bytes[0]);
}
}
}
prev = next;
}
__asm__ volatile("sti");
process_next();
__asm__ volatile("cli");
}
uint64_t size = buffer_length > max ? max : buffer_length;
if (buffer_offset + size <= BUFFER_SIZE) {
memory_copy(buffer + buffer_offset, size, to);
} else {
uint64_t chunk_0 = BUFFER_SIZE - buffer_offset;
memory_copy(buffer + buffer_offset, chunk_0, to);
memory_copy(buffer, size - chunk_0, to + chunk_0);
}
buffer_length -= size;
buffer_offset = (buffer_offset + size) % BUFFER_SIZE;
return size;
}
static uint64_t stream_truncate(__attribute__((unused)) stream_t *self, __attribute__((unused)) uint64_t size) {
return size;
}
static void stream_close(__attribute__((unused)) stream_t *self) {
}
static stream_t stream = {stream_write, stream_read, stream_truncate, stream_close};
stream_t *hid_keyboard_init() {
LOG_LN_INFO("Searching for suitable device...");
keyboard_port =
usb_find_by_interface(USB_INTERFACE_HID_KEYBOARD_CLASS, USB_INTERFACE_HID_KEYBOARD_SUBCLASS, USB_INTERFACE_HID_KEYBOARD_PROTOCOL);
ASSERT(keyboard_port != (xhci_port_t)-1, "hid_keyboard_init: No suitable device found.");
keyboard_slot = usb_attach((uint8_t)keyboard_port);
ASSERT(keyboard_slot != (xhci_slot_t)-1, "hid_keyboard_init: Could not attach the device.");
keyboard_endpoint = usb_open_endpoint(keyboard_slot, 7, USB_HID_BOOT_PACKET_SIZE);
ASSERT(keyboard_endpoint != (xhci_endpoint_t)-1, "hid_keyboard_init: Could not open endpoint.");
LOG_LN_INFO("Done.");
return &stream;
}
+7
View File
@@ -0,0 +1,7 @@
#pragma once
#include "src/kernel/stream.h"
#include <stdint.h>
stream_t *hid_keyboard_init(); // Assuming one and only one HID device.
+2 -2
View File
@@ -1,7 +1,7 @@
#include "src/kernel/fs.h"
#include "src/kernel/gdt.h"
#include "src/kernel/hid-keyboard.h"
#include "src/kernel/idt.h"
#include "src/kernel/keyboard.h"
#include "src/kernel/log.h"
#include "src/kernel/nvme.h"
#include "src/kernel/panic.h"
@@ -87,7 +87,7 @@ void kernel_main() {
kernel->kernel_stack = kernel->kernel_rsp = (uint8_t *)memory_allocate(PAGE_SIZE) + PAGE_SIZE;
kernel->user_stack = kernel->user_rsp = (uint8_t *)memory_allocate(PAGE_SIZE) + PAGE_SIZE;
kernel->cwd = memory_allocate(sizeof(path_t));
kernel->fds[STDIN] = keyboard_init();
kernel->fds[STDIN] = hid_keyboard_init();
kernel->fds[STDOUT] = kernel->fds[STDERR] = vga;
kernel->free_fd = STDERR + 1;
kernel->code = EXIT_CODE_OK;
+32 -44
View File
@@ -1,7 +1,6 @@
#pragma once
#include "src/kernel/stream.h"
#include "src/lib/memory.h"
#include "src/lib/string.h"
extern stream_t *kernel_log;
@@ -15,58 +14,47 @@ extern stream_t *kernel_log;
#define LOG_LEVEL_DATA 3
#define LOG_LEVEL_TRACE 4
static uint64_t print_size;
static char print_buffer[512];
#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__); \
stream->write(stream, _string, _size); \
memory_free(_string); \
} while (0);
print_size = string_format(fmt, sizeof(print_buffer) - 1, print_buffer, ##__VA_ARGS__); \
stream->write(stream, print_buffer, print_size);
#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__); \
print_size = string_format(fmt, sizeof(print_buffer) - 1, print_buffer, ##__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);
stream->write(stream, print_buffer, print_size); \
stream->write(stream, "\n", 1);
#define PRINT_VAL(stream, v, f) PRINT_LN(#v = f, v)
#define LOG_INFO(fmt, ...) \
if (LOG_LEVEL >= LOG_LEVEL_INFO) \
PRINT(kernel_log, fmt, ##__VA_ARGS__)
#define LOG_STEP(fmt, ...) \
if (LOG_LEVEL >= LOG_LEVEL_STEP) \
PRINT(kernel_log, fmt, ##__VA_ARGS__)
#define LOG_DATA(fmt, ...) \
if (LOG_LEVEL >= LOG_LEVEL_DATA) \
PRINT(kernel_log, fmt, ##__VA_ARGS__)
#define LOG_TRACE(fmt, ...) \
if (LOG_LEVEL >= LOG_LEVEL_TRACE) \
PRINT(kernel_log, fmt, ##__VA_ARGS__)
#define LOG(level, fmt, ...) \
if (LOG_LEVEL >= level) { \
PRINT(kernel_log, fmt, ##__VA_ARGS__) \
}
#define LOG_LN_INFO(fmt, ...) \
if (LOG_LEVEL >= LOG_LEVEL_INFO) \
PRINT_LN(kernel_log, fmt, ##__VA_ARGS__)
#define LOG_LN_STEP(fmt, ...) \
if (LOG_LEVEL >= LOG_LEVEL_STEP) \
PRINT_LN(kernel_log, fmt, ##__VA_ARGS__)
#define LOG_LN_DATA(fmt, ...) \
if (LOG_LEVEL >= LOG_LEVEL_DATA) \
PRINT_LN(kernel_log, fmt, ##__VA_ARGS__)
#define LOG_LN_TRACE(fmt, ...) \
if (LOG_LEVEL >= LOG_LEVEL_TRACE) \
PRINT_LN(kernel_log, fmt, ##__VA_ARGS__)
#define LOG_LN(level, fmt, ...) \
if (LOG_LEVEL >= level) { \
PRINT_LN(kernel_log, fmt, ##__VA_ARGS__) \
}
#define LOG_VAL_INFO(v, f) LOG_LN_INFO(#v "=" f, v)
#define LOG_VAL_STEP(v, f) LOG_LN_STEP(#v "=" f, v)
#define LOG_VAL_DATA(v, f) LOG_LN_DATA(#v "=" f, v)
#define LOG_VAL_TRACE(v, f) LOG_LN_TRACE(#v "=" f, v)
#define LOG_VAL(level, v, f) LOG_LN(level, #v "=" f, v)
#define LOG_INFO(fmt, ...) LOG(LOG_LEVEL_INFO, fmt, ##__VA_ARGS__)
#define LOG_STEP(fmt, ...) LOG(LOG_LEVEL_STEP, fmt, ##__VA_ARGS__)
#define LOG_DATA(fmt, ...) LOG(LOG_LEVEL_DATA, fmt, ##__VA_ARGS__)
#define LOG_TRACE(fmt, ...) LOG(LOG_LEVEL_TRACE, fmt, ##__VA_ARGS__)
#define LOG_LN_INFO(fmt, ...) LOG_LN(LOG_LEVEL_INFO, fmt, ##__VA_ARGS__)
#define LOG_LN_STEP(fmt, ...) LOG_LN(LOG_LEVEL_STEP, fmt, ##__VA_ARGS__)
#define LOG_LN_DATA(fmt, ...) LOG_LN(LOG_LEVEL_DATA, fmt, ##__VA_ARGS__)
#define LOG_LN_TRACE(fmt, ...) LOG_LN(LOG_LEVEL_TRACE, fmt, ##__VA_ARGS__)
#define LOG_VAL_INFO(v, f) LOG_VAL(LOG_LEVEL_INFO, v, f)
#define LOG_VAL_STEP(v, f) LOG_VAL(LOG_LEVEL_STEP, v, f)
#define LOG_VAL_DATA(v, f) LOG_VAL(LOG_LEVEL_DATA, v, f)
#define LOG_VAL_TRACE(v, f) LOG_VAL(LOG_LEVEL_TRACE, v, f)
void log_init(stream_t *log);
+1 -1
View File
@@ -167,7 +167,7 @@ static void io_exec_sync(nvme_sqe_t *cmd) {
void nvme_init() {
LOG_LN_INFO("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.");
ASSERT(bdf != (pci_bdf_t)-1, "nvme_init: No suitable devices found.");
LOG_LN_INFO("Mapping device to virtual memory...");
pci_map(bdf, regs, 4);
+1 -1
View File
@@ -2,7 +2,7 @@
#include "src/kernel/vga.h"
void kernel_panic(const char *msg, __attribute__((unused)) const char *file, __attribute__((unused)) int line) {
vga_set_string(0, VGA_HEIGHT - 1, msg, 0x28);
vga_set_string(VGA_HEIGHT - 1, 0, msg, 0x28);
while (1)
;
}
+7 -2
View File
@@ -1,6 +1,7 @@
#include "src/kernel/pci.h"
#include "src/kernel/log.h"
#include "src/kernel/memory.h"
#include "src/kernel/panic.h"
#include "src/kernel/util.h"
#include "src/lib/layout.h"
#include "src/lib/util.h"
@@ -70,7 +71,7 @@ void pci_enumerate(stream_t *out) {
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++) {
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;
@@ -85,10 +86,12 @@ pci_bdf_t pci_find_by_class(uint8_t class, uint8_t subclass, uint8_t iface) {
}
}
}
return NUL;
return (pci_bdf_t)-1;
}
void pci_map(pci_bdf_t bdf, volatile void *virt, uint8_t pages) {
ASSERT(bdf != (pci_bdf_t)-1, "pci_map: Invalid BDF.");
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);
@@ -101,6 +104,8 @@ 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) {
ASSERT(bdf != (pci_bdf_t)-1, "pci_unmap: Invalid BDF.");
LOG_LN_STEP("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));
@@ -1,4 +1,4 @@
#include "src/kernel/keyboard.h"
#include "src/kernel/ps2-keyboard.h"
#include "src/kernel/idt.h"
#include "src/kernel/process.h"
#include "src/kernel/stream.h"
@@ -158,15 +158,15 @@ static void on_key(uint8_t scancode) {
}
}
__attribute__((interrupt)) static void isr_keyboard(__attribute__((unused)) struct interrupt_frame *frame) {
__attribute__((interrupt)) static void isr_ps2_keyboard(__attribute__((unused)) struct interrupt_frame *frame) {
uint8_t scancode = inb(0x60);
outb(0x20, 0x20);
on_key(scancode);
}
stream_t *keyboard_init() {
stream_t *ps2_keyboard_init() {
outb(0x21, inb(0x21) & ~0x02);
idt_set_entry(33, isr_keyboard, 0x8E);
idt_set_entry(33, isr_ps2_keyboard, 0x8E);
return &stream;
}
@@ -3,4 +3,4 @@
#include "src/kernel/stream.h"
#include <stdint.h>
stream_t *keyboard_init();
stream_t *ps2_keyboard_init();
+240 -10
View File
@@ -1,16 +1,67 @@
#include "src/kernel/usb.h"
#include "src/kernel/log.h"
#include "src/kernel/panic.h"
#include "src/kernel/stream.h"
#include "src/kernel/xhci.h"
#include "src/lib/layout.h"
#include "src/lib/memory.h"
#include "src/lib/util.h"
typedef struct __attribute__((packed)) {
uint8_t bm_request_type;
uint8_t b_request;
uint16_t w_value;
uint16_t w_index;
uint16_t w_length;
} usb_setup_t;
#define USB_SETUP_DIRECTION_R(s) BITS_R((s)->bm_request_type, 7, 7)
#define USB_SETUP_DIRECTION_W(s, v) BITS_W((s)->bm_request_type, 7, 7, v)
#define USB_SETUP_TYPE_R(s) BITS_R((s)->bm_request_type, 6, 5)
#define USB_SETUP_TYPE_W(s, v) BITS_W((s)->bm_request_type, 6, 5, v)
#define USB_SETUP_RECIPIENT_R(s) BITS_R((s)->bm_request_type, 4, 0)
#define USB_SETUP_RECIPIENT_W(s, v) BITS_W((s)->bm_request_type, 4, 0, v)
#define USB_SETUP_DIRECTION_H2D 0
#define USB_SETUP_DIRECTION_D2H 1
#define USB_SETUP_TYPE_STANDARD 0
#define USB_SETUP_TYPE_CLASS 1
#define USB_SETUP_TYPE_VENDOR 2
#define USB_SETUP_RECIPIENT_DEVICE 0
#define USB_SETUP_RECIPIENT_INTERFACE 1
#define USB_SETUP_RECIPIENT_ENDPOINT 2
#define USB_SETUP_RECIPIENT_OTHER 3
#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_DESCRIPTOR_TYPE_R(s) BITS_R((s)->w_value, 15, 8)
#define USB_SETUP_DESCRIPTOR_TYPE_W(s, v) BITS_W((s)->w_value, 15, 8, v)
#define USB_SETUP_DESCRIPTOR_INDEX_R(s) BITS_R((s)->w_value, 7, 0)
#define USB_SETUP_DESCRIPTOR_INDEX_W(s, v) BITS_W((s)->w_value, 7, 0, v)
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 descriptor_type;
} usb_descriptor_base_t;
#define USB_DESCRIPTOR_TYPE_DEVICE 0x01
#define USB_DESCRIPTOR_TYPE_CONFIGURATION 0x02
#define USB_DESCRIPTOR_TYPE_INTERFACE 0x04
#define USB_DESCRIPTOR_TYPE_ENDPOINT 0x05
typedef struct __attribute__((packed)) {
usb_descriptor_base_t base;
uint16_t usb_version;
uint8_t device_class;
uint8_t device_subclass;
uint8_t device_protocol;
uint8_t max_packet_size; // for ep0
uint8_t max_packet_size;
uint16_t vendor_id;
uint16_t product_id;
uint16_t device_version;
@@ -20,7 +71,94 @@ typedef struct __attribute__((packed)) {
uint8_t num_configurations;
} usb_device_descriptor_t;
static usb_device_descriptor_t device_descriptor __attribute__((aligned(64)));
typedef struct __attribute__((packed)) {
usb_descriptor_base_t base;
uint16_t total_length;
uint8_t num_interfaces;
uint8_t configuration_value;
uint8_t configuration_str;
uint8_t attributes;
uint8_t max_power;
} usb_configuration_descriptor_t;
typedef struct __attribute__((packed)) {
usb_descriptor_base_t base;
uint8_t interface_number;
uint8_t alternate_setting;
uint8_t num_endpoints;
uint8_t interface_class;
uint8_t interface_subclass;
uint8_t interface_protocol;
uint8_t interface_str;
} usb_interface_descriptor_t;
typedef struct __attribute__((packed)) {
usb_descriptor_base_t base;
uint8_t endpoint_address;
uint8_t attributes;
uint16_t max_packet_size;
uint8_t interval;
} usb_endpoint_descriptor_t;
typedef struct __attribute__((packed)) {
usb_descriptor_base_t base;
uint16_t hid_version;
uint8_t country_code;
uint8_t num_descriptors;
uint8_t report_descriptor_type;
uint16_t report_descriptor_length;
} usb_hid_descriptor_t;
static uint8_t descriptor_buffer[PAGE_SIZE] __attribute__((aligned(64)));
static void scan_device(xhci_slot_t slot, usb_device_descriptor_t *device, usb_configuration_descriptor_t *configuration,
usb_interface_descriptor_t *interface, usb_endpoint_descriptor_t *endpoint) {
memory_set(0, sizeof(usb_device_descriptor_t), device);
memory_set(0, sizeof(usb_configuration_descriptor_t), configuration);
memory_set(0, sizeof(usb_interface_descriptor_t), interface);
memory_set(0, sizeof(usb_endpoint_descriptor_t), endpoint);
usb_setup_t setup;
memory_set(0, sizeof(setup), &setup);
USB_SETUP_DIRECTION_W(&setup, USB_SETUP_DIRECTION_D2H);
setup.b_request = USB_SETUP_GET_DESCRIPTOR;
USB_SETUP_DESCRIPTOR_TYPE_W(&setup, USB_DESCRIPTOR_TYPE_DEVICE);
setup.w_length = sizeof(usb_device_descriptor_t);
memory_set(0, sizeof(descriptor_buffer), descriptor_buffer);
xhci_control_transfer(slot, *(uint64_t *)&setup, &descriptor_buffer, setup.w_length);
memory_copy(descriptor_buffer, sizeof(usb_device_descriptor_t), device);
memory_set(0, sizeof(setup), &setup);
USB_SETUP_DIRECTION_W(&setup, USB_SETUP_DIRECTION_D2H);
setup.b_request = USB_SETUP_GET_DESCRIPTOR;
USB_SETUP_DESCRIPTOR_TYPE_W(&setup, USB_DESCRIPTOR_TYPE_CONFIGURATION);
setup.w_length = sizeof(usb_configuration_descriptor_t);
memory_set(0, sizeof(descriptor_buffer), descriptor_buffer);
xhci_control_transfer(slot, *(uint64_t *)&setup, &descriptor_buffer, setup.w_length);
memory_set(0, sizeof(setup), &setup);
USB_SETUP_DIRECTION_W(&setup, USB_SETUP_DIRECTION_D2H);
setup.b_request = USB_SETUP_GET_DESCRIPTOR;
USB_SETUP_DESCRIPTOR_TYPE_W(&setup, USB_DESCRIPTOR_TYPE_CONFIGURATION);
setup.w_length = ((usb_configuration_descriptor_t *)&descriptor_buffer)->total_length;
memory_set(0, sizeof(descriptor_buffer), descriptor_buffer);
xhci_control_transfer(slot, *(uint64_t *)&setup, &descriptor_buffer, setup.w_length);
usb_descriptor_base_t *descriptor = (usb_descriptor_base_t *)&descriptor_buffer;
while (descriptor->descriptor_type) { // Assuming `descriptor_buffer` longer than any potential descriptor set.
if (descriptor->descriptor_type == USB_DESCRIPTOR_TYPE_CONFIGURATION && !configuration->base.descriptor_type) {
memory_copy(descriptor, sizeof(usb_configuration_descriptor_t), configuration);
}
if (descriptor->descriptor_type == USB_DESCRIPTOR_TYPE_INTERFACE && !interface->base.descriptor_type) {
memory_copy(descriptor, sizeof(usb_interface_descriptor_t), interface);
}
if (descriptor->descriptor_type == USB_DESCRIPTOR_TYPE_ENDPOINT && !endpoint->base.descriptor_type) {
memory_copy(descriptor, sizeof(usb_endpoint_descriptor_t), endpoint);
}
descriptor = (usb_descriptor_base_t *)((uint8_t *)descriptor + descriptor->length);
}
}
void usb_init() {
PRINT_LN(kernel_log, "Enumerating devices...");
@@ -29,16 +167,108 @@ void usb_init() {
}
void usb_enumerate(stream_t *out) {
for (uint8_t i = 0; i < xhci_port_count(); i++) {
if (!xhci_port_connected(i)) {
for (xhci_port_t port = 0; port < xhci_port_count(); port++) {
if (!xhci_port_connected(port)) {
continue;
}
xhci_slot_t slot = xhci_attach(i);
xhci_slot_t slot = xhci_attach(port);
if (slot == (xhci_slot_t)-1) {
continue;
}
xhci_control_transfer(slot, 0x0012000001000680ULL, &device_descriptor, sizeof(usb_device_descriptor_t));
usb_device_descriptor_t device;
usb_configuration_descriptor_t configuration;
usb_interface_descriptor_t interface;
usb_endpoint_descriptor_t endpoint;
scan_device(slot, &device, &configuration, &interface, &endpoint);
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);
xhci_detach(slot);
PRINT_LN(
out,
"%hx:%hx usb_version=%hx device_class=%hhx device_subclass=%hhx device_protocol=%hhx interface_class=%hhx interface_subclass=%hhx "
"interface_protocol=%hhx endpoint_address=%hhx max_packet_size=%hx interval=%hhx",
device.vendor_id, device.product_id, device.usb_version, device.device_class, device.device_subclass, device.device_protocol,
interface.interface_class, interface.interface_subclass, interface.interface_protocol, endpoint.endpoint_address,
endpoint.max_packet_size, endpoint.interval);
}
}
xhci_port_t usb_find_by_interface(uint8_t class, uint8_t subclass, uint8_t protocol) {
for (xhci_port_t port = 0; port < xhci_port_count(); port++) {
if (!xhci_port_connected(port)) {
continue;
}
xhci_slot_t slot = xhci_attach(port);
if (slot == (xhci_slot_t)-1) {
continue;
}
usb_device_descriptor_t device;
usb_configuration_descriptor_t configuration;
usb_interface_descriptor_t interface;
usb_endpoint_descriptor_t endpoint;
scan_device(slot, &device, &configuration, &interface, &endpoint);
xhci_detach(slot);
if ((class == NUL || interface.interface_class == class) && (subclass == NUL || interface.interface_subclass == subclass) &&
(protocol == NUL || interface.interface_protocol == protocol)) {
return port;
}
}
return (xhci_port_t)-1;
}
xhci_slot_t usb_attach(xhci_port_t port) {
return xhci_attach(port);
}
xhci_endpoint_t usb_open_endpoint(xhci_slot_t slot, uint8_t type, uint16_t max_packet_size) {
usb_device_descriptor_t device;
usb_configuration_descriptor_t configuration;
usb_interface_descriptor_t interface;
usb_endpoint_descriptor_t endpoint;
scan_device(slot, &device, &configuration, &interface, &endpoint);
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);
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_PROTOCOL;
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.");
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) {
return xhci_read_endpoint(slot, endpoint, max, to);
}
void usb_close_endpoint(xhci_slot_t slot, xhci_endpoint_t endpoint) {
return xhci_close_endpoint(slot, endpoint);
}
void usb_detach(xhci_slot_t slot) {
return xhci_detach(slot);
}
+13
View File
@@ -1,8 +1,21 @@
#pragma once
#include "src/kernel/stream.h"
#include "src/kernel/xhci.h"
#include <stdint.h>
void usb_init();
void usb_enumerate(stream_t *out);
xhci_port_t usb_find_by_interface(uint8_t class, uint8_t subclass, uint8_t protocol);
xhci_slot_t usb_attach(xhci_port_t port);
xhci_endpoint_t usb_open_endpoint(xhci_slot_t slot, uint8_t type, uint16_t max_packet_size);
uint16_t usb_read_endpoint(xhci_slot_t slot, xhci_endpoint_t endpoint, uint16_t max, void *to);
void usb_close_endpoint(xhci_slot_t slot, xhci_endpoint_t endpoint);
void usb_detach(xhci_slot_t slot);
+294 -102
View File
@@ -1,5 +1,6 @@
#include "src/kernel/xhci.h"
#include "src/kernel/log.h"
#include "src/kernel/memory.h"
#include "src/kernel/panic.h"
#include "src/kernel/pci.h"
#include "src/kernel/stream.h"
@@ -28,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_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_SCRATCHPAD_BUFFERS(c) (BITS_R((c)->hcsparams2, 31, 27) << 5 | BITS_R((c)->hcsparams2, 4, 0))
typedef volatile struct __attribute__((packed)) {
uint32_t usbcmd;
@@ -68,6 +70,7 @@ typedef volatile struct __attribute__((packed)) {
#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_PLL(p) BITS_R((p)->portsc, 8, 5)
#define XHCI_PORT_REGS_PP(p) BITS_R((p)->portsc, 9, 9)
#define XHCI_PORT_REGS_PS(p) BITS_R((p)->portsc, 13, 10)
@@ -94,6 +97,8 @@ static xhci_db_regs *db_regs;
#define MAX_SLOTS 32
static void *scratchpads[32];
static void *dcbaa[MAX_SLOTS + 1] __attribute__((aligned(PAGE_SIZE)));
typedef struct __attribute__((packed)) {
@@ -112,14 +117,19 @@ typedef struct __attribute__((packed)) {
#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_IOC_R(t) BITS_R((t)->control, 5, 5)
#define XHCI_TRB_IOC_W(t, v) BITS_W((t)->control, 5, 5, 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_ENDPOINT_ID_R(t) BITS_R((t)->control, 20, 16)
#define XHCI_TRB_ENDPOINT_ID_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_NORMAL 1
#define XHCI_TRB_TYPE_TRANSFER_SETUP_STAGE 2
#define XHCI_TRB_TYPE_TRANSFER_DATA_STAGE 3
#define XHCI_TRB_TYPE_TRANSFER_STATUS_STAGE 4
@@ -127,6 +137,8 @@ typedef struct __attribute__((packed)) {
#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_CONFIGURE_ENDPOINT 12
#define XHCI_TRB_TYPE_COMMAND_STOP_ENDPOINT 15
#define XHCI_TRB_TYPE_COMMAND_NOOP 23
#define XHCI_TRB_TYPE_EVENT_TRANSFER_COMPLETION 32
#define XHCI_TRB_TYPE_EVENT_COMMAND_COMPLETION 33
@@ -136,13 +148,25 @@ 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)));
#define RING(name) \
static uint8_t name##_r_cycle = 1; \
static uint8_t name##_r_i = 0; \
static xhci_trb_t name##_r[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)));
#define RING_WRAP(name) \
xhci_trb_t *name##_wrapper = &name##_r[RING_LENGTH]; \
name##_wrapper->parameter = (uint64_t)VIRT_TO_PHYS(name##_r); \
name##_wrapper->status = 0; \
XHCI_TRB_TRB_TYPE_W(name##_wrapper, XHCI_TRB_TYPE_LINK); \
XHCI_TRB_TOGGLE_CYCLE_W(name##_wrapper, 1);
#define RING_ADVANCE(name) \
name##_r_i = (name##_r_i + 1) % RING_LENGTH; \
XHCI_TRB_CYCLE_W(&name##_r[RING_LENGTH], name##_r_cycle); \
name##_r_cycle ^= (name##_r_i == 0);
RING(cmd);
RING(evt);
typedef struct __attribute__((packed)) {
uint32_t drop_flags;
@@ -173,10 +197,14 @@ typedef struct __attribute__((packed)) {
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)
#define XHCI_EP_CTX_INTERVAL_R(e) BITS_R((e)->dw0, 23, 16)
#define XHCI_EP_CTX_INTERVAL_W(e, v) BITS_W((e)->dw0, 23, 16, v)
#define XHCI_EP_CTX_EP_TYPE_R(e) BITS_R((e)->dw1, 5, 3)
#define XHCI_EP_CTX_EP_TYPE_W(e, v) BITS_W((e)->dw1, 5, 3, v)
#define XHCI_EP_CTX_MAX_PACKET_SIZE_R(e) BITS_R((e)->dw1, 31, 16)
#define XHCI_EP_CTX_MAX_PACKET_SIZE_W(e, v) BITS_W((e)->dw1, 31, 16, v)
#define XHCI_EP_CTX_MAX_PACKET_SIZE 64
typedef struct __attribute__((packed)) {
xhci_input_control_ctx_t control;
@@ -193,10 +221,15 @@ typedef struct __attribute__((packed)) {
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 uint8_t tsf_r_attached_slot = 0;
RING(tsf);
static uint8_t ep_r_attached_endpoint = 0;
RING(ep);
static uint16_t ep_transfer_size = 0;
static uint8_t ep_buffer[RING_LENGTH][XHCI_EP_CTX_MAX_PACKET_SIZE] __attribute__((aligned(64)));
static uint8_t ep_processed_events = RING_LENGTH;
static const xhci_trb_t *command_exec_sync(const xhci_trb_t *cmd) {
LOG_LN_STEP("Queueing command TRB...");
@@ -204,14 +237,10 @@ static const xhci_trb_t *command_exec_sync(const xhci_trb_t *cmd) {
LOG_VAL_TRACE(cmd->status, "%x");
LOG_VAL_TRACE(cmd->control, "%x");
xhci_trb_t *cre = &cr[cr_i];
xhci_trb_t *cre = &cmd_r[cmd_r_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);
XHCI_TRB_CYCLE_W(cre, cmd_r_cycle);
RING_ADVANCE(cmd);
LOG_LN_STEP("Ringing command doorbell...");
LOG_VAL_TRACE(op_regs->usbsts, "%x");
@@ -220,16 +249,17 @@ static const xhci_trb_t *command_exec_sync(const xhci_trb_t *cmd) {
LOG_LN_STEP("Waiting for completion...");
while (1) {
xhci_trb_t *cmp = &er[er_i];
while (XHCI_TRB_CYCLE_R(cmp) != er_cycle)
xhci_trb_t *cmp = &evt_r[evt_r_i];
while (XHCI_TRB_CYCLE_R(cmp) != evt_r_cycle)
;
er_i = (er_i + 1) % RING_LENGTH;
intr_regs->erdp = (uint64_t)(VIRT_TO_PHYS(&er[er_i]));
er_cycle ^= (er_i == 0);
RING_ADVANCE(evt);
if (XHCI_TRB_TRB_TYPE_R(cmp) != XHCI_TRB_TYPE_EVENT_COMMAND_COMPLETION || (void *)cmp->parameter != VIRT_TO_PHYS(cre)) {
LOG_LN_STEP("Received irrelevant event, skipping...");
LOG_VAL_TRACE(cmp->parameter, "%lx");
LOG_VAL_TRACE(cmp->status, "%x");
LOG_VAL_TRACE(cmp->control, "%x");
} else {
LOG_LN_STEP("Received command completion event...");
@@ -246,10 +276,88 @@ static const xhci_trb_t *command_exec_sync(const xhci_trb_t *cmd) {
}
}
static const xhci_trb_t *control_transfer_exec_sync(uint64_t setup, void *data, uint16_t length) {
ASSERT(tsf_r_attached_slot, "control_transfer_exec_sync: Slot not attached.");
LOG_LN_STEP("Queueing transfer TRBs...");
xhci_trb_t *tre = &tsf_r[tsf_r_i];
tre->parameter = setup;
tre->status = sizeof(setup);
XHCI_TRB_TRB_TYPE_W(tre, XHCI_TRB_TYPE_TRANSFER_SETUP_STAGE);
BITS_W(tre->control, 17, 16, 3);
BITS_W(tre->control, 6, 6, 1);
XHCI_TRB_CYCLE_W(tre, tsf_r_cycle);
LOG_VAL_TRACE(tre->parameter, "%lx");
LOG_VAL_TRACE(tre->status, "%x");
LOG_VAL_TRACE(tre->control, "%x");
RING_ADVANCE(tsf);
if (data != NUL) {
tre = &tsf_r[tsf_r_i];
tre->parameter = (uint64_t)VIRT_TO_PHYS(data);
tre->status = length;
XHCI_TRB_TRB_TYPE_W(tre, XHCI_TRB_TYPE_TRANSFER_DATA_STAGE);
BITS_W(tre->control, 16, 16, 1);
XHCI_TRB_CYCLE_W(tre, tsf_r_cycle);
LOG_VAL_TRACE(tre->parameter, "%lx");
LOG_VAL_TRACE(tre->status, "%x");
LOG_VAL_TRACE(tre->control, "%x");
RING_ADVANCE(tsf);
}
tre = &tsf_r[tsf_r_i];
XHCI_TRB_TRB_TYPE_W(tre, XHCI_TRB_TYPE_TRANSFER_STATUS_STAGE);
BITS_W(tre->control, 16, 16, 0);
XHCI_TRB_IOC_W(tre, 1);
XHCI_TRB_CYCLE_W(tre, tsf_r_cycle);
RING_ADVANCE(tsf);
LOG_LN_STEP("Ringing slot doorbell...");
db_regs[tsf_r_attached_slot] = 1;
LOG_LN_STEP("Waiting for completion...");
while (1) {
xhci_trb_t *cmp = &evt_r[evt_r_i];
while (XHCI_TRB_CYCLE_R(cmp) != evt_r_cycle)
;
RING_ADVANCE(evt);
if (XHCI_TRB_TRB_TYPE_R(cmp) != XHCI_TRB_TYPE_EVENT_TRANSFER_COMPLETION || (void *)cmp->parameter != VIRT_TO_PHYS(tre)) {
LOG_LN_STEP("Received irrelevant event, skipping...");
LOG_VAL_TRACE(cmp->parameter, "%lx");
LOG_VAL_TRACE(cmp->status, "%x");
LOG_VAL_TRACE(cmp->control, "%x");
} else {
LOG_LN_STEP("Received transfer completion event...");
LOG_VAL_TRACE(cmp->parameter, "%lx");
LOG_VAL_TRACE(cmp->status, "%x");
LOG_VAL_TRACE(cmp->control, "%x");
ASSERT(XHCI_TRB_COMPLETION_CODE(cmp) == 1, "control_transfer_exec_sync: Transfer failed.");
LOG_LN_STEP("Done.");
return cmp;
}
}
}
void xhci_init() {
LOG_LN_INFO("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.");
ASSERT(bdf != (pci_bdf_t)-1, "xhci_init: No suitable controllers found.");
LOG_LN_INFO("Mapping controller to virtual memory...");
pci_map(bdf, cap_regs, 4);
@@ -300,19 +408,22 @@ void xhci_init() {
LOG_VAL_DATA(op_regs->usbsts, "%x");
LOG_LN_INFO("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);
RING_WRAP(cmd);
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...");
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;
op_regs->crcr = (uint64_t)VIRT_TO_PHYS(cmd_r) | cmd_r_cycle;
erst[0].size = RING_LENGTH;
erst[0].base = (uint64_t)VIRT_TO_PHYS(er);
erst[0].base = (uint64_t)VIRT_TO_PHYS(evt_r);
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;
@@ -336,6 +447,7 @@ void xhci_init() {
;
LOG_VAL_DATA(op_regs->usbcmd, "%x");
LOG_VAL_DATA(op_regs->usbsts, "%x");
sleep(1000); // Wait for ports to settle
PRINT_LN(kernel_log, "Enumerating ports...");
xhci_enumerate(kernel_log);
@@ -343,9 +455,9 @@ void xhci_init() {
}
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]));
for (uint16_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 portsc=%x", 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]), port_regs[i].portsc);
}
}
@@ -353,27 +465,37 @@ uint8_t xhci_port_count() {
return XHCI_CAP_REGS_MAX_PORTS(cap_regs);
}
uint8_t xhci_port_connected(uint8_t port) {
uint8_t xhci_port_connected(xhci_port_t port) {
ASSERT(port < XHCI_CAP_REGS_MAX_PORTS(cap_regs), "xhci_port_connected: Port doesn't exist.");
return XHCI_PORT_REGS_CCS(&port_regs[port]);
}
xhci_slot_t xhci_attach(uint8_t port) {
xhci_slot_t xhci_attach(xhci_port_t port) {
LOG_LN_STEP("Attaching port %d ...", port);
ASSERT(!tr_attached_slot, "xhci_attach: Can only attach one port at a time.")
ASSERT(port < XHCI_CAP_REGS_MAX_PORTS(cap_regs), "xhci_attach: Port doesn't exist.");
ASSERT(!tsf_r_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_STEP("Setting up transfer ring...");
memory_set(0, sizeof(tsf_r), tsf_r);
tsf_r_i = 0;
tsf_r_cycle = 1;
RING_WRAP(tsf);
LOG_LN_STEP("Resetting device...");
port_regs[port].portsc = 1 << 4;
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);
LOG_VAL_TRACE(port_regs[port].portsc, "%x");
while (XHCI_PORT_REGS_PR_R(&port_regs[port]))
;
while (!XHCI_PORT_REGS_PED(&port_regs[port]))
LOG_VAL_TRACE(port_regs[port].portsc, "%x");
while (!XHCI_PORT_REGS_PED(&port_regs[port]) && XHCI_PORT_REGS_PLL(&port_regs[port]) != 0)
;
LOG_VAL_TRACE(XHCI_PORT_REGS_PED(&port_regs[port]), "%hhx");
LOG_VAL_TRACE(port_regs[port].portsc, "%x");
LOG_LN_STEP("Enabling a slot...");
xhci_trb_t cmd_es;
@@ -387,9 +509,9 @@ xhci_slot_t xhci_attach(uint8_t port) {
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;
XHCI_EP_CTX_EP_TYPE_W(&input_ctx.ep[0], 4);
XHCI_EP_CTX_MAX_PACKET_SIZE_W(&input_ctx.ep[0], 64);
input_ctx.ep[0].dequeue = (uint64_t)VIRT_TO_PHYS(tsf_r) | tsf_r_cycle;
dcbaa[slot] = VIRT_TO_PHYS(&device_ctx);
@@ -400,7 +522,7 @@ xhci_slot_t xhci_attach(uint8_t port) {
XHCI_TRB_SLOT_ID_W(&cmd_ad, slot);
command_exec_sync(&cmd_ad);
tr_attached_slot = slot;
tsf_r_attached_slot = slot;
LOG_LN_STEP("Done.");
@@ -408,85 +530,155 @@ 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) {
ASSERT(tr_attached_slot == slot, "xhci_control_transfer: Can only attach one port at a time.");
ASSERT(slot < MAX_SLOTS, "xhci_control_transfer: Slot does not exist.");
ASSERT(tsf_r_attached_slot == slot, "xhci_control_transfer: Slot is not attached.");
ASSERT(!ep_r_attached_endpoint, "xhci_control_transfer: Can not control transfer while in polling mode.");
LOG_LN_STEP("Queueing transfer TRBs...");
return length - (control_transfer_exec_sync(setup, data, length)->status & 0xFFFFFF);
}
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);
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_STEP("Opening endpoint slot %d address %hhx...", slot, address);
LOG_VAL_TRACE(tr[tr_i].parameter, "%lx");
LOG_VAL_TRACE(tr[tr_i].status, "%x");
LOG_VAL_TRACE(tr[tr_i].control, "%x");
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(!ep_r_attached_endpoint, "xhci_open_endpoint: Can only open one endpoint at a time.");
ASSERT(max_packet_size <= XHCI_EP_CTX_MAX_PACKET_SIZE, "xhci_open_endpoint: Packet size too big.")
tr_i = (tr_i + 1) % RING_LENGTH;
XHCI_TRB_CYCLE_W(&tr[RING_LENGTH], tr_cycle);
tr_cycle ^= (tr_i == 0);
uint8_t ep_num = address & 0x0F;
uint8_t ep_dir = (address >> 7) & 1;
uint8_t dci = ep_num * 2 + ep_dir;
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_LN_STEP("Setting up endpoint ring...");
RING_WRAP(ep);
XHCI_TRB_CYCLE_W(&ep_r[RING_LENGTH], ep_r_cycle);
LOG_VAL_TRACE(tr[tr_i].parameter, "%lx");
LOG_VAL_TRACE(tr[tr_i].status, "%x");
LOG_VAL_TRACE(tr[tr_i].control, "%x");
LOG_LN_STEP("Configuring endpoint...");
memory_set(0, sizeof(xhci_input_ctx_t), &input_ctx);
input_ctx.slot = device_ctx.slot;
input_ctx.control.add_flags = (1 << 0) | (1 << dci);
XHCI_SLOT_CTX_CTX_ENTRIES_W(&input_ctx.slot, dci);
tr_i = (tr_i + 1) % RING_LENGTH;
XHCI_TRB_CYCLE_W(&tr[RING_LENGTH], tr_cycle);
tr_cycle ^= (tr_i == 0);
XHCI_EP_CTX_EP_TYPE_W(&input_ctx.ep[dci - 1], type);
XHCI_EP_CTX_MAX_PACKET_SIZE_W(&input_ctx.ep[dci - 1], max_packet_size);
XHCI_EP_CTX_INTERVAL_W(&input_ctx.ep[dci - 1], interval);
input_ctx.ep[dci - 1].dequeue = (uint64_t)VIRT_TO_PHYS(ep_r) | ep_r_cycle;
xhci_trb_t *tre = &tr[tr_i];
xhci_trb_t cmd_ce;
memory_set(0, sizeof(xhci_trb_t), &cmd_ce);
XHCI_TRB_TRB_TYPE_W(&cmd_ce, XHCI_TRB_TYPE_COMMAND_CONFIGURE_ENDPOINT);
cmd_ce.parameter = (uint64_t)VIRT_TO_PHYS(&input_ctx);
XHCI_TRB_SLOT_ID_W(&cmd_ce, slot);
command_exec_sync(&cmd_ce);
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);
ep_r_attached_endpoint = dci;
ep_transfer_size = max_packet_size;
tr_i = (tr_i + 1) % RING_LENGTH;
XHCI_TRB_CYCLE_W(&tr[RING_LENGTH], tr_cycle);
tr_cycle ^= (tr_i == 0);
return dci;
}
LOG_LN_STEP("Ringing slot doorbell...");
db_regs[tr_attached_slot] = 1;
uint16_t xhci_read_endpoint(xhci_slot_t slot, xhci_endpoint_t endpoint, uint16_t max, void *to) {
LOG_LN_STEP("Reading from endpoint %hhx...", endpoint);
LOG_LN_STEP("Waiting for completion...");
while (1) {
xhci_trb_t *cmp = &er[er_i];
while (XHCI_TRB_CYCLE_R(cmp) != er_cycle)
;
ASSERT(slot < MAX_SLOTS, "xhci_control_transfer: Slot does not exist.");
ASSERT(tsf_r_attached_slot == slot, "xhci_read_endpoint: Slot is not attached.");
ASSERT(ep_r_attached_endpoint == endpoint, "xhci_read_endpoint: Endpoint not open.");
er_i = (er_i + 1) % RING_LENGTH;
intr_regs->erdp = (uint64_t)(VIRT_TO_PHYS(&er[er_i]));
er_cycle ^= (er_i == 0);
if (ep_processed_events == RING_LENGTH) {
LOG_LN_STEP("Event batch finished, posting new transfer requests...");
ep_processed_events = 0;
if (XHCI_TRB_TRB_TYPE_R(cmp) != XHCI_TRB_TYPE_EVENT_TRANSFER_COMPLETION || (void *)cmp->parameter != VIRT_TO_PHYS(tre)) {
ASSERT(ep_r_i == 0, "xhci_read_endpoint: Endpoint ring cycle broken.");
for (uint64_t i = 0; i < RING_LENGTH; i++) {
xhci_trb_t *ere = &ep_r[i];
ere->parameter = (uint64_t)VIRT_TO_PHYS(&ep_buffer[i]);
ere->status = ep_transfer_size;
XHCI_TRB_TRB_TYPE_W(ere, XHCI_TRB_TYPE_TRANSFER_NORMAL);
XHCI_TRB_IOC_W(ere, 1);
XHCI_TRB_CYCLE_W(ere, ep_r_cycle);
}
ep_r_cycle ^= 1;
db_regs[tsf_r_attached_slot] = ep_r_attached_endpoint;
return 0;
}
uint16_t transferred = 0;
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];
if (XHCI_TRB_TRB_TYPE_R(cmp) != XHCI_TRB_TYPE_EVENT_TRANSFER_COMPLETION) {
LOG_LN_STEP("Received irrelevant event, skipping...");
LOG_VAL_TRACE(cmp->parameter, "%lx");
LOG_VAL_TRACE(cmp->status, "%x");
LOG_VAL_TRACE(cmp->control, "%x");
} else if (XHCI_TRB_COMPLETION_CODE(cmp) != 1) {
LOG_LN_STEP("Received unsuccessful transfer event, skipping...");
ep_processed_events++;
LOG_VAL_TRACE(cmp->parameter, "%lx");
LOG_VAL_TRACE(cmp->status, "%x");
LOG_VAL_TRACE(cmp->control, "%x");
} else {
LOG_LN_STEP("Received transfer completion event...");
LOG_LN_STEP("Received transfer event, returning...");
ep_processed_events++;
LOG_VAL_TRACE(cmp->parameter, "%lx");
LOG_VAL_TRACE(cmp->status, "%x");
LOG_VAL_TRACE(cmp->control, "%x");
ASSERT(XHCI_TRB_COMPLETION_CODE(cmp) == 1, "xhci_control_transfer: Transfer failed.");
xhci_trb_t *cmd = PHYS_TO_VIRT(cmp->parameter);
uint16_t size = max < cmd->status ? max : (uint16_t)cmd->status;
memory_copy(PHYS_TO_VIRT(cmd->parameter), size, to);
LOG_LN_STEP("Done.");
LOG_VAL_TRACE(size, "%d");
LOG_VAL_TRACE(*(uint64_t *)to, "%lx");
return length - (cmp->status & 0xFFFFFF);
transferred = size;
}
RING_ADVANCE(evt);
intr_regs->erdp = (uint64_t)VIRT_TO_PHYS(&evt_r[evt_r_i]);
}
return transferred;
}
void xhci_close_endpoint(xhci_slot_t slot, xhci_endpoint_t endpoint) {
LOG_LN_STEP("Closing endpoind %hhx...", endpoint);
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(ep_r_attached_endpoint == endpoint, "xhci_close_endpoint: Endpoint not open.");
ep_r_attached_endpoint = NUL;
xhci_trb_t cmd_se;
memory_set(0, sizeof(xhci_trb_t), &cmd_se);
XHCI_TRB_TRB_TYPE_W(&cmd_se, XHCI_TRB_TYPE_COMMAND_STOP_ENDPOINT);
XHCI_TRB_SLOT_ID_W(&cmd_se, slot);
XHCI_TRB_ENDPOINT_ID_W(&cmd_se, endpoint);
command_exec_sync(&cmd_se);
}
void xhci_detach(xhci_slot_t slot) {
LOG_LN_STEP("Detaching slot %d...", slot);
ASSERT(tr_attached_slot == slot, "xhci_detach: Slot not attached.");
ASSERT(slot < MAX_SLOTS, "xhci_control_transfer: Slot does not exist.");
ASSERT(tsf_r_attached_slot == slot, "xhci_detach: Slot not attached.");
ASSERT(!ep_r_attached_endpoint, "xhci_detach: Can not detach slot while in polling mode.");
xhci_trb_t cmd_ds;
memory_set(0, sizeof(xhci_trb_t), &cmd_ds);
@@ -495,7 +687,7 @@ void xhci_detach(xhci_slot_t slot) {
command_exec_sync(&cmd_ds);
dcbaa[slot] = 0;
tr_attached_slot = 0;
tsf_r_attached_slot = 0;
LOG_LN_STEP("Done.");
}
+10 -2
View File
@@ -3,7 +3,9 @@
#include "src/kernel/stream.h"
#include <stdint.h>
typedef uint8_t xhci_port_t;
typedef uint8_t xhci_slot_t;
typedef uint8_t xhci_endpoint_t;
void xhci_init(); // Assuming one and only one xHCI controller.
@@ -11,10 +13,16 @@ void xhci_enumerate(stream_t *out);
uint8_t xhci_port_count();
uint8_t xhci_port_connected(uint8_t port);
uint8_t xhci_port_connected(xhci_port_t port);
xhci_slot_t xhci_attach(uint8_t port);
xhci_slot_t xhci_attach(xhci_port_t port);
uint64_t xhci_control_transfer(xhci_slot_t slot, uint64_t setup, void *data, uint16_t length);
xhci_endpoint_t xhci_open_endpoint(xhci_slot_t slot, uint8_t address, uint8_t type, uint16_t max_packet_size, uint8_t interval);
uint16_t xhci_read_endpoint(xhci_slot_t slot, xhci_endpoint_t endpoint, uint16_t max, void *to);
void xhci_close_endpoint(xhci_slot_t slot, xhci_endpoint_t endpoint);
void xhci_detach(xhci_slot_t slot);
+5
View File
@@ -17,3 +17,8 @@ typedef uint64_t exit_code_t;
#define EXIT_CODE_OK 0
#define EXIT_CODE_NOT_FOUND ((uint64_t)-2)
#define EXIT_CODE_GENERAL_FAILURE ((uint64_t)-1)
static inline void sleep(uint64_t ms) {
for (volatile uint64_t i = 1000000 * ms; i; i--) // Very approximately
;
}
+1 -2
View File
@@ -10,8 +10,7 @@ exit_code_t main(uint64_t argc, const char **argv) {
uint64_t code = EXIT_CODE_OK;
for (uint64_t i = 1; i < argc; i++) {
uint64_t removed = remove(argv[i]);
if (removed == (uint64_t)-1) {
if (remove(argv[i]) == (uint64_t)-1) {
ERR_S("rm: could not remove file\n");
code = EXIT_CODE_GENERAL_FAILURE;
}
+7 -2
View File
@@ -96,7 +96,12 @@ static void execute(char *command) {
uint8_t pids_count = 0;
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];
uint8_t argc = (uint8_t)string_split(subcommands[i], ' ', 16, argv);
@@ -166,7 +171,7 @@ exit_code_t main() {
print_prompt();
char line[256];
uint64_t offset;
uint64_t offset = 0;
char chunk[64];
uint64_t received;
+3 -4
View File
@@ -3,8 +3,7 @@ ENTRY(_start)
SECTIONS {
. = 0x0000000000400000;
.text : { *(.text) }
.rodata : { *(.rodata) }
.data : { *(.data) }
.bss : { *(.bss) }
.text : { *(.text*) }
.rodata : { *(.rodata*) *(.lrodata*) }
.data : { *(.data*) *(.ldata*) *(.bss*) *(.lbss*) }
}
+1 -1
View File
@@ -9,7 +9,7 @@ fi
qemu-system-x86_64 \
"${debug_flags[@]}" \
-monitor stdio \
-drive file=build/os.img,format=raw,if=none,id=nvme0 \
-drive file=build/os.raw,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 \