Compare commits

...
34 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
freywar 14c3262c14 Separate log and output streams 2026-07-17 20:30:35 +03:00
freywar 85cfbb2050 Add pipes to shell, add wc 2026-07-16 21:39:00 +03:00
freywar 4ad32dda17 Merge fat16_inode_t and fat16_node_t, make fs_node_t shared 2026-07-15 23:14:56 +03:00
freywar 1ff38ffad4 Add file removal, rm 2026-07-15 21:39:17 +03:00
freywar 1148596a81 Add file and directory creation, mkdir 2026-06-30 20:37:24 +03:00
freywar 01cd3f528c Add writing to existing files and overwriting cp 2026-06-19 23:09:11 +03:00
freywar 325dc6339a Make pipes blocking 2026-06-19 21:49:18 +03:00
freywar 415f78fd40 Add process scheduler, pipes, and separate terminal from shell 2026-06-19 21:49:29 +03:00
freywar 452125782e Convert directory reading to generic stream 2026-06-18 22:59:18 +03:00
freywar 9761e3e870 Convert file descriptors to generic streams 2026-06-18 21:46:18 +03:00
freywar bcf2e5bac6 Add file descriptors and basic cat 2026-06-17 23:20:43 +03:00
freywar 968bb02aa8 Add basic ls 2026-06-17 22:20:08 +03:00
freywar 03b3ddac73 Add subprocesses and convert terminal to an app 2026-06-17 21:20:14 +03:00
freywar 1c935cf154 Add first C user program 2026-06-11 20:19:24 +03:00
freywar b32c12b311 Add an example user space process 2026-06-08 21:17:27 +03:00
freywar 2d4f76a5dd Add process state 2026-06-05 20:48:16 +03:00
freywar c8edeab750 Use streams for inter-"process" communications 2026-06-05 21:44:09 +03:00
95 changed files with 5860 additions and 1303 deletions
+3 -1
View File
@@ -1,4 +1,6 @@
target remote localhost:1234 target remote localhost:1234
set architecture i386:x86-64 set architecture i386:x86-64
set disassembly-flavor intel set disassembly-flavor intel
display/i ($cs * 16 + $rip) display/i $rip
break *0x0000000000400000
+1
View File
@@ -0,0 +1 @@
![It works!](./WATCHME.png)
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 949 KiB

+26 -7
View File
@@ -3,15 +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))" 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.raw@@"$((partition_offset * sector_size))" ::bin
mcopy -i build/os.raw@@"$((partition_offset * sector_size))" build/terminal ::bin/terminal
mcopy -i build/os.raw@@"$((partition_offset * sector_size))" build/shell ::bin/shell
mcopy -i build/os.raw@@"$((partition_offset * sector_size))" build/echo ::bin/echo
mcopy -i build/os.raw@@"$((partition_offset * sector_size))" build/ls ::bin/ls
mcopy -i build/os.raw@@"$((partition_offset * sector_size))" build/cat ::bin/cat
mcopy -i build/os.raw@@"$((partition_offset * sector_size))" build/cp ::bin/cp
mcopy -i build/os.raw@@"$((partition_offset * sector_size))" build/mkdir ::bin/mkdir
mcopy -i build/os.raw@@"$((partition_offset * sector_size))" build/rm ::bin/rm
mcopy -i build/os.raw@@"$((partition_offset * sector_size))" build/wc ::bin/wc
Executable
+19
View File
@@ -0,0 +1,19 @@
#!/usr/bin/env bash
set -euo pipefail
device=${1:-}
if [[ ! -b ${device} ]]; then
echo "ERROR: \"${device}\" is not a block device" >&2
fi
if [[ ! -f build/os.raw ]]; then
echo "ERROR: \"build/os.raw\" does not appear to be built, run \"build.sh\"" >&2
fi
echo 'Copying the image...' >&2
mnt=$(mktemp --directory)
mount "${device}2" "${mnt}"
cp --update "build/os.raw" "${mnt}"
umount "${mnt}"
rm -rf "${mnt}"
echo 'Done.' >&2
+114 -24
View File
@@ -33,46 +33,90 @@ custom_target(
kernel_entry_o = custom_target( kernel_entry_o = custom_target(
'kernel_entry', 'kernel_entry',
input: 'src/kernel_entry.asm', input: 'src/kernel/kernel_entry.asm',
output: 'kernel_entry.o', output: 'kernel_entry.o',
command: [nasm, '-f', 'elf64', '@INPUT@', '-o', '@OUTPUT@'], command: [nasm, '-f', 'elf64', '@INPUT@', '-o', '@OUTPUT@'],
) )
timer_o = custom_target(
'timer',
input: 'src/kernel/timer.asm',
output: 'timer.o',
command: [nasm, '-f', 'elf64', '@INPUT@', '-o', '@OUTPUT@'],
)
process_o = custom_target(
'process',
input: 'src/kernel/process.asm',
output: 'process.o',
command: [nasm, '-f', 'elf64', '@INPUT@', '-o', '@OUTPUT@'],
)
syscall_o = custom_target(
'syscall',
input: 'src/kernel/syscall.asm',
output: 'syscall.o',
command: [nasm, '-f', 'elf64', '@INPUT@', '-o', '@OUTPUT@'],
)
cc = meson.get_compiler('c') cc = meson.get_compiler('c')
kernel_sources = files( lib_sources = files([
'src/ata.c', 'src/lib/memory.c',
'src/fat16.c', 'src/lib/string.c',
'src/fs.c', ])
'src/idt.c',
'src/kernel.c', kernel_sources = files([
'src/keyboard.c', 'src/kernel/ata.c',
'src/memory.c', 'src/kernel/fat16.c',
'src/panic.c', 'src/kernel/fs.c',
'src/pic.c', 'src/kernel/gdt.c',
'src/string.c', 'src/kernel/hid-keyboard.c',
'src/terminal.c', 'src/kernel/idt.c',
'src/vga.c', 'src/kernel/kernel.c',
) 'src/kernel/log.c',
'src/kernel/memory.c',
'src/kernel/nvme.c',
'src/kernel/panic.c',
'src/kernel/path.c',
'src/kernel/pci.c',
'src/kernel/pic.c',
'src/kernel/pipe.c',
'src/kernel/process.c',
'src/kernel/ps2-keyboard.c',
'src/kernel/syscall.c',
'src/kernel/timer.c',
'src/kernel/tss.c',
'src/kernel/usb.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, kernel_sources], sources: [kernel_entry_o, timer_o, process_o, syscall_o, lib_sources, kernel_sources],
c_args: [ c_args: [
'-ffreestanding', c_args,
'-nostdlib', '-mcmodel=kernel',
'-nostartfiles',
'-mno-red-zone',
'-mgeneral-regs-only',
'-Wno-unused-command-line-argument', '-Wno-unused-command-line-argument',
'-Wconversion', '-Wconversion',
'-DVERSION="' + meson.project_version() + '"', '-DKERNEL="yes"',
], ],
link_args: [ link_args: [
'-T', meson.project_source_root() / 'src/linker.ld', '-T', meson.project_source_root() / 'src/kernel/linker.ld',
'-nostdlib', '-nostdlib',
], ],
link_depends: 'src/linker.ld', link_depends: 'src/kernel/linker.ld',
) )
custom_target( custom_target(
@@ -82,3 +126,49 @@ custom_target(
command: ['objcopy', '-O', 'binary', '@INPUT@', '@OUTPUT@'], command: ['objcopy', '-O', 'binary', '@INPUT@', '@OUTPUT@'],
build_by_default: true, build_by_default: true,
) )
foreach app : ['terminal', 'shell', 'echo', 'ls', 'cat', 'cp', 'mkdir', 'rm', 'wc']
start = custom_target(
f'@app@_start',
input: 'src/user/start.asm',
output: f'@app@_start.o',
command: [nasm, '-f', 'elf64', '-o', '@OUTPUT@', '@INPUT@'],
)
syscall = custom_target(
f'@app@_syscall',
input: 'src/user/syscall.asm',
output: f'@app@_syscall.o',
command: [nasm, '-f', 'elf64', '-o', '@OUTPUT@', '@INPUT@'],
)
elf = executable(
f'@app@.elf',
sources: [start, syscall, lib_sources, f'src/user/app/@app@/@app@.c'],
c_args: [
c_args,
'-mcmodel=large',
],
link_args: [
'-T', meson.project_source_root() / 'src/user/linker.ld',
'-nostdlib',
],
link_depends: 'src/user/linker.ld',
)
elf_data = custom_target(
f'@app@_data.elf',
input: elf,
output: f'@app@_data.elf',
command: ['objcopy', '--set-section-flags', '.data=alloc,load,contents', '@INPUT@', '@OUTPUT@'],
build_by_default: true,
)
custom_target(
app,
input: elf_data,
output: app,
command: ['objcopy', '-O', 'binary', '@INPUT@', '@OUTPUT@'],
build_by_default: true,
)
endforeach
+29 -20
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
@@ -224,30 +226,30 @@ jmp load_gdt
gdt_start: gdt_start:
gdt_0: dq 0x0000000000000000 gdt_0: dq 0x0000000000000000
gdt_1: gdt_32_code:
.limit_low: dw 0xFFFF .limit_low: dw 0xFFFF
.base_low: dw 0x0000 .base_low: dw 0x0000
.base_middle: db 0x00 .base_middle: db 0x00
.access: db 10011010b ; present, ring 0, code, executable, readable .access: db 10011010b ; present, ring 0, code, executable, readable
.flags: db 11001111b ; 32-bit, 4KB granularity .flags: db 11001111b ; 32-bit, 4KB granularity
.base_high: db 0x00 .base_high: db 0x00
gdt_1_end: gdt_32_code_end:
gdt_2: gdt_64_code:
.limit_low: dw 0xFFFF .limit_low: dw 0xFFFF
.base_low: dw 0x0000 .base_low: dw 0x0000
.base_middle: db 0x00 .base_middle: db 0x00
.access: db 10011010b ; present, ring 0, code, executable, readable .access: db 10011010b ; present, ring 0, code, executable, readable
.flags: db 10101111b ; long mode (L bit) .flags: db 10101111b ; long mode (L bit)
.base_high: db 0x00 .base_high: db 0x00
gdt_2_end: gdt_64_code_end:
gdt_3: gdt_data:
.limit_low: dw 0xFFFF .limit_low: dw 0xFFFF
.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_3_end: gdt_data_end:
gdt_end: gdt_end:
gdt_descriptor: gdt_descriptor:
@@ -276,23 +278,30 @@ protected_mode:
mov ss, ax mov ss, ax
mov esp, 0x00090000 mov esp, 0x00090000
; zero page table memory at 0x00010000 (3 pages = 768 dwords) ; zero page table memory
mov edi, 0x00010000 mov edi, 0x00010000
mov ecx, 768 mov ecx, 256 * 5
xor eax, eax xor eax, eax
rep stosd rep stosd
; PML4[0] -> PDPT at 0x00011000 ; PML4[0] -> PDPT at 0x00011000
mov dword [0x00010000], 0x00011003 mov dword [0x00010000], 0x00011003
; PDPT[0] -> PD at 0x00012000 ; PML4[511] -> PDPT at 0x00012000
mov dword [0x00011000], 0x00012003 mov dword [0x00010FF8], 0x00012003
; PML4[0].PDPT[0] -> PD at 0x00013000
mov dword [0x00011000], 0x00013003
; PML4[511].PDPT[510] -> PD at 0x00014000
mov dword [0x00012FF0], 0x00014003
; PD: fill 64 entries, each mapping 2MB ; PD: fill 64 entries, each mapping 2MB
mov edi, 0x00012000 mov edi, 0x00013000
mov eax, 0x00000083 ; present, writable, huge page, base 0 mov eax, 0x00000083 ; present, writable, huge page, base 0
mov ecx, 64 mov ecx, 64
.fill_pd: .fill_pd:
mov ebx, edi
add ebx, 0x1000
mov dword [edi], eax mov dword [edi], eax
mov dword [ebx], eax
add edi, 8 add edi, 8
add eax, 0x00200000 ; next 2MB add eax, 0x00200000 ; next 2MB
loop .fill_pd loop .fill_pd
@@ -327,11 +336,11 @@ long_mode:
mov ds, ax mov ds, ax
mov es, ax mov es, ax
mov ss, ax mov ss, ax
mov rsp, 0x0000000000090000 mov rsp, 0xFFFFFFFF80F00000
xor rax, rax xor rax, rax
xor rbx, rbx xor rbx, rbx
xor rcx, rcx xor rcx, rcx
xor rdx, rdx xor rdx, rdx
jmp 0x00020000 jmp 0xFFFFFFFF80020000
-259
View File
@@ -1,259 +0,0 @@
#include "src/fat16.h"
#include "src/ata.h"
#include "src/memory.h"
#include "src/string.h"
#include "src/util.h"
#include <stdint.h>
#define SECTOR_SIZE 512
#define FIRST_PARTITION_SECTOR 2048
#define ATTRIBUTE_SUBDIRECTORY 0x10
typedef struct __attribute__((packed)) {
uint16_t bytes_per_sector;
uint8_t sectors_per_cluster;
uint16_t reserved_sectors;
uint8_t fats_count;
uint16_t root_entry_count;
uint16_t total_sectors_16;
uint8_t media_type;
uint16_t sectors_per_fat;
} fat16_bpb_t;
typedef struct __attribute__((packed)) {
char name[8];
char ext[3];
uint8_t attributes;
uint8_t reserved[10];
uint16_t modified_time;
uint16_t modified_date;
uint16_t first_cluster;
uint32_t size;
} fat16_dir_entry_t;
typedef struct {
fs_node_t base;
fat16_dir_entry_t entry;
} fat16_node_t;
static fat16_bpb_t bpb; // Assuming one partition.
static fs_node_t *fs = NUL;
static uint16_t *fat = NUL;
static void to_8_3(const char *name, char *output) {
memory_set(' ', 11, output);
output[11] = '\0';
const char *c = name;
uint64_t i = 0;
uint8_t ext = 0;
while (*c) {
if (*c == '.') {
i = 8;
ext = 1;
} else if (i < (!ext ? 8 : 11)) {
output[i++] = *c >= 'a' && *c <= 'z' ? *c - 32 : *c;
}
c++;
}
}
static void from_8_3(const char *name, const char *extension, char *output) {
memory_set(0, 13, output);
uint16_t ni = 0, ei = 0, oi = 0;
while (ni < 8 && name[ni] != ' ') {
output[oi++] = name[ni++];
}
if (extension[ei] != ' ') {
output[oi++] = '.';
while (ei < 3 && extension[ei] != ' ') {
output[oi++] = extension[ei++];
}
}
}
fs_node_t *fat16_mount() { // Assuming one partition.
uint8_t sector[512];
ata_read_sectors(FIRST_PARTITION_SECTOR, 1, &sector);
memory_copy(sector + 11, sizeof(fat16_bpb_t), &bpb);
fat16_node_t *node = memory_allocate(sizeof(fat16_node_t));
node->base.type = FAT16;
node->base.name[0] = node->entry.name[0] = '/';
node->base.size = node->entry.size = sizeof(fat16_dir_entry_t) * bpb.root_entry_count;
node->base.is_dir = 1;
return fs = (fs_node_t *)node;
}
static void ensure_fat() {
ASSERT(bpb.sectors_per_fat<256, "ensure_fat: big FAT not implemented")
if (!fat) {
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);
}
}
static fat16_dir_entry_t *load_directory(fat16_node_t *directory) {
fat16_dir_entry_t *entries;
if (!directory->entry.first_cluster) {
uint8_t sectors = (uint8_t)((directory->base.size + SECTOR_SIZE - 1) / SECTOR_SIZE);
entries = memory_allocate(sectors * SECTOR_SIZE + sizeof(fat16_dir_entry_t)); // One extra as null terminator.
ata_read_sectors(FIRST_PARTITION_SECTOR + bpb.reserved_sectors + bpb.fats_count * bpb.sectors_per_fat, sectors, entries);
} else {
ensure_fat();
uint16_t next_cluster = directory->entry.first_cluster;
uint32_t cluster_count = 0;
while (next_cluster < 0xFFF8) {
cluster_count++;
next_cluster = fat[next_cluster];
}
entries =
memory_allocate(cluster_count * bpb.sectors_per_cluster * SECTOR_SIZE + sizeof(fat16_dir_entry_t)); // One extra as null terminator.
uint8_t *chunk = (uint8_t *)entries;
next_cluster = directory->entry.first_cluster;
while (next_cluster < 0xFFF8) {
ata_read_sectors(FIRST_PARTITION_SECTOR + bpb.reserved_sectors + bpb.sectors_per_fat * bpb.fats_count +
(bpb.root_entry_count * sizeof(fat16_dir_entry_t) + SECTOR_SIZE - 1) / SECTOR_SIZE +
bpb.sectors_per_cluster * (next_cluster - 2),
bpb.sectors_per_cluster, chunk);
chunk += bpb.sectors_per_cluster * SECTOR_SIZE;
next_cluster = fat[next_cluster];
}
}
return entries;
}
static fs_node_t *open_entry(const char *name, const fat16_dir_entry_t *entry) {
if (!entry->name[0]) {
return NUL;
}
fat16_node_t *result = memory_allocate(sizeof(fat16_node_t));
memory_copy((char *)name, string_length(name) + 1, &(result->base.name));
result->base.type = FAT16;
result->base.size = entry->size;
result->base.is_dir = entry->attributes & ATTRIBUTE_SUBDIRECTORY;
memory_copy((fat16_dir_entry_t *)entry, sizeof(fat16_dir_entry_t), (uint8_t *)result + sizeof(fs_node_t));
return (fs_node_t *)result;
}
fs_node_t *fat16_open_by(const fs_node_t *directory, const char *name) {
ASSERT(directory->type == FAT16, "fat16_open_by: directory is not FAT16");
ASSERT(directory->is_dir, "fat16_open_by: directory is not a directory");
char name_8_3[12];
to_8_3(name, name_8_3);
fat16_dir_entry_t *entries = load_directory((fat16_node_t *)directory);
fat16_dir_entry_t *entry = entries;
while (entry->name[0]) {
if ((uint8_t)entry->name[0] != 0xE5 && (uint8_t)entry->attributes != 0x0F && bytes_equal(name_8_3, (char *)entry, 11)) {
break;
}
entry++;
}
fs_node_t *result = open_entry(name, entry);
memory_free(entries);
return result;
}
fs_node_t *fat16_open_at(const fs_node_t *directory, uint64_t index) {
ASSERT(directory->type == FAT16, "fat16_open_at: directory is not FAT16");
ASSERT(directory->is_dir, "fat16_open_at: directory is not a directory");
fat16_dir_entry_t *entries = load_directory((fat16_node_t *)directory);
uint64_t ei = 0, vi = 0;
while (entries[ei].name[0]) {
if ((uint8_t)entries[ei].name[0] != 0xE5 && (uint8_t)entries[ei].attributes != 0x0F) {
if (vi == index) {
break;
}
vi++;
}
ei++;
}
if (!entries[ei].name[0]) {
return NUL;
}
char name[13];
from_8_3(entries[ei].name, entries[ei].ext, name);
fs_node_t *result = open_entry(name, entries + ei);
memory_free(entries);
return result;
}
void fat16_read(const fs_node_t *file, uint64_t offset, uint64_t size, void *to) {
ASSERT(file->type == FAT16, "fat16_read: file is not FAT16");
ASSERT(!file->is_dir, "fat16_read: can not read directory");
ASSERT(file->size >= offset + size, "fat16_read: offset/size are out of bounds");
ensure_fat();
fat16_node_t *fat_file = (fat16_node_t *)file;
uint32_t cluster_size = bpb.sectors_per_cluster * SECTOR_SIZE;
uint32_t next_cluster = fat_file->entry.first_cluster;
// Assuming filesystem is correct. TODO Check for real.
while (offset >= cluster_size) {
next_cluster = fat[next_cluster];
offset -= cluster_size;
}
uint8_t *cursor = to;
uint8_t *tmp = memory_allocate(cluster_size);
ata_read_sectors(FIRST_PARTITION_SECTOR + bpb.reserved_sectors + bpb.sectors_per_fat * bpb.fats_count +
(bpb.root_entry_count * sizeof(fat16_dir_entry_t) + SECTOR_SIZE - 1) / SECTOR_SIZE +
bpb.sectors_per_cluster * (next_cluster - 2),
bpb.sectors_per_cluster, tmp);
uint64_t prefix_size = size <= cluster_size - offset ? size : cluster_size - offset;
memory_copy(tmp + offset, prefix_size, cursor);
size -= prefix_size;
cursor += prefix_size;
next_cluster = fat[next_cluster];
while (size >= cluster_size) {
ata_read_sectors(FIRST_PARTITION_SECTOR + bpb.reserved_sectors + bpb.sectors_per_fat * bpb.fats_count +
(bpb.root_entry_count * sizeof(fat16_dir_entry_t) + SECTOR_SIZE - 1) / SECTOR_SIZE +
bpb.sectors_per_cluster * (next_cluster - 2),
bpb.sectors_per_cluster, cursor);
size -= cluster_size;
cursor += cluster_size;
next_cluster = fat[next_cluster];
}
if (size) {
ata_read_sectors(FIRST_PARTITION_SECTOR + bpb.reserved_sectors + bpb.sectors_per_fat * bpb.fats_count +
(bpb.root_entry_count * sizeof(fat16_dir_entry_t) + SECTOR_SIZE - 1) / SECTOR_SIZE +
bpb.sectors_per_cluster * (next_cluster - 2),
bpb.sectors_per_cluster, tmp);
memory_copy(tmp, size, cursor);
}
memory_free(tmp);
}
void fat16_close(fs_node_t *node) {
ASSERT(node->type == FAT16, "fat16_close: node is not FAT16");
ASSERT(node != fs, "fat16_close: can not unmount FS");
memory_free(node);
}
void fat16_unmount(fs_node_t *node) {
ASSERT(node->type == FAT16, "fat16_unmount: node is not FAT16");
ASSERT(node == fs, "fat16_unmount: node is not filesystem");
ASSERT(0, "fat16_unmount: not implemented");
}
-17
View File
@@ -1,17 +0,0 @@
#pragma once
#include "src/fs.h"
#define FAT16 1
fs_node_t *fat16_mount();
fs_node_t *fat16_open_by(const fs_node_t *directory, const char *name);
fs_node_t *fat16_open_at(const fs_node_t *directory, uint64_t index);
void fat16_read(const fs_node_t *file, uint64_t offset, uint64_t size, void *to);
void fat16_close(fs_node_t *node);
void fat16_unmount(fs_node_t *fs);
-26
View File
@@ -1,26 +0,0 @@
#include "src/fs.h"
#include "src/fat16.h"
fs_node_t *fs_mount() {
return fat16_mount();
}
fs_node_t *fs_open_by(const fs_node_t *directory, const char *name) {
return fat16_open_by(directory, name);
}
fs_node_t *fs_open_at(const fs_node_t *directory, uint64_t index) {
return fat16_open_at(directory, index);
}
void fs_read(const fs_node_t *file, uint64_t offset, uint64_t size, void *to) {
fat16_read(file, offset, size, to);
}
void fs_close(fs_node_t *node) {
fat16_close(node);
}
void fs_unmount(fs_node_t *fs) {
fat16_unmount(fs);
}
-25
View File
@@ -1,25 +0,0 @@
#pragma once
#include <stdint.h>
#define FILENAME_SIZE_LIMIT 255
typedef struct fs_node {
char name[FILENAME_SIZE_LIMIT + 1];
uint32_t size;
uint8_t is_dir;
uint8_t type;
} fs_node_t;
fs_node_t *fs_mount();
fs_node_t *fs_open_by(const fs_node_t *directory, const char *name);
fs_node_t *fs_open_at(const fs_node_t *directory, uint64_t index);
void fs_read(const fs_node_t *file, uint64_t offset, uint64_t size, void *to);
void fs_close(fs_node_t *node);
void fs_unmount(fs_node_t *fs);
-48
View File
@@ -1,48 +0,0 @@
#include "src/idt.h"
#include "src/keyboard.h"
#include "src/memory.h"
#include "src/pic.h"
#include "src/terminal.h"
#include "src/util.h"
#include "src/vga.h"
__attribute__((interrupt)) void isr_divide_by_zero([[maybe_unused]] struct interrupt_frame *frame) {
vga_set_string(VGA_HEIGHT - 1, 0, "EXCEPTION: divide by zero", 0x4F);
while (1)
;
}
__attribute__((interrupt)) void isr_page_fault([[maybe_unused]] struct interrupt_frame *frame) {
vga_set_string(VGA_HEIGHT - 1, 0, "EXCEPTION: page fault", 0x4F);
while (1)
;
}
__attribute__((interrupt)) void isr_general_violation([[maybe_unused]] struct interrupt_frame *frame) {
vga_set_string(VGA_HEIGHT - 1, 0, "EXCEPTION: general violation", 0x4F);
while (1)
;
}
__attribute__((interrupt)) void isr_ata_primary([[maybe_unused]] struct interrupt_frame *frame) {
outb(0x20, 0x20);
outb(0xA0, 0x20);
}
void kernel_main() {
pic_init();
idt_init();
outb(0x21, inb(0x21) | 0x01); // mask out timer interrupt
idt_set_entry(0, isr_divide_by_zero, 0x8E);
idt_set_entry(0x0E, isr_page_fault, 0x8E);
idt_set_entry(0x0D, isr_general_violation, 0x8E);
idt_set_entry(46, isr_ata_primary, 0x8E);
__asm__ volatile("sti");
memory_init();
keyboard_init();
terminal_init();
while (1)
;
}
+3 -3
View File
@@ -1,6 +1,6 @@
#include "src/ata.h" #include "src/kernel/ata.h"
#include "src/panic.h" #include "src/kernel/panic.h"
#include "src/util.h" #include "src/kernel/util.h"
#define BSY 0b10000000 #define BSY 0b10000000
#define DF 0b00100000 #define DF 0b00100000
View File
+767
View File
@@ -0,0 +1,767 @@
#include "src/kernel/fat16.h"
#include "src/kernel/fs.h"
#include "src/kernel/nvme.h"
#include "src/kernel/panic.h"
#include "src/kernel/stream.h"
#include "src/lib/memory.h"
#include "src/lib/string.h"
#include "src/lib/syscall.h"
#include "src/lib/util.h"
#define SECTOR_SIZE 512
#define FIRST_PARTITION_SECTOR 2048
#define ATTRIBUTE_SUBDIRECTORY 0x10
typedef struct __attribute__((packed)) {
uint16_t bytes_per_sector;
uint8_t sectors_per_cluster;
uint16_t reserved_sectors;
uint8_t fats_count;
uint16_t root_entry_count;
uint16_t total_sectors_16;
uint8_t media_type;
uint16_t sectors_per_fat;
} fat16_bpb_t;
typedef struct __attribute__((packed)) {
char name[8];
char ext[3];
uint8_t attributes;
uint8_t reserved[10];
uint16_t modified_time;
uint16_t modified_date;
uint16_t first_cluster;
uint32_t size;
} fat16_dir_entry_t;
// Assuming one persistently mounted partition.
static fat16_bpb_t *bpb = NUL;
static uint32_t data_start;
static uint16_t *fat = NUL;
typedef struct {
fs_node_t base;
uint16_t dir_cluster;
uint16_t dir_index;
uint16_t first_cluster;
} fat16_node_t;
#define MAX_NODES 256
static fat16_node_t nodes[MAX_NODES];
static fs_node_t *node_create(uint16_t dir_cluster, uint16_t dir_index, const char *name, uint8_t is_dir, uint16_t first_cluster,
uint32_t size) {
for (uint64_t i = 0; i < MAX_NODES; i++) {
if (!nodes[i].base.refs) {
nodes[i].base.type = FAT16;
nodes[i].base.refs = 1;
memory_copy(name, FILENAME_SIZE_LIMIT + 1, nodes[i].base.name);
nodes[i].base.size = size;
nodes[i].base.is_dir = is_dir;
nodes[i].base.removed = 0;
nodes[i].dir_cluster = dir_cluster;
nodes[i].dir_index = dir_index;
nodes[i].first_cluster = first_cluster;
return (fs_node_t *)&nodes[i];
}
}
return NUL;
}
static fs_node_t *node_get(uint32_t dir_cluster, uint16_t dir_index) {
for (uint64_t i = 0; i < MAX_NODES; i++) {
if (nodes[i].base.refs && nodes[i].dir_cluster == dir_cluster && nodes[i].dir_index == dir_index) {
return (fs_node_t *)&nodes[i];
}
}
return NUL;
}
static fs_node_t *node_use(uint16_t dir_cluster, uint16_t dir_index, const char *name, uint8_t is_dir, uint16_t first_cluster,
uint32_t size) {
fs_node_t *result = node_get(dir_cluster, dir_index);
if (!result) {
result = node_create(dir_cluster, dir_index, name, is_dir, first_cluster, size);
} else {
ASSERT(result->refs < UINT32_MAX, "node_use: too many references");
result->refs++;
}
return result;
}
static void node_free(fs_node_t *node) {
if (node->refs) {
node->refs--;
}
}
static void to_8_3(const char *name, char *output) {
memory_set(' ', 11, output);
output[11] = '\0';
const char *c = name;
uint64_t i = 0;
uint8_t ext = 0;
while (*c) {
if (*c == '.') {
i = 8;
ext = 1;
} else if (i < (!ext ? 8 : 11)) {
output[i++] = *c >= 'a' && *c <= 'z' ? *c - 32 : *c;
}
c++;
}
}
static void from_8_3(const char *name, const char *extension, char *output) {
memory_set(0, 13, output);
uint16_t ni = 0, ei = 0, oi = 0;
while (ni < 8 && name[ni] != ' ') {
output[oi++] = name[ni++];
}
if (extension[ei] != ' ') {
output[oi++] = '.';
while (ei < 3 && extension[ei] != ' ') {
output[oi++] = extension[ei++];
}
}
}
static void ensure_bpb() {
if (bpb) {
return;
}
uint8_t sector[512];
nvme_read_sectors(FIRST_PARTITION_SECTOR, 1, &sector);
bpb = memory_allocate(sizeof(fat16_bpb_t));
memory_copy(sector + 11, sizeof(fat16_bpb_t), bpb);
data_start = FIRST_PARTITION_SECTOR + bpb->reserved_sectors + bpb->sectors_per_fat * bpb->fats_count +
(bpb->root_entry_count * sizeof(fat16_dir_entry_t) + SECTOR_SIZE - 1) / SECTOR_SIZE;
}
static void ensure_fat() {
if (fat) {
return;
}
ensure_bpb();
ASSERT(bpb->sectors_per_fat < 256, "ensure_fat: big FAT not implemented");
fat = memory_allocate(bpb->sectors_per_fat * SECTOR_SIZE);
nvme_read_sectors(FIRST_PARTITION_SECTOR + bpb->reserved_sectors, (uint8_t)bpb->sectors_per_fat, fat);
}
static uint16_t allocate_cluster() {
ensure_fat();
for (uint16_t i = 2; i < bpb->sectors_per_fat * SECTOR_SIZE / 2; i++) {
if (fat[i] == 0x0000) {
fat[i] = 0xFFFF;
return i;
}
}
return 0;
}
static void flush_fat() {
ensure_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) {
ensure_bpb();
if (!dir_cluster) {
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);
nvme_read_sectors(FIRST_PARTITION_SECTOR + bpb->reserved_sectors + bpb->fats_count * bpb->sectors_per_fat, sectors, *entries);
return bpb->root_entry_count;
} else {
ensure_fat();
uint16_t next_cluster = dir_cluster;
uint16_t cluster_count = 0;
while (next_cluster < 0xFFF8) {
cluster_count++;
next_cluster = fat[next_cluster];
}
*entries = memory_allocate(cluster_count * bpb->sectors_per_cluster * SECTOR_SIZE);
uint8_t *chunk = (uint8_t *)*entries;
next_cluster = dir_cluster;
while (next_cluster < 0xFFF8) {
nvme_read_sectors(data_start + bpb->sectors_per_cluster * (next_cluster - 2), bpb->sectors_per_cluster, chunk);
chunk += bpb->sectors_per_cluster * SECTOR_SIZE;
next_cluster = fat[next_cluster];
}
return cluster_count * bpb->sectors_per_cluster * SECTOR_SIZE / sizeof(fat16_dir_entry_t);
}
return (uint16_t)-1;
}
static fs_node_t *open_entry(uint16_t dir_cluster, const fat16_dir_entry_t *entries, uint16_t index) {
const fat16_dir_entry_t *entry = &entries[index];
if (!entry->name[0]) {
return NUL;
}
char name[FILENAME_SIZE_LIMIT + 1];
from_8_3(entry->name, entry->ext, name);
return node_use(dir_cluster, index, name, entry->attributes & ATTRIBUTE_SUBDIRECTORY, entry->first_cluster, entry->size);
}
static void write_directory(uint16_t dir_cluster, const fat16_dir_entry_t *entries, uint16_t count) {
ensure_bpb();
ASSERT((count * sizeof(fat16_dir_entry_t)) % (bpb->sectors_per_cluster * SECTOR_SIZE) == 0,
"write_directory: entries must be aligned to clusters");
if (!dir_cluster) {
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);
} else {
ensure_fat();
uint8_t *chunk = (uint8_t *)entries;
uint16_t next_cluster = dir_cluster;
while (count) {
nvme_write_sectors(data_start + bpb->sectors_per_cluster * (next_cluster - 2), bpb->sectors_per_cluster, chunk);
chunk += bpb->sectors_per_cluster * SECTOR_SIZE;
count -= bpb->sectors_per_cluster * SECTOR_SIZE / sizeof(fat16_dir_entry_t);
if (count && fat[next_cluster] >= 0xFFF8) {
fat[next_cluster] = allocate_cluster();
}
next_cluster = fat[next_cluster];
}
flush_fat();
}
}
fs_node_t *fat16_open_root() {
ensure_bpb();
return node_use(0, UINT16_MAX, "", 1, 0, sizeof(fat16_dir_entry_t) * bpb->root_entry_count);
}
fs_node_t *fat16_open_by(const fs_node_t *directory, const char *name, uint64_t flags) {
if (!directory || directory->type != FAT16 || !directory->is_dir || directory->removed) {
return NUL;
}
uint16_t dir_cluster = ((fat16_node_t *)directory)->first_cluster;
char name_8_3[12];
to_8_3(name, name_8_3);
fat16_dir_entry_t *entries;
uint16_t entries_count = read_directory(dir_cluster, &entries);
fat16_dir_entry_t *entry = entries;
uint16_t index = 0;
while (index < entries_count && entry->name[0]) {
if ((uint8_t)entry->name[0] != 0xE5 && (uint8_t)entry->attributes != 0x0F && bytes_equal(name_8_3, (char *)entry, 11)) {
break;
}
entry++;
index++;
}
fs_node_t *result = open_entry(dir_cluster, entries, index);
if (result && (flags & OPEN_EXCLUSIVE)) {
memory_free(entries);
return NUL;
}
if (!result && (flags & OPEN_CREATE)) {
index = 0;
while (index < entries_count && entries[index].name[0]) {
index++;
}
if (index >= entries_count) {
if (!dir_cluster) {
memory_free(entries);
return NUL;
} else {
uint16_t new_entries_count = entries_count + bpb->sectors_per_cluster * SECTOR_SIZE / sizeof(fat16_dir_entry_t);
fat16_dir_entry_t *new_entries = memory_allocate(new_entries_count * sizeof(fat16_dir_entry_t));
memory_copy(entries, entries_count * sizeof(fat16_dir_entry_t), new_entries);
memory_free(entries);
entries_count = new_entries_count;
entries = new_entries;
}
}
memory_copy(name_8_3, 11, &entries[index]);
if (flags & OPEN_DIRECTORY && !(flags & OPEN_FILE)) {
entries[index].attributes = ATTRIBUTE_SUBDIRECTORY;
entries[index].first_cluster = allocate_cluster();
fat16_dir_entry_t *inner_entries = memory_allocate(bpb->sectors_per_cluster * SECTOR_SIZE);
memory_copy(". ", 11, inner_entries[0].name);
inner_entries[0].attributes = ATTRIBUTE_SUBDIRECTORY;
inner_entries[0].first_cluster = entries[index].first_cluster;
memory_copy(".. ", 11, inner_entries[1].name);
inner_entries[1].attributes = ATTRIBUTE_SUBDIRECTORY;
inner_entries[1].first_cluster = dir_cluster;
write_directory(entries[index].first_cluster, inner_entries, bpb->sectors_per_cluster * SECTOR_SIZE / sizeof(fat16_dir_entry_t));
memory_free(inner_entries);
}
write_directory(dir_cluster, entries, entries_count);
result = open_entry(dir_cluster, entries, index);
}
memory_free(entries);
if (!result) {
return NUL;
}
if (result->is_dir && !(flags & OPEN_DIRECTORY)) {
return NUL;
}
if (!result->is_dir && !(flags & OPEN_FILE)) {
return NUL;
}
return result;
}
fs_node_t *fat16_open_at(const fs_node_t *directory, uint16_t index, uint64_t flags) {
if (!directory || directory->type != FAT16 || !directory->is_dir || directory->removed) {
return NUL;
}
uint16_t dir_cluster = ((fat16_node_t *)directory)->first_cluster;
fat16_dir_entry_t *entries;
uint16_t entries_count = read_directory(dir_cluster, &entries);
uint16_t ei = 0, vi = 0;
while (ei < entries_count && entries[ei].name[0]) {
if ((uint8_t)entries[ei].name[0] != 0xE5 && (uint8_t)entries[ei].attributes != 0x0F) {
if (vi == index) {
break;
}
vi++;
}
ei++;
}
fs_node_t *result = ei < entries_count ? open_entry(dir_cluster, entries, ei) : NUL;
memory_free(entries);
if (result && (flags & OPEN_EXCLUSIVE)) {
return NUL;
}
if (!result) {
return NUL;
}
if (result->is_dir && !(flags & OPEN_DIRECTORY)) {
return NUL;
}
if (!result->is_dir && !(flags & OPEN_FILE)) {
return NUL;
}
return result;
}
fs_node_t *fat16_open_again(fs_node_t *source) {
ASSERT(source->refs < UINT32_MAX, "fat16_open_again: too many references");
source->refs++;
return source;
}
uint64_t fat16_read(const fs_node_t *file, uint32_t offset, uint32_t bytes, void *to) {
if (!file || file->type != FAT16 || file->is_dir) {
return (uint64_t)-1;
}
if (file->removed) {
return 0;
}
if (offset >= file->size) {
return 0;
}
if (offset + bytes >= file->size) {
bytes = file->size - offset;
}
ensure_fat();
uint32_t cluster_size = bpb->sectors_per_cluster * SECTOR_SIZE;
uint32_t next_cluster = ((fat16_node_t *)file)->first_cluster;
while (offset >= cluster_size) {
next_cluster = fat[next_cluster];
offset -= cluster_size;
}
uint32_t readden = 0;
uint8_t *cursor = to;
uint8_t *tmp = memory_allocate(cluster_size);
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;
memory_copy(tmp + offset, prefix_size, cursor);
bytes -= prefix_size;
readden += prefix_size;
cursor += prefix_size;
next_cluster = fat[next_cluster];
while (bytes >= cluster_size) {
nvme_read_sectors(data_start + bpb->sectors_per_cluster * (next_cluster - 2), bpb->sectors_per_cluster, cursor);
bytes -= cluster_size;
readden += cluster_size;
cursor += cluster_size;
next_cluster = fat[next_cluster];
}
if (bytes) {
nvme_read_sectors(data_start + bpb->sectors_per_cluster * (next_cluster - 2), bpb->sectors_per_cluster, tmp);
memory_copy(tmp, bytes, cursor);
readden += bytes;
}
memory_free(tmp);
return readden;
}
uint64_t fat16_write(fs_node_t *file, uint32_t offset, const void *from, uint32_t bytes) {
if (!file || file->type != FAT16 || file->is_dir) {
return (uint64_t)-1;
}
if (file->removed) {
return 0;
}
ensure_fat();
fat16_node_t *fat_file = (fat16_node_t *)file;
if (!fat_file->first_cluster) {
fat_file->first_cluster = allocate_cluster();
}
uint32_t cluster_size = bpb->sectors_per_cluster * SECTOR_SIZE;
uint32_t next_cluster = fat_file->first_cluster;
uint32_t allocated = cluster_size;
while (allocated < offset + bytes) {
if (fat[next_cluster] >= 0xFFF8) {
fat[next_cluster] = allocate_cluster();
}
next_cluster = fat[next_cluster];
allocated += cluster_size;
}
flush_fat();
cluster_size = bpb->sectors_per_cluster * SECTOR_SIZE;
next_cluster = fat_file->first_cluster;
while (offset >= cluster_size) {
next_cluster = fat[next_cluster];
offset -= cluster_size;
}
uint32_t written = 0;
const uint8_t *cursor = from;
uint8_t *tmp = memory_allocate(cluster_size);
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;
memory_copy(cursor, prefix_size, tmp + offset);
nvme_write_sectors(data_start + bpb->sectors_per_cluster * (next_cluster - 2), bpb->sectors_per_cluster, tmp);
bytes -= prefix_size;
written += prefix_size;
cursor += prefix_size;
next_cluster = fat[next_cluster];
while (bytes >= cluster_size) {
nvme_write_sectors(data_start + bpb->sectors_per_cluster * (next_cluster - 2), bpb->sectors_per_cluster, cursor);
bytes -= cluster_size;
written += cluster_size;
cursor += cluster_size;
next_cluster = fat[next_cluster];
}
if (bytes) {
nvme_read_sectors(data_start + bpb->sectors_per_cluster * (next_cluster - 2), bpb->sectors_per_cluster, tmp);
memory_copy(cursor, bytes, tmp);
nvme_write_sectors(data_start + bpb->sectors_per_cluster * (next_cluster - 2), bpb->sectors_per_cluster, tmp);
written += bytes;
}
memory_free(tmp);
if (offset + written > file->size) {
file->size = offset + written;
fat16_dir_entry_t *entries;
uint16_t entries_count = read_directory(fat_file->dir_cluster, &entries);
entries[fat_file->dir_index].first_cluster = fat_file->first_cluster;
entries[fat_file->dir_index].size = file->size;
write_directory(fat_file->dir_cluster, entries, entries_count);
memory_free(entries);
}
return written;
}
uint64_t fat16_truncate(fs_node_t *file, uint32_t size) {
if (!file || file->type != FAT16 || file->is_dir) {
return (uint64_t)-1;
}
if (file->removed) {
return 0;
}
if (file->size == size) {
return size;
} else if (file->size < size) {
uint32_t diff = size - file->size;
uint8_t *tmp = memory_allocate(diff);
uint64_t written = fat16_write(file, file->size, tmp, diff);
memory_free(tmp);
return written == (uint64_t)-1 ? (uint64_t)-1 : file->size + written;
} else if (file->size > size) {
fat16_node_t *fat_file = (fat16_node_t *)file;
uint32_t cluster_size = bpb->sectors_per_cluster * SECTOR_SIZE;
uint32_t prev_cluster = 0;
uint32_t next_cluster = fat_file->first_cluster;
ensure_fat();
uint32_t allocated = 0;
while (allocated < size) {
prev_cluster = next_cluster;
next_cluster = fat[next_cluster];
allocated += cluster_size;
}
if (prev_cluster) {
fat[prev_cluster] = 0xFFFF;
} else {
fat_file->first_cluster = 0;
}
do {
uint32_t swap = fat[next_cluster];
fat[next_cluster] = 0;
next_cluster = swap;
} while (next_cluster < 0xFFF8);
flush_fat();
file->size = size;
fat16_dir_entry_t *entries;
uint16_t entries_count = read_directory(fat_file->dir_cluster, &entries);
entries[fat_file->dir_index].first_cluster = fat_file->first_cluster;
entries[fat_file->dir_index].size = file->size;
write_directory(fat_file->dir_cluster, entries, entries_count);
memory_free(entries);
return size;
}
return (uint64_t)-1;
}
uint64_t fat16_remove(fs_node_t *file) {
if (!file || file->type != FAT16) {
return (uint64_t)-1;
}
if (file->removed) {
return 0;
}
fat16_node_t *fat_file = (fat16_node_t *)file;
if (file->is_dir) {
fat16_dir_entry_t *subentries;
uint16_t subentries_count = read_directory(fat_file->first_cluster, &subentries);
uint16_t ei = 0, vi = 0;
while (ei < subentries_count && subentries[ei].name[0]) {
if ((uint8_t)subentries[ei].name[0] != 0xE5 && (uint8_t)subentries[ei].attributes != 0x0F) {
vi++;
}
ei++;
}
memory_free(subentries);
if (vi > 2) {
return (uint64_t)-1;
}
}
file->removed = 1;
if (fat_file->first_cluster) {
ensure_fat();
uint64_t next_cluster = fat_file->first_cluster;
do {
uint32_t swap = fat[next_cluster];
fat[next_cluster] = 0;
next_cluster = swap;
} while (next_cluster < 0xFFF8);
flush_fat();
}
fat16_dir_entry_t *entries;
uint16_t entries_count = read_directory(fat_file->dir_cluster, &entries);
entries[fat_file->dir_index].name[0] = 0xE5;
entries[fat_file->dir_index].first_cluster = 0;
entries[fat_file->dir_index].size = 0;
write_directory(fat_file->dir_cluster, entries, entries_count);
memory_free(entries);
return 1;
}
typedef struct {
stream_t stream;
fs_node_t *node;
uint32_t offset;
} fat16_file_stream_t;
static uint64_t file_stream_write(stream_t *self, const char *from, uint64_t bytes) {
if (!self) {
return (uint64_t)-1;
}
fat16_file_stream_t *ffs = (fat16_file_stream_t *)self;
uint64_t written = fat16_write(ffs->node, ffs->offset, from, bytes > UINT32_MAX ? UINT32_MAX : (uint32_t)bytes);
if (written != (uint64_t)-1) {
ffs->offset += written;
}
return written;
}
static uint64_t file_stream_read(stream_t *self, uint64_t max, char *to) {
if (!self) {
return (uint64_t)-1;
}
fat16_file_stream_t *ffs = (fat16_file_stream_t *)self;
uint64_t readden = fat16_read(ffs->node, ffs->offset, max > UINT32_MAX ? UINT32_MAX : (uint32_t)max, to);
if (readden != (uint64_t)-1) {
ffs->offset += readden;
}
return readden;
}
static uint64_t file_stream_truncate(stream_t *self, uint64_t size) {
if (!self) {
return (uint64_t)-1;
}
fat16_file_stream_t *ffs = (fat16_file_stream_t *)self;
uint64_t resized = fat16_truncate(ffs->node, size > UINT32_MAX ? UINT32_MAX : (uint32_t)size);
if (resized != (uint64_t)-1 && resized < ffs->offset) {
ffs->offset = (uint32_t)resized;
}
return resized;
}
static void file_stream_close(stream_t *self) {
if (!self) {
return;
}
fat16_file_stream_t *ffs = (fat16_file_stream_t *)self;
fat16_close(ffs->node);
memory_free(self);
}
typedef struct {
stream_t stream;
fs_node_t *node;
uint16_t index;
} fat16_directory_stream_t;
static uint64_t directory_stream_write(__attribute__((unused)) stream_t *self, __attribute__((unused)) const char *from,
__attribute__((unused)) uint64_t bytes) {
return (uint64_t)-1;
}
static uint64_t directory_stream_read(stream_t *self, uint64_t max, char *to) {
if (!self) {
return (uint64_t)-1;
}
fat16_directory_stream_t *fds = (fat16_directory_stream_t *)self;
fs_node_t *node = fat16_open_at(fds->node, fds->index++, OPEN_FILE | OPEN_DIRECTORY);
if (!node) {
return 0;
}
uint64_t length = string_length(node->name);
uint64_t to_read = length > max ? max : length;
memory_copy(node->name, to_read, to);
return to_read;
}
static uint64_t directory_stream_truncate(__attribute__((unused)) stream_t *self, __attribute__((unused)) uint64_t size) {
return (uint64_t)-1;
}
static void directory_stream_close(stream_t *self) {
if (!self) {
return;
}
fat16_directory_stream_t *fds = (fat16_directory_stream_t *)self;
fat16_close(fds->node);
memory_free(self);
}
stream_t *fat16_open_stream(fs_node_t *source) {
if (!source) {
return NUL;
}
if (!source->is_dir) {
fat16_file_stream_t *result = memory_allocate(sizeof(fat16_file_stream_t));
result->stream.read = file_stream_read;
result->stream.write = file_stream_write;
result->stream.truncate = file_stream_truncate;
result->stream.close = file_stream_close;
result->node = fat16_open_again(source);
result->offset = 0;
return (stream_t *)result;
} else {
fat16_directory_stream_t *result = memory_allocate(sizeof(fat16_directory_stream_t));
result->stream.read = directory_stream_read;
result->stream.write = directory_stream_write;
result->stream.truncate = directory_stream_truncate;
result->stream.close = directory_stream_close;
result->node = fat16_open_again(source);
result->index = 0;
return (stream_t *)result;
}
}
void fat16_close(fs_node_t *node) {
if (!node || node->type != FAT16) {
return;
}
node_free(node);
}
+26
View File
@@ -0,0 +1,26 @@
#pragma once
#include "src/kernel/fs.h"
#include "src/kernel/stream.h"
#define FAT16 1
fs_node_t *fat16_open_root();
fs_node_t *fat16_open_by(const fs_node_t *directory, const char *name, uint64_t flags);
fs_node_t *fat16_open_at(const fs_node_t *directory, uint16_t index, uint64_t flags);
fs_node_t *fat16_open_again(fs_node_t *source);
stream_t *fat16_open_stream(fs_node_t *source);
uint64_t fat16_read(const fs_node_t *file, uint32_t offset, uint32_t bytes, void *to);
uint64_t fat16_write(fs_node_t *file, uint32_t offset, const void *from, uint32_t bytes);
uint64_t fat16_truncate(fs_node_t *file, uint32_t size);
uint64_t fat16_remove(fs_node_t *file);
void fat16_close(fs_node_t *node);
+42
View File
@@ -0,0 +1,42 @@
#include "src/kernel/fs.h"
#include "src/kernel/fat16.h"
fs_node_t *fs_open_root() {
return fat16_open_root();
}
fs_node_t *fs_open_by(const fs_node_t *directory, const char *name, uint64_t flags) {
return fat16_open_by(directory, name, flags);
}
fs_node_t *fs_open_at(const fs_node_t *directory, uint16_t index, uint64_t flags) {
return fat16_open_at(directory, index, flags);
}
fs_node_t *fs_open_again(fs_node_t *source) {
return fat16_open_again(source);
}
stream_t *fs_open_stream(fs_node_t *source) {
return fat16_open_stream(source);
}
void fs_read(const fs_node_t *file, uint32_t offset, uint32_t bytes, void *to) {
fat16_read(file, offset, bytes, to);
}
void fs_write(fs_node_t *file, uint32_t offset, const void *from, uint32_t bytes) {
fat16_write(file, offset, from, bytes);
}
void fs_truncate(fs_node_t *file, uint32_t size) {
fat16_truncate(file, size);
}
void fs_remove(fs_node_t *file) {
fat16_remove(file);
}
void fs_close(fs_node_t *node) {
fat16_close(node);
}
+34
View File
@@ -0,0 +1,34 @@
#pragma once
#include "src/kernel/stream.h"
#define FILENAME_SIZE_LIMIT 255
typedef struct fs_node {
uint8_t type;
uint32_t refs;
char name[FILENAME_SIZE_LIMIT + 1];
uint32_t size;
uint8_t is_dir;
uint8_t removed;
} fs_node_t;
fs_node_t *fs_open_root();
fs_node_t *fs_open_by(const fs_node_t *directory, const char *name, uint64_t flags);
fs_node_t *fs_open_at(const fs_node_t *directory, uint16_t index, uint64_t flags);
fs_node_t *fs_open_again(fs_node_t *source);
stream_t *fs_open_stream(fs_node_t *source);
void fs_read(const fs_node_t *file, uint32_t offset, uint32_t bytes, void *to);
void fs_write(fs_node_t *file, uint32_t offset, const void *from, uint32_t bytes);
void fs_truncate(fs_node_t *file, uint32_t size);
void fs_remove(fs_node_t *file);
void fs_close(fs_node_t *node);
+67
View File
@@ -0,0 +1,67 @@
#include "src/kernel/gdt.h"
#include "src/kernel/tss.h"
typedef struct {
uint16_t limit_low;
uint16_t base_low;
uint8_t base_mid;
uint8_t access;
uint8_t flags_limit_high;
uint8_t base_high;
} __attribute__((packed)) gdt_entry_t;
typedef struct {
gdt_entry_t base;
uint32_t base_upper;
uint32_t reserved;
} __attribute__((packed)) gdt_system_entry_t;
typedef struct {
gdt_entry_t gdt[6];
gdt_system_entry_t tss_entry;
} __attribute__((packed)) gdt_table_t;
typedef struct {
uint16_t limit;
uint64_t offset;
} __attribute__((packed)) gdt_descriptor_t;
static gdt_table_t gdt;
static gdt_descriptor_t desc;
static void set_entry(gdt_entry_t *e, uint8_t access, uint8_t flags) {
e->limit_low = 0xFFFF;
e->base_low = 0;
e->base_mid = 0;
e->access = access;
e->flags_limit_high = flags | 0x0F;
e->base_high = 0;
}
static void set_tss_entry(void *base) {
uint64_t ibase = (uint64_t)base;
gdt.tss_entry.base.limit_low = sizeof(tss_t) - 1;
gdt.tss_entry.base.base_low = ibase & 0xFFFF;
gdt.tss_entry.base.base_mid = (ibase >> 16) & 0xFF;
gdt.tss_entry.base.access = 0x89; // present, type=TSS available
gdt.tss_entry.base.flags_limit_high = 0x00;
gdt.tss_entry.base.base_high = (ibase >> 24) & 0xFF;
gdt.tss_entry.base_upper = ibase >> 32;
gdt.tss_entry.reserved = 0;
}
void gdt_init() {
gdt.gdt[0] = (gdt_entry_t){0};
set_entry(&gdt.gdt[1], 0x9A, 0xCF); // present, ring 0, code, executable, readable, 32-bit, 4KB granularity
set_entry(&gdt.gdt[2], 0x9A, 0xA0); // present, ring 0, code, executable, readable, 64-bit
set_entry(&gdt.gdt[3], 0x92, 0x00); // present, ring 0, data, writable
set_entry(&gdt.gdt[4], 0xF2, 0x00); // present, ring 3, data, writable
set_entry(&gdt.gdt[5], 0xFA, 0xA0); // present, ring 3, code, executable, readable, 64-bit
set_tss_entry(&tss);
desc.limit = sizeof(gdt) - 1;
desc.offset = (uint64_t)&gdt;
__asm__ volatile("lgdt %0" : : "m"(desc));
__asm__ volatile("ltr %0" : : "r"((uint16_t)TSS_SEL));
}
+12
View File
@@ -0,0 +1,12 @@
#pragma once
#define OBSOLETE_CS 0x08
#define KERNEL_CS (OBSOLETE_CS + 0x08)
#define KERNEL_DS (KERNEL_CS + 0x08)
#define USER_DS_BASE (KERNEL_DS + 0x08)
#define USER_DS (USER_DS_BASE | 3)
#define USER_CS_BASE (USER_DS_BASE + 0x08)
#define USER_CS (USER_CS_BASE | 3)
#define TSS_SEL (USER_CS_BASE + 0x08)
void gdt_init();
+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.
+5 -1
View File
@@ -1,4 +1,4 @@
#include "src/idt.h" #include "src/kernel/idt.h"
struct idt_entry { struct idt_entry {
uint16_t offset_low; uint16_t offset_low;
@@ -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);
+131
View File
@@ -0,0 +1,131 @@
#include "src/kernel/fs.h"
#include "src/kernel/gdt.h"
#include "src/kernel/hid-keyboard.h"
#include "src/kernel/idt.h"
#include "src/kernel/log.h"
#include "src/kernel/nvme.h"
#include "src/kernel/panic.h"
#include "src/kernel/path.h"
#include "src/kernel/pci.h"
#include "src/kernel/pic.h"
#include "src/kernel/pipe.h"
#include "src/kernel/process.h"
#include "src/kernel/stream.h"
#include "src/kernel/syscall.h"
#include "src/kernel/timer.h"
#include "src/kernel/tss.h"
#include "src/kernel/usb.h"
#include "src/kernel/util.h"
#include "src/kernel/vga.h"
#include "src/kernel/xhci.h"
#include "src/lib/layout.h"
#include "src/lib/memory.h"
#include "src/lib/syscall.h"
__attribute__((interrupt)) void isr_divide_by_zero(__attribute__((unused)) struct interrupt_frame *frame) {
vga_set_string(VGA_HEIGHT - 1, 0, "EXCEPTION: divide by zero", 0x4F);
while (1)
;
}
__attribute__((interrupt)) void isr_invalid_opcode(struct interrupt_frame *frame) {
char msg[80];
string_format("INVALID OPCODE: ip=%x cs=%x flags=%x sp=%x", 80, msg, frame->ip, frame->cs, frame->flags, frame->sp);
vga_set_string(VGA_HEIGHT - 1, 0, msg, 0x4F);
while (1)
;
}
__attribute__((interrupt)) void isr_page_fault(__attribute__((unused)) struct interrupt_frame *frame, uint64_t error_code) {
uint64_t cr2;
__asm__ volatile("mov %%cr2, %0" : "=r"(cr2));
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)
;
}
__attribute__((interrupt)) void isr_general_violation(__attribute__((unused)) struct interrupt_frame *frame) {
vga_set_string(VGA_HEIGHT - 1, 0, "EXCEPTION: general violation", 0x4F);
while (1)
;
}
__attribute__((interrupt)) void isr_ata_primary(__attribute__((unused)) struct interrupt_frame *frame) {
outb(0x20, 0x20);
outb(0xA0, 0x20);
}
void kernel_main() {
stream_t *vga = vga_init();
log_init(vga);
pic_init();
idt_init();
outb(0x21, inb(0x21) | 0x01); // mask out timer interrupt
idt_set_entry(0, isr_divide_by_zero, 0x8E);
idt_set_entry(0x06, isr_invalid_opcode, 0x8E);
idt_set_entry_ec(0x0E, isr_page_fault, 0x8E);
idt_set_entry(0x0D, isr_general_violation, 0x8E);
idt_set_entry(46, isr_ata_primary, 0x8E);
gdt_init();
syscall_init();
pci_init();
nvme_init();
xhci_init();
usb_init();
process_t *kernel = memory_allocate(sizeof(process_t));
kernel->pml4 = (uint64_t *)KERNEL_VIRTUAL_PML4;
kernel->pid = 0;
kernel->state = PROCESS_RUNNING;
kernel->kernel_stack = kernel->kernel_rsp = (uint8_t *)memory_allocate(PAGE_SIZE) + PAGE_SIZE;
kernel->user_stack = kernel->user_rsp = (uint8_t *)memory_allocate(PAGE_SIZE) + PAGE_SIZE;
kernel->cwd = memory_allocate(sizeof(path_t));
kernel->fds[STDIN] = hid_keyboard_init();
kernel->fds[STDOUT] = kernel->fds[STDERR] = vga;
kernel->free_fd = STDERR + 1;
kernel->code = EXIT_CODE_OK;
current_process = kernel;
tss.rsp0 = (uint64_t)kernel->kernel_stack;
fs_node_t *terminal_node = path_open_node(kernel->cwd, "bin/terminal", OPEN_FILE);
fs_node_t *shell_node = path_open_node(kernel->cwd, "bin/shell", OPEN_FILE);
ASSERT(terminal_node, "kernel: terminal not found");
ASSERT(shell_node, "kernel: shell not found");
uint8_t *terminal_code = memory_allocate(terminal_node->size);
fs_read(terminal_node, 0, terminal_node->size, terminal_code);
fs_close(terminal_node);
uint8_t *shell_code = memory_allocate(shell_node->size);
fs_read(shell_node, 0, shell_node->size, shell_code);
fs_close(shell_node);
process_t *terminal = process_create(kernel, terminal_code, terminal_node->size, STDIN, STDOUT);
memory_free(terminal_code);
process_t *shell = process_create(kernel, shell_code, shell_node->size, STDIN, STDOUT);
memory_free(shell_code);
uint64_t *terminal_stack = (uint64_t *)terminal->user_stack;
*(--terminal_stack) = 0;
terminal->user_rsp = (void *)(USER_VIRTUAL_STACK_TOP - ((uint64_t)terminal->user_stack - (uint64_t)terminal_stack));
uint64_t *shell_stack = (uint64_t *)shell->user_stack;
*(--shell_stack) = 0;
shell->user_rsp = (void *)(USER_VIRTUAL_STACK_TOP - ((uint64_t)shell->user_stack - (uint64_t)shell_stack));
pipe_init(&(terminal->fds[terminal->free_fd++]), &(shell->fds[0]));
timer_init();
__asm__ volatile("sti; hlt");
}
+1 -1
View File
@@ -1,7 +1,7 @@
ENTRY(kernel_main) ENTRY(kernel_main)
SECTIONS { SECTIONS {
. = 0x0000000000020000; . = 0xFFFFFFFF80020000;
.text : { .text : {
*(.text) *(.text)
+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);
+133
View File
@@ -0,0 +1,133 @@
#include "src/kernel/memory.h"
#include "src/kernel/log.h"
#include "src/kernel/panic.h"
#include "src/lib/layout.h"
#include "src/lib/memory.h"
#define MAP_UNIT 64
#define FULL_UNIT 0xFFFFFFFFFFFFFFFF
// TODO keep aligned with `src/lib/layout.h`.
static uint16_t free_page = MAP_UNIT * 12;
static uint64_t allocation[PAGE_COUNT / MAP_UNIT] = {
FULL_UNIT,
FULL_UNIT,
FULL_UNIT,
FULL_UNIT,
FULL_UNIT,
FULL_UNIT,
FULL_UNIT,
FULL_UNIT,
FULL_UNIT,
FULL_UNIT,
FULL_UNIT,
FULL_UNIT,
[59] = 0x8000000000000000ULL,
};
static uint8_t get_allocated(uint16_t page) {
return !!(allocation[page / MAP_UNIT] & ((uint64_t)1 << (page % MAP_UNIT)));
}
static void set_allocated(uint16_t page, uint8_t allocated) {
if (allocated) {
allocation[page / MAP_UNIT] |= ((uint64_t)1 << (page % MAP_UNIT));
} else {
allocation[page / MAP_UNIT] &= ~((uint64_t)1 << (page % MAP_UNIT));
}
}
void *memory_page_allocate() {
ASSERT(free_page < PAGE_COUNT, "memory_page_allocate: out of memory");
const uint16_t page = free_page;
set_allocated(page, 1);
for (uint16_t unit = free_page / MAP_UNIT; unit < PAGE_COUNT / MAP_UNIT; unit++) {
if (allocation[unit] != FULL_UNIT) {
uint16_t bit = (uint16_t)__builtin_ctzll(~allocation[unit]);
free_page = unit * MAP_UNIT + bit;
break;
}
}
return (void *)((uint64_t)page * PAGE_SIZE);
}
void memory_page_free(void *address) {
uint16_t page = (uint16_t)((uint64_t)address / PAGE_SIZE);
ASSERT(get_allocated(page), "memory_page_free: page not allocated")
set_allocated(page, 0);
if (page < free_page) {
free_page = page;
}
}
void memory_page_map(uint64_t *pml4, void *virt, void *phys, uint64_t flags) {
const uint64_t ivirt = (uint64_t)virt;
const uint64_t pml4_index = (ivirt >> 39) & 0x1FF;
const uint64_t pdpt_index = (ivirt >> 30) & 0x1FF;
const uint64_t pd_index = (ivirt >> 21) & 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)) {
LOG_LN_TRACE("PML4 entry %u does not exist, creating...", pml4_index);
uint64_t *pdpt = memory_page_allocate();
memory_set(0, PAGE_SIZE, PHYS_TO_VIRT(pdpt));
pml4[pml4_index] = (uint64_t)pdpt | PAGE_PRESENT | PAGE_WRITABLE | PAGE_USER;
}
uint64_t *pdpt = PHYS_TO_VIRT(pml4[pml4_index] & ~(uint64_t)0xFFF);
if (!(pdpt[pdpt_index] & PAGE_PRESENT)) {
LOG_LN_TRACE("PDPT entry %u does not exist, creating...", pdpt_index);
uint64_t *pd = memory_page_allocate();
memory_set(0, PAGE_SIZE, PHYS_TO_VIRT(pd));
pdpt[pdpt_index] = (uint64_t)pd | PAGE_PRESENT | PAGE_WRITABLE | PAGE_USER;
}
uint64_t *pd = PHYS_TO_VIRT(pdpt[pdpt_index] & ~(uint64_t)0xFFF);
ASSERT(!(pd[pd_index] & 0x80), "memory_page_map: Huge page in PD.");
if (!(pd[pd_index] & PAGE_PRESENT)) {
LOG_LN_TRACE("PD entry %u does not exist, creating...", pd_index);
uint64_t *pt = memory_page_allocate();
memory_set(0, PAGE_SIZE, PHYS_TO_VIRT(pt));
pd[pd_index] = (uint64_t)pt | PAGE_PRESENT | PAGE_WRITABLE | PAGE_USER;
}
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;
LOG_VAL_TRACE(pt[pt_index], "%lx");
LOG_LN_TRACE("Done.");
}
void memory_page_unmap(uint64_t *pml4, void *virt) {
const uint64_t ivirt = (uint64_t)virt;
const uint64_t pml4_index = (ivirt >> 39) & 0x1FF;
const uint64_t pdpt_index = (ivirt >> 30) & 0x1FF;
const uint64_t pd_index = (ivirt >> 21) & 0x1FF;
const uint64_t pt_index = (ivirt >> 12) & 0x1FF;
if (!(pml4[pml4_index] & PAGE_PRESENT)) {
return;
}
uint64_t *pdpt = PHYS_TO_VIRT(pml4[pml4_index] & ~(uint64_t)0xFFF);
if (!(pdpt[pdpt_index] & PAGE_PRESENT)) {
return;
}
uint64_t *pd = PHYS_TO_VIRT(pdpt[pdpt_index] & ~(uint64_t)0xFFF);
if (!(pd[pd_index] & PAGE_PRESENT)) {
return;
}
uint64_t *pt = PHYS_TO_VIRT(pd[pd_index] & ~(uint64_t)0xFFF);
memory_page_free((void *)(pt[pt_index] & ~(uint64_t)0xFFF));
pt[pt_index] = 0;
__asm__ volatile("invlpg (%0)" : : "r"(virt) : "memory");
}
+22
View File
@@ -0,0 +1,22 @@
#pragma once
#include <stdint.h>
#define PAGE_PRESENT 0x01
#define PAGE_WRITABLE 0x02
#define PAGE_USER 0x04
#define PAGE_PWT 0x08
#define PAGE_PCD 0x10
#define PAGE_ACCESSED 0x20
#define PAGE_DIRTY 0x40
#define PAGE_HUGE 0x80
#define PAGE_GLOBAL 0x100
#define PAGE_NX (1ULL << 63)
void *memory_page_allocate();
void memory_page_free(void *page);
void memory_page_map(uint64_t *pml4, void *virt, void *phys, uint64_t flags);
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);
+8
View File
@@ -0,0 +1,8 @@
#include "src/kernel/panic.h"
#include "src/kernel/vga.h"
void kernel_panic(const char *msg, __attribute__((unused)) const char *file, __attribute__((unused)) int line) {
vga_set_string(VGA_HEIGHT - 1, 0, msg, 0x28);
while (1)
;
}
+1 -1
View File
@@ -2,7 +2,7 @@
#define ASSERT(cond, msg) \ #define ASSERT(cond, msg) \
if (!(cond)) { \ if (!(cond)) { \
kernel_panic(msg " (assertion: " #cond ")", __FILE__, __LINE__); \ kernel_panic(msg " (assertion: " #cond ")", __FILE__, __LINE__); \
} }
void kernel_panic(const char *msg, const char *file, int line); void kernel_panic(const char *msg, const char *file, int line);
+98
View File
@@ -0,0 +1,98 @@
#include "src/kernel/path.h"
#include "src/kernel/fs.h"
#include "src/kernel/stream.h"
#include "src/lib/memory.h"
#include "src/lib/string.h"
#include "src/lib/syscall.h"
#include "src/lib/util.h"
path_t *path_open(const path_t *base, const char *path, uint64_t flags) {
if (!base || !path) {
return NUL;
}
uint64_t length = string_length(path);
char *path_own = memory_allocate(length + 1);
memory_copy(path, length + 1, path_own);
path_t *result = memory_allocate(sizeof(path_t));
char *path_components[PATH_DEPTH];
uint8_t path_length = (uint8_t)string_split(path_own, '/', PATH_DEPTH, path_components);
if (!string_empty(path_components[0])) {
for (uint8_t i = 0; i < base->depth; i++) {
result->stack[result->depth++] = fs_open_again(base->stack[i]);
}
}
for (uint8_t i = 0; i < path_length; i++) {
if (!result->depth) {
result->stack[result->depth++] = fs_open_root();
}
if (string_empty(path_components[i]) || string_equal(path_components[i], ".")) {
if (i == path_length - 1 && result->depth) {
// TODO Apply flags to current top of stack.
}
continue;
} else if (string_equal(path_components[i], "..")) {
if (result->depth > 1) {
fs_close(result->stack[--result->depth]);
}
} else {
fs_node_t *next = fs_open_by(result->stack[result->depth - 1], path_components[i],
i < path_length - 1 ? (flags & ~(uint64_t)OPEN_EXCLUSIVE) | OPEN_DIRECTORY : flags);
if (!next || result->depth >= PATH_DEPTH) {
path_close(result);
memory_free(path_own);
return NUL;
} else {
result->stack[result->depth++] = next;
}
}
}
memory_free(path_own);
return result;
}
path_t *path_open_again(const path_t *path) {
if (!path) {
return NUL;
}
path_t *p = memory_allocate(sizeof(path_t));
p->depth = path->depth;
for (uint8_t i = 0; i < path->depth; i++) {
p->stack[i] = fs_open_again(path->stack[i]);
}
return p;
}
fs_node_t *path_open_node(const path_t *base, const char *path, uint64_t flags) {
path_t *p = path_open(base, path, flags);
if (!p) {
return NUL;
}
fs_node_t *n = fs_open_again(p->stack[p->depth - 1]);
path_close(p);
return n;
}
stream_t *path_open_stream(const path_t *base, const char *path, uint64_t flags) {
path_t *p = path_open(base, path, flags);
if (!p) {
return NUL;
}
stream_t *s = fs_open_stream(p->stack[p->depth - 1]);
path_close(p);
return s;
}
void path_close(path_t *path) {
for (uint64_t i = 0; i < path->depth; i++) {
fs_close(path->stack[i]);
}
memory_free(path);
}
+21
View File
@@ -0,0 +1,21 @@
#pragma once
#include "src/kernel/fs.h"
#include "src/kernel/stream.h"
#define PATH_DEPTH 255
typedef struct {
fs_node_t *stack[PATH_DEPTH];
uint8_t depth;
} path_t;
path_t *path_open(const path_t *base, const char *path, uint64_t flags);
path_t *path_open_again(const path_t *path);
fs_node_t *path_open_node(const path_t *base, const char *path, uint64_t flags);
stream_t *path_open_stream(const path_t *base, const char *path, uint64_t flags);
void path_close(path_t *path);
+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);
+2 -2
View File
@@ -1,5 +1,5 @@
#include "src/pic.h" #include "src/kernel/pic.h"
#include "src/util.h" #include "src/kernel/util.h"
static void pic_remap() { static void pic_remap() {
uint8_t mask1 = inb(0x21); uint8_t mask1 = inb(0x21);
View File
+134
View File
@@ -0,0 +1,134 @@
#include "src/kernel/pipe.h"
#include "src/kernel/process.h"
#include "src/kernel/stream.h"
#include "src/lib/memory.h"
#define PIPE_BUFFER_SIZE 65536
typedef struct {
char buffer[PIPE_BUFFER_SIZE];
uint64_t begin;
uint64_t end;
uint8_t write_closed;
uint8_t read_closed;
} pipe_t;
typedef struct {
stream_t stream;
pipe_t *pipe;
} pipe_read_stream_t;
typedef struct {
stream_t stream;
pipe_t *pipe;
} pipe_write_stream_t;
static uint64_t null_read(__attribute__((unused)) stream_t *self, __attribute__((unused)) uint64_t max,
__attribute__((unused)) char *to) {
return 0;
}
static uint64_t null_write(__attribute__((unused)) stream_t *self, __attribute__((unused)) const char *from,
__attribute__((unused)) uint64_t bytes) {
return 0;
}
static uint64_t pipe_read(stream_t *self, uint64_t max, char *to) {
pipe_t *pipe = ((pipe_read_stream_t *)self)->pipe;
while (pipe->begin == pipe->end) {
if (pipe->write_closed) {
return 0;
}
__asm__ volatile("sti");
process_next();
__asm__ volatile("cli");
}
uint64_t available = (pipe->end + PIPE_BUFFER_SIZE - pipe->begin) % PIPE_BUFFER_SIZE;
uint64_t to_read = available < max ? available : max;
if (pipe->begin + to_read <= PIPE_BUFFER_SIZE) {
memory_copy(pipe->buffer + pipe->begin, to_read, to);
} else {
uint64_t chunk = PIPE_BUFFER_SIZE - pipe->begin;
memory_copy(pipe->buffer + pipe->begin, chunk, to);
memory_copy(pipe->buffer, to_read - chunk, to + chunk);
}
pipe->begin = (pipe->begin + to_read) % PIPE_BUFFER_SIZE;
return to_read;
}
static uint64_t pipe_write(stream_t *self, const char *from, uint64_t bytes) {
pipe_t *pipe = ((pipe_read_stream_t *)self)->pipe;
uint64_t written = 0;
while (written < bytes) {
while (((pipe->end + 1) % PIPE_BUFFER_SIZE) == pipe->begin) {
if (pipe->read_closed) {
return written;
}
__asm__ volatile("sti");
process_next();
__asm__ volatile("cli");
}
uint64_t available = PIPE_BUFFER_SIZE - (pipe->end + PIPE_BUFFER_SIZE - pipe->begin) % PIPE_BUFFER_SIZE - 1;
uint64_t to_write = available < bytes - written ? available : bytes - written;
if (pipe->end + to_write <= PIPE_BUFFER_SIZE) {
memory_copy(from, to_write, pipe->buffer + pipe->end);
} else {
uint64_t chunk = PIPE_BUFFER_SIZE - pipe->end;
memory_copy(from, chunk, pipe->buffer + pipe->end);
memory_copy(from + chunk, to_write - chunk, pipe->buffer);
}
pipe->end = (pipe->end + to_write) % PIPE_BUFFER_SIZE;
written += to_write;
}
return written;
}
static void read_close(stream_t *self) {
pipe_t *pipe = ((pipe_read_stream_t *)self)->pipe;
pipe->read_closed = 1;
if (pipe->read_closed && pipe->write_closed) {
memory_free(pipe);
}
memory_free(self);
}
static void write_close(stream_t *self) {
pipe_t *pipe = ((pipe_write_stream_t *)self)->pipe;
pipe->write_closed = 1;
if (pipe->read_closed && pipe->write_closed) {
memory_free(pipe);
}
memory_free(self);
}
void pipe_init(stream_t **write, stream_t **read) {
pipe_t *pipe = memory_allocate(sizeof(pipe_t));
pipe_write_stream_t *ws = memory_allocate(sizeof(pipe_write_stream_t));
ws->stream.read = null_read;
ws->stream.write = pipe_write;
ws->stream.close = write_close;
ws->pipe = pipe;
pipe_read_stream_t *rs = memory_allocate(sizeof(pipe_read_stream_t));
rs->stream.read = pipe_read;
rs->stream.write = null_write;
rs->stream.close = read_close;
rs->pipe = pipe;
*write = (stream_t *)ws;
*read = (stream_t *)rs;
}
+5
View File
@@ -0,0 +1,5 @@
#pragma once
#include "src/kernel/stream.h"
void pipe_init(stream_t **write, stream_t **read);
+50
View File
@@ -0,0 +1,50 @@
bits 64
global process_trampoline
process_trampoline:
extern current_process
mov rax, [current_process]
mov rdx, [rax + 40] ; current_process->user_rsp
; Keep in sync with `gdt.h` and `layout.h`
mov rax, 0x0000000000400000
mov rbx, 0x2B
mov rcx, 0x202
mov rsi, 0x23
push rsi
push rdx
push rcx
push rbx
push rax
iretq
global process_switch_to
; process_switch_to(uint64_t *save_rsp, uint64_t new_rsp, uint64_t new_pml4_phys)
process_switch_to:
; save callee-saved registers of the OUTGOING process
push rbp
push rbx
push r12
push r13
push r14
push r15
; save current rsp into *save_rsp (rdi)
mov [rdi], rsp
; switch address space
mov cr3, rdx ; rdx = new_pml4_phys (3rd arg)
; switch to incoming process's stack
mov rsp, rsi ; rsi = new_rsp (2nd arg)
; restore callee-saved registers of the INCOMING process
pop r15
pop r14
pop r13
pop r12
pop rbx
pop rbp
ret
+191
View File
@@ -0,0 +1,191 @@
#include "src/kernel/process.h"
#include "src/kernel/memory.h"
#include "src/kernel/path.h"
#include "src/kernel/tss.h"
#include "src/kernel/util.h"
#include "src/lib/layout.h"
#include "src/lib/memory.h"
#include "src/lib/util.h"
#define MAX_PROCESSES 256
static process_t *processes[MAX_PROCESSES];
static uint64_t free_pid = 1;
process_t *current_process;
process_t *process_create(const process_t *parent, const uint8_t *code, uint64_t size, uint64_t stdin, uint64_t stdout) {
if (size >= USER_VIRTUAL_HEAP - USER_VIRTUAL_CODE) {
return NUL;
}
uint64_t free_pi;
for (free_pi = 0; free_pi <= MAX_PROCESSES; free_pi++) {
if (!processes[free_pi]) {
break;
}
}
if (free_pi == MAX_PROCESSES) {
return NUL;
}
if (stdin >= MAX_FDS || !parent->fds[stdin] || stdout > MAX_FDS || !parent->fds[stdout]) {
return NUL;
}
process_t *proc = processes[free_pi] = memory_allocate(sizeof(process_t));
proc->parent = parent;
proc->kernel_stack = (uint8_t *)memory_allocate(PAGE_SIZE) + PAGE_SIZE;
uint64_t *kstackv = (uint64_t *)((uint8_t *)proc->kernel_stack);
*(--kstackv) = (uint64_t)process_trampoline; // "return address" for switch_to's ret
*(--kstackv) = 0; // r15
*(--kstackv) = 0; // r14
*(--kstackv) = 0; // r13
*(--kstackv) = 0; // r12
*(--kstackv) = 0; // rbx
*(--kstackv) = 0; // rbp
proc->kernel_rsp = kstackv;
proc->pml4 = PHYS_TO_VIRT(memory_page_allocate());
memory_set(0, PAGE_SIZE / 2, proc->pml4);
memory_copy((uint64_t *)KERNEL_VIRTUAL_PML4 + 256, PAGE_SIZE / 2, proc->pml4 + 256);
void *ustackp = memory_page_allocate();
proc->user_stack = PHYS_TO_VIRT((uint64_t)ustackp + PAGE_SIZE);
memory_page_map(proc->pml4, (void *)USER_VIRTUAL_STACK, ustackp, PAGE_PRESENT | PAGE_WRITABLE | PAGE_USER);
for (uint64_t page = 0; size > 0; page++) {
void *p = memory_page_allocate();
uint64_t s = size < PAGE_SIZE ? size : PAGE_SIZE;
memory_page_map(proc->pml4, (void *)(USER_VIRTUAL_CODE + page * PAGE_SIZE), p,
PAGE_PRESENT | PAGE_WRITABLE | PAGE_USER); // TODO Restrict writeability of code.
memory_copy(code, s, PHYS_TO_VIRT(p));
code += s;
size -= s;
}
for (uint64_t page = 0; page < HEAP_PAGE_COUNT; page++) {
memory_page_map(proc->pml4, (void *)(USER_VIRTUAL_HEAP + page * PAGE_SIZE), memory_page_allocate(),
PAGE_PRESENT | PAGE_WRITABLE | PAGE_USER);
}
proc->pid = free_pid++;
proc->state = PROCESS_RUNNING;
proc->cwd = path_open_again(parent->cwd);
proc->fds[STDIN] = parent->fds[stdin];
proc->fds[STDOUT] = parent->fds[stdout];
proc->fds[STDERR] = parent->fds[STDERR];
proc->free_fd = STDERR + 1;
proc->code = EXIT_CODE_OK;
return proc;
}
process_t *process_get(uint64_t pid) {
for (uint64_t i = 0; i < MAX_PROCESSES; i++) {
if (processes[i] && processes[i]->pid == pid) {
return processes[i];
}
}
return NUL;
}
void process_next() {
process_t *next = NUL;
uint64_t cpi = 0;
for (uint64_t i = 0; i < MAX_PROCESSES; i++) {
if (processes[i] == current_process) {
cpi = i;
break;
}
}
for (uint64_t i = 1; i <= MAX_PROCESSES; i++) {
process_t *candidate = processes[(cpi + i) % MAX_PROCESSES];
if (candidate && candidate->state == PROCESS_RUNNING) {
next = candidate;
break;
}
}
if (!next) {
outw(0x604, 0x2000);
__asm__ volatile("cli; hlt");
}
if (next == current_process) {
return;
}
process_t *prev = current_process;
tss.rsp0 = (uint64_t)next->kernel_stack;
current_process = next;
__asm__ volatile("cli");
process_switch_to(&prev->kernel_rsp, next->kernel_rsp, VIRT_TO_PHYS(next->pml4));
__asm__ volatile("sti");
}
void process_destroy(process_t *proc) {
for (uint64_t i = STDERR + 1; i < MAX_FDS; i++) {
if (proc->fds[i]) {
proc->fds[i]->close(proc->fds[i]);
proc->fds[i] = NUL;
}
}
path_close((path_t *)proc->cwd);
for (uint16_t pml4i = 0; pml4i < 256; pml4i++) {
if (!(proc->pml4[pml4i] & PAGE_PRESENT)) {
continue;
}
uint64_t *pdpt = PHYS_TO_VIRT(proc->pml4[pml4i] & ~(uint64_t)0xFFF);
for (uint16_t pdpti = 0; pdpti < 512; pdpti++) {
if (!(pdpt[pdpti] & PAGE_PRESENT)) {
continue;
}
uint64_t *pd = PHYS_TO_VIRT(pdpt[pdpti] & ~(uint64_t)0xFFF);
for (uint16_t pdi = 0; pdi < 512; pdi++) {
if (!(pd[pdi] & PAGE_PRESENT)) {
continue;
}
if (pd[pdi] & PAGE_HUGE) {
memory_page_free((void *)(pd[pdi] & ~(uint64_t)0xFFF));
continue;
}
uint64_t *pt = PHYS_TO_VIRT(pd[pdi] & ~(uint64_t)0xFFF);
for (uint16_t pti = 0; pti < 512; pti++) {
if (!(pt[pti] & PAGE_PRESENT)) {
continue;
}
memory_page_free((void *)(pt[pti] & ~(uint64_t)0xFFF));
}
memory_page_free((void *)(pd[pdi] & ~(uint64_t)0xFFF));
}
memory_page_free((void *)(pdpt[pdpti] & ~(uint64_t)0xFFF));
}
memory_page_free((void *)(proc->pml4[pml4i] & ~(uint64_t)0xFFF));
}
memory_page_free(VIRT_TO_PHYS(proc->pml4));
memory_free(proc->kernel_stack - PAGE_SIZE);
for (uint64_t i = 0; i < MAX_PROCESSES; i++) {
if (processes[i] == proc) {
processes[i] = NUL;
break;
}
}
memory_free(proc);
}
+47
View File
@@ -0,0 +1,47 @@
#pragma once
#include "src/kernel/path.h"
#include "src/kernel/stream.h"
#include "src/lib/util.h"
#define USER_RFLAGS 0x202
#define MAX_FDS 16
typedef enum {
PROCESS_RUNNING,
PROCESS_WAITING,
PROCESS_ZOMBIE,
} process_state_t;
typedef struct process {
const struct process *parent;
uint64_t *pml4;
void *kernel_stack;
void *kernel_rsp;
void *user_stack;
void *user_rsp;
uint64_t pid;
process_state_t state;
uint64_t waiting_for;
const path_t *cwd;
stream_t *fds[MAX_FDS];
uint64_t free_fd;
exit_code_t code;
} process_t;
typedef void (*app_t)(process_t *proc, uint8_t argc, char **argv);
extern process_t *current_process;
extern void process_trampoline();
process_t *process_create(const process_t *parent, const uint8_t *code, uint64_t size, uint64_t stdin, uint64_t stdout);
process_t *process_get(uint64_t pid);
extern void process_switch_to(void **save_rsp, void *new_rsp, void *new_pml4_phys);
void process_next();
void process_destroy(process_t *proc);
+172
View File
@@ -0,0 +1,172 @@
#include "src/kernel/ps2-keyboard.h"
#include "src/kernel/idt.h"
#include "src/kernel/process.h"
#include "src/kernel/stream.h"
#include "src/kernel/util.h"
#include "src/lib/memory.h"
#define KEYBOARD_STATE_LSHIFT 0b00000001
#define KEYBOARD_STATE_RSHIFT 0b00000010
#define KEYBOARD_STATE_LCTRL 0b00000100
#define KEYBOARD_STATE_RCTRL 0b00001000
#define KEYBOARD_STATE_LALT 0b00010000
#define KEYBOARD_STATE_RALT 0b00100000
#define KEYBOARD_STATE_SEQ 0b01000000
static uint8_t keyboard_state = 0;
// clang-format off
static const char scancode_normal[128] = {
0, 0, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b', '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u',
'i', 'o', 'p', '[', ']', '\n', 0, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', 0, '\\', 'z', 'x',
'c', 'v', 'b', 'n', 'm', ',', '.', '/', 0, '*', 0, ' ', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, '-', 0, 0, 0, '+', 0, 0, 0, 0, 0, 0, 0, 0, 0
};
static const char scancode_shifted[128] = {
0, 0, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', '\b', '\t', 'Q', 'W', 'E', 'R', 'T', 'Y', 'U',
'I', 'O', 'P', '{', '}', '\n', 0, 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '~', 0, '|', 'Z', 'X',
'C', 'V', 'B', 'N', 'M', '<', '>', '?', 0, '*', 0, ' ', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, '-', 0, 0, 0, '+', 0, 0, 0, 0, 0, 0, 0, 0, 0
};
// clang-format on
#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 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) {
__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};
static void on_key(uint8_t scancode) {
const uint8_t pressed = !(scancode & 0x80);
const uint8_t code = scancode & ~0x80;
if (!(keyboard_state & KEYBOARD_STATE_SEQ)) {
switch (code) {
case 0x60:
keyboard_state = keyboard_state | KEYBOARD_STATE_SEQ;
break;
case 0x2A:
keyboard_state = pressed ? keyboard_state | KEYBOARD_STATE_LSHIFT : keyboard_state & ~KEYBOARD_STATE_LSHIFT;
break;
case 0x36:
keyboard_state = pressed ? keyboard_state | KEYBOARD_STATE_RSHIFT : keyboard_state & ~KEYBOARD_STATE_RSHIFT;
break;
case 0x38:
keyboard_state = pressed ? keyboard_state | KEYBOARD_STATE_LALT : keyboard_state & ~KEYBOARD_STATE_LALT;
break;
case 0x1D:
keyboard_state = pressed ? keyboard_state | KEYBOARD_STATE_LCTRL : keyboard_state & ~KEYBOARD_STATE_LCTRL;
break;
case 0x0E:
pressed ? append('\b') : 0;
break;
case 0x1C:
pressed ? append('\n') : 0;
break;
default:
if (pressed && scancode_normal[code]) {
if (keyboard_state && (keyboard_state & (KEYBOARD_STATE_LCTRL | KEYBOARD_STATE_RCTRL)) == keyboard_state) {
if ((scancode_normal[code] >= '0' && scancode_normal[code] <= '9') ||
(scancode_normal[code] >= 'a' && scancode_normal[code] <= 'z')) {
append(scancode_normal[code] - 'a' + 1);
}
} else if (keyboard_state && (keyboard_state & (KEYBOARD_STATE_LALT | KEYBOARD_STATE_RALT)) == keyboard_state) {
// TODO Alt combinations.
} else {
append((keyboard_state & (KEYBOARD_STATE_LSHIFT | KEYBOARD_STATE_RSHIFT) ? scancode_shifted : scancode_normal)[code]);
}
}
break;
}
} else {
keyboard_state = keyboard_state & ~KEYBOARD_STATE_SEQ;
switch (code) {
case 0x38:
keyboard_state = pressed ? keyboard_state | KEYBOARD_STATE_RALT : keyboard_state & ~KEYBOARD_STATE_RALT;
break;
case 0x1D:
keyboard_state = pressed ? keyboard_state | KEYBOARD_STATE_RCTRL : keyboard_state & ~KEYBOARD_STATE_RCTRL;
break;
case 0x47:
pressed ? append_sequence(STREAM_SEQ_HOME) : 0;
break;
case 0x4B:
pressed ? append_sequence(STREAM_SEQ_LEFT) : 0;
break;
case 0x4D:
pressed ? append_sequence(STREAM_SEQ_RIGHT) : 0;
break;
case 0x4F:
pressed ? append_sequence(STREAM_SEQ_END) : 0;
break;
case 0x53:
pressed ? append_sequence(STREAM_SEQ_DELETE) : 0;
break;
}
}
}
__attribute__((interrupt)) static void isr_ps2_keyboard(__attribute__((unused)) struct interrupt_frame *frame) {
uint8_t scancode = inb(0x60);
outb(0x20, 0x20);
on_key(scancode);
}
stream_t *ps2_keyboard_init() {
outb(0x21, inb(0x21) & ~0x02);
idt_set_entry(33, isr_ps2_keyboard, 0x8E);
return &stream;
}
+6
View File
@@ -0,0 +1,6 @@
#pragma once
#include "src/kernel/stream.h"
#include <stdint.h>
stream_t *ps2_keyboard_init();
+40
View File
@@ -0,0 +1,40 @@
#pragma once
#include <stdint.h>
#define STREAM_SEQ_UP "\x1B[A"
#define STREAM_SEQ_DOWN "\x1B[B"
#define STREAM_SEQ_RIGHT "\x1B[C"
#define STREAM_SEQ_LEFT "\x1B[D"
#define STREAM_SEQ_HOME "\x1B[H"
#define STREAM_SEQ_END "\x1B[F"
#define STREAM_SEQ_INSERT "\x1B[2~"
#define STREAM_SEQ_DELETE "\x1B[3~"
#define STREAM_SEQ_PAGE_UP "\x1B[5~"
#define STREAM_SEQ_PAGE_DOWN "\x1B[6~"
#define STREAM_SEQ_F1 "\x1B[11~"
#define STREAM_SEQ_F2 "\x1B[12~"
#define STREAM_SEQ_F3 "\x1B[13~"
#define STREAM_SEQ_F4 "\x1B[14~"
#define STREAM_SEQ_F5 "\x1B[15~"
#define STREAM_SEQ_F6 "\x1B[16~"
#define STREAM_SEQ_F7 "\x1B[17~"
#define STREAM_SEQ_F8 "\x1B[18~"
#define STREAM_SEQ_F9 "\x1B[19~"
#define STREAM_SEQ_F10 "\x1B[1A~"
#define STREAM_SEQ_F11 "\x1B[1B~"
#define STREAM_SEQ_F12 "\x1B[1C~"
typedef struct stream stream_t;
typedef uint64_t (*stream_write_t)(stream_t *self, const char *from, uint64_t bytes);
typedef uint64_t (*stream_read_t)(stream_t *self, uint64_t max, char *to);
typedef uint64_t (*stream_truncate_t)(stream_t *self, uint64_t size);
typedef void (*stream_close_t)(stream_t *self);
typedef struct stream {
stream_write_t write;
stream_read_t read;
stream_truncate_t truncate;
stream_close_t close;
} stream_t;
+57
View File
@@ -0,0 +1,57 @@
bits 64
extern tss
extern syscall_dispatch
extern current_process
global syscall_entry
syscall_entry:
mov [rel user_rsp_tmp], rsp
mov rsp, [tss + 4]
push rax
push rcx
mov rax, [rel current_process]
mov rcx, [rel user_rsp_tmp]
mov [rax + 40], rcx ; current_process->user_rsp
pop rcx
pop rax
push rcx
push r11
push rbp
push rbx
push r12
push r13
push r14
push r15
mov r9, r8 ; arg5
mov r8, r10 ; arg4
mov rcx, rdx ; arg3
mov rdx, rsi ; arg2
mov rsi, rdi ; arg1
mov rdi, rax ; syscall number
call syscall_dispatch
pop r15
pop r14
pop r13
pop r12
pop rbx
pop rbp
pop r11
pop rcx
push rax
mov rax, [rel current_process]
mov rax, [rax + 40] ; current_process->user_rsp
mov [rel user_rsp_tmp], rax
pop rax
mov rsp, [rel user_rsp_tmp]
o64 sysret
user_rsp_tmp: dq 0
+250
View File
@@ -0,0 +1,250 @@
#include "src/kernel/syscall.h"
#include "src/kernel/fs.h"
#include "src/kernel/gdt.h"
#include "src/kernel/path.h"
#include "src/kernel/pipe.h"
#include "src/kernel/process.h"
#include "src/kernel/stream.h"
#include "src/lib/layout.h"
#include "src/lib/memory.h"
#include "src/lib/string.h"
#include "src/lib/syscall.h"
#include "src/lib/util.h"
#define MSR_EFER 0xC0000080
#define MSR_STAR 0xC0000081
#define MSR_LSTAR 0xC0000082
#define MSR_FMASK 0xC0000084
static inline uint64_t rdmsr(uint32_t msr) {
uint32_t lo, hi;
__asm__ volatile("rdmsr" : "=a"(lo), "=d"(hi) : "c"(msr));
return ((uint64_t)hi << 32) | lo;
}
static inline void wrmsr(uint32_t msr, uint64_t value) {
__asm__ volatile("wrmsr" : : "c"(msr), "a"((uint32_t)value), "d"((uint32_t)(value >> 32)));
}
extern void syscall_entry();
void syscall_init() {
wrmsr(MSR_EFER, rdmsr(MSR_EFER) | 0x01);
wrmsr(MSR_STAR, ((uint64_t)KERNEL_DS << 48) | ((uint64_t)KERNEL_CS << 32));
wrmsr(MSR_LSTAR, (uint64_t)syscall_entry);
wrmsr(MSR_FMASK, 0x200);
}
static uint64_t read(uint64_t fd, uint64_t max, char *to) {
if (fd >= MAX_FDS || !current_process->fds[fd]) {
return (uint64_t)-1;
}
return current_process->fds[fd]->read(current_process->fds[fd], max, to);
}
static uint64_t write(uint64_t fd, const char *from, uint64_t bytes) {
if (fd >= MAX_FDS || !current_process->fds[fd]) {
return (uint64_t)-1;
}
return current_process->fds[fd]->write(current_process->fds[fd], from, bytes);
}
static uint64_t getcwd(uint64_t max, char *to) {
if (max == 0) {
return 0;
}
if (max == 1) {
to[0] = '\0';
return 0;
}
if (max == 2 || !current_process->cwd->depth) {
to[0] = '/';
to[1] = '\0';
return 1;
}
uint64_t i = 0;
for (uint8_t j = 0; j < current_process->cwd->depth; j++) {
uint64_t l = string_length(current_process->cwd->stack[j]->name);
if (i + l + 1 > max) {
break;
}
to[i++] = '/';
memory_copy(current_process->cwd->stack[j]->name, l, to + i);
i += l;
}
to[i] = '\0';
return i;
}
static uint64_t chdir(const char *path) {
path_t *cwd = path_open(current_process->cwd, path, OPEN_DIRECTORY);
if (!cwd) {
return (uint64_t)-1;
}
path_close((path_t *)current_process->cwd);
current_process->cwd = cwd;
return current_process->cwd->depth;
}
static uint64_t spawn(const char *path, uint64_t argc, const char **argv, uint64_t stdin, uint64_t stdout) {
fs_node_t *node = path_open_node(current_process->cwd, path, OPEN_FILE);
if (!node) {
return EXIT_CODE_NOT_FOUND;
}
uint8_t *bin = memory_allocate(node->size);
fs_read(node, 0, node->size, bin);
uint64_t size = 0;
uint64_t sizes[16];
uint64_t offsets[16];
for (uint64_t i = 0; i < argc; i++) {
offsets[i] = size;
size += (sizes[i] = string_length(argv[i]) + 1);
}
char *blob = memory_allocate(size);
for (uint64_t i = 0; i < argc; i++) {
memory_copy(argv[i], sizes[i], blob + offsets[i]);
}
process_t *child = process_create(current_process, bin, node->size, stdin, stdout);
memory_free(bin);
fs_close(node);
__asm__ volatile("mov %0, %%cr3" : : "r"((uint64_t)VIRT_TO_PHYS((void *)KERNEL_VIRTUAL_PML4)) : "memory");
uint8_t *stack = (uint8_t *)child->user_stack;
stack -= size;
memory_copy(blob, size, stack);
memory_free(blob);
stack = (uint8_t *)((uint64_t)stack & ~7ULL);
stack -= argc * sizeof(char *);
for (uint64_t i = 0; i < argc; i++) {
((char **)stack)[i] = (char *)(USER_VIRTUAL_STACK_TOP - size + offsets[i]);
}
stack -= sizeof(uint64_t);
*(uint64_t *)stack = argc;
child->user_rsp = (void *)(USER_VIRTUAL_STACK_TOP - ((uint64_t)child->user_stack - (uint64_t)stack));
__asm__ volatile("mov %0, %%cr3" : : "r"((uint64_t)VIRT_TO_PHYS((void *)current_process->pml4)) : "memory");
return child->pid;
}
static exit_code_t wait(uint64_t pid) {
process_t *child = process_get(pid);
if (!child) {
return EXIT_CODE_GENERAL_FAILURE;
}
current_process->state = PROCESS_WAITING;
current_process->waiting_for = pid;
while (child->state != PROCESS_ZOMBIE) {
process_next();
}
current_process->state = PROCESS_RUNNING;
exit_code_t code = child->code;
process_destroy(child);
return code;
}
static uint64_t open(const char *path, uint64_t flags) {
if (current_process->free_fd >= MAX_FDS) {
return (uint64_t)-1;
}
stream_t *stream = path_open_stream(current_process->cwd, path, flags);
if (!stream) {
return (uint64_t)-1;
}
current_process->fds[current_process->free_fd++] = stream;
return current_process->free_fd - 1;
}
static uint64_t close(uint64_t fd) {
if (fd >= MAX_FDS || !current_process->fds[fd]) {
return (uint64_t)-1;
}
current_process->fds[fd]->close(current_process->fds[fd]);
current_process->fds[fd] = NUL;
return 0;
}
static uint64_t truncate(uint64_t fd, uint64_t size) {
if (fd >= MAX_FDS || !current_process->fds[fd]) {
return (uint64_t)-1;
}
return current_process->fds[fd]->truncate(current_process->fds[fd], size);
}
static uint64_t remove(const char *path) {
fs_node_t *node = path_open_node(current_process->cwd, path, OPEN_FILE | OPEN_DIRECTORY);
fs_remove(node);
fs_close(node);
return 0;
}
static uint64_t pipe(uint64_t *write_fd, uint64_t *read_fd) {
if (current_process->free_fd + 2 >= MAX_FDS) {
return (uint64_t)-1;
}
*write_fd = current_process->free_fd++;
*read_fd = current_process->free_fd++;
pipe_init(&current_process->fds[*write_fd], &current_process->fds[*read_fd]);
return 0;
}
static void exit(exit_code_t code) {
current_process->state = PROCESS_ZOMBIE;
current_process->code = code;
if (current_process->parent) {
process_t *parent = (process_t *)current_process->parent;
if (parent->state == PROCESS_WAITING && parent->waiting_for == current_process->pid) {
parent->state = PROCESS_RUNNING;
}
}
process_next();
}
uint64_t syscall_dispatch(uint64_t func, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4, uint64_t arg5) {
switch (func) {
case SYSCALL_READ:
return read(arg1, arg2, (char *)arg3);
case SYSCALL_WRITE:
return write(arg1, (const char *)arg2, arg3);
case SYSCALL_GETCWD:
return getcwd(arg1, (char *)arg2);
case SYSCALL_CHDIR:
return chdir((const char *)arg1);
case SYSCALL_SPAWN:
return spawn((const char *)arg1, arg2, (const char **)arg3, arg4, arg5);
case SYSCALL_WAIT:
return wait(arg1);
case SYSCALL_OPEN:
return open((const char *)arg1, arg2);
case SYSCALL_CLOSE:
return close(arg1);
case SYSCALL_TRUNCATE:
return truncate(arg1, arg2);
case SYSCALL_REMOVE:
return remove((char *)arg1);
case SYSCALL_PIPE:
return pipe((uint64_t *)arg1, (uint64_t *)arg2);
case SYSCALL_EXIT:
exit(arg1);
return 0;
default:
return (uint64_t)-1;
}
}
+7
View File
@@ -0,0 +1,7 @@
#pragma once
#include <stdint.h>
void syscall_init();
uint64_t syscall_dispatch(uint64_t func, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4, uint64_t arg5);
+28
View File
@@ -0,0 +1,28 @@
bits 64
extern timer_handler
global timer_entry
timer_entry:
push rax
push rcx
push rdx
push rsi
push rdi
push r8
push r9
push r10
push r11
call timer_handler
pop r11
pop r10
pop r9
pop r8
pop rdi
pop rsi
pop rdx
pop rcx
pop rax
iretq
+20
View File
@@ -0,0 +1,20 @@
#include "src/kernel/timer.h"
#include "src/kernel/idt.h"
#include "src/kernel/process.h"
#include "src/kernel/util.h"
extern void timer_entry();
void timer_init() {
// PIT channel 0, rate generator, ~100Hz
outb(0x43, 0x36);
outb(0x40, 0xA9); // divisor low byte (11932 for ~100Hz)
outb(0x40, 0x2E); // divisor high byte
outb(0x21, inb(0x21) & ~0x01); // unmask IRQ0
idt_set_entry(32, timer_entry, 0x8E);
}
void timer_handler() {
outb(0x20, 0x20);
process_next();
}
+3
View File
@@ -0,0 +1,3 @@
#pragma once
void timer_init();
+9
View File
@@ -0,0 +1,9 @@
#include "src/kernel/tss.h"
tss_t tss = {
.iopb_offset = sizeof(tss_t) // points past end of TSS = no I/O permissions
};
void tss_set_kernel_stack(void *rsp0) {
tss.rsp0 = (uint64_t)rsp0;
}
+20
View File
@@ -0,0 +1,20 @@
// tss.h
#pragma once
#include <stdint.h>
typedef struct __attribute__((packed)) {
uint32_t reserved0;
uint64_t rsp0;
uint64_t rsp1;
uint64_t rsp2;
uint64_t reserved1;
uint64_t ist[7];
uint64_t reserved2;
uint16_t reserved3;
uint16_t iopb_offset;
} tss_t;
extern tss_t tss;
void tss_set_kernel_stack(void *rsp0);
+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 -2
View File
@@ -2,8 +2,6 @@
#include <stdint.h> #include <stdint.h>
#define NUL 0 // TODO Fix VSCode thinking `NULL` conflicts with some other definition.
static inline uint8_t inb(uint16_t port) { static inline uint8_t inb(uint16_t port) {
uint8_t value; uint8_t value;
__asm__ volatile("inb %1, %0" : "=a"(value) : "Nd"(port)); __asm__ volatile("inb %1, %0" : "=a"(value) : "Nd"(port));
@@ -24,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");
} }
+181
View File
@@ -0,0 +1,181 @@
#include "src/kernel/vga.h"
#include "src/kernel/panic.h"
#include "src/kernel/util.h"
#include "src/lib/memory.h"
static uint16_t *vga = (uint16_t *)0xFFFFFFFF800B8000;
static uint16_t color = (uint16_t)0x0F << 8;
static uint16_t offset = 0;
static void set_char(char c) {
vga[offset] = color | (uint16_t)c;
}
static void set_cursor() {
outb(0x3D4, 0x0F);
outb(0x3D5, offset & 0xFF);
outb(0x3D4, 0x0E);
outb(0x3D5, (offset >> 8) & 0xFF);
}
static void scroll() {
memory_move(vga + VGA_WIDTH, 2 * VGA_WIDTH * (VGA_HEIGHT - 1), vga);
for (offset = VGA_WIDTH * (VGA_HEIGHT - 1); offset < VGA_WIDTH * VGA_HEIGHT; offset++) {
set_char(' ');
}
offset = VGA_WIDTH * (VGA_HEIGHT - 1);
}
static void advance() {
offset++;
if (offset >= VGA_WIDTH * VGA_HEIGHT) {
scroll();
}
}
static void clear() {
uint16_t *ptr = vga;
uint16_t fill = 0x0F20;
uint32_t count = VGA_WIDTH * VGA_HEIGHT;
__asm__ volatile("rep stosw" : "=D"(ptr), "=c"(count) : "D"(ptr), "a"(fill), "c"(count) : "memory");
offset = 0;
set_cursor();
}
typedef enum {
PARSE_NORMAL,
PARSE_ESC,
PARSE_CSI,
} parse_state_t;
static parse_state_t parser_state = PARSE_NORMAL;
static char parser_csi_param[8];
static uint8_t parser_csi_len;
static void on_char_received(char c) {
switch (parser_state) {
case PARSE_NORMAL:
switch (c) {
case '\x1B':
parser_state = PARSE_ESC;
break;
case '\b':
case '\x7F':
if (offset > 0) {
offset--;
set_cursor();
}
break;
case '\r':
offset -= offset % VGA_WIDTH;
set_cursor();
break;
case '\n':
if (offset / VGA_WIDTH == VGA_HEIGHT - 1) {
scroll();
} else {
offset += VGA_WIDTH;
}
offset -= offset % VGA_WIDTH;
set_cursor();
break;
default:
if (c >= '\x01' && c <= '\x1A') {
set_char('^');
advance();
set_char(c + 'A' - 1);
advance();
set_cursor();
} else if (c >= ' ') {
set_char(c);
advance();
set_cursor();
}
break;
}
break;
case PARSE_ESC:
if (c == '[') {
parser_state = PARSE_CSI;
parser_csi_len = 0;
} else {
parser_state = PARSE_NORMAL;
}
break;
case PARSE_CSI:
if ((c >= '0' && c <= '9') || c == ';') {
if (parser_csi_len < sizeof(parser_csi_param) - 1) {
parser_csi_param[parser_csi_len++] = c;
}
} else {
parser_state = PARSE_NORMAL;
parser_csi_param[parser_csi_len] = '\0';
switch (c) {
case 'C':
if (offset < VGA_WIDTH * VGA_HEIGHT - 1) {
offset++;
}
set_cursor();
break;
case 'D':
if (offset > 0) {
offset--;
}
set_cursor();
break;
case 'H':
offset -= offset % VGA_WIDTH;
set_cursor();
break;
case 'F':
offset -= offset % VGA_WIDTH;
offset += VGA_WIDTH - 1;
set_cursor();
break;
}
}
break;
}
}
static uint64_t stream_write(__attribute__((unused)) stream_t *self, const char *from, uint64_t bytes) {
uint64_t left = bytes;
while (left--) {
on_char_received(*from++);
}
return bytes;
}
static uint64_t stream_read(__attribute__((unused)) stream_t *self, __attribute__((unused)) uint64_t max,
__attribute__((unused)) char *to) {
return 0;
}
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 *vga_init() {
clear();
return &stream;
}
void vga_set_string(uint8_t row, uint8_t col, const char *str, uint8_t c) {
ASSERT(row < VGA_HEIGHT && col < VGA_WIDTH, "vga_set_string: invalid coordinates")
color = (uint16_t)((uint16_t)c << 8);
uint16_t prev_offset = offset;
offset = row * VGA_WIDTH + col;
while (*str) {
on_char_received(*str++);
}
offset = prev_offset;
}
+10
View File
@@ -0,0 +1,10 @@
#pragma once
#include "src/kernel/stream.h"
#define VGA_WIDTH 80
#define VGA_HEIGHT 25
stream_t *vga_init();
void vga_set_string(uint8_t row, uint8_t col, const char *str, uint8_t color);
+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);
-22
View File
@@ -1,22 +0,0 @@
#include "src/keyboard.h"
#include "src/idt.h"
#include "src/util.h"
static keyboard_handler_t current_handler = 0;
__attribute__((interrupt)) static void isr_keyboard([[maybe_unused]] struct interrupt_frame *frame) {
uint8_t scancode = inb(0x60);
outb(0x20, 0x20);
if (current_handler) {
current_handler(scancode);
}
}
void keyboard_init() {
outb(0x21, inb(0x21) & ~0x02);
idt_set_entry(33, isr_keyboard, 0x8E);
}
void keyboard_set_handler(keyboard_handler_t handler) {
current_handler = handler;
}
-9
View File
@@ -1,9 +0,0 @@
#pragma once
#include <stdint.h>
void keyboard_init();
typedef void (*keyboard_handler_t)(uint8_t scancode);
void keyboard_set_handler(keyboard_handler_t handler);
+31
View File
@@ -0,0 +1,31 @@
#pragma once
#define MEMORY_SIZE 0x08000000
#define PAGE_SIZE 4096
#define PAGE_COUNT (MEMORY_SIZE / PAGE_SIZE)
#define HEAP_PAGE_COUNT 256
#define HEAP_SIZE (HEAP_PAGE_COUNT * PAGE_SIZE)
#define KERNEL_VIRTUAL_BASE 0xFFFFFFFF80000000ULL
#define PHYS_TO_VIRT(phys) ((void *)((uint64_t)(phys) + KERNEL_VIRTUAL_BASE))
#define VIRT_TO_PHYS(virt) ((void *)((uint64_t)(virt) - KERNEL_VIRTUAL_BASE))
#define NVME_PAGE_COUNT 4
#define USB_PAGE_COUNT 4
#define KERNEL_VIRTUAL_PML4 (KERNEL_VIRTUAL_BASE + 0x10000)
#define KERNEL_VIRTUAL_CODE (KERNEL_VIRTUAL_PML4 + 0x10000)
#define KERNEL_VIRTUAL_HEAP (KERNEL_VIRTUAL_CODE + 0x100000)
#define KERNEL_VIRTUAL_UNUSED (KERNEL_VIRTUAL_HEAP + HEAP_SIZE)
#define KERNEL_VIRTUAL_STACK (KERNEL_VIRTUAL_BASE + 0xF00000 - PAGE_SIZE)
#define KERNEL_VIRTUAL_STACK_TOP (KERNEL_VIRTUAL_STACK + PAGE_SIZE)
#define KERNEL_VIRTUAL_NVME (KERNEL_VIRTUAL_BASE + MEMORY_SIZE)
#define KERNEL_VIRTUAL_XHCI (KERNEL_VIRTUAL_NVME + NVME_PAGE_COUNT * PAGE_SIZE)
#define USER_VIRTUAL_BASE 0x0000000000000000ULL
#define USER_VIRTUAL_CODE (USER_VIRTUAL_BASE + 0x400000)
#define USER_VIRTUAL_HEAP (USER_VIRTUAL_CODE + 0x100000)
#define USER_VIRTUAL_UNUSED (USER_VIRTUAL_HEAP + HEAP_SIZE)
#define USER_VIRTUAL_STACK (0x0000700000000000ULL - PAGE_SIZE)
#define USER_VIRTUAL_STACK_TOP (USER_VIRTUAL_STACK + PAGE_SIZE)
+60
View File
@@ -0,0 +1,60 @@
#include "src/lib/memory.h"
#include "src/lib/layout.h"
#include "src/lib/util.h"
#ifdef KERNEL
#define HEAP_VIRTUAL_BASE KERNEL_VIRTUAL_HEAP
#else
#define HEAP_VIRTUAL_BASE USER_VIRTUAL_HEAP
#endif
typedef struct free_chunk {
uint64_t size; // header + data
struct free_chunk *next;
} free_chunk_t;
static free_chunk_t *free_list;
void *memory_allocate(uint64_t size) {
if (!free_list) {
free_list = (free_chunk_t *)HEAP_VIRTUAL_BASE;
free_list->size = HEAP_SIZE;
free_list->next = NUL;
}
size = sizeof(free_chunk_t) + ((size + 7) & (uint64_t)~7);
free_chunk_t *prev = NUL;
free_chunk_t *curr = free_list;
while (curr) {
if (curr->size >= size + (sizeof(free_chunk_t) + 8)) {
free_chunk_t *remainder = (free_chunk_t *)((uint8_t *)curr + size);
remainder->size = curr->size - size;
remainder->next = curr->next;
curr->size = size;
curr->next = remainder;
}
if (curr->size >= size) {
if (prev) {
prev->next = curr->next;
} else {
free_list = curr->next;
}
void *result = (void *)((uint8_t *)curr + sizeof(free_chunk_t));
memory_set(0, size - sizeof(free_chunk_t), result);
return result;
}
prev = curr;
curr = curr->next;
}
return NUL;
}
void memory_free(void *pointer) {
free_chunk_t *chunk = (free_chunk_t *)((uint8_t *)pointer - sizeof(free_chunk_t));
chunk->next = free_list;
free_list = chunk;
// TODO Merge adjacent free chunks.
}
+6 -8
View File
@@ -1,30 +1,28 @@
#pragma once #pragma once
#include "src/panic.h"
#include <stdint.h> #include <stdint.h>
void memory_init();
void *memory_allocate(uint64_t size); 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) {
__asm__ volatile("rep movsb" : "=D"(to), "=S"(from), "=c"(bytes) : "D"(to), "S"(from), "c"(bytes) : "memory"); __asm__ volatile("rep movsb" : "=D"(to), "=S"(from), "=c"(bytes) : "D"(to), "S"(from), "c"(bytes) : "memory");
} else { } else {
ASSERT(0, "memory_move: forward overlapping move not implemented"); // TODO ASSERT(0, "memory_move: forward overlapping move not implemented");
} }
} }
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) {
ASSERT((uint64_t)to + bytes <= (uint64_t)from || (uint64_t)to >= (uint64_t)from + bytes, "memory_copy: overlapping backward copy"); // TODO ASSERT((uint64_t)to + bytes <= (uint64_t)from || (uint64_t)to >= (uint64_t)from + bytes, "memory_copy: overlapping backward
// copy");
__asm__ volatile("rep movsb" : "=D"(to), "=S"(from), "=c"(bytes) : "D"(to), "S"(from), "c"(bytes) : "memory"); __asm__ volatile("rep movsb" : "=D"(to), "=S"(from), "=c"(bytes) : "D"(to), "S"(from), "c"(bytes) : "memory");
} }
+388
View File
@@ -0,0 +1,388 @@
#include "src/lib/string.h"
#include <stdarg.h>
uint8_t bytes_equal(const char *l, const char *r, uint64_t count) {
while (count--) {
if (*l++ != *r++) {
return 0;
}
}
return 1;
}
uint8_t string_empty(const char *s) {
return *s == '\0';
}
uint64_t string_length(const char *s) {
const char *e = s;
while (*e) {
e++;
}
return (uint64_t)(e - s);
}
uint8_t string_equal(const char *l, const char *r) {
while (*l && *r) {
if (*l != *r) {
return 0;
}
l++;
r++;
}
return *l == *r;
}
uint64_t string_trim(char *string, char character, char **result) {
while (*string == character) {
string++;
}
*result = string;
if (*string == '\0') {
return 0;
}
char *end = string;
while (*end != '\0') {
end++;
}
end--;
while (string < end && *end == character) {
*end = '\0';
end--;
}
return (uint64_t)(end - string + 1);
}
uint64_t string_split(char *string, char separator, uint64_t max, char **result) {
uint64_t count = 1;
*result = string;
while (*string && count < max) {
if (*string == separator) {
*string = '\0';
result++;
*result = string + 1;
count++;
}
string++;
}
return count;
}
uint64_t string_uint8_to_hex(uint8_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[3] = "00";
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 < 4 && i < max - 1) {
result[i] = tmp[3 - i];
i++;
}
result[i] = '\0';
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) {
if (max == 0) {
return 0;
}
char tmp[20];
uint8_t len = 0;
if (value == 0) {
tmp[len++] = '0';
} else {
while (value && len < 20) {
tmp[len++] = '0' + (value % 10);
value /= 10;
}
}
uint8_t i = 0;
while (i < len && i < max - 1) {
result[i] = tmp[len - 1 - i];
i++;
}
result[i] = '\0';
return i;
}
uint64_t string_int_to_decimal(int64_t value, uint64_t max, char *result) {
if (max == 0) {
return 0;
}
if (value >= 0) {
return string_uint_to_decimal((uint64_t)value, max, result);
}
result[0] = '-';
return 1 + string_uint_to_decimal((uint64_t)(-value), max - 1, result + 1);
}
uint64_t string_format(const char *format, uint64_t max, char *output, ...) {
va_list args;
va_start(args, output);
uint64_t limit = max - 1;
uint64_t fi = 0, ri = 0;
while (format[fi] && ri < limit) {
if (format[fi] != '%') {
output[ri++] = format[fi++];
} else {
switch (format[fi + 1]) {
case 'c': {
output[ri++] = (char)va_arg(args, int);
fi += 2;
break;
}
case 'd': {
string_int_to_decimal(va_arg(args, int64_t), limit - ri, output + ri);
while (output[ri]) {
ri++;
}
fi += 2;
break;
}
case 's': {
char *s = va_arg(args, char *);
while (*s && ri < limit) {
output[ri++] = *s;
s++;
}
fi += 2;
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 '%': {
output[ri++] = '%';
fi += 2;
break;
}
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++];
break;
}
}
}
}
output[ri] = '\0';
va_end(args);
return ri;
}
+10 -4
View File
@@ -10,14 +10,20 @@ uint64_t string_length(const char *s);
uint8_t string_equal(const char *l, const char *r); uint8_t string_equal(const char *l, const char *r);
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, ...);
char *string_format(const char *format, ...);
+17
View File
@@ -0,0 +1,17 @@
#define SYSCALL_READ 0
#define SYSCALL_WRITE 1
#define SYSCALL_GETCWD 2
#define SYSCALL_CHDIR 3
#define SYSCALL_SPAWN 4
#define SYSCALL_WAIT 5
#define SYSCALL_OPEN 6
#define SYSCALL_CLOSE 7
#define SYSCALL_TRUNCATE 8
#define SYSCALL_REMOVE 9
#define SYSCALL_PIPE 10
#define SYSCALL_EXIT 60
#define OPEN_CREATE 0b0001
#define OPEN_EXCLUSIVE 0b0010
#define OPEN_FILE 0b0100
#define OPEN_DIRECTORY 0b1000
+24
View File
@@ -0,0 +1,24 @@
#pragma once
#include <stdint.h>
#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 STDOUT 1
#define STDERR 2
typedef uint64_t exit_code_t;
#define EXIT_CODE_OK 0
#define EXIT_CODE_NOT_FOUND ((uint64_t)-2)
#define EXIT_CODE_GENERAL_FAILURE ((uint64_t)-1)
static inline void sleep(uint64_t ms) {
for (volatile uint64_t i = 1000000 * ms; i; i--) // Very approximately
;
}
-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:
-107
View File
@@ -1,107 +0,0 @@
#include "src/memory.h"
#include "src/panic.h"
#include "src/util.h"
#include <stdint.h>
#define MEMORY_SIZE 134217728
#define PAGE_SIZE 4096
#define PAGE_COUNT (MEMORY_SIZE / PAGE_SIZE)
#define MAP_UNIT 64
#define FULL_UNIT 0xFFFFFFFFFFFFFFFF
// Claim 2MB for bootloader, kernel, and stuff.
static uint16_t free_page = MAP_UNIT * 8;
static uint64_t allocation[PAGE_COUNT / MAP_UNIT] = {FULL_UNIT, FULL_UNIT, FULL_UNIT, FULL_UNIT,
FULL_UNIT, FULL_UNIT, FULL_UNIT, FULL_UNIT};
static uint8_t get_allocated(uint16_t page) {
return !!allocation[page / MAP_UNIT] & ((uint64_t)1 << (page % MAP_UNIT));
}
static void set_allocated(uint16_t page, uint8_t allocated) {
if (allocated) {
allocation[page / MAP_UNIT] |= ((uint64_t)1 << (page % MAP_UNIT));
} else {
allocation[page / MAP_UNIT] &= ~((uint64_t)1 << (page % MAP_UNIT));
}
}
static void *memory_page_allocate() {
ASSERT(free_page < PAGE_COUNT, "memory_page_allocate: out of memory");
const uint16_t page = free_page;
set_allocated(page, 1);
for (uint16_t unit = free_page / MAP_UNIT; unit < PAGE_COUNT / MAP_UNIT; unit++) {
if (allocation[unit] != FULL_UNIT) {
uint16_t bit = (uint16_t)__builtin_ctzll(~allocation[unit]);
free_page = unit * MAP_UNIT + bit;
break;
}
}
return (void *)((uint64_t)page * PAGE_SIZE);
}
[[maybe_unused]] static void memory_page_free(void *address) {
uint16_t page = (uint16_t)((uint64_t)address / PAGE_SIZE);
ASSERT(get_allocated(page), "memory_page_free: page not allocated")
set_allocated(page, 0);
if (page < free_page) {
free_page = page;
}
}
#define HEAP_PAGE_COUNT 256
typedef struct free_chunk {
uint64_t size; // header + data
struct free_chunk *next;
} free_chunk_t;
static free_chunk_t *free_list;
void memory_init() {
free_list = memory_page_allocate();
for (uint64_t i = 1; i < HEAP_PAGE_COUNT; i++) {
memory_page_allocate(); // TODO Map the pages.
}
free_list->size = HEAP_PAGE_COUNT * PAGE_SIZE;
free_list->next = NUL;
}
void *memory_allocate(uint64_t size) {
size = sizeof(free_chunk_t) + (size + 7) & (uint64_t)~7;
free_chunk_t *prev = NUL;
free_chunk_t *curr = free_list;
while (curr) {
if (curr->size >= size + (sizeof(free_chunk_t) + 8)) {
free_chunk_t *remainder = (free_chunk_t *)((uint8_t *)curr + size);
remainder->size = curr->size - size;
remainder->next = curr->next;
curr->size = size;
curr->next = remainder;
}
if (curr->size >= size) {
if (prev) {
prev->next = curr->next;
} else {
free_list = curr->next;
}
void *result = (void *)((uint8_t *)curr + sizeof(free_chunk_t));
memory_set(0, size - sizeof(free_chunk_t), result);
return result;
}
prev = curr;
curr = curr->next;
}
ASSERT(0, "memory_heap_allocate: out of heap memory");
return 0;
}
void memory_free(void *pointer) {
free_chunk_t *chunk = (free_chunk_t *)((uint8_t *)pointer - sizeof(free_chunk_t));
chunk->next = free_list;
free_list = chunk;
// TODO Merge adjacent free chunks.
}
-8
View File
@@ -1,8 +0,0 @@
#include "src/panic.h"
#include "src/vga.h"
void kernel_panic(const char *msg, [[maybe_unused]] const char *file, [[maybe_unused]] int line) {
vga_set_string(0, VGA_HEIGHT - 1, msg, 0x28);
while (1)
;
}
-230
View File
@@ -1,230 +0,0 @@
#include "src/string.h"
#include "src/memory.h"
#include <stdarg.h>
#include <stdint.h>
uint8_t bytes_equal(const char *l, const char *r, uint64_t count) {
while (count--) {
if (*l++ != *r++) {
return 0;
}
}
return 1;
}
uint8_t string_empty(const char *s) {
return *s == '\0';
}
uint64_t string_length(const char *s) {
const char *e = s;
while (*e) {
e++;
}
return (uint64_t)(e - s);
}
uint8_t string_equal(const char *l, const char *r) {
while (*l && *r) {
if (*l != *r) {
return 0;
}
l++;
r++;
}
return *l == *r;
}
uint64_t string_split(char *string, char separator, uint64_t max, char **result) {
uint64_t count = 1;
*result = string;
while (*string && count < max) {
if (*string == separator) {
*string = '\0';
result++;
*result = string + 1;
count++;
}
string++;
}
return count;
}
uint64_t string_byte_to_hex(uint8_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[3] = "00";
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 < 4 && i < max - 1) {
result[i] = tmp[3 - i];
i++;
}
result[i] = '\0';
return i;
}
uint64_t string_uint_to_decimal(uint64_t value, uint64_t max, char *result) {
if (max == 0) {
return 0;
}
char tmp[20];
uint8_t len = 0;
if (value == 0) {
tmp[len++] = '0';
} else {
while (value && len < 20) {
tmp[len++] = '0' + (value % 10);
value /= 10;
}
}
uint8_t i = 0;
while (i < len && i < max - 1) {
result[i] = tmp[len - 1 - i];
i++;
}
result[i] = '\0';
return i;
}
uint64_t string_int_to_decimal(int64_t value, uint64_t max, char *result) {
if (max == 0) {
return 0;
}
if (value >= 0) {
return string_uint_to_decimal((uint64_t)value, max, result);
}
result[0] = '-';
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;
}
char *string_format(const char *format, ...) {
va_list args;
va_start(args, format);
uint64_t limit = 1023;
char *result = memory_allocate(limit + 1);
uint64_t fi = 0, ri = 0;
while (format[fi] && ri < limit) {
if (format[fi] != '%') {
result[ri++] = format[fi++];
} else {
switch (format[fi + 1]) {
case 'c':
result[ri++] = (char)va_arg(args, int);
fi += 2;
break;
case 'd':
string_int_to_decimal(va_arg(args, int64_t), limit - ri, result + ri);
while (result[ri]) {
ri++;
}
fi += 2;
break;
case 'u':
string_uint_to_decimal(va_arg(args, uint64_t), limit - ri, result + ri);
while (result[ri]) {
ri++;
}
fi += 2;
break;
case 'x':
string_uint_to_hex(va_arg(args, uint64_t), limit - ri, result + ri);
while (result[ri]) {
ri++;
}
fi += 2;
break;
case 's': {
char *s = va_arg(args, char *);
while (*s && ri < limit) {
result[ri++] = *s;
s++;
}
fi += 2;
break;
}
case '%': {
result[ri++] = '%';
fi += 2;
break;
}
default:
result[ri++] = format[fi++];
result[ri++] = format[fi++];
break;
}
}
}
result[ri] = '\0';
va_end(args);
return result;
}
-397
View File
@@ -1,397 +0,0 @@
#include "src/terminal.h"
#include "src/fs.h"
#include "src/keyboard.h"
#include "src/memory.h"
#include "src/string.h"
#include "src/util.h"
#include "src/vga.h"
#include <stdint.h>
#define KEYBOARD_STATE_LSHIFT 0b00000001
#define KEYBOARD_STATE_RSHIFT 0b00000010
#define KEYBOARD_STATE_LCTRL 0b00000100
#define KEYBOARD_STATE_RCTRL 0b00001000
#define KEYBOARD_STATE_LALT 0b00010000
#define KEYBOARD_STATE_RALT 0b00100000
#define KEYBOARD_STATE_SEQ 0b01000000
static uint8_t keyboard_state = 0;
// clang-format off
static const char scancode_normal[128] = {
0, 0, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b', '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u',
'i', 'o', 'p', '[', ']', '\n', 0, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', 0, '\\', 'z', 'x',
'c', 'v', 'b', 'n', 'm', ',', '.', '/', 0, '*', 0, ' ', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, '-', 0, 0, 0, '+', 0, 0, 0, 0, 0, 0, 0, 0, 0
};
static const char scancode_shifted[128] = {
0, 0, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', '\b', '\t', 'Q', 'W', 'E', 'R', 'T', 'Y', 'U',
'I', 'O', 'P', '{', '}', '\n', 0, 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '~', 0, '|', 'Z', 'X',
'C', 'V', 'B', 'N', 'M', '<', '>', '?', 0, '*', 0, ' ', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, '-', 0, 0, 0, '+', 0, 0, 0, 0, 0, 0, 0, 0, 0
};
// clang-format on
#define PROMPT_PREFIX 2
#define PROMPT_LENGTH 255
fs_node_t *root;
static char prompt[PROMPT_LENGTH + 1];
static char prompt_swap[PROMPT_LENGTH + 1];
static uint8_t prompt_row = 0;
static uint8_t prompt_length = 0;
static uint8_t prompt_offset = 0;
static char prompt_buf[VGA_WIDTH + 1] = "$ ";
static void render_prompt() {
uint8_t offset = PROMPT_PREFIX + prompt_offset < VGA_WIDTH ? 0 : PROMPT_PREFIX + prompt_offset - VGA_WIDTH + 1;
uint8_t length = PROMPT_PREFIX + prompt_length < VGA_WIDTH ? prompt_length : VGA_WIDTH - PROMPT_PREFIX;
prompt_buf[1] = PROMPT_PREFIX + prompt_offset < VGA_WIDTH ? ' ' : '<';
memory_copy(prompt + offset, length, prompt_buf + PROMPT_PREFIX);
if (PROMPT_PREFIX + prompt_length > VGA_WIDTH && prompt_offset < prompt_length - 1) {
prompt_buf[VGA_WIDTH - 1] = '>';
}
prompt_buf[PROMPT_PREFIX + length] = '\0';
vga_clear(prompt_row, 1);
vga_set_string(prompt_row, 0, prompt_buf, 0x0F);
vga_set_cursor(prompt_row, PROMPT_PREFIX + prompt_offset - offset);
}
static void on_home_pressed() {
if (keyboard_state || !prompt_offset) {
return;
}
prompt_offset = 0;
render_prompt();
}
static void on_left_pressed() {
if (keyboard_state || !prompt_offset) {
return;
}
prompt_offset--;
render_prompt();
}
static void on_right_pressed() {
if (keyboard_state || prompt_offset == prompt_length) {
return;
}
prompt_offset++;
render_prompt();
}
static void on_end_pressed() {
if (keyboard_state || prompt_offset == prompt_length) {
return;
}
prompt_offset = prompt_length;
render_prompt();
}
static void advance_row(uint8_t rows) {
while (rows--) {
if (prompt_row == VGA_HEIGHT - 1) {
vga_copy(1, VGA_HEIGHT - 1, 0);
} else {
prompt_row++;
}
}
}
static void print_line(const char *string) {
advance_row(1);
vga_clear(prompt_row - 1, 1);
vga_set_string(prompt_row - 1, 0, string, 0x0F);
}
static void help(uint8_t argc, [[maybe_unused]] char **argv) {
if (argc > 1) {
print_line("help: accepts no arguments");
}
advance_row(4);
vga_set_string(prompt_row - 4, 0, "Available commands:", 0x0F);
vga_set_string(prompt_row - 3, 0, "help", 0x0F);
vga_set_string(prompt_row - 2, 0, "ls DIR", 0x0F);
vga_set_string(prompt_row - 1, 0, "cat FILE", 0x0F);
}
static void ls(uint8_t argc, char **argv) {
if (argc != 2) {
print_line("ls: requires a single path");
return;
}
char *path_components[16];
uint64_t path_length = string_split(argv[1], '/', 16, path_components);
if (!string_empty(path_components[0])) {
print_line("ls: relative paths not supported");
return;
}
fs_node_t *prev = NUL;
fs_node_t *curr = root;
uint8_t i = 1;
while (i < path_length) {
if (string_empty(path_components[i])) {
i++;
continue;
}
if (!curr->is_dir) {
print_line("ls: can not navigate into a file");
fs_close(curr);
return;
}
prev = curr;
curr = fs_open_by(curr, path_components[i]);
if (prev != root) {
fs_close(prev);
}
if (!curr) {
print_line("ls: invalid path");
return;
}
i++;
}
if (!curr->is_dir) {
print_line("ls: can not list file");
fs_close(curr);
return;
}
uint8_t j = 0;
fs_node_t *item = fs_open_at(curr, j++);
while (item) {
print_line(item->name);
fs_close(item);
item = fs_open_at(curr, j++);
}
if (curr != root) {
fs_close(curr);
}
}
static void cat(uint8_t argc, char **argv) {
if (argc != 2) {
print_line("cat: requires a single path");
return;
}
char *path_components[16];
uint64_t path_length = string_split(argv[1], '/', 16, path_components);
if (!string_empty(path_components[0])) {
print_line("cat: relative paths not supported");
return;
}
fs_node_t *prev = NUL;
fs_node_t *curr = root;
uint8_t i = 1;
while (i < path_length) {
if (string_empty(path_components[i])) {
i++;
continue;
}
if (!curr->is_dir) {
print_line("cat: can not navigate into a file");
fs_close(curr);
return;
}
prev = curr;
curr = fs_open_by(curr, path_components[i]);
if (prev != root) {
fs_close(prev);
}
if (!curr) {
print_line("cat: invalid path");
return;
}
i++;
}
if (curr->is_dir) {
print_line("cat: can not print a directory");
if (curr != root) {
fs_close(curr);
}
return;
}
char *content = memory_allocate(curr->size + 1);
fs_read(curr, 0, curr->size, content);
content[curr->size] = '\0';
char **lines = memory_allocate(curr->size * sizeof(char *));
uint64_t lines_count = string_split(content, '\n', curr->size, lines);
for (uint64_t i = 0; i < lines_count; i++) {
print_line(lines[i]);
}
memory_free(lines);
memory_free(content);
fs_close(curr);
}
static void on_enter_pressed() {
prompt[prompt_length] = '\0';
char *argv[16];
uint8_t argc = (uint8_t)string_split(prompt, ' ', 16, argv);
advance_row(1);
if (string_equal(argv[0], "help")) {
help(argc, argv);
} else if (string_equal(argv[0], "ls")) {
ls(argc, argv);
} else if (string_equal(argv[0], "cat")) {
cat(argc, argv);
} else {
print_line("Unknown command");
}
prompt_length = prompt_offset = 0;
render_prompt();
}
static void on_cancel_pressed() {
advance_row(1);
prompt_length = prompt_offset = 0;
render_prompt();
}
static void on_character_pressed(uint8_t code) {
if (keyboard_state && (keyboard_state & (KEYBOARD_STATE_LCTRL | KEYBOARD_STATE_RCTRL)) == keyboard_state) {
switch (code) {
case 0x2E: // C
on_cancel_pressed();
}
return;
}
if (keyboard_state & ~(KEYBOARD_STATE_LSHIFT | KEYBOARD_STATE_RSHIFT) || prompt_length >= PROMPT_LENGTH) {
return;
}
if (prompt_offset == prompt_length) {
prompt[prompt_offset + 1] = '\0';
} else {
memory_copy(prompt, prompt_length + 1, prompt_swap);
memory_copy(prompt_swap + prompt_offset, prompt_length - prompt_offset, prompt + prompt_offset + 1);
}
prompt[prompt_offset] = (keyboard_state & (KEYBOARD_STATE_LSHIFT | KEYBOARD_STATE_RSHIFT) ? scancode_shifted : scancode_normal)[code];
prompt_offset++;
prompt_length++;
render_prompt();
}
static void on_backspace_pressed() {
if (keyboard_state || !prompt_offset) {
return;
}
if (prompt_offset == prompt_length) {
prompt[prompt_offset - 1] = '\0';
} else {
memory_copy(prompt, prompt_length + 1, prompt_swap);
memory_copy(prompt_swap + prompt_offset, prompt_length - prompt_offset, prompt + prompt_offset - 1);
}
prompt_offset--;
prompt_length--;
render_prompt();
}
static void on_delete_pressed() {
if (keyboard_state || prompt_offset == prompt_length) {
return;
}
prompt_offset++;
on_backspace_pressed();
}
static void terminal_on_key(uint8_t scancode) {
const uint8_t pressed = !(scancode & 0x80);
const uint8_t code = scancode & ~0x80;
if (!(keyboard_state & KEYBOARD_STATE_SEQ)) {
switch (code) {
case 0x60:
keyboard_state = keyboard_state | KEYBOARD_STATE_SEQ;
break;
case 0x2A:
keyboard_state = pressed ? keyboard_state | KEYBOARD_STATE_LSHIFT : keyboard_state & ~KEYBOARD_STATE_LSHIFT;
break;
case 0x36:
keyboard_state = pressed ? keyboard_state | KEYBOARD_STATE_RSHIFT : keyboard_state & ~KEYBOARD_STATE_RSHIFT;
break;
case 0x38:
keyboard_state = pressed ? keyboard_state | KEYBOARD_STATE_LALT : keyboard_state & ~KEYBOARD_STATE_LALT;
break;
case 0x1D:
keyboard_state = pressed ? keyboard_state | KEYBOARD_STATE_LCTRL : keyboard_state & ~KEYBOARD_STATE_LCTRL;
break;
case 0x0E:
pressed ? on_backspace_pressed() : 0;
break;
case 0x1C:
pressed ? on_enter_pressed() : 0;
break;
default:
pressed &&scancode_normal[code] ? on_character_pressed(code) : 0;
break;
}
} else {
keyboard_state = keyboard_state & ~KEYBOARD_STATE_SEQ;
switch (code) {
case 0x38:
keyboard_state = pressed ? keyboard_state | KEYBOARD_STATE_RALT : keyboard_state & ~KEYBOARD_STATE_RALT;
break;
case 0x1D:
keyboard_state = pressed ? keyboard_state | KEYBOARD_STATE_RCTRL : keyboard_state & ~KEYBOARD_STATE_RCTRL;
break;
case 0x47:
pressed ? on_home_pressed() : 0;
break;
case 0x4B:
pressed ? on_left_pressed() : 0;
break;
case 0x4D:
pressed ? on_right_pressed() : 0;
break;
case 0x4F:
pressed ? on_end_pressed() : 0;
break;
case 0x53:
pressed ? on_delete_pressed() : 0;
break;
}
}
}
void terminal_init() {
root = fs_mount();
keyboard_set_handler(terminal_on_key);
vga_clear(0, VGA_HEIGHT);
char *greeting = string_format("Welcome to FreywarOS v%s!", VERSION);
vga_set_string(0, 0, greeting, 0x0F);
memory_free(greeting);
prompt_row += 2;
render_prompt();
}
-3
View File
@@ -1,3 +0,0 @@
#pragma once
void terminal_init();
+50
View File
@@ -0,0 +1,50 @@
#include "src/lib/syscall.h"
#include "src/lib/util.h"
#include "src/user/syscall.h"
#define BLOCK_SIZE 65536
exit_code_t pass(uint64_t fd) {
static char buffer[BLOCK_SIZE];
uint64_t bytes;
while ((bytes = read(fd, BLOCK_SIZE, buffer))) {
if (bytes == (uint64_t)-1) {
ERR_S("cat: could not read file\n");
return EXIT_CODE_GENERAL_FAILURE;
}
write(STDOUT, buffer, bytes);
}
return EXIT_CODE_OK;
}
exit_code_t cat(const char *path) {
uint64_t fd = open(path, OPEN_FILE);
if (fd == (uint64_t)-1) {
ERR_S("cat: path does not exist\n");
return EXIT_CODE_GENERAL_FAILURE;
}
exit_code_t code = pass(fd);
close(fd);
return code;
}
exit_code_t main(uint64_t argc, const char **argv) {
if (argc == 1) {
return pass(0);
}
uint64_t code = EXIT_CODE_OK;
for (uint64_t i = 1; i < argc; i++) {
exit_code_t c = cat(argv[i]);
if (c != EXIT_CODE_OK) {
code = c;
}
}
return code;
}
+54
View File
@@ -0,0 +1,54 @@
#include "src/lib/syscall.h"
#include "src/lib/util.h"
#include "src/user/syscall.h"
#define BLOCK_SIZE 65536
exit_code_t main(uint64_t argc, const char **argv) {
if (argc != 3) {
ERR_S("cp: requires source and target\n");
return EXIT_CODE_GENERAL_FAILURE;
}
uint64_t source = open(argv[1], OPEN_FILE);
if (source == (uint64_t)-1) {
ERR_S("cp: source does not exist\n");
return EXIT_CODE_GENERAL_FAILURE;
}
uint64_t target = open(argv[2], OPEN_FILE | OPEN_CREATE);
if (target == (uint64_t)-1) {
ERR_S("cp: target path does not exist\n");
close(source);
return EXIT_CODE_GENERAL_FAILURE;
}
if (truncate(target, 0) == (uint64_t)-1) {
ERR_S("cp: could not write file\n");
close(source);
close(target);
return EXIT_CODE_GENERAL_FAILURE;
}
static char buffer[BLOCK_SIZE];
uint64_t bytes;
while ((bytes = read(source, BLOCK_SIZE, buffer))) {
if (bytes == (uint64_t)-1) {
ERR_S("cp: could not read file\n");
close(source);
close(target);
return EXIT_CODE_GENERAL_FAILURE;
}
if (write(target, buffer, bytes) == (uint64_t)-1) {
ERR_S("cp: could not write file\n");
close(source);
close(target);
return EXIT_CODE_GENERAL_FAILURE;
}
}
close(source);
close(target);
return EXIT_CODE_OK;
}
+14
View File
@@ -0,0 +1,14 @@
#include "src/lib/util.h"
#include "src/user/syscall.h"
exit_code_t main(uint64_t argc, const char **argv) {
for (uint64_t i = 1; i < argc; i++) {
if (i > 1) {
OUT_S(" ");
}
OUT_D(argv[i]);
}
OUT_S("\n");
return EXIT_CODE_OK;
}
+51
View File
@@ -0,0 +1,51 @@
#include "src/lib/syscall.h"
#include "src/lib/util.h"
#include "src/user/syscall.h"
#define BLOCK_SIZE 256
exit_code_t ls(const char *path) {
uint64_t fd = open(path, OPEN_DIRECTORY);
if (fd == (uint64_t)-1) {
ERR_S("ls: path does not exist\n");
return EXIT_CODE_GENERAL_FAILURE;
}
char buffer[BLOCK_SIZE];
uint64_t bytes;
while ((bytes = read(fd, BLOCK_SIZE, buffer))) {
if (bytes == (uint64_t)-1) {
ERR_S("ls: could not read directory\n");
close(fd);
return EXIT_CODE_GENERAL_FAILURE;
}
write(STDOUT, buffer, bytes);
OUT_S("\n");
}
close(fd);
return 0;
}
exit_code_t main(uint64_t argc, const char **argv) {
if (argc == 1) {
return ls(".");
}
exit_code_t code = EXIT_CODE_OK;
for (uint64_t i = 1; i < argc; i++) {
if (argc > 2) {
OUT_S("\n");
OUT_D(argv[i]);
OUT_S(":\n");
}
exit_code_t c = ls(argv[i]);
if (c != EXIT_CODE_OK) {
code = c;
}
}
return code;
}
+23
View File
@@ -0,0 +1,23 @@
#include "src/lib/syscall.h"
#include "src/lib/util.h"
#include "src/user/syscall.h"
exit_code_t main(uint64_t argc, const char **argv) {
if (argc < 2) {
ERR_S("mkdir: requires target(s)\n");
return EXIT_CODE_GENERAL_FAILURE;
}
exit_code_t code = EXIT_CODE_OK;
for (uint64_t i = 1; i < argc; i++) {
uint64_t fd = open(argv[i], OPEN_DIRECTORY | OPEN_CREATE | OPEN_EXCLUSIVE);
if (fd == (uint64_t)-1) {
ERR_S("mkdir: could not create directory\n");
code = EXIT_CODE_GENERAL_FAILURE;
} else {
close(fd);
}
}
return code;
}
+20
View File
@@ -0,0 +1,20 @@
#include "src/lib/syscall.h"
#include "src/lib/util.h"
#include "src/user/syscall.h"
exit_code_t main(uint64_t argc, const char **argv) {
if (argc < 2) {
ERR_S("rm: requires one or more arguments\n");
return EXIT_CODE_GENERAL_FAILURE;
}
uint64_t code = EXIT_CODE_OK;
for (uint64_t i = 1; i < argc; i++) {
if (remove(argv[i]) == (uint64_t)-1) {
ERR_S("rm: could not remove file\n");
code = EXIT_CODE_GENERAL_FAILURE;
}
}
return code;
}
+192
View File
@@ -0,0 +1,192 @@
#include "src/lib/memory.h"
#include "src/lib/string.h"
#include "src/lib/util.h"
#include "src/user/syscall.h"
static exit_code_t help(uint8_t argc, char **argv, uint64_t stdin, uint64_t stdout);
static exit_code_t cd(uint8_t argc, char **argv, uint64_t stdin, uint64_t stdout);
#define PATH "/bin/"
#define WRITE_S(o, s) write(o, s, sizeof(s) - 1);
#define WRITE_D(o, s) write(o, s, string_length(s));
typedef exit_code_t (*app_t)(uint8_t argc, char **argv, uint64_t stdin, uint64_t stdout);
typedef struct {
const char *name;
app_t app;
} app_entry_t;
static app_entry_t builtins[] = {
{"help", help},
{"cd", cd},
};
static exit_code_t help(uint8_t argc, __attribute__((unused)) char **argv, __attribute__((unused)) uint64_t stdin, uint64_t stdout) {
if (argc > 1) {
ERR_S("help: expects no arguments\n");
return EXIT_CODE_GENERAL_FAILURE;
}
WRITE_S(stdout, "Available commands:\n");
for (uint64_t i = 0; i < sizeof(builtins) / sizeof(app_entry_t); i++) {
WRITE_D(stdout, builtins[i].name);
WRITE_S(stdout, "\n");
}
return EXIT_CODE_OK;
}
static exit_code_t cd(uint8_t argc, char **argv, __attribute__((unused)) uint64_t stdin, __attribute__((unused)) uint64_t stdout) {
if (argc != 2) {
ERR_S("cd: requires a single path\n");
return EXIT_CODE_GENERAL_FAILURE;
}
if (chdir(argv[1]) == (uint64_t)-1) {
ERR_S("cd: path does not exist\n");
return EXIT_CODE_GENERAL_FAILURE;
}
return EXIT_CODE_OK;
}
static void print_prompt() {
char path[256];
uint8_t pl = getcwd(256, path);
char *components[16];
uint8_t cl = string_split(path, '/', 16, components);
OUT_S("[")
OUT_D(pl == 1 ? "/" : components[cl - 1]);
OUT_S("]$ ");
}
static void free_fds(uint64_t *fds, uint8_t fds_count) {
for (uint8_t i = 0; i < fds_count; i++) {
close(fds[i]);
}
}
static void kill_pids(__attribute__((unused)) uint64_t *pids, __attribute__((unused)) uint8_t pid_count) {
// TODO.
}
static void execute(char *command) {
OUT_S("\n");
if (string_equal(command, "^C")) {
print_prompt();
return;
}
if (string_equal(command, "^D")) {
exit(EXIT_CODE_OK);
return;
}
char *subcommands[16];
uint8_t subcommands_count = (uint8_t)string_split(command, '|', 16, subcommands);
uint64_t fds[32];
uint8_t fds_count = 0;
uint64_t pids[16];
uint8_t pids_count = 0;
for (uint8_t i = 0; i < subcommands_count; i++) {
if (!string_trim(subcommands[i], ' ', &subcommands[i])) {
if (subcommands_count > 1) {
ERR_S("Syntax error.\n");
}
break;
}
char *argv[16];
uint8_t argc = (uint8_t)string_split(subcommands[i], ' ', 16, argv);
uint64_t stdin = 0;
uint64_t stdout = 1;
if (i > 0) {
stdin = fds[fds_count - 1];
}
if (i < subcommands_count - 1) {
if (pipe(&fds[fds_count], &fds[fds_count + 1]) == (uint64_t)-1) {
kill_pids(pids, pids_count);
free_fds(fds, fds_count);
print_prompt();
return;
}
fds_count += 2;
stdout = fds[fds_count - 2];
}
uint8_t j;
for (j = 0; j < sizeof(builtins) / sizeof(app_entry_t); j++) {
if (string_equal(argv[0], builtins[j].name)) {
builtins[j].app(argc, argv, stdin, stdout);
pids[pids_count++] = 0;
break;
}
}
if (j == sizeof(builtins) / sizeof(app_entry_t)) {
uint64_t size = sizeof(PATH) + string_length(argv[0]) + 1;
char *path = memory_allocate(size);
memory_copy(PATH, sizeof(PATH), path);
memory_copy(argv[0], size - sizeof(PATH), path + sizeof(PATH) - 1);
uint64_t pid = spawn(path, argc, (const char **)argv, stdin, stdout);
memory_free(path);
if (pid == (uint64_t)-1) {
kill_pids(pids, pids_count);
free_fds(fds, fds_count);
print_prompt();
return;
} else {
pids[pids_count++] = pid;
}
}
}
for (uint8_t i = 0; i < pids_count; i++) {
if (pids[i]) {
waitpid(pids[i]);
}
if (i > 0) {
close(fds[(i - 1) * 2 + 1]);
}
if (i < pids_count - 1) {
close(fds[i * 2]);
}
}
print_prompt();
}
exit_code_t main() {
ERR_S("Welcome to FreywarOS v" VERSION "!\n\n");
print_prompt();
char line[256];
uint64_t offset = 0;
char chunk[64];
uint64_t received;
while ((received = read(0, sizeof(chunk), chunk))) {
for (uint64_t i = 0; i < received; i++) {
if (chunk[i] == '\n') {
line[offset] = '\0';
execute(line);
offset = 0;
} else if (offset < 255) {
line[offset++] = chunk[i];
}
}
}
return EXIT_CODE_OK;
}
+202
View File
@@ -0,0 +1,202 @@
#include "src/lib/memory.h"
#include "src/lib/util.h"
#include "src/user/syscall.h"
#define PROMPT_LENGTH 255
static const char bs[PROMPT_LENGTH + 1];
static const char ws[PROMPT_LENGTH + 1];
static char prompt[PROMPT_LENGTH + 1];
static uint8_t prompt_length = 0;
static uint8_t prompt_offset = 0;
static void on_home_pressed() {
if (!prompt_offset) {
return;
}
write(STDOUT, bs, prompt_offset);
prompt_offset = 0;
}
static void on_left_pressed() {
if (!prompt_offset) {
return;
}
write(STDOUT, bs, 1);
prompt_offset--;
}
static void on_right_pressed() {
if (prompt_offset == prompt_length) {
return;
}
write(STDOUT, prompt + prompt_offset, 1);
prompt_offset++;
}
static void on_end_pressed() {
if (prompt_offset == prompt_length) {
return;
}
write(STDOUT, prompt + prompt_offset, prompt_length - prompt_offset);
prompt_offset = prompt_length;
}
static void on_enter_pressed() {
prompt[prompt_length] = '\n';
write(3, prompt, prompt_length + 1);
prompt_length = prompt_offset = 0;
}
static void on_cancel_pressed() {
OUT_S("^C\n");
write(3, "^C\n", 3);
prompt_length = prompt_offset = 0;
}
static void on_disconnect_pressed() {
OUT_S("^D\n");
write(3, "^D\n", 3);
exit(EXIT_CODE_OK);
}
static void on_ctrl_character_pressed(char c) {
if (c == 'c') {
on_cancel_pressed();
} else if (c == 'd') {
on_disconnect_pressed();
}
}
static void redraw_from_cursor() {
uint64_t tail = prompt_length - prompt_offset;
write(STDOUT, prompt + prompt_offset, tail);
write(STDOUT, ws, 1); // erase the character past the end
write(STDOUT, bs, tail + 1); // move back to cursor position
}
static void on_character_pressed(char c) {
if (prompt_offset >= PROMPT_LENGTH)
return;
if (prompt_offset != prompt_length) {
memory_move(prompt + prompt_offset, prompt_length - prompt_offset, prompt + prompt_offset + 1);
}
prompt[prompt_offset] = c;
prompt_length++;
prompt_offset++;
write(STDOUT, &c, 1); // emit the character itself
redraw_from_cursor();
}
static void on_backspace_pressed() {
if (!prompt_offset)
return;
if (prompt_offset != prompt_length) {
memory_move(prompt + prompt_offset, prompt_length - prompt_offset, prompt + prompt_offset - 1);
}
prompt_offset--;
prompt_length--;
write(STDOUT, bs, 1);
redraw_from_cursor();
}
static void on_delete_pressed() {
if (prompt_offset == prompt_length)
return;
memory_move(prompt + prompt_offset + 1, prompt_length - prompt_offset - 1, prompt + prompt_offset);
prompt_length--;
redraw_from_cursor();
}
typedef enum {
PARSE_NORMAL,
PARSE_ESC,
PARSE_CSI,
} parse_state_t;
static parse_state_t parser_state = PARSE_NORMAL;
static char parser_csi_param[8];
static uint8_t parser_csi_len;
static void on_char_received(char c) {
switch (parser_state) {
case PARSE_NORMAL:
if (c == '\x1B') {
parser_state = PARSE_ESC;
} else if (c == '\b' || c == '\x7F') {
on_backspace_pressed();
} else if (c == '\n' || c == '\r') {
on_enter_pressed();
} else if (c >= '\x01' && c <= '\x1A') {
on_ctrl_character_pressed(c + 'a' - 1);
} else if (c >= ' ') {
on_character_pressed(c);
}
break;
case PARSE_ESC:
if (c == '[') {
parser_state = PARSE_CSI;
parser_csi_len = 0;
} else {
parser_state = PARSE_NORMAL;
}
break;
case PARSE_CSI:
if ((c >= '0' && c <= '9') || c == ';') {
if (parser_csi_len < sizeof(parser_csi_param) - 1) {
parser_csi_param[parser_csi_len++] = c;
}
} else {
parser_state = PARSE_NORMAL;
parser_csi_param[parser_csi_len] = '\0';
switch (c) {
case 'C':
on_right_pressed();
break;
case 'D':
on_left_pressed();
break;
case 'H':
on_home_pressed();
break;
case 'F':
on_end_pressed();
break;
case '~':
if (parser_csi_param[0] == '3') {
on_delete_pressed();
}
break;
}
}
break;
}
}
uint64_t main() {
memory_set('\b', PROMPT_LENGTH, (char *)bs);
memory_set(' ', PROMPT_LENGTH, (char *)ws);
char c;
while (read(STDIN, 1, &c)) {
on_char_received(c);
}
return 0;
}
+56
View File
@@ -0,0 +1,56 @@
#include "src/lib/string.h"
#include "src/lib/syscall.h"
#include "src/lib/util.h"
#include "src/user/syscall.h"
#define BLOCK_SIZE 65536
exit_code_t pass(uint64_t fd) {
static char buffer[BLOCK_SIZE];
uint64_t bytes;
uint64_t total = 0;
while ((bytes = read(fd, BLOCK_SIZE, buffer))) {
if (bytes == (uint64_t)-1) {
ERR_S("wc: could not read file\n");
return EXIT_CODE_GENERAL_FAILURE;
} else {
total += bytes;
}
}
uint64_t l = string_format("%d\n", BLOCK_SIZE, buffer, total);
write(STDOUT, buffer, l);
return EXIT_CODE_OK;
}
exit_code_t wc(const char *path) {
uint64_t fd = open(path, OPEN_FILE);
if (fd == (uint64_t)-1) {
OUT_S("wc: path does not exist\n");
return EXIT_CODE_GENERAL_FAILURE;
}
exit_code_t code = pass(fd);
close(fd);
return code;
}
exit_code_t main(uint64_t argc, const char **argv) {
if (argc == 1) {
return pass(0);
}
uint64_t code = EXIT_CODE_OK;
for (uint64_t i = 1; i < argc; i++) {
exit_code_t c = wc(argv[i]);
if (c != EXIT_CODE_OK) {
code = c;
}
}
return code;
}
+9
View File
@@ -0,0 +1,9 @@
ENTRY(_start)
SECTIONS {
. = 0x0000000000400000;
.text : { *(.text*) }
.rodata : { *(.rodata*) *(.lrodata*) }
.data : { *(.data*) *(.ldata*) *(.bss*) *(.lbss*) }
}
+12
View File
@@ -0,0 +1,12 @@
bits 64
extern main
global _start
_start:
pop rdi
mov rsi, rsp
call main
mov rdi, rax ; return value
mov rax, 60 ; exit syscall
syscall
+85
View File
@@ -0,0 +1,85 @@
bits 64
global read
read:
mov rax, 0
mov r10, rcx
syscall
ret
global write
write:
mov rax, 1
mov r10, rcx
syscall
ret
global getcwd
getcwd:
mov rax, 2
mov r10, rcx
syscall
ret
global chdir
chdir:
mov rax, 3
mov r10, rcx
syscall
ret
global spawn
spawn:
mov rax, 4
mov r10, rcx
syscall
ret
global waitpid
waitpid:
mov rax, 5
mov r10, rcx
syscall
ret
global open
open:
mov rax, 6
mov r10, rcx
syscall
ret
global close
close:
mov rax, 7
mov r10, rcx
syscall
ret
global truncate
truncate:
mov rax, 8
mov r10, rcx
syscall
ret
global remove
remove:
mov rax, 9
mov r10, rcx
syscall
ret
global pipe
pipe:
mov rax, 10
mov r10, rcx
syscall
ret
global exit
exit:
mov rax, 60
mov r10, rcx
syscall
; never returns
+34
View File
@@ -0,0 +1,34 @@
#pragma once
#include "src/lib/util.h"
#include "src/lib/string.h"
#define OUT_S(s) write(STDOUT, s, sizeof(s) - 1);
#define OUT_D(s) write(STDOUT, s, string_length(s));
#define ERR_S(s) write(STDERR, s, sizeof(s) - 1);
#define ERR_D(s) write(STDERR, s, string_length(s));
uint64_t read(int64_t fd, uint64_t max, void *to);
uint64_t write(int64_t fd, const void *from, uint64_t bytes);
uint64_t getcwd(uint64_t max, void *to);
uint64_t chdir(const char *path);
uint64_t spawn(const char *path, uint64_t argc, const char **argv, uint64_t stdin, uint64_t stdout);
exit_code_t waitpid(uint64_t pid);
uint64_t open(const char *path, uint64_t flags);
uint64_t close(uint64_t fd);
uint64_t truncate(uint64_t fd, uint64_t size);
uint64_t remove(const char *path);
uint64_t pipe(uint64_t *write_fd, uint64_t *read_fd);
void exit(exit_code_t code);
-48
View File
@@ -1,48 +0,0 @@
#include "src/vga.h"
#include "src/memory.h"
#include "src/panic.h"
#include "src/util.h"
static uint16_t *vga = (uint16_t *)0x000B8000;
void vga_set_cursor(uint8_t row, uint8_t col) {
ASSERT(row < VGA_HEIGHT && col < VGA_WIDTH, "vga_set_cursor: invalid coordinates")
uint16_t pos = row * VGA_WIDTH + col;
outb(0x3D4, 0x0F);
outb(0x3D5, pos & 0xFF);
outb(0x3D4, 0x0E);
outb(0x3D5, (pos >> 8) & 0xFF);
}
void vga_set_char(uint8_t row, uint8_t col, char c, uint8_t color) {
ASSERT(row < VGA_HEIGHT && col < VGA_WIDTH, "vga_set_char: invalid coordinates");
vga[row * VGA_WIDTH + col] = (uint16_t)(((uint16_t)color << 8) | (uint16_t)c);
}
void vga_set_string(uint8_t row, uint8_t col, const char *str, uint8_t color) {
ASSERT(row < VGA_HEIGHT && col < VGA_WIDTH, "vga_set_string: invalid coordinates")
while (*str) {
vga_set_char(row, col++, *str++, color);
if (col >= VGA_WIDTH) {
col = 0;
row++;
ASSERT(row < VGA_HEIGHT && col < VGA_WIDTH, "vga_set_string: invalid coordinates")
}
}
}
void vga_copy(uint8_t src_row, uint8_t rows, uint8_t dst_row) {
ASSERT(src_row < VGA_HEIGHT && dst_row < VGA_HEIGHT && src_row + rows <= VGA_HEIGHT && dst_row + rows <= VGA_HEIGHT,
"vga_copy: invalid copy dimensions");
memory_move(vga + src_row * VGA_WIDTH, rows * VGA_WIDTH * 2, vga + dst_row * VGA_WIDTH);
}
void vga_clear(uint8_t row, uint8_t rows) {
ASSERT(row < VGA_HEIGHT && rows <= VGA_HEIGHT && row + rows <= VGA_HEIGHT, "vga_clear: invalid clear dimensions");
uint16_t *dst = vga + row * VGA_WIDTH;
uint16_t fill = 0x0F20;
uint32_t count = VGA_WIDTH * rows;
__asm__ volatile("rep stosw" : "=D"(dst), "=c"(count) : "D"(dst), "a"(fill), "c"(count) : "memory");
}
-16
View File
@@ -1,16 +0,0 @@
#pragma once
#include <stdint.h>
#define VGA_WIDTH 80
#define VGA_HEIGHT 25
void vga_set_cursor(uint8_t row, uint8_t col);
void vga_set_char(uint8_t row, uint8_t col, char c, uint8_t color);
void vga_set_string(uint8_t row, uint8_t col, const char *str, uint8_t color);
void vga_copy(uint8_t src_row, uint8_t rows, uint8_t dst_row);
void vga_clear(uint8_t row, uint8_t rows);
+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