Compare commits
34
Commits
bfb74dd57a
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ae7af6e6cf | ||
|
|
532d8e757b | ||
|
|
b5215d5327 | ||
|
|
dccb93fb82 | ||
|
|
b78ef69da8 | ||
|
|
d72169473f | ||
|
|
c58605b56d | ||
|
|
63c1720e60 | ||
|
|
bbb9988bcf | ||
|
|
0cb2169c29 | ||
|
|
e4b9a79a5f | ||
|
|
4813725cf3 | ||
|
|
a2128de3e2 | ||
|
|
572cb7647f | ||
|
|
3b8125eb0c | ||
|
|
065aa261f5 | ||
|
|
ba9beebf8e | ||
|
|
14c3262c14 | ||
|
|
85cfbb2050 | ||
|
|
4ad32dda17 | ||
|
|
1ff38ffad4 | ||
|
|
1148596a81 | ||
|
|
01cd3f528c | ||
|
|
325dc6339a | ||
|
|
415f78fd40 | ||
|
|
452125782e | ||
|
|
9761e3e870 | ||
|
|
bcf2e5bac6 | ||
|
|
968bb02aa8 | ||
|
|
03b3ddac73 | ||
|
|
1c935cf154 | ||
|
|
b32c12b311 | ||
|
|
2d4f76a5dd | ||
|
|
c8edeab750 |
@@ -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
|
||||||
|
|||||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 949 KiB |
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
@@ -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, §or);
|
|
||||||
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
@@ -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);
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
@@ -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)
|
|
||||||
;
|
|
||||||
}
|
|
||||||
@@ -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
|
||||||
@@ -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, §or);
|
||||||
|
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);
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
@@ -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));
|
||||||
|
}
|
||||||
@@ -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();
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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.
|
||||||
|
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
@@ -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,7 +1,7 @@
|
|||||||
ENTRY(kernel_main)
|
ENTRY(kernel_main)
|
||||||
|
|
||||||
SECTIONS {
|
SECTIONS {
|
||||||
. = 0x0000000000020000;
|
. = 0xFFFFFFFF80020000;
|
||||||
|
|
||||||
.text : {
|
.text : {
|
||||||
*(.text)
|
*(.text)
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
@@ -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");
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
@@ -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.");
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
@@ -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)
|
||||||
|
;
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
@@ -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));
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "src/kernel/stream.h"
|
||||||
|
|
||||||
|
typedef uint16_t pci_bdf_t;
|
||||||
|
|
||||||
|
void pci_init();
|
||||||
|
|
||||||
|
void pci_enumerate(stream_t *out);
|
||||||
|
|
||||||
|
pci_bdf_t pci_find_by_class(uint8_t class, uint8_t subclass, uint8_t iface);
|
||||||
|
|
||||||
|
void pci_map(pci_bdf_t bdf, volatile void *virt, uint8_t pages);
|
||||||
|
|
||||||
|
void pci_unmap(pci_bdf_t bdf, volatile void *virt, uint8_t pages);
|
||||||
@@ -1,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);
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "src/kernel/stream.h"
|
||||||
|
|
||||||
|
void pipe_init(stream_t **write, stream_t **read);
|
||||||
@@ -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
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "src/kernel/stream.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
stream_t *ps2_keyboard_init();
|
||||||
@@ -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;
|
||||||
@@ -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
|
||||||
@@ -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(¤t_process->fds[*write_fd], ¤t_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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
@@ -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
|
||||||
@@ -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();
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
void timer_init();
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
@@ -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");
|
||||||
}
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
@@ -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.");
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
@@ -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)
|
||||||
@@ -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.
|
||||||
|
}
|
||||||
@@ -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");
|
||||||
}
|
}
|
||||||
@@ -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,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, ...);
|
|
||||||
@@ -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
|
||||||
@@ -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
@@ -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
@@ -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.
|
|
||||||
}
|
|
||||||
@@ -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
@@ -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
@@ -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();
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
void terminal_init();
|
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
ENTRY(_start)
|
||||||
|
|
||||||
|
SECTIONS {
|
||||||
|
. = 0x0000000000400000;
|
||||||
|
|
||||||
|
.text : { *(.text*) }
|
||||||
|
.rodata : { *(.rodata*) *(.lrodata*) }
|
||||||
|
.data : { *(.data*) *(.ldata*) *(.bss*) *(.lbss*) }
|
||||||
|
}
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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);
|
||||||
@@ -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");
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user