Compare commits

...
17 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
freywar e4b9a79a5f Add more logging levels 2026-08-10 21:01:39 +03:00
freywar 4813725cf3 Add basic xHCI driver and USB devices enumeration 2026-08-07 21:11:08 +03:00
freywar a2128de3e2 Add common way to access bit ranges, log all NVMe devices on startup 2026-07-27 20:45:00 +03:00
freywar 572cb7647f Improve kernel logging
Current function name is automatically prepended to all log entries.
Helper macros added to log local variables.
2026-07-27 20:41:45 +03:00
freywar 3b8125eb0c Fix GDT and remove unused code from MBR 2026-07-23 21:23:38 +03:00
freywar 065aa261f5 Add simple NVMe module and switch to NVMe emulation 2026-07-23 20:52:10 +03:00
freywar ba9beebf8e Add simple kernel logging and improve error messages 2026-07-23 20:50:51 +03:00
37 changed files with 2145 additions and 492 deletions
+1
View File
@@ -0,0 +1 @@
![It works!](./WATCHME.png)
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 949 KiB

+26 -17
View File
@@ -3,25 +3,34 @@ set -euo pipefail
export CC=clang export CC=clang
meson setup build --reconfigure LOG_LEVEL=1
for arg in "${@}"; do
case "${arg}" in
-v) LOG_LEVEL=2 ;;
-vv) LOG_LEVEL=3 ;;
-vvv) LOG_LEVEL=4 ;;
esac
done
meson setup build --reconfigure -Dc_args="-DLOG_LEVEL=${LOG_LEVEL}"
meson compile -v -C build meson compile -v -C build
sector_size=512 sector_size=512
partition_offset=2048 partition_offset=2048
dd if=/dev/zero of=build/os.img bs="${sector_size}" count="$((partition_offset + 20480))" dd if=/dev/zero of=build/os.raw 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/mbr.bin of=build/os.raw 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 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.img mkfs.fat -F 16 --offset "${partition_offset}" build/os.raw
mcopy -i build/os.img@@"$((partition_offset * sector_size))" -s src ::src mcopy -i build/os.raw@@"$((partition_offset * sector_size))" -s src ::src
mcopy -i build/os.img@@"$((partition_offset * sector_size))" build/kernel.bin ::kernel.bin mcopy -i build/os.raw@@"$((partition_offset * sector_size))" build/kernel.bin ::kernel.bin
mmd -i build/os.img@@"$((partition_offset * sector_size))" ::bin mmd -i build/os.raw@@"$((partition_offset * sector_size))" ::bin
mcopy -i build/os.img@@"$((partition_offset * sector_size))" build/terminal ::bin/terminal mcopy -i build/os.raw@@"$((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.raw@@"$((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.raw@@"$((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.raw@@"$((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.raw@@"$((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.raw@@"$((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.raw@@"$((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.raw@@"$((partition_offset * sector_size))" build/rm ::bin/rm
mcopy -i build/os.img@@"$((partition_offset * sector_size))" build/wc ::bin/wc 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
+59 -353
View File
@@ -71,35 +71,46 @@ kernel_sources = files([
'src/kernel/fat16.c', 'src/kernel/fat16.c',
'src/kernel/fs.c', 'src/kernel/fs.c',
'src/kernel/gdt.c', 'src/kernel/gdt.c',
'src/kernel/hid-keyboard.c',
'src/kernel/idt.c', 'src/kernel/idt.c',
'src/kernel/kernel.c', 'src/kernel/kernel.c',
'src/kernel/keyboard.c', 'src/kernel/log.c',
'src/kernel/memory.c', 'src/kernel/memory.c',
'src/kernel/nvme.c',
'src/kernel/panic.c', 'src/kernel/panic.c',
'src/kernel/path.c', 'src/kernel/path.c',
'src/kernel/pci.c',
'src/kernel/pic.c', 'src/kernel/pic.c',
'src/kernel/pipe.c', 'src/kernel/pipe.c',
'src/kernel/process.c', 'src/kernel/process.c',
'src/kernel/ps2-keyboard.c',
'src/kernel/syscall.c', 'src/kernel/syscall.c',
'src/kernel/timer.c', 'src/kernel/timer.c',
'src/kernel/tss.c', 'src/kernel/tss.c',
'src/kernel/usb.c',
'src/kernel/vga.c', 'src/kernel/vga.c',
'src/kernel/xhci.c',
]) ])
c_args = [
'-ffreestanding',
'-nostdlib',
'-nostartfiles',
'-mno-red-zone',
'-mgeneral-regs-only',
'-fno-stack-protector',
'-DVERSION="' + meson.project_version() + '"',
]
kernel_elf = executable( kernel_elf = executable(
'kernel.elf', 'kernel.elf',
sources: [kernel_entry_o, timer_o, process_o, syscall_o, lib_sources, kernel_sources], sources: [kernel_entry_o, timer_o, process_o, syscall_o, lib_sources, kernel_sources],
c_args: [ c_args: [
'-ffreestanding', c_args,
'-nostdlib',
'-nostartfiles',
'-mno-red-zone',
'-mgeneral-regs-only',
'-mcmodel=kernel', '-mcmodel=kernel',
'-Wno-unused-command-line-argument', '-Wno-unused-command-line-argument',
'-Wconversion', '-Wconversion',
'-DKERNEL="yes"', '-DKERNEL="yes"',
'-DVERSION="' + meson.project_version() + '"',
], ],
link_args: [ link_args: [
'-T', meson.project_source_root() / 'src/kernel/linker.ld', '-T', meson.project_source_root() / 'src/kernel/linker.ld',
@@ -116,353 +127,48 @@ custom_target(
build_by_default: true, build_by_default: true,
) )
terminal_start = custom_target( foreach app : ['terminal', 'shell', 'echo', 'ls', 'cat', 'cp', 'mkdir', 'rm', 'wc']
'terminal_start', start = custom_target(
input: 'src/user/start.asm', f'@app@_start',
output: 'terminal_start.o', input: 'src/user/start.asm',
command: [nasm, '-f', 'elf64', '-o', '@OUTPUT@', '@INPUT@'], output: f'@app@_start.o',
) command: [nasm, '-f', 'elf64', '-o', '@OUTPUT@', '@INPUT@'],
)
terminal_syscall = custom_target( syscall = custom_target(
'terminal_syscall', f'@app@_syscall',
input: 'src/user/syscall.asm', input: 'src/user/syscall.asm',
output: 'terminal_syscall.o', output: f'@app@_syscall.o',
command: [nasm, '-f', 'elf64', '-o', '@OUTPUT@', '@INPUT@'], command: [nasm, '-f', 'elf64', '-o', '@OUTPUT@', '@INPUT@'],
) )
terminal_elf = executable( elf = executable(
'terminal.elf', f'@app@.elf',
sources: [terminal_start, terminal_syscall, lib_sources, 'src/user/app/terminal/terminal.c'], sources: [start, syscall, lib_sources, f'src/user/app/@app@/@app@.c'],
c_args: [ c_args: [
'-ffreestanding', c_args,
'-nostdlib', '-mcmodel=large',
'-fno-stack-protector', ],
'-mcmodel=large', link_args: [
'-DVERSION="' + meson.project_version() + '"', '-T', meson.project_source_root() / 'src/user/linker.ld',
], '-nostdlib',
link_args: [ ],
'-T', meson.project_source_root() / 'src/user/linker.ld', link_depends: 'src/user/linker.ld',
'-nostdlib', )
],
link_depends: 'src/user/linker.ld',
)
custom_target( elf_data = custom_target(
'terminal', f'@app@_data.elf',
input: terminal_elf, input: elf,
output: 'terminal', output: f'@app@_data.elf',
command: ['objcopy', '-O', 'binary', '@INPUT@', '@OUTPUT@'], command: ['objcopy', '--set-section-flags', '.data=alloc,load,contents', '@INPUT@', '@OUTPUT@'],
build_by_default: true, build_by_default: true,
) )
shell_start = custom_target( custom_target(
'shell_start', app,
input: 'src/user/start.asm', input: elf_data,
output: 'shell_start.o', output: app,
command: [nasm, '-f', 'elf64', '-o', '@OUTPUT@', '@INPUT@'], command: ['objcopy', '-O', 'binary', '@INPUT@', '@OUTPUT@'],
) build_by_default: true,
)
shell_syscall = custom_target( endforeach
'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,
)
+8 -6
View File
@@ -10,7 +10,6 @@ mov sp, 0x7000
FIRST_PARTITION_SECTOR equ 2048 ; TODO Read MBR. FIRST_PARTITION_SECTOR equ 2048 ; TODO Read MBR.
STAGE_BUFFER_SEGMENT equ 0x0900 STAGE_BUFFER_SEGMENT equ 0x0900
STAGE_BUFFER_OFFSET equ 0x0000 STAGE_BUFFER_OFFSET equ 0x0000
KERNEL_BUFFER_SEGMENT equ 0x2000
jmp prepare_kernel jmp prepare_kernel
@@ -39,9 +38,10 @@ root_start: dw 0
root_size: dw 0 root_size: dw 0
data_start: dw 0 data_start: dw 0
kernel_filename: db 'KERNEL BIN' kernel_filename: db 'KERNEL BIN'
kernel_cluster: dw 0 kernel_cluster: dw 0
kernel_buffer_offset: dw 0 kernel_buffer_segment: dw 0x2000
kernel_buffer_offset: dw 0
read_sectors: read_sectors:
xor bx, bx xor bx, bx
@@ -192,7 +192,7 @@ load_kernel_cluster:
add ax, [data_start] add ax, [data_start]
xor cx, cx xor cx, cx
mov cl, [bpb.sectors_per_cluster] mov cl, [bpb.sectors_per_cluster]
mov bx, KERNEL_BUFFER_SEGMENT mov bx, [kernel_buffer_segment]
mov es, bx mov es, bx
mov di, [kernel_buffer_offset] mov di, [kernel_buffer_offset]
call read_sectors call read_sectors
@@ -201,6 +201,8 @@ load_kernel_cluster:
shl ax, 9 ; * 512 shl ax, 9 ; * 512
add ax, [kernel_buffer_offset] add ax, [kernel_buffer_offset]
mov [kernel_buffer_offset], ax mov [kernel_buffer_offset], ax
jnc find_next_kernel_cluster
add word [kernel_buffer_segment], 0x1000
find_next_kernel_cluster: find_next_kernel_cluster:
xor bx, bx xor bx, bx
@@ -245,7 +247,7 @@ gdt_data:
.base_low: dw 0x0000 .base_low: dw 0x0000
.base_middle: db 0x00 .base_middle: db 0x00
.access: db 10010010b ; present, ring 0, data, writable .access: db 10010010b ; present, ring 0, data, writable
.flags: db 00000000b .flags: db 10000000b
.base_high: db 0x00 .base_high: db 0x00
gdt_data_end: gdt_data_end:
gdt_end: gdt_end:
+18 -18
View File
@@ -1,6 +1,6 @@
#include "src/kernel/fat16.h" #include "src/kernel/fat16.h"
#include "src/kernel/ata.h"
#include "src/kernel/fs.h" #include "src/kernel/fs.h"
#include "src/kernel/nvme.h"
#include "src/kernel/panic.h" #include "src/kernel/panic.h"
#include "src/kernel/stream.h" #include "src/kernel/stream.h"
#include "src/lib/memory.h" #include "src/lib/memory.h"
@@ -135,7 +135,7 @@ static void ensure_bpb() {
} }
uint8_t sector[512]; uint8_t sector[512];
ata_read_sectors(FIRST_PARTITION_SECTOR, 1, &sector); nvme_read_sectors(FIRST_PARTITION_SECTOR, 1, &sector);
bpb = memory_allocate(sizeof(fat16_bpb_t)); bpb = memory_allocate(sizeof(fat16_bpb_t));
memory_copy(sector + 11, sizeof(fat16_bpb_t), bpb); memory_copy(sector + 11, sizeof(fat16_bpb_t), bpb);
data_start = FIRST_PARTITION_SECTOR + bpb->reserved_sectors + bpb->sectors_per_fat * bpb->fats_count + data_start = FIRST_PARTITION_SECTOR + bpb->reserved_sectors + bpb->sectors_per_fat * bpb->fats_count +
@@ -152,7 +152,7 @@ static void ensure_fat() {
ASSERT(bpb->sectors_per_fat < 256, "ensure_fat: big FAT not implemented"); ASSERT(bpb->sectors_per_fat < 256, "ensure_fat: big FAT not implemented");
fat = memory_allocate(bpb->sectors_per_fat * SECTOR_SIZE); fat = memory_allocate(bpb->sectors_per_fat * SECTOR_SIZE);
ata_read_sectors(FIRST_PARTITION_SECTOR + bpb->reserved_sectors, (uint8_t)bpb->sectors_per_fat, fat); nvme_read_sectors(FIRST_PARTITION_SECTOR + bpb->reserved_sectors, (uint8_t)bpb->sectors_per_fat, fat);
} }
static uint16_t allocate_cluster() { static uint16_t allocate_cluster() {
@@ -168,7 +168,7 @@ static uint16_t allocate_cluster() {
static void flush_fat() { static void flush_fat() {
ensure_fat(); ensure_fat();
ata_write_sectors(FIRST_PARTITION_SECTOR + bpb->reserved_sectors, (uint8_t)bpb->sectors_per_fat, fat); nvme_write_sectors(FIRST_PARTITION_SECTOR + bpb->reserved_sectors, (uint8_t)bpb->sectors_per_fat, fat);
} }
static uint16_t read_directory(uint16_t dir_cluster, fat16_dir_entry_t **entries) { static uint16_t read_directory(uint16_t dir_cluster, fat16_dir_entry_t **entries) {
@@ -177,7 +177,7 @@ static uint16_t read_directory(uint16_t dir_cluster, fat16_dir_entry_t **entries
if (!dir_cluster) { if (!dir_cluster) {
uint8_t sectors = (uint8_t)((sizeof(fat16_dir_entry_t) * bpb->root_entry_count + SECTOR_SIZE - 1) / SECTOR_SIZE); uint8_t sectors = (uint8_t)((sizeof(fat16_dir_entry_t) * bpb->root_entry_count + SECTOR_SIZE - 1) / SECTOR_SIZE);
*entries = memory_allocate(sectors * SECTOR_SIZE); *entries = memory_allocate(sectors * SECTOR_SIZE);
ata_read_sectors(FIRST_PARTITION_SECTOR + bpb->reserved_sectors + bpb->fats_count * bpb->sectors_per_fat, sectors, *entries); nvme_read_sectors(FIRST_PARTITION_SECTOR + bpb->reserved_sectors + bpb->fats_count * bpb->sectors_per_fat, sectors, *entries);
return bpb->root_entry_count; return bpb->root_entry_count;
} else { } else {
ensure_fat(); ensure_fat();
@@ -193,7 +193,7 @@ static uint16_t read_directory(uint16_t dir_cluster, fat16_dir_entry_t **entries
uint8_t *chunk = (uint8_t *)*entries; uint8_t *chunk = (uint8_t *)*entries;
next_cluster = dir_cluster; next_cluster = dir_cluster;
while (next_cluster < 0xFFF8) { while (next_cluster < 0xFFF8) {
ata_read_sectors(data_start + bpb->sectors_per_cluster * (next_cluster - 2), bpb->sectors_per_cluster, chunk); nvme_read_sectors(data_start + bpb->sectors_per_cluster * (next_cluster - 2), bpb->sectors_per_cluster, chunk);
chunk += bpb->sectors_per_cluster * SECTOR_SIZE; chunk += bpb->sectors_per_cluster * SECTOR_SIZE;
next_cluster = fat[next_cluster]; next_cluster = fat[next_cluster];
} }
@@ -223,15 +223,15 @@ static void write_directory(uint16_t dir_cluster, const fat16_dir_entry_t *entri
"write_directory: entries must be aligned to clusters"); "write_directory: entries must be aligned to clusters");
if (!dir_cluster) { if (!dir_cluster) {
ata_write_sectors(FIRST_PARTITION_SECTOR + bpb->reserved_sectors + bpb->fats_count * bpb->sectors_per_fat, nvme_write_sectors(FIRST_PARTITION_SECTOR + bpb->reserved_sectors + bpb->fats_count * bpb->sectors_per_fat,
(uint8_t)((sizeof(fat16_dir_entry_t) * bpb->root_entry_count + SECTOR_SIZE - 1) / SECTOR_SIZE), entries); (uint8_t)((sizeof(fat16_dir_entry_t) * bpb->root_entry_count + SECTOR_SIZE - 1) / SECTOR_SIZE), entries);
} else { } else {
ensure_fat(); ensure_fat();
uint8_t *chunk = (uint8_t *)entries; uint8_t *chunk = (uint8_t *)entries;
uint16_t next_cluster = dir_cluster; uint16_t next_cluster = dir_cluster;
while (count) { while (count) {
ata_write_sectors(data_start + bpb->sectors_per_cluster * (next_cluster - 2), bpb->sectors_per_cluster, chunk); nvme_write_sectors(data_start + bpb->sectors_per_cluster * (next_cluster - 2), bpb->sectors_per_cluster, chunk);
chunk += bpb->sectors_per_cluster * SECTOR_SIZE; chunk += bpb->sectors_per_cluster * SECTOR_SIZE;
count -= bpb->sectors_per_cluster * SECTOR_SIZE / sizeof(fat16_dir_entry_t); count -= bpb->sectors_per_cluster * SECTOR_SIZE / sizeof(fat16_dir_entry_t);
if (count && fat[next_cluster] >= 0xFFF8) { if (count && fat[next_cluster] >= 0xFFF8) {
@@ -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);
@@ -418,7 +418,7 @@ uint64_t fat16_read(const fs_node_t *file, uint32_t offset, uint32_t bytes, void
uint8_t *cursor = to; uint8_t *cursor = to;
uint8_t *tmp = memory_allocate(cluster_size); uint8_t *tmp = memory_allocate(cluster_size);
ata_read_sectors(data_start + bpb->sectors_per_cluster * (next_cluster - 2), bpb->sectors_per_cluster, tmp); nvme_read_sectors(data_start + bpb->sectors_per_cluster * (next_cluster - 2), bpb->sectors_per_cluster, tmp);
uint32_t prefix_size = bytes <= cluster_size - offset ? bytes : cluster_size - offset; uint32_t prefix_size = bytes <= cluster_size - offset ? bytes : cluster_size - offset;
memory_copy(tmp + offset, prefix_size, cursor); memory_copy(tmp + offset, prefix_size, cursor);
bytes -= prefix_size; bytes -= prefix_size;
@@ -427,7 +427,7 @@ uint64_t fat16_read(const fs_node_t *file, uint32_t offset, uint32_t bytes, void
next_cluster = fat[next_cluster]; next_cluster = fat[next_cluster];
while (bytes >= cluster_size) { while (bytes >= cluster_size) {
ata_read_sectors(data_start + bpb->sectors_per_cluster * (next_cluster - 2), bpb->sectors_per_cluster, cursor); nvme_read_sectors(data_start + bpb->sectors_per_cluster * (next_cluster - 2), bpb->sectors_per_cluster, cursor);
bytes -= cluster_size; bytes -= cluster_size;
readden += cluster_size; readden += cluster_size;
cursor += cluster_size; cursor += cluster_size;
@@ -435,7 +435,7 @@ uint64_t fat16_read(const fs_node_t *file, uint32_t offset, uint32_t bytes, void
} }
if (bytes) { if (bytes) {
ata_read_sectors(data_start + bpb->sectors_per_cluster * (next_cluster - 2), bpb->sectors_per_cluster, tmp); nvme_read_sectors(data_start + bpb->sectors_per_cluster * (next_cluster - 2), bpb->sectors_per_cluster, tmp);
memory_copy(tmp, bytes, cursor); memory_copy(tmp, bytes, cursor);
readden += bytes; readden += bytes;
} }
@@ -489,17 +489,17 @@ uint64_t fat16_write(fs_node_t *file, uint32_t offset, const void *from, uint32_
const uint8_t *cursor = from; const uint8_t *cursor = from;
uint8_t *tmp = memory_allocate(cluster_size); uint8_t *tmp = memory_allocate(cluster_size);
ata_read_sectors(data_start + bpb->sectors_per_cluster * (next_cluster - 2), bpb->sectors_per_cluster, tmp); nvme_read_sectors(data_start + bpb->sectors_per_cluster * (next_cluster - 2), bpb->sectors_per_cluster, tmp);
uint32_t prefix_size = bytes <= cluster_size - offset ? bytes : cluster_size - offset; uint32_t prefix_size = bytes <= cluster_size - offset ? bytes : cluster_size - offset;
memory_copy(cursor, prefix_size, tmp + offset); memory_copy(cursor, prefix_size, tmp + offset);
ata_write_sectors(data_start + bpb->sectors_per_cluster * (next_cluster - 2), bpb->sectors_per_cluster, tmp); nvme_write_sectors(data_start + bpb->sectors_per_cluster * (next_cluster - 2), bpb->sectors_per_cluster, tmp);
bytes -= prefix_size; bytes -= prefix_size;
written += prefix_size; written += prefix_size;
cursor += prefix_size; cursor += prefix_size;
next_cluster = fat[next_cluster]; next_cluster = fat[next_cluster];
while (bytes >= cluster_size) { while (bytes >= cluster_size) {
ata_write_sectors(data_start + bpb->sectors_per_cluster * (next_cluster - 2), bpb->sectors_per_cluster, cursor); nvme_write_sectors(data_start + bpb->sectors_per_cluster * (next_cluster - 2), bpb->sectors_per_cluster, cursor);
bytes -= cluster_size; bytes -= cluster_size;
written += cluster_size; written += cluster_size;
cursor += cluster_size; cursor += cluster_size;
@@ -507,9 +507,9 @@ uint64_t fat16_write(fs_node_t *file, uint32_t offset, const void *from, uint32_
} }
if (bytes) { if (bytes) {
ata_read_sectors(data_start + bpb->sectors_per_cluster * (next_cluster - 2), bpb->sectors_per_cluster, tmp); nvme_read_sectors(data_start + bpb->sectors_per_cluster * (next_cluster - 2), bpb->sectors_per_cluster, tmp);
memory_copy(cursor, bytes, tmp); memory_copy(cursor, bytes, tmp);
ata_write_sectors(data_start + bpb->sectors_per_cluster * (next_cluster - 2), bpb->sectors_per_cluster, tmp); nvme_write_sectors(data_start + bpb->sectors_per_cluster * (next_cluster - 2), bpb->sectors_per_cluster, tmp);
written += bytes; written += bytes;
} }
+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.
+4
View File
@@ -38,3 +38,7 @@ void idt_set_entry(int vector, void (*handler)(struct interrupt_frame *), uint8_
idt[vector].offset_high = (addr >> 32) & 0xFFFFFFFF; idt[vector].offset_high = (addr >> 32) & 0xFFFFFFFF;
idt[vector].zero = 0; idt[vector].zero = 0;
} }
void idt_set_entry_ec(int vector, void (*handler)(struct interrupt_frame *, uint64_t error), uint8_t flags) {
idt_set_entry(vector, (void *)handler, flags);
}
+2
View File
@@ -13,3 +13,5 @@ struct interrupt_frame {
void idt_init(); void idt_init();
void idt_set_entry(int vector, void (*handler)(struct interrupt_frame *), uint8_t flags); void idt_set_entry(int vector, void (*handler)(struct interrupt_frame *), uint8_t flags);
void idt_set_entry_ec(int vector, void (*handler)(struct interrupt_frame *, uint64_t error), uint8_t flags);
+35 -7
View File
@@ -1,17 +1,23 @@
#include "src/kernel/fs.h" #include "src/kernel/fs.h"
#include "src/kernel/gdt.h" #include "src/kernel/gdt.h"
#include "src/kernel/hid-keyboard.h"
#include "src/kernel/idt.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" #include "src/kernel/panic.h"
#include "src/kernel/path.h" #include "src/kernel/path.h"
#include "src/kernel/pci.h"
#include "src/kernel/pic.h" #include "src/kernel/pic.h"
#include "src/kernel/pipe.h" #include "src/kernel/pipe.h"
#include "src/kernel/process.h" #include "src/kernel/process.h"
#include "src/kernel/stream.h"
#include "src/kernel/syscall.h" #include "src/kernel/syscall.h"
#include "src/kernel/timer.h" #include "src/kernel/timer.h"
#include "src/kernel/tss.h" #include "src/kernel/tss.h"
#include "src/kernel/usb.h"
#include "src/kernel/util.h" #include "src/kernel/util.h"
#include "src/kernel/vga.h" #include "src/kernel/vga.h"
#include "src/kernel/xhci.h"
#include "src/lib/layout.h" #include "src/lib/layout.h"
#include "src/lib/memory.h" #include "src/lib/memory.h"
#include "src/lib/syscall.h" #include "src/lib/syscall.h"
@@ -22,8 +28,23 @@ __attribute__((interrupt)) void isr_divide_by_zero(__attribute__((unused)) struc
; ;
} }
__attribute__((interrupt)) void isr_page_fault(__attribute__((unused)) struct interrupt_frame *frame) { __attribute__((interrupt)) void isr_invalid_opcode(struct interrupt_frame *frame) {
vga_set_string(VGA_HEIGHT - 1, 0, "EXCEPTION: page fault", 0x4F); 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));
char msg[80];
string_format("EXCEPTION: page fault; addr=%lx err=%lx", 80, msg, cr2, error_code);
vga_set_string(VGA_HEIGHT - 1, 0, msg, 0x4F);
while (1) while (1)
; ;
} }
@@ -40,16 +61,24 @@ __attribute__((interrupt)) void isr_ata_primary(__attribute__((unused)) struct i
} }
void kernel_main() { void kernel_main() {
stream_t *vga = vga_init();
log_init(vga);
pic_init(); pic_init();
idt_init(); idt_init();
outb(0x21, inb(0x21) | 0x01); // mask out timer interrupt outb(0x21, inb(0x21) | 0x01); // mask out timer interrupt
idt_set_entry(0, isr_divide_by_zero, 0x8E); idt_set_entry(0, isr_divide_by_zero, 0x8E);
idt_set_entry(0x0E, isr_page_fault, 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(0x0D, isr_general_violation, 0x8E);
idt_set_entry(46, isr_ata_primary, 0x8E); idt_set_entry(46, isr_ata_primary, 0x8E);
gdt_init(); gdt_init();
syscall_init(); syscall_init();
pci_init();
nvme_init();
xhci_init();
usb_init();
process_t *kernel = memory_allocate(sizeof(process_t)); process_t *kernel = memory_allocate(sizeof(process_t));
kernel->pml4 = (uint64_t *)KERNEL_VIRTUAL_PML4; kernel->pml4 = (uint64_t *)KERNEL_VIRTUAL_PML4;
@@ -58,9 +87,8 @@ void kernel_main() {
kernel->kernel_stack = kernel->kernel_rsp = (uint8_t *)memory_allocate(PAGE_SIZE) + PAGE_SIZE; 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->user_stack = kernel->user_rsp = (uint8_t *)memory_allocate(PAGE_SIZE) + PAGE_SIZE;
kernel->cwd = memory_allocate(sizeof(path_t)); kernel->cwd = memory_allocate(sizeof(path_t));
kernel->fds[STDIN] = keyboard_init(); kernel->fds[STDIN] = hid_keyboard_init();
kernel->fds[STDOUT] = vga_init(); kernel->fds[STDOUT] = kernel->fds[STDERR] = vga;
kernel->fds[STDERR] = kernel->fds[STDOUT];
kernel->free_fd = STDERR + 1; kernel->free_fd = STDERR + 1;
kernel->code = EXIT_CODE_OK; kernel->code = EXIT_CODE_OK;
+8
View File
@@ -0,0 +1,8 @@
#include "src/kernel/log.h"
#include "src/kernel/stream.h"
stream_t *kernel_log;
void log_init(stream_t *log) {
kernel_log = log;
}
+60
View File
@@ -0,0 +1,60 @@
#pragma once
#include "src/kernel/stream.h"
#include "src/lib/string.h"
extern stream_t *kernel_log;
#ifndef LOG_LEVEL
#define LOG_LEVEL 1
#endif
#define LOG_LEVEL_INFO 1
#define LOG_LEVEL_STEP 2
#define LOG_LEVEL_DATA 3
#define LOG_LEVEL_TRACE 4
static uint64_t print_size;
static char print_buffer[512];
#define PRINT(stream, fmt, ...) \
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, ...) \
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, print_buffer, print_size); \
stream->write(stream, "\n", 1);
#define PRINT_VAL(stream, v, f) PRINT_LN(#v = f, v)
#define LOG(level, fmt, ...) \
if (LOG_LEVEL >= level) { \
PRINT(kernel_log, fmt, ##__VA_ARGS__) \
}
#define LOG_LN(level, fmt, ...) \
if (LOG_LEVEL >= level) { \
PRINT_LN(kernel_log, fmt, ##__VA_ARGS__) \
}
#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);
+14 -1
View File
@@ -1,4 +1,5 @@
#include "src/kernel/memory.h" #include "src/kernel/memory.h"
#include "src/kernel/log.h"
#include "src/kernel/panic.h" #include "src/kernel/panic.h"
#include "src/lib/layout.h" #include "src/lib/layout.h"
#include "src/lib/memory.h" #include "src/lib/memory.h"
@@ -66,7 +67,10 @@ void memory_page_map(uint64_t *pml4, void *virt, void *phys, uint64_t flags) {
const uint64_t pd_index = (ivirt >> 21) & 0x1FF; const uint64_t pd_index = (ivirt >> 21) & 0x1FF;
const uint64_t pt_index = (ivirt >> 12) & 0x1FF; const uint64_t pt_index = (ivirt >> 12) & 0x1FF;
LOG_LN_TRACE("Mapping physical %lx to virtual %lx / [%u, %u, %u, %u]...", phys, virt, pml4_index, pdpt_index, pd_index, pt_index);
if (!(pml4[pml4_index] & PAGE_PRESENT)) { if (!(pml4[pml4_index] & PAGE_PRESENT)) {
LOG_LN_TRACE("PML4 entry %u does not exist, creating...", pml4_index);
uint64_t *pdpt = memory_page_allocate(); uint64_t *pdpt = memory_page_allocate();
memory_set(0, PAGE_SIZE, PHYS_TO_VIRT(pdpt)); memory_set(0, PAGE_SIZE, PHYS_TO_VIRT(pdpt));
pml4[pml4_index] = (uint64_t)pdpt | PAGE_PRESENT | PAGE_WRITABLE | PAGE_USER; pml4[pml4_index] = (uint64_t)pdpt | PAGE_PRESENT | PAGE_WRITABLE | PAGE_USER;
@@ -74,21 +78,30 @@ void memory_page_map(uint64_t *pml4, void *virt, void *phys, uint64_t flags) {
uint64_t *pdpt = PHYS_TO_VIRT(pml4[pml4_index] & ~(uint64_t)0xFFF); uint64_t *pdpt = PHYS_TO_VIRT(pml4[pml4_index] & ~(uint64_t)0xFFF);
if (!(pdpt[pdpt_index] & PAGE_PRESENT)) { if (!(pdpt[pdpt_index] & PAGE_PRESENT)) {
LOG_LN_TRACE("PDPT entry %u does not exist, creating...", pdpt_index);
uint64_t *pd = memory_page_allocate(); uint64_t *pd = memory_page_allocate();
memory_set(0, PAGE_SIZE, PHYS_TO_VIRT(pd)); memory_set(0, PAGE_SIZE, PHYS_TO_VIRT(pd));
pdpt[pdpt_index] = (uint64_t)pd | PAGE_PRESENT | PAGE_WRITABLE | PAGE_USER; pdpt[pdpt_index] = (uint64_t)pd | PAGE_PRESENT | PAGE_WRITABLE | PAGE_USER;
} }
uint64_t *pd = PHYS_TO_VIRT(pdpt[pdpt_index] & ~(uint64_t)0xFFF); uint64_t *pd = PHYS_TO_VIRT(pdpt[pdpt_index] & ~(uint64_t)0xFFF);
ASSERT(!(pd[pd_index] & 0x80), "memory_page_map: huge page in PD"); ASSERT(!(pd[pd_index] & 0x80), "memory_page_map: Huge page in PD.");
if (!(pd[pd_index] & PAGE_PRESENT)) { if (!(pd[pd_index] & PAGE_PRESENT)) {
LOG_LN_TRACE("PD entry %u does not exist, creating...", pd_index);
uint64_t *pt = memory_page_allocate(); uint64_t *pt = memory_page_allocate();
memory_set(0, PAGE_SIZE, PHYS_TO_VIRT(pt)); memory_set(0, PAGE_SIZE, PHYS_TO_VIRT(pt));
pd[pd_index] = (uint64_t)pt | PAGE_PRESENT | PAGE_WRITABLE | PAGE_USER; pd[pd_index] = (uint64_t)pt | PAGE_PRESENT | PAGE_WRITABLE | PAGE_USER;
} }
uint64_t *pt = PHYS_TO_VIRT(pd[pd_index] & ~(uint64_t)0xFFF); uint64_t *pt = PHYS_TO_VIRT(pd[pd_index] & ~(uint64_t)0xFFF);
ASSERT(!(pt[pt_index] & PAGE_PRESENT), "memory_page_map: Page already mapped.")
LOG_LN_TRACE("PT entry %u does not exist, creating...", pt_index);
pt[pt_index] = (uint64_t)phys | flags | PAGE_PRESENT; pt[pt_index] = (uint64_t)phys | flags | PAGE_PRESENT;
LOG_VAL_TRACE(pt[pt_index], "%lx");
LOG_LN_TRACE("Done.");
} }
void memory_page_unmap(uint64_t *pml4, void *virt) { void memory_page_unmap(uint64_t *pml4, void *virt) {
+278
View File
@@ -0,0 +1,278 @@
#include "src/kernel/nvme.h"
#include "src/kernel/log.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
#define NVME_PCI_CLASS 0x01
#define NVME_PCI_SUBCLASS 0x08
typedef volatile struct __attribute__((packed)) {
uint64_t cap;
uint32_t vs;
uint32_t intms;
uint32_t intmc;
uint32_t cc;
uint32_t rsvd;
uint32_t csts;
uint32_t nssr;
uint32_t aqa;
uint64_t asq;
uint64_t acq;
} nvme_regs_t;
#define NVME_REGS_NVME_CS_SUPPORTED(r) BITS_R(r->cap, 37, 37)
#define NVME_REGS_MQES(r) BITS_R(r->cap, 15, 0)
#define NVME_REGS_IOCQES_R(r) BITS_R(r->cc, 23, 20)
#define NVME_REGS_IOCQES_W(r, v) BITS_W(r->cc, 23, 20, v)
#define NVME_REGS_IOSQES_R(r) BITS_R(r->cc, 19, 16)
#define NVME_REGS_IOSQES_W(r, v) BITS_W(r->cc, 19, 16, v)
#define NVME_REGS_CSS_R(r) BITS_R(r->cc, 6, 4)
#define NVME_REGS_CSS_W(r, v) BITS_W(r->cc, 6, 4, v)
#define NVME_REGS_EN_R(r) BITS_R(r->cc, 0, 0)
#define NVME_REGS_EN_W(r, v) BITS_W(r->cc, 0, 0, v)
#define NVME_REGS_RDY(r) BITS_R(r->csts, 0, 0)
#define NVME_REGS_ACQS_R(r) BITS_R(r->aqa, 27, 16)
#define NVME_REGS_ACQS_W(r, v) BITS_W(r->aqa, 27, 16, v)
#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_NVME;
static volatile uint32_t *db_regs = (uint32_t *)(KERNEL_VIRTUAL_NVME + 0x1000); // TODO Get stride from caps.
#define QUEUE_DEPTH 2
typedef volatile struct __attribute__((packed)) {
uint8_t opc;
uint8_t flags;
uint16_t cid;
uint32_t nsid;
uint64_t reserved;
uint64_t mptr;
uint64_t prp1;
uint64_t prp2;
uint32_t cdw10;
uint32_t cdw11;
uint32_t cdw12;
uint32_t cdw13;
uint32_t cdw14;
uint32_t cdw15;
} nvme_sqe_t;
typedef volatile struct __attribute__((packed)) {
uint32_t dw0;
uint32_t reserved;
uint16_t sqhd;
uint16_t sqid;
uint16_t cid;
uint16_t status;
} nvme_cqe_t;
#define NVME_CQE_STATUS(e) BITS_R(e->status, 15, 1)
#define NVME_CQE_P(e) BITS_R(e->status, 0, 0)
static uint8_t admin_cq_phase = 1;
static nvme_sqe_t admin_sq[QUEUE_DEPTH] __attribute__((aligned(PAGE_SIZE)));
static uint16_t admin_sq_tail = 0;
static nvme_cqe_t admin_cq[QUEUE_DEPTH] __attribute__((aligned(PAGE_SIZE)));
static uint16_t admin_cq_head = 0;
static uint8_t io_cq_phase = 1;
static nvme_sqe_t io_sq[QUEUE_DEPTH] __attribute__((aligned(PAGE_SIZE)));
static uint16_t io_sq_tail = 0;
static nvme_cqe_t io_cq[QUEUE_DEPTH] __attribute__((aligned(PAGE_SIZE)));
static uint16_t io_cq_head = 0;
static void admin_exec_sync(nvme_sqe_t *cmd) {
cmd->cid = admin_sq_tail;
LOG_LN_STEP("Queueing submission...");
LOG_VAL_TRACE(cmd->opc, "%hhx");
LOG_VAL_TRACE(cmd->nsid, "%x");
LOG_VAL_TRACE(cmd->cdw10, "%x");
LOG_VAL_TRACE(cmd->cdw11, "%x");
LOG_VAL_TRACE(cmd->cdw12, "%x");
LOG_VAL_TRACE(cmd->prp1, "%lx");
LOG_VAL_TRACE(cmd->prp2, "%lx");
memory_copy(cmd, sizeof(nvme_sqe_t), &admin_sq[admin_sq_tail]);
LOG_LN_STEP("Advancing submission doorbell...");
db_regs[0] = admin_sq_tail = (admin_sq_tail + 1) % QUEUE_DEPTH;
LOG_VAL_TRACE(admin_sq_tail, "%u");
LOG_LN_STEP("Waiting for completion...");
nvme_cqe_t *cmp = &admin_cq[admin_cq_head];
while (NVME_CQE_P(cmp) != admin_cq_phase)
;
LOG_LN_STEP("Received completion...");
LOG_VAL_TRACE(cmp->dw0, "%x");
LOG_VAL_TRACE(cmp->reserved, "%x");
LOG_VAL_TRACE(cmp->sqhd, "%hx");
LOG_VAL_TRACE(cmp->sqid, "%hx");
LOG_VAL_TRACE(cmp->cid, "%hx");
LOG_VAL_TRACE(cmp->status, "%hx");
ASSERT(NVME_CQE_STATUS(cmp) == 0, "admin_exec_sync: NVMe command failed.");
LOG_LN_STEP("Advancing completion doorbell...");
db_regs[1] = admin_cq_head = (admin_cq_head + 1) % QUEUE_DEPTH;
admin_cq_phase ^= (admin_cq_head == 0);
LOG_LN_STEP("Done.");
}
static void io_exec_sync(nvme_sqe_t *cmd) {
cmd->cid = io_sq_tail;
LOG_LN_STEP("Queueing submission...");
LOG_VAL_TRACE(cmd->opc, "%hhx");
LOG_VAL_TRACE(cmd->nsid, "%x");
LOG_VAL_TRACE(cmd->cdw10, "%x");
LOG_VAL_TRACE(cmd->cdw11, "%x");
LOG_VAL_TRACE(cmd->cdw12, "%x");
LOG_VAL_TRACE(cmd->prp1, "%x");
LOG_VAL_TRACE(cmd->prp2, "%x");
memory_copy(cmd, sizeof(nvme_sqe_t), &io_sq[io_sq_tail]);
LOG_LN_STEP("Advancing submission doorbell...");
db_regs[2] = io_sq_tail = (io_sq_tail + 1) % QUEUE_DEPTH;
LOG_LN_STEP("Waiting for completion...");
nvme_cqe_t *cmp = &io_cq[io_cq_head];
while (NVME_CQE_P(cmp) != io_cq_phase)
;
LOG_LN_STEP("Received completion...");
LOG_VAL_TRACE(cmp->dw0, "%x");
LOG_VAL_TRACE(cmp->reserved, "%x");
LOG_VAL_TRACE(cmp->sqhd, "%hx");
LOG_VAL_TRACE(cmp->sqid, "%hx");
LOG_VAL_TRACE(cmp->cid, "%hx");
LOG_VAL_TRACE(cmp->status, "%hx");
ASSERT(NVME_CQE_STATUS(cmp) == 0, "io_exec_sync: NVMe command failed.");
LOG_LN_STEP("Advancing completion doorbell...");
db_regs[3] = io_cq_head = (io_cq_head + 1) % QUEUE_DEPTH;
io_cq_phase ^= (io_cq_head == 0);
LOG_LN_STEP("Done.");
}
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 != (pci_bdf_t)-1, "nvme_init: No suitable devices found.");
LOG_LN_INFO("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_INFO("Disabling device...");
regs->cc = 0;
LOG_VAL_DATA(regs->cc, "%lx");
while (NVME_REGS_RDY(regs))
;
LOG_VAL_DATA(regs->cc, "%lx");
LOG_LN_INFO("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);
regs->acq = (uint64_t)(VIRT_TO_PHYS(admin_cq));
LOG_VAL_DATA(regs->asq, "%lx");
LOG_VAL_DATA(regs->acq, "%lx");
LOG_VAL_DATA(regs->aqa, "%lx");
LOG_LN_INFO("Enabling device...");
NVME_REGS_IOCQES_W(regs, 4);
NVME_REGS_IOSQES_W(regs, 6);
NVME_REGS_CSS_W(regs, 0);
NVME_REGS_EN_W(regs, 1);
LOG_VAL_DATA(regs->cc, "%lx");
while (!NVME_REGS_RDY(regs))
;
LOG_VAL_DATA(regs->cc, "%lx");
LOG_LN_INFO("Creating I/O completion queue...");
nvme_sqe_t create_io_cq = {
.opc = 0x05,
.prp1 = (uint64_t)VIRT_TO_PHYS(io_cq),
.cdw10 = ((QUEUE_DEPTH - 1) << 16) | 1,
.cdw11 = 1,
};
admin_exec_sync(&create_io_cq);
LOG_LN_INFO("Creating I/O submission queue...");
nvme_sqe_t create_io_sq = {
.opc = 0x01,
.prp1 = (uint64_t)VIRT_TO_PHYS(io_sq),
.cdw10 = ((QUEUE_DEPTH - 1) << 16) | 1,
.cdw11 = (1 << 16) | 1,
};
admin_exec_sync(&create_io_sq);
LOG_LN_INFO("Done.");
}
void nvme_read_sectors(uint32_t index, uint8_t count, void *to) {
LOG_LN_STEP("Reading %u sectors at %u...", count, index);
while (count) {
uint64_t remainder = PAGE_SIZE - (uint64_t)to % PAGE_SIZE;
uint8_t i_count = (uint8_t)((remainder + PAGE_SIZE) / SECTOR_SIZE);
i_count = i_count > count ? count : i_count;
nvme_sqe_t read_sq = {
.opc = 0x02,
.nsid = 1,
.cdw10 = index,
.cdw11 = 0,
.cdw12 = i_count - 1,
.prp1 = (uint64_t)VIRT_TO_PHYS(to),
.prp2 = i_count * SECTOR_SIZE > remainder ? (uint64_t)VIRT_TO_PHYS(to + remainder) : 0,
};
io_exec_sync(&read_sq);
index += i_count;
count -= i_count;
to = (void *)((uint8_t *)to + i_count * SECTOR_SIZE);
}
LOG_LN_STEP("Done.");
}
void nvme_write_sectors(uint32_t index, uint8_t count, const void *from) {
LOG_LN_STEP("Writing %u sectors at %u...", count, index);
while (count) {
uint64_t remainder = PAGE_SIZE - (uint64_t)from % PAGE_SIZE;
uint8_t i_count = (uint8_t)((remainder + PAGE_SIZE) / SECTOR_SIZE);
i_count = i_count > count ? count : i_count;
nvme_sqe_t write_sq = {
.opc = 0x01,
.nsid = 1,
.cdw10 = index,
.cdw11 = 0,
.cdw12 = i_count - 1,
.prp1 = (uint64_t)VIRT_TO_PHYS(from),
.prp2 = i_count * SECTOR_SIZE > remainder ? (uint64_t)VIRT_TO_PHYS(from + remainder) : 0,
};
io_exec_sync(&write_sq);
index += i_count;
count -= i_count;
from = (void *)((uint8_t *)from + i_count * SECTOR_SIZE);
}
LOG_LN_STEP("Done.");
}
+9
View File
@@ -0,0 +1,9 @@
#pragma once
#include <stdint.h>
void nvme_init(); // Assuming one and only one NVMe device.
void nvme_read_sectors(uint32_t index, uint8_t count, void *to);
void nvme_write_sectors(uint32_t index, uint8_t count, const void *from);
+1 -1
View File
@@ -2,7 +2,7 @@
#include "src/kernel/vga.h" #include "src/kernel/vga.h"
void kernel_panic(const char *msg, __attribute__((unused)) const char *file, __attribute__((unused)) int line) { 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) while (1)
; ;
} }
+116
View File
@@ -0,0 +1,116 @@
#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"
#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
#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)
#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);
}
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);
}
void pci_init() {
PRINT_LN(kernel_log, "Scanning devices...");
pci_enumerate(kernel_log);
PRINT_LN(kernel_log, "Done.");
}
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));
}
}
}
}
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; 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 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 (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);
LOG_LN_STEP("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) {
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));
}
pci_write(bdf, PCI_OFFSET_STATUS_COMMAND,
pci_read(bdf, PCI_OFFSET_STATUS_COMMAND) & ~(uint32_t)(PCI_COMMAND_MEMORY_SPACE | PCI_COMMAND_BUS_MASTER));
}
+15
View File
@@ -0,0 +1,15 @@
#pragma once
#include "src/kernel/stream.h"
typedef uint16_t pci_bdf_t;
void pci_init();
void pci_enumerate(stream_t *out);
pci_bdf_t pci_find_by_class(uint8_t class, uint8_t subclass, uint8_t iface);
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);
@@ -1,4 +1,4 @@
#include "src/kernel/keyboard.h" #include "src/kernel/ps2-keyboard.h"
#include "src/kernel/idt.h" #include "src/kernel/idt.h"
#include "src/kernel/process.h" #include "src/kernel/process.h"
#include "src/kernel/stream.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); uint8_t scancode = inb(0x60);
outb(0x20, 0x20); outb(0x20, 0x20);
on_key(scancode); on_key(scancode);
} }
stream_t *keyboard_init() { stream_t *ps2_keyboard_init() {
outb(0x21, inb(0x21) & ~0x02); outb(0x21, inb(0x21) & ~0x02);
idt_set_entry(33, isr_keyboard, 0x8E); idt_set_entry(33, isr_ps2_keyboard, 0x8E);
return &stream; return &stream;
} }
@@ -3,4 +3,4 @@
#include "src/kernel/stream.h" #include "src/kernel/stream.h"
#include <stdint.h> #include <stdint.h>
stream_t *keyboard_init(); stream_t *ps2_keyboard_init();
+274
View File
@@ -0,0 +1,274 @@
#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;
} 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;
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;
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...");
usb_enumerate(kernel_log);
PRINT_LN(kernel_log, "Done.");
}
void usb_enumerate(stream_t *out) {
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);
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);
}
+21
View File
@@ -0,0 +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);
+10
View File
@@ -22,6 +22,16 @@ static inline void outw(uint16_t port, uint16_t value) {
__asm__ volatile("outw %0, %1" : : "a"(value), "Nd"(port)); __asm__ volatile("outw %0, %1" : : "a"(value), "Nd"(port));
} }
static inline uint32_t inl(uint16_t port) {
uint32_t value;
__asm__ volatile("inl %1, %0" : "=a"(value) : "Nd"(port));
return value;
}
static inline void outl(uint16_t port, uint32_t value) {
__asm__ volatile("outl %0, %1" : : "a"(value), "Nd"(port));
}
static inline void insw(uint16_t port, void *to, uint32_t count) { static inline void insw(uint16_t port, void *to, uint32_t count) {
__asm__ volatile("rep insw" : "=D"(to), "=c"(count) : "d"(port), "D"(to), "c"(count) : "memory"); __asm__ volatile("rep insw" : "=D"(to), "=c"(count) : "d"(port), "D"(to), "c"(count) : "memory");
} }
+693
View File
@@ -0,0 +1,693 @@
#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"
#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)
#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;
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_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)
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 *scratchpads[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_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
#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_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
#define XHCI_TRB_TYPE_EVENT_PORT_STATUS_CHANGE 34
static xchi_erst_entry_t erst[1] __attribute__((aligned(PAGE_SIZE)));
#define RING_LENGTH 32
#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)));
#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;
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 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;
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 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...");
LOG_VAL_TRACE(cmd->parameter, "%lx");
LOG_VAL_TRACE(cmd->status, "%x");
LOG_VAL_TRACE(cmd->control, "%x");
xhci_trb_t *cre = &cmd_r[cmd_r_i];
memory_copy(cmd, sizeof(xhci_trb_t), cre);
XHCI_TRB_CYCLE_W(cre, cmd_r_cycle);
RING_ADVANCE(cmd);
LOG_LN_STEP("Ringing command doorbell...");
LOG_VAL_TRACE(op_regs->usbsts, "%x");
db_regs[0] = 0;
LOG_VAL_TRACE(op_regs->usbsts, "%x");
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_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...");
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, "command_exec_sync: Command failed.");
LOG_LN_STEP("Done.");
return cmp;
}
}
}
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 != (pci_bdf_t)-1, "xhci_init: No suitable controllers found.");
LOG_LN_INFO("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);
LOG_VAL_DATA(cap_regs->caplength, "%hhx");
LOG_VAL_DATA(cap_regs->hciversion, "%hx");
LOG_VAL_DATA(cap_regs->hcsparams1, "%x");
LOG_VAL_DATA(cap_regs->hcsparams2, "%x");
LOG_VAL_DATA(cap_regs->hcsparams3, "%x");
LOG_VAL_DATA(cap_regs->hccparams1, "%x");
LOG_VAL_DATA(cap_regs->dboff, "%x");
LOG_VAL_DATA(cap_regs->rtsoff, "%x");
LOG_VAL_DATA(cap_regs->hccparams2, "%x");
LOG_VAL_DATA(op_regs->usbcmd, "%x");
LOG_VAL_DATA(op_regs->usbsts, "%x");
LOG_VAL_DATA(op_regs->pagesize, "%x");
LOG_VAL_DATA(op_regs->dnctrl, "%x");
LOG_VAL_DATA(op_regs->crcr, "%lx");
LOG_VAL_DATA(op_regs->dcbaap, "%lx");
LOG_VAL_DATA(op_regs->config, "%x");
LOG_LN_INFO("Stopping controller...");
XHCI_OP_REGS_RS_W(op_regs, 0);
LOG_VAL_DATA(op_regs->usbcmd, "%x");
LOG_VAL_DATA(op_regs->usbsts, "%x");
while (!XHCI_OP_REGS_HCH(op_regs))
;
LOG_VAL_DATA(op_regs->usbcmd, "%x");
LOG_VAL_DATA(op_regs->usbsts, "%x");
LOG_LN_INFO("Resetting controller...");
XHCI_OP_REGS_EINT_W(op_regs, 0);
XHCI_OP_REGS_HCRST_W(op_regs, 1);
LOG_VAL_DATA(op_regs->usbcmd, "%x");
LOG_VAL_DATA(op_regs->usbsts, "%x");
while (XHCI_OP_REGS_HCRST_R(op_regs))
;
LOG_VAL_DATA(op_regs->usbcmd, "%x");
LOG_VAL_DATA(op_regs->usbsts, "%x");
while (XHCI_OP_REGS_CNR(op_regs))
;
LOG_VAL_DATA(op_regs->usbcmd, "%x");
LOG_VAL_DATA(op_regs->usbsts, "%x");
LOG_LN_INFO("Setting up command ring...");
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(cmd_r) | cmd_r_cycle;
erst[0].size = RING_LENGTH;
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;
LOG_VAL_DATA(op_regs->config, "%x");
LOG_VAL_DATA(op_regs->dcbaap, "%lx");
LOG_VAL_DATA(op_regs->crcr, "%lx");
LOG_VAL_DATA(intr_regs->erstba, "%lx");
LOG_VAL_DATA(intr_regs->erstsz, "%x");
LOG_VAL_DATA(intr_regs->erdp, "%lx");
LOG_VAL_DATA(erst[0].size, "%hx");
LOG_VAL_DATA(erst[0].base, "%lx");
LOG_LN_INFO("Starting controller...");
LOG_VAL_DATA(op_regs->usbcmd, "%x");
LOG_VAL_DATA(op_regs->usbsts, "%x");
XHCI_OP_REGS_RS_W(op_regs, 1);
LOG_VAL_DATA(op_regs->usbcmd, "%x");
LOG_VAL_DATA(op_regs->usbsts, "%x");
while (XHCI_OP_REGS_HCH(op_regs))
;
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);
PRINT_LN(kernel_log, "Done.");
}
void xhci_enumerate(stream_t *out) {
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);
}
}
uint8_t xhci_port_count() {
return XHCI_CAP_REGS_MAX_PORTS(cap_regs);
}
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(xhci_port_t port) {
LOG_LN_STEP("Attaching port %d ...", port);
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.");
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...");
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]))
;
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(port_regs[port].portsc, "%x");
LOG_LN_STEP("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_STEP("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])));
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);
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);
tsf_r_attached_slot = slot;
LOG_LN_STEP("Done.");
return slot;
}
uint64_t xhci_control_transfer(xhci_slot_t slot, uint64_t setup, void *data, uint16_t length) {
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.");
return length - (control_transfer_exec_sync(setup, data, length)->status & 0xFFFFFF);
}
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);
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.")
uint8_t ep_num = address & 0x0F;
uint8_t ep_dir = (address >> 7) & 1;
uint8_t dci = ep_num * 2 + ep_dir;
LOG_LN_STEP("Setting up endpoint ring...");
RING_WRAP(ep);
XHCI_TRB_CYCLE_W(&ep_r[RING_LENGTH], ep_r_cycle);
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);
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 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);
ep_r_attached_endpoint = dci;
ep_transfer_size = max_packet_size;
return dci;
}
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);
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.");
if (ep_processed_events == RING_LENGTH) {
LOG_LN_STEP("Event batch finished, posting new transfer requests...");
ep_processed_events = 0;
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 event, returning...");
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);
uint16_t size = max < cmd->status ? max : (uint16_t)cmd->status;
memory_copy(PHYS_TO_VIRT(cmd->parameter), size, to);
LOG_VAL_TRACE(size, "%d");
LOG_VAL_TRACE(*(uint64_t *)to, "%lx");
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(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);
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;
tsf_r_attached_slot = 0;
LOG_LN_STEP("Done.");
}
+28
View File
@@ -0,0 +1,28 @@
#pragma once
#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.
void xhci_enumerate(stream_t *out);
uint8_t xhci_port_count();
uint8_t xhci_port_connected(xhci_port_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
@@ -11,12 +11,17 @@
#define PHYS_TO_VIRT(phys) ((void *)((uint64_t)(phys) + KERNEL_VIRTUAL_BASE)) #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 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_PML4 (KERNEL_VIRTUAL_BASE + 0x10000)
#define KERNEL_VIRTUAL_CODE (KERNEL_VIRTUAL_PML4 + 0x10000) #define KERNEL_VIRTUAL_CODE (KERNEL_VIRTUAL_PML4 + 0x10000)
#define KERNEL_VIRTUAL_HEAP (KERNEL_VIRTUAL_CODE + 0x100000) #define KERNEL_VIRTUAL_HEAP (KERNEL_VIRTUAL_CODE + 0x100000)
#define KERNEL_VIRTUAL_UNUSED (KERNEL_VIRTUAL_HEAP + HEAP_SIZE) #define KERNEL_VIRTUAL_UNUSED (KERNEL_VIRTUAL_HEAP + HEAP_SIZE)
#define KERNEL_VIRTUAL_STACK (KERNEL_VIRTUAL_BASE + 0xF00000 - PAGE_SIZE) #define KERNEL_VIRTUAL_STACK (KERNEL_VIRTUAL_BASE + 0xF00000 - PAGE_SIZE)
#define KERNEL_VIRTUAL_STACK_TOP (KERNEL_VIRTUAL_STACK + 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_BASE 0x0000000000000000ULL
#define USER_VIRTUAL_CODE (USER_VIRTUAL_BASE + 0x400000) #define USER_VIRTUAL_CODE (USER_VIRTUAL_BASE + 0x400000)
+3 -3
View File
@@ -6,11 +6,11 @@ void *memory_allocate(uint64_t size);
void memory_free(void *pointer); void memory_free(void *pointer);
static inline void memory_set(uint8_t value, uint64_t bytes, void *to) { static inline void memory_set(uint8_t value, uint64_t bytes, volatile void *to) {
__asm__ volatile("rep stosb" : "=D"(to), "=c"(bytes) : "D"(to), "a"(value), "c"(bytes) : "memory"); __asm__ volatile("rep stosb" : "=D"(to), "=c"(bytes) : "D"(to), "a"(value), "c"(bytes) : "memory");
} }
static inline void memory_move(void *from, uint64_t bytes, void *to) { static inline void memory_move(volatile void *from, uint64_t bytes, volatile void *to) {
if (to == from) { if (to == from) {
return; return;
} else if (to < from) { } else if (to < from) {
@@ -20,7 +20,7 @@ static inline void memory_move(void *from, uint64_t bytes, void *to) {
} }
} }
static inline void memory_copy(const void *from, uint64_t bytes, void *to) { static inline void memory_copy(volatile const void *from, uint64_t bytes, volatile void *to) {
// TODO ASSERT((uint64_t)to + bytes <= (uint64_t)from || (uint64_t)to >= (uint64_t)from + bytes, "memory_copy: overlapping backward // TODO ASSERT((uint64_t)to + bytes <= (uint64_t)from || (uint64_t)to >= (uint64_t)from + bytes, "memory_copy: overlapping backward
// copy"); // copy");
+193 -56
View File
@@ -73,7 +73,7 @@ uint64_t string_split(char *string, char separator, uint64_t max, char **result)
return count; return count;
} }
uint64_t string_byte_to_hex(uint8_t value, uint64_t max, char *result) { uint64_t string_uint8_to_hex(uint8_t value, uint64_t max, char *result) {
if (max == 0) { if (max == 0) {
return 0; return 0;
} }
@@ -94,7 +94,7 @@ uint64_t string_byte_to_hex(uint8_t value, uint64_t max, char *result) {
uint8_t i = 0; uint8_t i = 0;
while (value) { while (value) {
uint8_t nibble = value & 0xF; uint8_t nibble = value & 0xF;
tmp[i++] = nibble < 10 ? '0' + nibble : 'a' + nibble - 10; tmp[i++] = nibble < 10 ? '0' + nibble : 'A' + nibble - 10;
value >>= 4; value >>= 4;
} }
@@ -110,6 +110,117 @@ uint64_t string_byte_to_hex(uint8_t value, uint64_t max, char *result) {
return i; return i;
} }
uint64_t string_uint16_to_hex(uint16_t value, uint64_t max, char *result) {
if (max == 0) {
return 0;
}
if (max == 1) {
result[0] = '\0';
return 0;
}
if (max == 2) {
result[0] = '0';
result[1] = '\0';
return 1;
}
char tmp[5] = "0000";
uint8_t i = 0;
while (value) {
uint8_t nibble = value & 0xF;
tmp[i++] = nibble < 10 ? '0' + nibble : 'A' + nibble - 10;
value >>= 4;
}
result[0] = '0';
result[1] = 'x';
i = 2;
while (i < 6 && i < max - 1) {
result[i] = tmp[5 - i];
i++;
}
result[i] = '\0';
return i;
}
uint64_t string_uint32_to_hex(uint32_t value, uint64_t max, char *result) {
if (max == 0) {
return 0;
}
if (max == 1) {
result[0] = '\0';
return 0;
}
if (max == 2) {
result[0] = '0';
result[1] = '\0';
return 1;
}
char tmp[9] = "00000000";
uint8_t i = 0;
while (value) {
uint8_t nibble = value & 0xF;
tmp[i++] = nibble < 10 ? '0' + nibble : 'A' + nibble - 10;
value >>= 4;
}
result[0] = '0';
result[1] = 'x';
i = 2;
while (i < 10 && i < max - 1) {
result[i] = tmp[9 - i];
i++;
}
result[i] = '\0';
return i;
}
uint64_t string_uint64_to_hex(uint64_t value, uint64_t max, char *result) {
if (max == 0) {
return 0;
}
if (max == 1) {
result[0] = '\0';
return 0;
}
if (max == 2) {
result[0] = '0';
result[1] = '\0';
return 1;
}
char tmp[17] = "0000000000000000";
uint8_t i = 0;
while (value) {
uint8_t nibble = value & 0xF;
tmp[i++] = nibble < 10 ? '0' + nibble : 'A' + nibble - 10;
value >>= 4;
}
result[0] = '0';
result[1] = 'x';
i = 2;
while (i < 18 && i < max - 1) {
result[i] = tmp[17 - i];
i++;
}
result[i] = '\0';
return i;
}
uint64_t string_uint_to_decimal(uint64_t value, uint64_t max, char *result) { uint64_t string_uint_to_decimal(uint64_t value, uint64_t max, char *result) {
if (max == 0) { if (max == 0) {
return 0; return 0;
@@ -149,43 +260,6 @@ uint64_t string_int_to_decimal(int64_t value, uint64_t max, char *result) {
return 1 + string_uint_to_decimal((uint64_t)(-value), max - 1, result + 1); return 1 + string_uint_to_decimal((uint64_t)(-value), max - 1, result + 1);
} }
uint64_t string_uint_to_hex(uint64_t value, uint64_t max, char *result) {
if (max == 0) {
return 0;
}
if (max == 1) {
result[0] = '\0';
return 0;
}
if (max == 2) {
result[0] = '0';
result[1] = '\0';
return 1;
}
char tmp[17] = "0000000000000000";
uint8_t i = 0;
while (value) {
uint8_t nibble = value & 0xF;
tmp[i++] = nibble < 10 ? '0' + nibble : 'A' + nibble - 10;
value >>= 4;
}
result[0] = '0';
result[1] = 'x';
i = 2;
while (i < 18 && i < max - 1) {
result[i] = tmp[17 - i];
i++;
}
result[i] = '\0';
return i;
}
uint64_t string_format(const char *format, uint64_t max, char *output, ...) { uint64_t string_format(const char *format, uint64_t max, char *output, ...) {
va_list args; va_list args;
va_start(args, output); va_start(args, output);
@@ -198,31 +272,19 @@ uint64_t string_format(const char *format, uint64_t max, char *output, ...) {
output[ri++] = format[fi++]; output[ri++] = format[fi++];
} else { } else {
switch (format[fi + 1]) { switch (format[fi + 1]) {
case 'c': case 'c': {
output[ri++] = (char)va_arg(args, int); output[ri++] = (char)va_arg(args, int);
fi += 2; fi += 2;
break; break;
case 'd': }
case 'd': {
string_int_to_decimal(va_arg(args, int64_t), limit - ri, output + ri); string_int_to_decimal(va_arg(args, int64_t), limit - ri, output + ri);
while (output[ri]) { while (output[ri]) {
ri++; ri++;
} }
fi += 2; fi += 2;
break; break;
case 'u': }
string_uint_to_decimal(va_arg(args, uint64_t), limit - ri, output + ri);
while (output[ri]) {
ri++;
}
fi += 2;
break;
case 'x':
string_uint_to_hex(va_arg(args, uint64_t), limit - ri, output + ri);
while (output[ri]) {
ri++;
}
fi += 2;
break;
case 's': { case 's': {
char *s = va_arg(args, char *); char *s = va_arg(args, char *);
while (*s && ri < limit) { while (*s && ri < limit) {
@@ -232,16 +294,91 @@ uint64_t string_format(const char *format, uint64_t max, char *output, ...) {
fi += 2; fi += 2;
break; break;
} }
case 'u': {
string_uint_to_decimal(va_arg(args, uint64_t), limit - ri, output + ri);
while (output[ri]) {
ri++;
}
fi += 2;
break;
}
case 'x': {
string_uint32_to_hex(va_arg(args, uint32_t), limit - ri, output + ri);
while (output[ri]) {
ri++;
}
fi += 2;
break;
}
case '%': { case '%': {
output[ri++] = '%'; output[ri++] = '%';
fi += 2; fi += 2;
break; break;
} }
default: case 'h': {
switch (format[fi + 2]) {
case 'x': {
string_uint16_to_hex(va_arg(args, uint64_t), limit - ri, output + ri);
while (output[ri]) {
ri++;
}
fi += 3;
break;
}
case 'h': {
switch (format[fi + 3]) {
case 'x': {
string_uint8_to_hex(va_arg(args, uint64_t), limit - ri, output + ri);
while (output[ri]) {
ri++;
}
fi += 4;
break;
}
default: {
output[ri++] = format[fi++];
output[ri++] = format[fi++];
output[ri++] = format[fi++];
output[ri++] = format[fi++];
break;
}
}
break;
}
default: {
output[ri++] = format[fi++];
output[ri++] = format[fi++];
output[ri++] = format[fi++];
break;
}
}
break;
}
case 'l': {
switch (format[fi + 2]) {
case 'x': {
string_uint64_to_hex(va_arg(args, uint64_t), limit - ri, output + ri);
while (output[ri]) {
ri++;
}
fi += 3;
break;
}
default: {
output[ri++] = format[fi++];
output[ri++] = format[fi++];
output[ri++] = format[fi++];
break;
}
}
break;
}
default: {
output[ri++] = format[fi++]; output[ri++] = format[fi++];
output[ri++] = format[fi++]; output[ri++] = format[fi++];
break; break;
} }
}
} }
} }
output[ri] = '\0'; output[ri] = '\0';
+7 -3
View File
@@ -14,12 +14,16 @@ uint64_t string_trim(char *string, char character, char **result);
uint64_t string_split(char *string, char separator, uint64_t max, char **result); uint64_t string_split(char *string, char separator, uint64_t max, char **result);
uint64_t string_byte_to_hex(uint8_t value, uint64_t max, char *result); uint64_t string_uint8_to_hex(uint8_t value, uint64_t max, char *result);
uint64_t string_uint16_to_hex(uint16_t value, uint64_t max, char *result);
uint64_t string_uint32_to_hex(uint32_t value, uint64_t max, char *result);
uint64_t string_uint64_to_hex(uint64_t value, uint64_t max, char *result);
uint64_t string_uint_to_decimal(uint64_t value, uint64_t max, char *result); uint64_t string_uint_to_decimal(uint64_t value, uint64_t max, char *result);
uint64_t string_int_to_decimal(int64_t value, uint64_t max, char *result); uint64_t string_int_to_decimal(int64_t value, uint64_t max, char *result);
uint64_t string_uint_to_hex(uint64_t value, uint64_t max, char *result);
uint64_t string_format(const char *format, uint64_t max, char *output, ...); uint64_t string_format(const char *format, uint64_t max, char *output, ...);
+9
View File
@@ -4,6 +4,10 @@
#define NUL 0 // TODO Fix VSCode thinking `NULL` conflicts with some other definition. #define NUL 0 // TODO Fix VSCode thinking `NULL` conflicts with some other definition.
#define ONES(h, l) ((1ULL << ((h) - (l) + 1)) - 1)
#define BITS_R(s, h, l) (((s) >> (l)) & ONES(h, l))
#define BITS_W(s, h, l, v) ((s) = (__typeof__(s))(((s) & ~(ONES(h, l) << (l))) | (((v) & ONES(h, l)) << (l))))
#define STDIN 0 #define STDIN 0
#define STDOUT 1 #define STDOUT 1
#define STDERR 2 #define STDERR 2
@@ -13,3 +17,8 @@ typedef uint64_t exit_code_t;
#define EXIT_CODE_OK 0 #define EXIT_CODE_OK 0
#define EXIT_CODE_NOT_FOUND ((uint64_t)-2) #define EXIT_CODE_NOT_FOUND ((uint64_t)-2)
#define EXIT_CODE_GENERAL_FAILURE ((uint64_t)-1) #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
;
}
-13
View File
@@ -19,19 +19,6 @@ load_bootloader:
jmp 0x8000 jmp 0x8000
times 446 - ($ - $$) db 0
partition_table:
partition_1:
.bootable: db 0x80
.chs_start: db 0x20, 0x21, 0x00
.type: db 0x06
.chs_end: db 0xFF, 0xFF, 0xFF
.lba_start: dd 0x00000800
.lba_size dd 0x00005000
partition_1_end:
partition_table_end:
times 510 - ($ - $$) db 0 times 510 - ($ - $$) db 0
boot_signature: boot_signature:
+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;
+3 -4
View File
@@ -3,8 +3,7 @@ ENTRY(_start)
SECTIONS { SECTIONS {
. = 0x0000000000400000; . = 0x0000000000400000;
.text : { *(.text) } .text : { *(.text*) }
.rodata : { *(.rodata) } .rodata : { *(.rodata*) *(.lrodata*) }
.data : { *(.data) } .data : { *(.data*) *(.ldata*) *(.bss*) *(.lbss*) }
.bss : { *(.bss) }
} }
+5 -1
View File
@@ -9,6 +9,10 @@ fi
qemu-system-x86_64 \ qemu-system-x86_64 \
"${debug_flags[@]}" \ "${debug_flags[@]}" \
-monitor stdio \ -monitor stdio \
-drive file="build/os.img",format=raw,if=ide \ -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 \
-no-reboot \ -no-reboot \
-trace "*xhci*" \
-d int -d int