├── .gdbinit ├── .gitignore ├── .lldbinit ├── .vscode ├── c_cpp_properties.json └── settings.json ├── LICENSE ├── README.md ├── bin ├── build-addr2line-cache └── diode-ldc2 ├── copyfiles.sh ├── createdisk.sh ├── createinitrd.sh ├── data └── banner.txt ├── env ├── import.py ├── import.sh ├── meson.build ├── source ├── boot.s ├── crt.s ├── defines.s ├── kernel.ld ├── kernel │ ├── acpi │ │ └── rsdp.d │ ├── autoinit.d │ ├── cpio.d │ ├── elf.d │ ├── guards.d │ ├── hashmap.d │ ├── io.d │ ├── irq.d │ ├── loafencode.d │ ├── main.d │ ├── mm.d │ ├── optional.d │ ├── pcie │ │ ├── mcfg.d │ │ └── virtio.d │ ├── platform.d │ ├── pmap.d │ ├── port.d │ ├── ports │ │ └── kbootstrap.d │ ├── refptr.d │ ├── rtti.d │ ├── stivale.d │ ├── symbols.d │ ├── syscall │ │ ├── dispatch.d │ │ ├── exec.d │ │ ├── exit.d │ │ ├── generic.d │ │ ├── io.d │ │ ├── map.d │ │ └── util.d │ ├── task.d │ └── util.d ├── libled │ └── except.d ├── libsys │ ├── entry.d │ ├── errno.d │ ├── io.d │ ├── loaf.d │ ├── mem.d │ ├── port.d │ ├── syscall.d │ └── util.d ├── progs │ ├── init.d │ └── init_.d ├── task.c ├── user.ld └── vshared │ └── share.d ├── trace.py └── x64-llvm.ini /.gdbinit: -------------------------------------------------------------------------------- 1 | set disassembly-flavor intel 2 | file build/kernel.elf 3 | target remote :1234 4 | maintenance packet qqemu.sstep=1 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .dub 2 | docs.json 3 | __dummy.html 4 | *.o 5 | *.obj 6 | /builddir 7 | /.config 8 | /data 9 | /fs -------------------------------------------------------------------------------- /.lldbinit: -------------------------------------------------------------------------------- 1 | # target create builddir/kernel.elf 2 | target create builddir/init.elf 3 | gdb-remote 1234 4 | settings set target.x86-disassembly-flavor intel 5 | command alias mon process plugin packet monitor 6 | command alias m mon -------------------------------------------------------------------------------- /.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "Mac", 5 | "includePath": [ 6 | "${workspaceFolder}/**" 7 | ], 8 | "defines": [], 9 | "macFrameworkPath": [ 10 | "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks" 11 | ], 12 | "compilerPath": "/usr/bin/clang", 13 | "cStandard": "c11", 14 | "cppStandard": "c++98", 15 | "intelliSenseMode": "macos-clang-x64", 16 | "compileCommands": "${workspaceFolder}/builddir/compile_commands.json" 17 | } 18 | ], 19 | "version": 4 20 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "d.neverUseDub": true, 3 | "d.stdlibPath": [ 4 | "/opt/homebrew/Cellar/ldc/1.25.0/include/dlang/ldc", 5 | "${workspaceFolder}/build" 6 | ], 7 | "files.associations": { 8 | "Tracefile": "yaml", 9 | "stdint.h": "c", 10 | "assert.h": "c", 11 | "vector": "cpp", 12 | "exception": "cpp" 13 | }, 14 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # diodeOS 2 | This is the home of diodeOS, a kernel (and in the future userland) with the goal of not crashing and getting to userland (and also posix compliance) 3 | 4 | ## Todo 5 | - [x] fix that task switcher crash bug 6 | - [x] userland 7 | - [x] syscall 8 | - [x] initrd parsing 9 | - [ ] new VM subsystem 10 | - [ ] vfs 11 | - [ ] fs 12 | - [ ] posix compliance 13 | -------------------------------------------------------------------------------- /bin/build-addr2line-cache: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | let { platform } = require('os') 3 | let { existsSync, writeFileSync } = require('fs') 4 | const { execSync } = require('child_process') 5 | let nm = 'nm' 6 | if (platform() == 'darwin') { 7 | if (existsSync('/opt/homebrew/opt/binutils/bin/nm')) 8 | nm = '/opt/homebrew/opt/binutils/bin/nm' 9 | else if (existsSync('/usr/local/opt/binutils/bin/nm')) 10 | nm = '/usr/local/opt/binutils/bin/nm' 11 | } 12 | 13 | let out = execSync(`${nm} --line-numbers builddir/kernel.elf | ddemangle`) 14 | .toString() 15 | .trim() 16 | .split('\n') 17 | .map(e => e.trim()) 18 | .filter(e => e) 19 | .map(e => e.replaceAll('\t', ' ')) 20 | .map(e => e.replace(/ [^ ]+\/\.\.\//g, '')) 21 | 22 | let p = Buffer.alloc(out.map(e => e.length - 19 + 16 + 1).reduce((a, b) => a + b, 0) + 8) 23 | p.writeBigUInt64LE(BigInt(out.length), 0) 24 | let i = 8 25 | out.forEach(e => { 26 | console.log(e) 27 | if (e[0] == 'U') return 28 | let addr = BigInt('0x' + e.slice(0, 16)) 29 | let sym = e.slice(19) 30 | p.writeBigUInt64LE(addr, i) 31 | i += 8 32 | p.writeBigUInt64LE(BigInt(sym.length), i) 33 | i += 8 34 | for (let c of sym) p.writeInt8(c.charCodeAt(0), i++) 35 | p.writeInt8(0, i++) 36 | }) 37 | 38 | writeFileSync('data/symbol.map', p.slice(0, i)) 39 | -------------------------------------------------------------------------------- /bin/diode-ldc2: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | const FILTER = true 3 | const os = require('os') 4 | let arch = os.arch() 5 | 6 | if (os.platform() == 'darwin' && os.release().startsWith('20.')) { 7 | // we _may_ be on apple silicon. 8 | // are we? 9 | try { 10 | require('child_process').execSync('arch -arm64 id').toString() 11 | arch = 'arm64' 12 | } catch {} 13 | } 14 | const args = 15 | (os.platform() == 'darwin' && arch == 'arm64' && os.arch() == 'x64' 16 | ? 'arch -arm64 ' 17 | : '') + 18 | 'ldc2 ' + 19 | process.argv 20 | .slice(2) 21 | .filter(e => !FILTER || !e.includes('model=')) 22 | .join(' ') + ' --code-model=large' 23 | console.log(args) 24 | require('fs').writeFileSync('AAAA.txt', args) 25 | process.exit( 26 | require('child_process').spawnSync('zsh', ['-c', args], { stdio: 'inherit' }).status 27 | ) 28 | -------------------------------------------------------------------------------- /copyfiles.sh: -------------------------------------------------------------------------------- 1 | cp "$@" ../data 2 | touch env_out -------------------------------------------------------------------------------- /createdisk.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | set -v 4 | IMPORT_PY=`echo \`pwd\`/$0 | sed 's/createdisk\.sh//g'`import.py 5 | KERN=`echo $1` 6 | WORK_DIR=`pwd` 7 | HDD=`echo $1 | sed 's/\.elf/.hdd/g'` 8 | MAGIC_CPIO=$2 9 | if [ ! -e $HDD ]; then truncate $HDD -s 64M; duogpt $HDD; fi 10 | cd $WORK_DIR 11 | mkdir -vp env 12 | cd env 13 | mkdir -vp boot 14 | cd boot 15 | cp ../../kernel.elf . 16 | cp ../../initrd.bin . 17 | cp $HOME/limine/limine.sys . 18 | cd .. 19 | cat >limine.cfg <$TARGET_PATH -------------------------------------------------------------------------------- /data/banner.txt: -------------------------------------------------------------------------------- 1 | +---------------------+ 2 | | Welcome to diodeOS! | 3 | +---------------------+ -------------------------------------------------------------------------------- /env: -------------------------------------------------------------------------------- 1 | run() { 2 | ninja -C builddir run 2>&1 3 | } 4 | conf() { 5 | if [ -e builddir ]; then 6 | meson builddir --cross-file=x64-llvm.ini --reconfigure 7 | else 8 | meson builddir --cross-file=x64-llvm.ini 9 | fi 10 | } 11 | reconf() { 12 | rm -rf builddir 13 | conf 14 | } 15 | 16 | export PATH=`pwd`/bin:$PATH -------------------------------------------------------------------------------- /import.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | print(f'echfs-utils -g -p0 ../kernel.hdd import {sys.argv[1]} {sys.argv[1]}') 4 | os.system(f'echfs-utils -g -p0 ../kernel.hdd import {sys.argv[1]} {sys.argv[1]}') -------------------------------------------------------------------------------- /import.sh: -------------------------------------------------------------------------------- 1 | cd build/env 2 | find * -type d | xargs -L 1 echfs-utils -g -p0 ../../build/kernel.hdd mkdir 3 | find * -type f | xargs -L 1 bash -c 'python3 ../../import.py $1 || true' -- 4 | -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | project('code', ['d', 'c'], 2 | default_options : [ 3 | 'c_std=c99' 4 | ] 5 | ) 6 | 7 | libled_src = run_command('find', ['source/libled', '-type', 'f']).stdout().strip().split('\n') 8 | libsys_src = run_command('find', ['source/libsys', '-type', 'f']).stdout().strip().split('\n') 9 | kernelsrc = run_command('find', ['source/kernel', '-type', 'f']).stdout().strip().split('\n') 10 | initrd_members = files(run_command('find', ['data', '-type', 'f']).stdout().strip().split('\n')) 11 | 12 | yasm = generator(find_program('yasm'), 13 | output : '@BASENAME@.o', 14 | arguments : [ '-felf64', '@INPUT@', '-o', '@OUTPUT@', '-i', meson.current_build_dir() + '/', '-gdwarf2' ] 15 | ) 16 | 17 | link = generator(find_program('ld.lld'), 18 | output : '@BASENAME@.elf', 19 | arguments : [ '@INPUT@', '-o', '@OUTPUT@', '-m', 'elf_x86_64' ] 20 | ) 21 | 22 | crt = yasm.process([ 23 | 'source/crt.s' 24 | ]) 25 | 26 | kernel_stlib = build_target('kernel', 'source/vshared/share.d', kernelsrc, [ 27 | 'source/task.c', 28 | ], yasm.process([ 29 | 'source/boot.s' 30 | ]), 31 | include_directories: ['source'], 32 | link_args: ['-betterC', '--gcc=fake-gcc'], 33 | d_module_versions: ['DiodeNoDebug'], 34 | d_args: ['-betterC'], 35 | target_type: 'static_library' 36 | ) 37 | 38 | kernel = custom_target('kernel.elf', 39 | build_by_default: true, 40 | command: [find_program('ld.lld'), kernel_stlib, '-o', 'kernel.elf', '-m', 'elf_x86_64', '-T../source/kernel.ld'], 41 | output: 'kernel.elf' 42 | ) 43 | 44 | src = include_directories('source') 45 | 46 | userinit_stlib = build_target('init', 'source/vshared/share.d', libsys_src, libled_src, crt, 'source/progs/init.d', 47 | include_directories: ['source'], 48 | d_args: ['-betterC'], 49 | link_args: ['-betterC', '--gcc=fake-gcc'], 50 | target_type: 'static_library' 51 | ) 52 | 53 | userinit = custom_target('init.elf', 54 | build_by_default: true, 55 | command: [find_program('ld.lld'), userinit_stlib, '-o', 'init.elf', '-m', 'elf_x86_64', '-T../source/user.ld'], 56 | output: 'init.elf' 57 | ) 58 | 59 | initrd_user_bins = custom_target('userbins', 60 | build_by_default: true, 61 | command: ['sh', files('copyfiles.sh'), userinit], 62 | output: 'env_out' 63 | ) 64 | 65 | initrd = custom_target('initrd', 66 | build_by_default: true, 67 | command: ['/bin/sh', files('createinitrd.sh'), initrd_user_bins, files('data/banner.txt'), kernel], 68 | output: 'initrd.bin' 69 | ) 70 | 71 | iso = custom_target('iso', 72 | build_by_default: true, 73 | command: ['/bin/sh', files('createdisk.sh'), kernel], 74 | output: 'kernel.hdd', 75 | depends: [initrd] 76 | ) 77 | 78 | qemu = find_program('qemu-system-x86_64') 79 | 80 | run_target('run', 81 | command: [ 82 | qemu, 83 | '-drive', 'id=asd,file=kernel.hdd,if=none', 84 | '-device', 'virtio-blk-pci,drive=asd', 85 | '-s', 86 | '-debugcon', 'stdio', 87 | '-global', 'isa-debugcon.iobase=0xe9', 88 | '-cpu', 'max', 89 | '-machine', 'q35' 90 | ], 91 | depends: [iso] 92 | ) 93 | run_target('runp', 94 | command: [ 95 | qemu, 96 | '-drive', 'id=asd,file=kernel.hdd,if=none', 97 | '-device', 'virtio-blk-pci,drive=asd', 98 | '-s', 99 | '-debugcon', 'stdio', 100 | '-global', 'isa-debugcon.iobase=0xe9', 101 | '-cpu', 'max', 102 | '-machine', 'q35', 103 | '-S' 104 | ], 105 | depends: [iso] 106 | ) 107 | 108 | run_target('nsnr', 109 | command: [ 110 | qemu, 111 | '-device', 'virtio-blk-pci,drive=kernel.hdd', 112 | '-s', 113 | '-debugcon', 'stdio', 114 | '-global', 'isa-debugcon.iobase=0xe9', 115 | '-cpu', 'max', 116 | '-no-shutdown', '-no-reboot', 117 | '-machine', 'q35' 118 | ], 119 | depends: [iso] 120 | ) 121 | 122 | 123 | run_target('rstop', 124 | command: [ 125 | qemu, 126 | '-S', 127 | '-hda', iso, 128 | '-s', 129 | '-debugcon', 'stdio', 130 | '-global', 'isa-debugcon.iobase=0xe9', 131 | '-cpu', 'max' 132 | ] 133 | ) -------------------------------------------------------------------------------- /source/boot.s: -------------------------------------------------------------------------------- 1 | extern isrhandle_ec 2 | extern isrhandle_noec 3 | bits 64 4 | 5 | %define HEADER_TAG_FRAMEBUFFER_ID 0x3ecc1bc43d0f7971 6 | %define HEADER_TAG_FB_MTRR_ID 0x4c7bb07731282e00 7 | %define HEADER_TAG_SMP_ID 0x1ab015085f3273df 8 | %define HEADER_TAG_5LV_PAGING_ID 0x932f477032007e8f 9 | %define STRUCT_TAG_CMDLINE_ID 0xe5e76a1b4597a781 10 | %define STRUCT_TAG_MEMMAP_ID 0x2187f79e8612de07 11 | %define STRUCT_TAG_FRAMEBUFFER_ID 0x506461d2950408fa 12 | %define STRUCT_TAG_FB_MTRR_ID 0x6bc1a78ebe871172 13 | %define STRUCT_TAG_MODULES_ID 0x4b6fe466aade04ce 14 | %define STRUCT_TAG_RSDP_ID 0x9e1786930a375e78 15 | %define STRUCT_TAG_EPOCH_ID 0x566a7bed888e1407 16 | %define STRUCT_TAG_FIRMWARE_ID 0x359d837855e3858c 17 | %define STRUCT_TAG_SMP_ID 0x34d1d96339647025 18 | %define STRUCT_TAG_PXE_SERVER_INFO 0x29d1e96239247032 19 | 20 | TASK_FLD_STATE equ 0 21 | TASK_FLD_PREV equ 8 22 | TASK_FLD_NEXT equ 16 23 | TASK_FLD_DATA equ 24 24 | 25 | section .stivale2hdr 26 | global stivale_hdr 27 | global task_call 28 | 29 | stivale_hdr: 30 | dq 0 31 | dq stack_top 32 | dq 0 33 | dq STIVALE_TAG_0 34 | 35 | section .text 36 | 37 | STIVALE_TAG_0: 38 | dq STRUCT_TAG_FRAMEBUFFER_ID 39 | dq 0 40 | dw 0 41 | dw 0 42 | dw 0 43 | 44 | rdrandom: 45 | rdrand rax 46 | ret 47 | 48 | global asm_switch 49 | global setjmp 50 | global longjmp 51 | global _rdrand 52 | 53 | setjmp: 54 | mov [rdi + 0], rbx 55 | mov [rdi + 8], rbp 56 | mov [rdi + 16], r12 57 | mov [rdi + 24], r13 58 | mov [rdi + 32], r14 59 | mov [rdi + 40], r15 60 | lea rax, [rsp + 8] 61 | mov [rdi + 48], rax 62 | mov rax, [rsp] 63 | mov [rdi + 56], rax 64 | mov rax, 0 65 | ret 66 | 67 | longjmp: 68 | mov rax, rsi 69 | mov rbp, [rdi + 8] 70 | mov [rdi + 48], rsp 71 | push QWORD [rdi + 56] 72 | mov rbx, [rdi + 0] 73 | mov r12, [rdi + 16] 74 | mov r13, [rdi + 24] 75 | mov r14, [rdi + 32] 76 | mov r15, [rdi + 40] 77 | ret 78 | 79 | _rdrand: 80 | rdrand rax 81 | jnc _rdrand 82 | ret 83 | 84 | fgdt: 85 | lgdt [rel GDT64.Pointer] 86 | mov ax, GDT64.Data 87 | mov ss, ax 88 | mov ds, ax 89 | mov es, ax 90 | mov fs, ax 91 | mov gs, ax 92 | pop rax 93 | push GDT64.Code 94 | push rax 95 | retf 96 | 97 | section .data 98 | 99 | 100 | %macro idtend 1 101 | dq isr%1 102 | %endmacro 103 | 104 | %macro isrgen 1 105 | 106 | isr%1: 107 | push rbp 108 | push rax 109 | push rbx 110 | push rcx 111 | push rdx 112 | push rsi 113 | push rdi 114 | push r8 115 | push r9 116 | push r10 117 | push r11 118 | push r12 119 | push r13 120 | push r14 121 | push r15 122 | 123 | xor rbp, rbp 124 | xor rax, rax 125 | xor rbx, rbx 126 | xor rcx, rcx 127 | xor rdx, rdx 128 | xor rsi, rsi 129 | xor rdi, rdi 130 | xor r8, r8 131 | xor r9, r9 132 | xor r10, r10 133 | xor r11, r11 134 | xor r12, r12 135 | xor r13, r13 136 | xor r14, r14 137 | xor r15, r15 138 | 139 | mov rdi, %1 140 | lea rsi, [rsp] 141 | %if (%1 >= 0x8 && %1 <= 0xE) || %1 == 0x11 || %1 == 0x1E 142 | call isrhandle_ec 143 | %else 144 | call isrhandle_noec 145 | %endif 146 | pop r15 147 | pop r14 148 | pop r13 149 | pop r12 150 | pop r11 151 | pop r10 152 | pop r9 153 | pop r8 154 | pop rdi 155 | pop rsi 156 | pop rdx 157 | pop rcx 158 | pop rbx 159 | pop rax 160 | pop rbp 161 | %if (%1 >= 0x8 && %1 <= 0xE) || %1 == 0x11 || %1 == 0x1E 162 | add rsp, 8 ; error code 163 | %endif 164 | iretq 165 | 166 | %endmacro 167 | 168 | global get_idt_targets 169 | get_idt_targets: 170 | lea rax, [rel idt_targets] 171 | ret 172 | 173 | global idt_targets 174 | idt_targets: 175 | %assign i 0 176 | %rep 256 177 | idtend i 178 | %assign i i+1 179 | %endrep 180 | 181 | %assign i 0 182 | %rep 256 183 | isrgen i 184 | %assign i i+1 185 | %endrep 186 | 187 | global setrsp0 188 | global setist1 189 | global getrsp0 190 | global gettss 191 | global gettssgdt 192 | global gettssgdtid 193 | global load_tss 194 | global platform_sc 195 | global user_branch 196 | global _stac 197 | global _clac 198 | is_smap: dq 0 199 | is_smap_i: dq 0 200 | 201 | smap_i: 202 | mov rax, 1 203 | cmp [rel is_smap_i], rax 204 | je .retr 205 | mov eax, 7 206 | mov ecx, 0 207 | cpuid 208 | shr ebx, 20 209 | and ebx, 1 210 | mov [rel is_smap], rbx 211 | mov rax, 1 212 | mov [rel is_smap_i], rax 213 | .retr: 214 | ret 215 | 216 | _stac: 217 | call smap_i 218 | mov rax, 1 219 | cmp [rel is_smap], rax 220 | jne .retr 221 | stac 222 | .retr: 223 | ret 224 | _clac: 225 | call smap_i 226 | mov rax, 1 227 | cmp [rel is_smap], rax 228 | jne .retr 229 | clac 230 | .retr: 231 | ret 232 | 233 | user_branch: 234 | mov rdx, 0x18 | 3 235 | mov ax, 0x23 | 3 236 | mov ds,ax 237 | mov es,ax 238 | mov fs,ax 239 | mov gs,ax 240 | 241 | xor rbx, rbx 242 | xor rcx, rcx 243 | push rax 244 | xor rax, rax 245 | push rsi 246 | xor rsi, rsi 247 | push 0x200 248 | push rdx 249 | xor rdx, rdx 250 | push rdi 251 | xor rdi, rdi 252 | iretq 253 | 254 | extern d_syscall 255 | 256 | ; 1. Return: rax, rdx 257 | ; 2. Parameter Registers: rdi, rsi, rdx, rcx, r8, r9 258 | ; 3. Extra Parameters: stack (right to left) 259 | ; 4. Stack Alignment: 16-byte at call 260 | ; 5. Scratch Registers: rdi, rsi, rdx, rcx, r8, r9, r10, r11, rax 261 | 262 | platform_sc: 263 | push rcx 264 | push r11 265 | ; call d_syscall 266 | pop r11 267 | pop rcx 268 | ; here i _would_ just sysret, but we need to clean up 269 | ; all the scratch regs, since while they are scratch, 270 | ; we don't want to leak kernel poineters and some 271 | ; such every syscall. But since we get to fuck them up, 272 | ; i just xor them out with themselves 273 | xor rdi, rdi 274 | xor rsi, rsi 275 | xor rdx, rdx 276 | xor rcx, rcx 277 | xor r8, r8 278 | xor r9, r9 279 | xor r10, r10 280 | xor r11, r11 281 | sysret 282 | 283 | load_tss: 284 | mov ax, 0x28 285 | ltr ax 286 | ret 287 | setrsp0: 288 | mov [rel TSS.tss_rsp0], rdi 289 | ret 290 | setist1: 291 | mov [rel TSS.ist1], rdi 292 | ret 293 | getrsp0: 294 | mov rax, [rel TSS.tss_rsp0] 295 | ret 296 | 297 | gettss: 298 | lea rax, [rel TSS] 299 | ret 300 | 301 | gettssgdt: 302 | lea rax, [rel GDT64.TSSE] 303 | ret 304 | gettssgdtid: 305 | mov rax, GDT64.TSS 306 | ret 307 | 308 | 309 | section .data 310 | global fgdt 311 | align 16 312 | 313 | GDT64: ; Global Descriptor Table (64-bit). 314 | .Null: equ $ - GDT64 ; The null descriptor. 315 | dw 0xFFFF ; Limit (low). 316 | dw 0 ; Base (low). 317 | db 0 ; Base (middle) 318 | db 0 ; Access. 319 | db 1 ; Granularity. 320 | db 0 ; Base (high). 321 | .Code: equ $ - GDT64 ; The code descriptor. 322 | dq 0x00af9b000000ffff 323 | .Data: equ $ - GDT64 ; The data descriptor. 324 | dq 0x00af93000000ffff ; probably wrong tho 325 | .UserCode: equ $ - GDT64 ; The code descriptor. 326 | dq 0x00affb000000ffff 327 | .UserData: equ $ - GDT64 ; The data descriptor. 328 | dq 0x00aff3000000ffff 329 | .TSS: equ $ - GDT64 330 | .TSSE: 331 | times 16 db 0 332 | 333 | 334 | .Pointer: ; The GDT-pointer. 335 | dw $ - GDT64 - 1 ; Limit. 336 | dq GDT64 ; Base. 337 | 338 | align 16 339 | times 4 db 0 340 | TSS: 341 | dd 0 ; 0x00 reserved 342 | .tss_rsp0: 343 | dd 0 ; 0x04 RSP0 (low) 344 | dd 0 ; 0x08 RSP0 (high) 345 | dd 0 ; 0x0C RSP1 (low) 346 | dd 0 ; 0x10 RSP1 (high) 347 | dd 0 ; 0x14 RSP2 (low) 348 | dd 0 ; 0x18 RSP2 (high) 349 | dd 0 ; 0x1C reserved 350 | dd 0 ; 0x20 reserved 351 | .ist1: 352 | dq death_stack_top ; 0x24 IST1 353 | dq 0 ; 0x2C IST2 354 | dq 0 ; 0x34 IST3 355 | dq 0 ; 0x3C IST4 356 | dq 0 ; 0x44 IST5 357 | dq 0 ; 0x4C IST6 358 | dq 0 ; 0x54 IST7 359 | dd 0 ; 0x5C reserved 360 | dd 0 ; 0x60 reserved 361 | dw 0 ; 0x64 reserved 362 | dw 13 ; 0x66 IOPB offset 363 | 364 | times 16-13 db 0 ; pad 365 | 366 | section .bss 367 | 368 | stack_bottom: 369 | resb 0x40000 370 | stack_top: 371 | death_stack_bottom: 372 | resb 0x40000 373 | death_stack_top: -------------------------------------------------------------------------------- /source/crt.s: -------------------------------------------------------------------------------- 1 | global _start 2 | global __psys_fork 3 | extern _main 4 | extern __init_begin 5 | extern __init_end 6 | extern __elf_begin 7 | extern __elf_end 8 | 9 | section .text 10 | 11 | stack: dq 0 12 | 13 | __psys_fork: 14 | push rbx 15 | push rcx 16 | push rdx 17 | push rbp 18 | push r8 19 | push r9 20 | push r10 21 | push r11 22 | push r12 23 | push r13 24 | push r14 25 | push r15 26 | 27 | mov [rel stack], rsp 28 | lea rsi, [rel __psys_resume] 29 | syscall 30 | 31 | pop r15 32 | pop r14 33 | pop r13 34 | pop r12 35 | pop r11 36 | pop r10 37 | pop r9 38 | pop r8 39 | pop rbp 40 | pop rdx 41 | pop rcx 42 | pop rbx 43 | ret 44 | 45 | __psys_resume: 46 | mov rsp, [rel stack] 47 | pop r15 48 | pop r14 49 | pop r13 50 | pop r12 51 | pop r11 52 | pop r10 53 | pop r9 54 | pop r8 55 | pop rbp 56 | pop rdx 57 | pop rcx 58 | pop rbx 59 | xor rax, rax 60 | ret 61 | 62 | _start: 63 | mov rdi, 0xC000 ; SYS_BOOTSTRAP_SETUPSTACK 64 | syscall ; this causes #UD. this is normal. easier to handle. ignore that 65 | mov rsp, rax ; they have to return in rax 66 | 67 | lea rdi, [rel __init_begin] 68 | lea rsi, [rel __init_end] 69 | 70 | mov rax, QWORD __elf_begin 71 | mov rdx, rax 72 | mov rax, QWORD __elf_end 73 | mov rcx, rax 74 | 75 | call _main 76 | 77 | db 0xeb, 0xfe -------------------------------------------------------------------------------- /source/defines.s: -------------------------------------------------------------------------------- 1 | ; multiboot definitions 2 | %define MULTIBOOT_HEADER_MAGIC 0x1BADB002 3 | %define MULTIBOOT_HEADER_FLAGS 0x00000003 4 | 5 | ; where is the kernel? 6 | %define KERNEL_VMA_BASE 0xFFFF800000000000 7 | %define KERNEL_LMA_BASE 0x100000 8 | 9 | ; the gdt entry to use for the kernel 10 | %define CS_KERNEL 0x10 11 | %define CS_KERNEL32 0x08 12 | 13 | ; other definitions 14 | 15 | %define STACK_SIZE 0x4000 16 | -------------------------------------------------------------------------------- /source/kernel.ld: -------------------------------------------------------------------------------- 1 | SECTIONS 2 | { 3 | 4 | . = 0xffffffff80200000; 5 | _text_begin = .; 6 | .text : ALIGN(4096) { 7 | *(.text*) 8 | } 9 | . = ALIGN(4096); 10 | _text_end = .; 11 | 12 | .rodata : ALIGN(4096) { 13 | *(.rodata*) 14 | } 15 | 16 | .data : ALIGN(4096) { 17 | *(.data*) 18 | } 19 | 20 | .tdata : ALIGN(4096) { 21 | put_tls_here = .; 22 | *(.tdata) 23 | zero_tls_begin = .; 24 | *(.tbss) 25 | zero_tls_end = .; 26 | } 27 | 28 | .bss : ALIGN(4096) { 29 | *(COMMON) 30 | *(.bss*) 31 | } 32 | 33 | .stivale2hdr : { 34 | KEEP(*(.stivale2hdr)) 35 | } 36 | _exec_end = .; 37 | } 38 | -------------------------------------------------------------------------------- /source/kernel/acpi/rsdp.d: -------------------------------------------------------------------------------- 1 | module kernel.acpi.rsdp; 2 | 3 | import kernel.io; 4 | import kernel.mm; 5 | import kernel.optional; 6 | 7 | uint load_u32(uint a) { 8 | return load_u32(cast(ulong)a); 9 | } 10 | uint load_u32(ulong a) { 11 | return *cast(uint*)a; 12 | } 13 | 14 | ulong value = 0; 15 | 16 | struct RSDPEntry { 17 | char[] id; 18 | void* table; 19 | } 20 | 21 | private __gshared RSDPEntry[] tables; 22 | 23 | Option!(void*) find_table(string id) { 24 | foreach (t; tables) { 25 | if (t.id == id) return Option!(void*)(t.table); 26 | } 27 | return Option!(void*)(); 28 | } 29 | 30 | void load_rsdp(ulong u) { 31 | uint rsdt = load_u32(u + 16); 32 | uint entcount = (load_u32(rsdt + 4) - 36) / 4; 33 | tables = alloca_unsafe!(RSDPEntry)(0); 34 | printk(DEBUG, "ACPI Tables:"); 35 | foreach (i; 0..entcount) { 36 | uint tbl = load_u32(i * 4 + 36 + rsdt); 37 | char[] s = array(cast(char*)tbl, 4); 38 | RSDPEntry e; 39 | e.table = cast(void*)tbl; 40 | e.id = s; 41 | push(tables, e); 42 | printk(DEBUG, " - {}", s); 43 | } 44 | } -------------------------------------------------------------------------------- /source/kernel/autoinit.d: -------------------------------------------------------------------------------- 1 | module kernel.autoinit; 2 | 3 | alias MakeFunction(T) = T function(); 4 | 5 | unittest { 6 | import kernel.io : printk; 7 | 8 | AutoInit!int i = AutoInit!int((() { return 3; })); 9 | printk("[autoinit] assert uninitialized"); 10 | assert(!i.is_init()); 11 | i.ensure_init(); 12 | printk("[autoinit] assert initialized..."); 13 | assert(i.is_init()); 14 | printk("[autoinit] assert initialized correctly..."); 15 | assert(*i.val() == 3); 16 | 17 | AutoInit!int j = AutoInit!int(3); 18 | printk("[autoinit] assert initialized correctly..."); 19 | assert(*j.val() == 3); 20 | 21 | AutoInit!int k = AutoInit!int((() { return 3; })); 22 | printk("[autoinit] assert uninitialized..."); 23 | assert(!k.is_init()); 24 | printk("[autoinit] assert autoinitialized correctly..."); 25 | assert(*k.val() == 3); 26 | } 27 | 28 | /// Auto-initializer 29 | struct AutoInit(T) { 30 | align(T.alignof) private byte[T.sizeof] data; 31 | private bool _is_init = false; 32 | private MakeFunction!(T) makefcn; 33 | 34 | /// Copy ctor 35 | this(ref AutoInit!T rhs) { 36 | assert(false); 37 | } 38 | 39 | /// Create AutoInit 40 | public this(MakeFunction!(T) val) { 41 | this._is_init = false; 42 | this.makefcn = val; 43 | } 44 | /// Create initatied AutoInit 45 | public this(T val) { 46 | this._is_init = true; 47 | *(cast(T*) this.data.ptr) = val; 48 | this.makefcn = () { assert(false); }; 49 | } 50 | /// Gets the value inside 51 | T* val() { 52 | this.ensure_init(); 53 | return (cast(T*) this.data.ptr); 54 | } 55 | /// Initializes the value inside 56 | void ensure_init() { 57 | if (!is_init) { 58 | this._is_init = true; 59 | *(cast(T*) this.data.ptr) = this.makefcn(); 60 | } 61 | } 62 | /// Is it inited? 63 | bool is_init() { 64 | return this._is_init; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /source/kernel/cpio.d: -------------------------------------------------------------------------------- 1 | module kernel.cpio; 2 | 3 | import kernel.mm; 4 | import kernel.io; 5 | 6 | struct CPIOFile { 7 | string name; 8 | ubyte[] data; 9 | ulong uid; 10 | } 11 | 12 | void find(out CPIOFile file, string name, ref CPIOFile[] arr) { 13 | foreach (ref fl; arr) { 14 | if (name == fl.name) { file = fl; return; } 15 | } 16 | printk(FATAL, "Unable to find {}", name); 17 | assert(0, "Unable to find crucial file, aborting!"); 18 | } 19 | bool try_find(out CPIOFile file, string name, ref CPIOFile[] arr) { 20 | foreach (ref fl; arr) { 21 | if (name == fl.name) { file = fl; return true; } 22 | } 23 | printk(WARN, "Unable to find {}", name); 24 | return false; 25 | } 26 | 27 | int parse_oct(ubyte[] raw, int start, int len) { 28 | int v = 0; 29 | int end = start + len; 30 | 31 | ubyte[] seg = raw[start .. end]; 32 | foreach (i; 0..len) { 33 | v <<= 3; 34 | v |= (seg[i] - '0'); 35 | } 36 | return v; 37 | } 38 | 39 | CPIOFile[] parse_cpio(ubyte[] raw) { 40 | int cursor = 0; 41 | CPIOFile[] filez = alloca!(CPIOFile)(0); 42 | while (cursor < raw.length) { 43 | int filebegin = cursor; 44 | cursor += 76; 45 | int uid = parse_oct(raw, filebegin + 24, 6); 46 | int namesz = parse_oct(raw, filebegin + 59, 6); 47 | int filesz = parse_oct(raw, filebegin + 65, 11); 48 | CPIOFile f; 49 | f.name = alloca!(immutable(char))(namesz - 1, cast(char)0); 50 | f.data = alloca!(ubyte)(filesz, cast(ubyte)0); 51 | char[] s = transmute!(string, char[])(f.name); 52 | foreach (i; 0..(namesz - 1)) { 53 | s[i] = cast(char)raw[cursor++]; 54 | } 55 | cursor++; 56 | foreach (i; 0..filesz) { 57 | f.data[i] = cast(char)raw[cursor++]; 58 | } 59 | f.uid = uid; 60 | if (f.name == "TRAILER!!!") break; 61 | push(filez, f); 62 | } 63 | return filez; 64 | } 65 | 66 | -------------------------------------------------------------------------------- /source/kernel/elf.d: -------------------------------------------------------------------------------- 1 | module kernel.elf; 2 | 3 | import kernel.io; 4 | import kernel.mm; 5 | import kernel.cpio; 6 | import kernel.pmap; 7 | import kernel.util; 8 | import kernel.task; 9 | import kernel.guards; 10 | 11 | /// 12 | extern (C) struct ELF { 13 | char[16] ident; 14 | ushort e_type; 15 | ushort e_machine; 16 | uint e_version; 17 | ulong e_entry; 18 | ulong e_phoff; 19 | ulong e_shoff; 20 | uint e_flags; 21 | ushort e_ehsize; 22 | ushort e_phentsize; 23 | ushort e_phnum; 24 | ushort e_shentsize; 25 | ushort e_shnum; 26 | ushort e_shstrndx; 27 | } 28 | 29 | /// 30 | struct ProgramHeader { 31 | uint p_type; 32 | uint p_flags; 33 | ulong p_offset; 34 | ulong p_vaddr; 35 | ulong p_paddr; 36 | ulong p_filesz; 37 | ulong p_memsz; 38 | ulong p_align; 39 | } 40 | 41 | const ushort ET_NONE = 0; 42 | const ushort ET_REL = 1; 43 | const ushort ET_EXEC = 2; 44 | const ushort ET_DYN = 3; 45 | const ushort ET_CORE = 4; 46 | 47 | const ushort EM_M32 = 1; 48 | const ushort EM_SPARC = 2; 49 | const ushort EM_386 = 3; 50 | const ushort EM_68K = 4; 51 | const ushort EM_88K = 5; 52 | const ushort EM_486 = 6; 53 | const ushort EM_860 = 7; 54 | const ushort EM_MIPS = 8; 55 | const ulong EM_PARISC = 15; 56 | const ulong EM_SPARC32PLUS = 18; 57 | const ulong EM_PPC = 20; 58 | const ulong EM_PPC64 = 21; 59 | const ulong EM_SPU = 23; 60 | const ulong EM_SH = 42; 61 | const ulong EM_SPARCV9 = 43; 62 | const ulong EM_IA_64 = 50; 63 | const ulong EM_X86_64 = 62; 64 | const ulong EM_S390 = 22; 65 | const ulong EM_CRIS = 76; 66 | const ulong EM_V850 = 87; 67 | const ulong EM_M32R = 88; 68 | const ulong EM_H8_300 = 46; 69 | const ulong EM_MN10300 = 89; 70 | const ulong EM_BLACKFIN = 106; 71 | const ulong EM_FRV = 0x5441; 72 | const ulong EM_AVR32 = 0x18ad; 73 | const ulong EM_SMIDISCA = 0xffef; 74 | 75 | bool load(out ulong rip, CPIOFile exe) { 76 | ELF elf = *cast(ELF*) exe.data.ptr; 77 | if (elf.ident != "\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00") 78 | return false; 79 | if (elf.e_type != ET_EXEC) 80 | return false; 81 | if (elf.e_machine != EM_X86_64) 82 | return false; 83 | if (elf.e_version != 1) 84 | return false; 85 | 86 | ProgramHeader* _phdrs = cast(ProgramHeader*)(exe.data.ptr + elf.e_phoff); 87 | ProgramHeader[] phs = array(_phdrs, elf.e_phnum); 88 | foreach (ref ProgramHeader ph; phs) { 89 | if (ph.p_type == /* PT_GNU_RELRO */ 0x6474e552) continue; 90 | if (ph.p_type == /* PT_GNU_STACK */ 0x6474e551) continue; 91 | if (ph.p_type != /* PT_LOAD */ 1) 92 | assert(0, "Invalid program header"); 93 | ulong pagec = (ph.p_memsz + 4095) / 4096; 94 | foreach (i; 0 .. pagec) { 95 | ulong va = cast(ulong)(ph.p_vaddr + (i * 4096)); 96 | user_map(cast(void*)va); 97 | } 98 | auto smap = no_smap(); 99 | memcpy(cast(byte*) ph.p_vaddr, cast(byte*)(ph.p_offset + exe.data.ptr), ph.p_filesz); 100 | memset(cast(byte*)(ph.p_vaddr + ph.p_filesz), 0, ph.p_memsz - ph.p_filesz); 101 | smap.die(); 102 | } 103 | rip = elf.e_entry; 104 | return true; 105 | } 106 | -------------------------------------------------------------------------------- /source/kernel/guards.d: -------------------------------------------------------------------------------- 1 | module kernel.guards; 2 | 3 | import kernel.platform; 4 | import kernel.io; 5 | 6 | private __gshared ulong rc = 0; 7 | 8 | /// SMAP guard 9 | struct SMAPGuard { 10 | @disable this(); 11 | package bool is_legit = false; 12 | 13 | /// 14 | this(ref SMAPGuard rhs) { 15 | if (is_legit) { 16 | rc += 1; 17 | is_legit = true; 18 | } 19 | } 20 | 21 | ~this() { 22 | if (is_legit) { 23 | rc -= 1; 24 | if (rc == 0) { 25 | // printk(DEBUG, "Reenabling SMAP"); 26 | clac(); 27 | } 28 | } 29 | } 30 | 31 | /// Kill this guard 32 | void die() { 33 | assert(is_legit); 34 | is_legit = false; 35 | rc -= 1; 36 | 37 | if (rc == 0) { 38 | // printk(DEBUG, "Reenabling SMAP"); 39 | clac(); 40 | } 41 | } 42 | } 43 | /// Disable SMAP 44 | SMAPGuard no_smap() { 45 | bool fake = false; 46 | stac(); 47 | // printk(DEBUG, "Disable SMAP"); 48 | SMAPGuard the = *cast(SMAPGuard*)&fake; 49 | the.is_legit = true; 50 | rc += 1; 51 | return the; 52 | } -------------------------------------------------------------------------------- /source/kernel/hashmap.d: -------------------------------------------------------------------------------- 1 | module kernel.hashmap; 2 | 3 | import kernel.io; 4 | import kernel.mm; 5 | import std.typecons; 6 | 7 | struct HMI { 8 | ulong addr; 9 | ulong* rc; 10 | bool isthere; 11 | } 12 | 13 | private ulong hash(ulong i, ulong v) { 14 | return (v * 1345) % i; 15 | } 16 | 17 | struct HashMap { 18 | HMI[] data = []; 19 | ulong c = 0; 20 | 21 | ~this() { 22 | free(data); 23 | } 24 | 25 | void deleteElem(ulong addr) { 26 | ulong v = hash(data.length, addr); 27 | while (data[v].isthere && data[v].addr != addr) v = hash(data.length, v); 28 | if (data[v].isthere) c -= 1; 29 | data[v].isthere = false; 30 | } 31 | 32 | void rehashSelf() { 33 | HMI[] elems = []; 34 | foreach (ref HMI e; data) { 35 | if (e.isthere) push(elems, e); 36 | } 37 | ulong size = (elems.length + 2) * 2; 38 | data = realloca(data, size); 39 | foreach (ref HMI e; data) { 40 | e.isthere = false; 41 | } 42 | c = 0; 43 | foreach (ref HMI e; elems) { 44 | insertElem(e.addr, e.rc); 45 | } 46 | free(elems); 47 | } 48 | 49 | void insertElem(ulong addr, ulong* rc) { 50 | c += 1; 51 | if (c >= data.length) { 52 | rehashSelf(); 53 | c += 1; 54 | } 55 | ulong v = hash(data.length, addr); 56 | while (data[v].isthere) { v += 1; v %= data.length; } 57 | data[v].isthere = true; 58 | data[v].rc = rc; 59 | data[v].addr = addr; 60 | } 61 | 62 | ulong** getElem(ulong addr) { 63 | assert(data.length != 0); 64 | ulong v = hash(data.length, addr); 65 | while (data[v].isthere && data[v].addr != addr) { v += 1; v %= data.length; } 66 | return &data[v].rc; 67 | } 68 | 69 | bool hasElem(ulong addr) { 70 | if (data.length == 0) return false; 71 | ulong v = hash(data.length, addr); 72 | while (data[v].isthere && data[v].addr != addr) { v += 1; v %= data.length; } 73 | return data[v].isthere; 74 | } 75 | 76 | bool opBinaryRight(string s)(ulong k) if (s == "in") 77 | { 78 | return hasElem(k); 79 | } 80 | 81 | ref ulong* opIndex(ulong addr) { 82 | assert(addr in this); 83 | return *getElem(addr); 84 | } 85 | } 86 | 87 | struct BetterHashMap(U) { 88 | HashMap _h; 89 | 90 | ref U* opIndex(ulong addr) { 91 | assert(addr in this); 92 | return *cast(U**)_h.getElem(addr); 93 | } 94 | 95 | bool opBinaryRight(string s)(ulong k) if (s == "in") 96 | { 97 | return _h.hasElem(k); 98 | } 99 | 100 | void deleteElem(ulong addr) { 101 | if (!(addr in _h)) return; 102 | ulong* a = _h[addr]; 103 | free(a); 104 | _h.deleteElem(addr); 105 | } 106 | 107 | void insertElem(Args...)(ulong addr, Args argz) { 108 | U* a = alloc!U(U(argz)); 109 | _h.insertElem(addr, cast(ulong*)a); 110 | } 111 | 112 | BHMIter!U iter() { 113 | return BHMIter!(U)(_h.data); 114 | } 115 | } 116 | 117 | struct BHMIter(T) { 118 | HMI[] _inner; 119 | ulong i = 0; 120 | 121 | /// Is it empty? 122 | bool empty() const { 123 | ulong i = this.i; 124 | while (i < _inner.length && !this._inner[i].isthere) i++; 125 | // The range is consumed when begin equals end 126 | return i >= _inner.length; 127 | } 128 | 129 | /// Next element pls 130 | void popFront() { 131 | // Skipping the first element is achieved by 132 | // incrementing the beginning of the range 133 | i++; 134 | while (i < _inner.length && !this._inner[i].isthere) i++; 135 | } 136 | 137 | /// First element ptr (reborrowed) 138 | Tuple!(ulong, T*) front() const { 139 | return tuple(cast(ulong)this._inner[i].addr, cast(T*) this._inner[i].rc); 140 | } 141 | } -------------------------------------------------------------------------------- /source/kernel/io.d: -------------------------------------------------------------------------------- 1 | module kernel.io; 2 | 3 | import core.vararg; 4 | import core.volatile; 5 | import std.traits; 6 | import std.algorithm; 7 | 8 | import kernel.optional; 9 | import kernel.platform; 10 | import kernel.util; 11 | 12 | /// putsk_const_str is like putsk but for const(string) 13 | void putsk_const_str(const(string) s) { 14 | foreach (chr; s) { 15 | if (chr == 0) 16 | break; 17 | putck(chr); 18 | } 19 | } 20 | 21 | /// putsk_const_str is like putsk but for const(string) 22 | void putsk_const_str(string s) { 23 | foreach (chr; s) { 24 | if (chr == 0) 25 | break; 26 | putck(chr); 27 | } 28 | } 29 | 30 | /// putsk is a dumb version of printk (with no newline!) 31 | void putsk(char* s) { 32 | for (int i = 0; s[i] != 0; i++) { 33 | putck(s[i]); 34 | } 35 | } 36 | 37 | /// putsk is a dumb version of printk (with no newline!) 38 | void putsk(immutable(char)[] s) { 39 | foreach (chr; s) { 40 | putck(chr); 41 | } 42 | } 43 | 44 | /// putsk is a dumb version of printk (with no newline!) 45 | private void putsk_string(T)(T s) { 46 | foreach (chr; s) { 47 | putck(chr); 48 | } 49 | } 50 | 51 | /// putck prints a char (cough cough outp(DEBUG_IO_PORT, chr) cough) 52 | void putck(char c) { 53 | if (c != 0) { 54 | outp(DEBUG_IO_PORT_NUM, c); 55 | } 56 | } 57 | 58 | /// putsk is a dumb version of printk (with no newline!) 59 | void putsk(T)(T s) { 60 | static assert(is(Unqual!(T) : ArrayMarker!char)); 61 | foreach (char chr; s) { 62 | putck(chr); 63 | } 64 | } 65 | 66 | /// putsk is a dumb version of printk (with no newline!) 67 | void putsk(char s) { 68 | putck(s); 69 | } 70 | 71 | private struct EnumMarker { 72 | 73 | } 74 | 75 | private struct ArrayMarker(T) { 76 | } 77 | 78 | private struct IOIterMarker(T) { 79 | } 80 | 81 | /// A hex printer for `T` 82 | extern (C) struct Hex(T) { 83 | /// The value of the `hex` 84 | align(T.alignof) T inner; 85 | /// Create a new Hex 86 | this(T inner) { 87 | this.inner = inner; 88 | } 89 | } 90 | 91 | private template isEnum(alias symb) { 92 | static if (is(symb == enum)) 93 | enum bool isEnum = true; 94 | else 95 | enum bool isEnum = false; 96 | } 97 | 98 | private template Unqual(T) { 99 | static if (is(T U == shared(const U))) 100 | alias Unqual = U; 101 | else static if (is(T U == const U)) 102 | alias Unqual = U; 103 | else static if (is(T U == immutable U)) 104 | alias Unqual = U; 105 | else static if (is(T U == shared U)) 106 | alias Unqual = U; 107 | else static if (is(T == string)) 108 | alias Unqual = ArrayMarker!char; 109 | else static if (__traits(hasMember, T, "ioiter")) 110 | alias Unqual = IOIterMarker!(ReturnType!(__traits(getMember, T, "ioiter"))); 111 | else static if (isArray!(T)) { 112 | alias Unqual = ArrayMarker!(typeof(T.init[0])); 113 | } else static if (isEnum!(T)) 114 | alias Unqual = EnumMarker; 115 | else 116 | alias Unqual = T; 117 | } 118 | 119 | private template UnPtr(T) { 120 | static if (is(T U == U*)) 121 | alias UnPtr = void*; 122 | else 123 | alias UnPtr = T; 124 | } 125 | 126 | private template Deref(T) { 127 | static if (is(T U == U*)) 128 | alias Deref = Deref(U); 129 | else 130 | alias Deref = T; 131 | } 132 | 133 | private void _printk_outint(string subarray, ulong arg, bool bare = false) { 134 | int pad = 0; 135 | int base = 10; 136 | switch (subarray) { 137 | case "_chr_oob": 138 | base = 16; 139 | pad = 2; 140 | break; 141 | case "hex": 142 | if (!bare) 143 | putsk("0x"); 144 | base = 16; 145 | break; 146 | case "ptr": 147 | if (!bare) 148 | putsk("0x"); 149 | base = 16; 150 | pad = 16; 151 | break; 152 | case "": 153 | break; 154 | case "oct": 155 | if (!bare) 156 | putsk("0o"); 157 | base = 8; 158 | break; 159 | case "bin": 160 | if (!bare) 161 | putsk("0b"); 162 | base = 2; 163 | break; 164 | default: 165 | assert(false); 166 | } 167 | char[70] arr; 168 | char* arr_offset_correctly = intToString(arg, arr.ptr, base); 169 | const long arr_offset_correctly_len = strlen(arr_offset_correctly); 170 | const long pad_needed = max(0, pad - arr_offset_correctly_len); 171 | for (int i = 0; i < pad_needed; i++) 172 | putsk('0'); 173 | putsk(arr_offset_correctly); 174 | } 175 | 176 | private void _printk_outint(string subarray, long arg) { 177 | int pad = 0; 178 | int base = 10; 179 | switch (subarray) { 180 | case "hex": 181 | putsk("0x"); 182 | base = 16; 183 | break; 184 | case "ptr": 185 | putsk("0x"); 186 | base = 16; 187 | pad = 8; 188 | break; 189 | case "": 190 | break; 191 | case "oct": 192 | putsk("0o"); 193 | base = 8; 194 | break; 195 | case "bin": 196 | base = 2; 197 | putsk("0b"); 198 | break; 199 | default: 200 | assert(false); 201 | } 202 | char[70] arr; 203 | char* arr_offset_correctly = intToString(arg, arr.ptr, base); 204 | const long arr_offset_correctly_len = strlen(arr_offset_correctly); 205 | const long pad_needed = max(0, pad - arr_offset_correctly_len); 206 | for (int i = 0; i < pad_needed; i++) 207 | putsk('0'); 208 | putsk(arr_offset_correctly); 209 | } 210 | 211 | private template GetOMeta(string Target) { 212 | const char[] GetOMeta = "OMeta meta = arg._ometa_" ~ Target ~ ";"; 213 | } 214 | 215 | private template HasOMeta(string Target) { 216 | const char[] HasOMeta = "__traits(compiles, arg._ometa_" ~ Target ~ ")"; 217 | } 218 | /// An O-Meta 219 | struct OMeta { 220 | /// Should we ignore the value? If yes, print `fmt` 221 | bool ignore = false; 222 | /// Should we _fully_ ignore the value? 223 | bool nuke = false; 224 | /// The format string for this O-Meta 225 | string fmt = ""; 226 | /// Should we print it raw? 227 | bool print_raw = false; 228 | /// Internal print skipper 229 | byte _oskip = 0; 230 | } 231 | /// Get a hex printer O-Meta 232 | OMeta hex_ometa() { 233 | OMeta m; 234 | m.fmt = "hex"; 235 | m.print_raw = false; 236 | return m; 237 | } 238 | /// Get a hex printer O-Meta 239 | OMeta disabler_ometa() { 240 | OMeta m; 241 | m.nuke = true; 242 | return m; 243 | } 244 | /// Get a pointer printer O-Meta 245 | OMeta ptr_ometa() { 246 | OMeta m; 247 | m.fmt = "ptr"; 248 | m.print_raw = false; 249 | return m; 250 | } 251 | 252 | unittest { 253 | printk("Test printk [short]: {}", cast(short) 3); 254 | printk("Test printk [ushort]: {}", cast(ushort) 3); 255 | printk("Test printk [int]: {}", cast(int) 3); 256 | printk("Test printk [uint]: {}", cast(uint) 3); 257 | printk("Test printk [long]: {}", cast(long) 3); 258 | printk("Test printk [ulong]: {}", cast(ulong) 3); 259 | printk("Test printk [string]: {}", "asdf"); 260 | printk("Test printk [char*]: {}", "asdf".ptr); 261 | } 262 | 263 | /// Print an object! 264 | void putdyn(ObjTy)(string subarray, ObjTy arg, int prenest = 0, bool is_field = false) { 265 | ObjTy arg2 = arg; 266 | putdyn(subarray, arg2, prenest, is_field); 267 | } 268 | 269 | /// Print an object! 270 | void putdyn(ObjTy)(string subarray, ref ObjTy arg, int prenest = 0, bool is_field = false) { 271 | if (subarray == ":?") { 272 | subarray = ""; 273 | is_field = true; 274 | } 275 | alias T = Unqual!(typeof(arg)); 276 | static if (is(T : const char[])) { 277 | assert(subarray == ""); 278 | if (is_field) { 279 | putsk('"'); 280 | foreach (chr; arg) { 281 | if (chr == '\n') { 282 | putsk("\\n"); 283 | } else if (chr > 0x7e || chr < 0x20) { 284 | putsk("\\x"); 285 | _printk_outint("_chr_oob", cast(ulong) chr); 286 | } else { 287 | putsk(chr); 288 | } 289 | } 290 | putsk('"'); 291 | } else { 292 | putsk_string(arg); 293 | } 294 | } 295 | static if (is(T == EnumMarker)) { 296 | static foreach (k; __traits(allMembers, ObjTy)) { 297 | { 298 | if (__traits(getMember, ObjTy, k) == arg) { 299 | putsk_string(k); 300 | return; 301 | } 302 | } 303 | } 304 | // it's a bitfield 305 | putck('['); 306 | bool do_space = true; 307 | static foreach (k; __traits(allMembers, ObjTy)) { 308 | { 309 | if (__traits(getMember, ObjTy, k) & arg) { 310 | if (do_space) { 311 | do_space = false; 312 | putck(' '); 313 | } 314 | putsk_string(k); 315 | putck(' '); 316 | } 317 | } 318 | } 319 | putck(']'); 320 | } else static if (is(T : const char*)) { 321 | assert(subarray == ""); 322 | if (is_field) { 323 | putsk('"'); 324 | for (int i = 0; arg[i]; i++) { 325 | if (arg[i] == '\n') { 326 | putsk("\\n"); 327 | } else if (arg[i] > 0x7e || arg[i] < 0x20) { 328 | putsk("\\x"); 329 | _printk_outint("_chr_oob", cast(ulong) arg[i]); 330 | } else { 331 | putsk(arg[i]); 332 | } 333 | } 334 | putsk('"'); 335 | } else { 336 | for (int i = 0; arg[i]; i++) { 337 | putck(arg[i]); 338 | } 339 | } 340 | } else static if (is(T : ArrayMarker!char)) { 341 | if (is_field) { 342 | putsk('"'); 343 | foreach (chr; arg) { 344 | if (chr == '\n') { 345 | putsk("\\n"); 346 | } else if (chr > 0x7e || chr < 0x20) { 347 | putsk("\\x"); 348 | _printk_outint("_chr_oob", cast(ulong) chr); 349 | } else { 350 | putsk(chr); 351 | } 352 | } 353 | putsk('"'); 354 | } else { 355 | putsk_string(arg); 356 | } 357 | } else static if (is(T == char)) { 358 | putsk("'"); 359 | if (arg == '\n') { 360 | putsk("\\n"); 361 | } else if (arg > 0x7e || arg < 0x20) { 362 | putsk("\\x"); 363 | _printk_outint("_chr_oob", cast(ulong) arg); 364 | } else { 365 | putsk(arg); 366 | } 367 | putsk("'"); 368 | } else static if (is(T U == Hex!U)) { 369 | assert(subarray == ""); 370 | putdyn("hex", arg.inner, prenest, is_field); 371 | } else static if (is(T == byte)) { 372 | _printk_outint(subarray, cast(long) arg); 373 | } else static if (is(T == ubyte)) { 374 | _printk_outint(subarray, cast(ulong) arg); 375 | } else static if (is(T == int)) { 376 | _printk_outint(subarray, cast(long) arg); 377 | } else static if (is(T == uint)) { 378 | _printk_outint(subarray, cast(ulong) arg); 379 | } else static if (is(T == short)) { 380 | _printk_outint(subarray, cast(long) arg); 381 | } else static if (is(T == ushort)) { 382 | _printk_outint(subarray, cast(ulong) arg); 383 | } else static if (is(T == long)) { 384 | _printk_outint(subarray, arg); 385 | } else static if (is(T == ulong)) { 386 | _printk_outint(subarray, arg); 387 | } else static if (is(T == bool)) { 388 | putsk(arg ? "true" : "false"); 389 | } else static if (is(T == void)) { 390 | putsk("void"); 391 | } else static if (is(T == void*)) { 392 | assert(subarray == ""); 393 | _printk_outint("ptr", cast(ulong) arg); 394 | } else static if (is(T U == ArrayMarker!U)) { 395 | putsk('['); 396 | bool is_first = true; 397 | foreach (member; arg) { 398 | if (is_first) { 399 | putsk('\n'); 400 | for (int i = 0; i < prenest; i++) { 401 | putsk(" "); 402 | } 403 | is_first = false; 404 | } 405 | putsk(" "); 406 | { 407 | putdyn(subarray, member, prenest + 4, true); 408 | } 409 | putsk('\n'); 410 | for (int i = 0; i < prenest; i++) { 411 | putsk(" "); 412 | } 413 | } 414 | putsk(']'); 415 | } else static if (is(T U == IOIterMarker!U)) { 416 | putsk('['); 417 | bool is_first = true; 418 | int max_counter = 0; 419 | foreach (member; arg.ioiter()) { 420 | max_counter++; 421 | if (max_counter > 15) { 422 | putsk(" ... snip"); 423 | putsk('\n'); 424 | for (int i = 0; i < prenest; i++) { 425 | putsk(" "); 426 | } 427 | break; 428 | } 429 | if (is_first) { 430 | putsk('\n'); 431 | for (int i = 0; i < prenest; i++) { 432 | putsk(" "); 433 | } 434 | is_first = false; 435 | } 436 | putsk(" "); 437 | { 438 | putdyn(subarray, member, prenest + 4, true); 439 | } 440 | putsk('\n'); 441 | for (int i = 0; i < prenest; i++) { 442 | putsk(" "); 443 | } 444 | } 445 | putsk(']'); 446 | } else static if (is(T U == Option!U)) { 447 | if (arg.is_some()) { 448 | putsk("Some("); 449 | putdyn(subarray, arg.unwrap(), prenest, true); 450 | putsk(")"); 451 | } else { 452 | assert(arg.is_none()); 453 | putsk("None"); 454 | } 455 | } else { 456 | alias U = UnPtr!(T); 457 | static if (is(U == void*) && __traits(hasMember, T, "__hide_deref")) { 458 | if (cast(ulong) arg != 0) { 459 | putdyn(subarray, *arg, prenest); 460 | } else { 461 | putsk("(null)"); 462 | } 463 | } else static if (is(U == void*)) { 464 | string asdf = T.stringof; 465 | putsk_const_str(asdf); 466 | putsk(" @ "); 467 | _printk_outint("ptr", cast(ulong) arg); 468 | if (cast(ulong) arg != 0) { 469 | putsk(" "); 470 | putdyn(subarray, *arg, prenest); 471 | } 472 | } else { 473 | static if (__traits(hasMember, T, "opFormat")) { 474 | putdyn(subarray, T.opFormat(), prenest); 475 | } else static if (__traits(hasMember, T, "opFormatter")) { 476 | arg.opFormatter(subarray, prenest); 477 | } else { 478 | putsk('{'); 479 | bool is_first = true; 480 | static foreach (member; [__traits(allMembers, T)]) { 481 | static if (member.length > 6 && member[0 .. 6] == "_prnt_") { 482 | if (is_first) { 483 | putsk('\n'); 484 | for (int i = 0; i < prenest; i++) { 485 | putsk(" "); 486 | } 487 | is_first = false; 488 | } 489 | putsk(" "); 490 | putsk(member[6 .. member.length]); 491 | putsk(": "); 492 | __traits(getMember, arg, member)(subarray, prenest + 4); 493 | putsk('\n'); 494 | for (int i = 0; i < prenest; i++) { 495 | putsk(" "); 496 | } 497 | } else static if (!isCallable!(__traits(getMember, arg, member)) && __traits(compiles, putdyn( 498 | subarray, __traits(getMember, arg, 499 | member), prenest + 4, true))) { 500 | static if (!__traits(compiles, __traits(getMember, arg, member) 501 | ._oskip) && !__traits(hasMember, arg, "__noshow_" ~ member)) { 502 | if (is_first) { 503 | putsk('\n'); 504 | for (int i = 0; i < prenest; i++) { 505 | putsk(" "); 506 | } 507 | is_first = false; 508 | } 509 | putsk(" "); 510 | putsk(member); 511 | putsk(": "); 512 | static if (mixin(HasOMeta!(member))) { 513 | { 514 | mixin(GetOMeta!(member)); 515 | putdyn(meta.fmt, __traits(getMember, arg, 516 | member), prenest + 4, meta.print_raw); 517 | } 518 | } else { 519 | putdyn(subarray, __traits(getMember, arg, 520 | member), prenest + 4, true); 521 | } 522 | putsk('\n'); 523 | for (int i = 0; i < prenest; i++) { 524 | putsk(" "); 525 | } 526 | } 527 | } 528 | } 529 | putsk('}'); 530 | } 531 | } 532 | } 533 | } 534 | 535 | /// Log level 536 | enum Log { 537 | DEBUG, 538 | INFO, 539 | WARN, 540 | ERROR, 541 | FATAL, 542 | } 543 | 544 | /// Debug 545 | public const Log DEBUG = Log.DEBUG; 546 | /// Info 547 | public const Log INFO = Log.INFO; 548 | /// Warn 549 | public const Log WARN = Log.WARN; 550 | /// Error 551 | public const Log ERROR = Log.ERROR; 552 | /// Fatal 553 | public const Log FATAL = Log.FATAL; 554 | 555 | private template Digit(uint n) { 556 | public __gshared enum char[] Digit = [("0123456789"[n .. n + 1])[0]]; 557 | } 558 | 559 | private template Itoa(uint n) { 560 | static if (n < 0) 561 | public __gshared const char[] Itoa = "-" ~ Itoa!(-n); 562 | else static if (n < 10) 563 | public __gshared const char[] Itoa = Digit!(n); 564 | else 565 | public __gshared const char[] Itoa = Itoa!(n / 10) ~ Digit!(n % 10); 566 | } 567 | 568 | private __gshared ulong lineno_max = 3; 569 | 570 | /// Print a string 571 | void printk(string A = __FILE__, int L = __LINE__, Args...)(Log l, string s, Args args) { 572 | printk(L, A, l, s, args); 573 | } 574 | /// Print a string 575 | void printk(Args...)(int L, string A, Log l, string s, Args args) { 576 | version(DiodeNoDebug) { 577 | if (l == Log.DEBUG) return; 578 | } 579 | const ulong f = flags; 580 | cli(); 581 | ulong maxl = 4; 582 | putck('['); 583 | switch (l) { 584 | case DEBUG: 585 | putsk("\x1b[30;1mDEBUG"); 586 | maxl = 5; 587 | break; 588 | case INFO: 589 | putsk("\x1b[32;1mINFO"); 590 | break; 591 | case WARN: 592 | putsk("\x1b[33;1mWARN"); 593 | break; 594 | case ERROR: 595 | putsk("\x1b[31;1mERROR"); 596 | maxl = 5; 597 | break; 598 | case FATAL: 599 | putsk("\x1b[31;1mFATAL"); 600 | maxl = 5; 601 | break; 602 | default: 603 | printk(FATAL, "Invalid level: {}", cast(int) l); 604 | assert(0); 605 | } 606 | putsk("\x1b[0m] "); 607 | putsk_string(A[3 .. A.length]); 608 | putsk(":"); 609 | char[32] buffer; 610 | char* asds = intToString(L, buffer.ptr, 10); 611 | putsk(asds); 612 | putck(' '); 613 | ulong asdslength = strlen(asds); 614 | if (lineno_max < asdslength) 615 | lineno_max = asdslength; 616 | for (ulong i = asdslength; i < lineno_max; i++) 617 | putck(' '); 618 | 619 | int offinit = cast(int)(lineno_max + A.length - 5 + maxl); 620 | 621 | int idx_into_s = 0; 622 | foreach (arg; args) { 623 | 624 | // advance s 625 | while (s[idx_into_s] != '{') 626 | putck(s[idx_into_s++]); 627 | const int og_idx = idx_into_s + 1; 628 | while (s[idx_into_s++] != '}') { 629 | } 630 | const string subarray = s[og_idx .. idx_into_s - 1]; 631 | 632 | putdyn(subarray, arg, offinit); 633 | } 634 | while (idx_into_s < s.length) 635 | putck(s[idx_into_s++]); 636 | putck('\n'); 637 | flags = f; 638 | } 639 | 640 | /// Print a string 641 | void printk(string A = __FILE__, int L = __LINE__, Args...)(string s, Args args) { 642 | printk!(A, L, Args)(INFO, s, args); 643 | } 644 | -------------------------------------------------------------------------------- /source/kernel/irq.d: -------------------------------------------------------------------------------- 1 | module kernel.irq; 2 | 3 | import kernel.io; 4 | import kernel.mm; 5 | import kernel.pmap; 6 | import kernel.task; 7 | import kernel.util; 8 | import kernel.guards; 9 | import kernel.platform; 10 | import kernel.syscall.dispatch; 11 | import kernel.task : sched_yield; 12 | 13 | enum PageFaultError { 14 | PRESENT = 1, 15 | WRITE = 2, 16 | USER = 4, 17 | RESERVED = 8, 18 | INSTRUCTION_FETCH = 16 19 | } 20 | 21 | private extern (C) struct ISRFrameNOEC { 22 | ulong r15; 23 | ulong r14; 24 | ulong r13; 25 | ulong r12; 26 | ulong r11; 27 | ulong r10; 28 | ulong r9; 29 | ulong r8; 30 | ulong rdi; 31 | ulong rsi; 32 | ulong rdx; 33 | ulong rcx; 34 | ulong rbx; 35 | ulong rax; 36 | ulong rbp; 37 | // ulong error; 38 | 39 | ulong rip; 40 | ulong cs; 41 | ulong flags; 42 | ulong rsp; 43 | ulong ss; 44 | } 45 | 46 | /// 47 | extern (C) struct ISRFrame { 48 | /// The value of the register `r15` 49 | ulong r15; 50 | /// The value of the register `r14` 51 | ulong r14; 52 | /// The value of the register `r13` 53 | ulong r13; 54 | /// The value of the register `r12` 55 | ulong r12; 56 | /// The value of the register `r11` 57 | ulong r11; 58 | /// The value of the register `r10` 59 | ulong r10; 60 | /// The value of the register `r9` 61 | ulong r9; 62 | /// The value of the register `r8` 63 | ulong r8; 64 | /// The value of the register `rdi` 65 | ulong rdi; 66 | /// The value of the register `rsi` 67 | ulong rsi; 68 | /// The value of the register `rdx` 69 | ulong rdx; 70 | /// The value of the register `rcx` 71 | ulong rcx; 72 | /// The value of the register `rbx` 73 | ulong rbx; 74 | /// The value of the register `rax` 75 | ulong rax; 76 | /// The value of the register `rbp` 77 | ulong rbp; 78 | /// The value of the register `error` 79 | ulong error; 80 | /// The value of the register `rip` 81 | ulong rip; 82 | /// The value of the register `cs` 83 | ulong cs; 84 | /// The value of the register `flags` 85 | ulong flags; 86 | /// The value of the register `rsp` 87 | ulong rsp; 88 | /// The value of the register `ss` 89 | ulong ss; 90 | 91 | } 92 | 93 | /// Acknowledge end of interrupt on the legacy PIC. 94 | void pic_eoi() { 95 | outp(0x20, 0x20); 96 | } 97 | 98 | /// Handle an ISR 99 | extern (C) void isrhandle_ec(ulong isr, ISRFrame* frame) { 100 | if (isr == 0xe) { 101 | ulong pfaddr; 102 | asm { 103 | mov RAX, CR2; 104 | mov pfaddr, RAX; 105 | } 106 | printk(ERROR, "Page fault addr: {hex}", pfaddr); 107 | if (frame.error & 4) { 108 | if (cur_t.user_stack_bottom && pfaddr + 0x1000 > cur_t.user_stack_bottom && pfaddr < cur_t.user_stack_top) { 109 | printk("Demand paging stack at {hex} (for {ptr})", cur_t.user_stack_bottom - 0x1000, frame.rip); 110 | cur_t.user_stack_bottom -= 0x1000; 111 | user_map(cast(void*)cur_t.user_stack_bottom); 112 | return; 113 | } 114 | } 115 | } 116 | import kernel.syscall.util : is_safe_function; 117 | if (is_safe_function) printk(ERROR, " (in safe fn)"); 118 | printk(ERROR, "ISR: {hex} code={hex}", isr, frame.error); 119 | printk(ERROR, "Frame: {hex}", frame); 120 | foreach (ref pg; cur_t.pages_that_we_own.data) { 121 | if (pg.isthere) { 122 | printk(" Page @ {ptr}", pg.addr); 123 | } 124 | } 125 | printk(ERROR, "in PID {}", cur_t.tid); 126 | if (isr == /* page fault */ 0x0e) printk(ERROR, "PF error: {}", cast(PageFaultError)frame.error); 127 | assert(false); 128 | } 129 | 130 | /// Handle an ISR 131 | extern (C) void isrhandle_noec(ulong isr, ISRFrameNOEC* frame) { 132 | // isrhandle_ec(isr, &frame2); 133 | if (isr == /* timer */ 0x20) { 134 | pic_eoi(); 135 | sched_yield(); 136 | return; 137 | } 138 | if (isr == /* weird bochs thing */ 0x27) { 139 | return; 140 | } 141 | if (isr == /* invalid opcode */ 0x6) { 142 | // if the CPU has no SCE, we just trap #UDs from syscall. 143 | auto smoff = no_smap(); 144 | ubyte[2] insn = [/* syscall */ 0x0f, 0x05]; 145 | if (memcmp(cast(byte*)frame.rip, cast(byte*)insn.ptr, 2) == 0) { 146 | smoff.die(); 147 | frame.rip += 2; 148 | frame.rax = cast(ulong) syscall(frame.rdi, cast(void*)frame.rsi); 149 | return; 150 | } 151 | } 152 | printk(ERROR, "ISR: {hex}", isr); 153 | printk(ERROR, "Frame: {hex}", frame); 154 | assert(0); 155 | 156 | } 157 | 158 | /// An IDTR 159 | extern (C) struct IDTR { 160 | /// The size of the IDT 161 | align(1) ushort size; 162 | /// The address of the IDT 163 | align(1) ulong addr; 164 | 165 | /// O-Meta 166 | OMeta _ometa_size() { 167 | return hex_ometa(); 168 | } 169 | /// O-Meta 170 | OMeta _ometa_addr() { 171 | return hex_ometa(); 172 | } 173 | 174 | /// Load this IDTR 175 | void load() { 176 | ulong idtr = cast(ulong)&this; 177 | asm { 178 | mov RAX, idtr; 179 | lidt [RAX]; 180 | } 181 | } 182 | } 183 | 184 | extern (C) private ulong* get_idt_targets(); 185 | 186 | extern (C) private struct IDTEntry { 187 | align(1) ushort pointer_low; 188 | align(1) ushort gdt_selector = 8; 189 | align(1) ubyte ist = 0; 190 | align(1) ubyte type = 0x8e; 191 | align(1) ushort pointer_middle; 192 | align(1) uint pointer_high; 193 | align(1) uint reserved; 194 | 195 | void addr(ulong addr) { 196 | pointer_low = cast(ushort) addr; 197 | pointer_middle = cast(ushort)(addr >> 16); 198 | pointer_high = addr >> 32; 199 | } 200 | } 201 | 202 | static assert(IDTEntry.sizeof == 16); 203 | extern (C) private struct IDTRStruct { 204 | align(1) ushort length; 205 | align(1) ulong base; 206 | } 207 | 208 | private __gshared IDTEntry[256] idt; 209 | 210 | /// Allocate an IDT and return its corresponding IDTR 211 | IDTR new_idtr() { 212 | IDTR idtr; 213 | const ulong* targets = get_idt_targets(); 214 | foreach (i; 0 .. 256) { 215 | idt[i].addr = targets[i]; 216 | idt[i].ist = 0; 217 | } 218 | // idt[8].ist = 1; 219 | idtr.addr = cast(ulong) idt.ptr; 220 | idtr.size = 4096; 221 | return idtr; 222 | } 223 | 224 | private const ubyte PIC1 = 0x20; 225 | private const ubyte PIC2 = 0xA0; 226 | private const ubyte PIC1_COMMAND = PIC1; 227 | private const ubyte PIC1_DATA = (PIC1 + 1); 228 | private const ubyte PIC2_COMMAND = PIC2; 229 | private const ubyte PIC2_DATA = (PIC2 + 1); 230 | 231 | /// ICW4 (not) needed 232 | private const ubyte ICW1_ICW4 = 0x01; 233 | /// Single (cascade) mode 234 | private const ubyte ICW1_SINGLE = 0x02; 235 | /// Call address interval 4 (8) 236 | private const ubyte ICW1_INTERVAL4 = 0x04; 237 | /// Level triggered (edge) mode 238 | private const ubyte ICW1_LEVEL = 0x08; 239 | /// Initialization - required! 240 | private const ubyte ICW1_INIT = 0x10; 241 | /// 8086/88 (MCS-80/85) mode 242 | private const ubyte ICW4_8086 = 0x01; 243 | /// Auto (normal) EOI 244 | private const ubyte ICW4_AUTO = 0x02; 245 | /// Buffered mode/slave 246 | private const ubyte ICW4_BUF_SLAVE = 0x08; 247 | /// Buffered mode/master 248 | private const ubyte ICW4_BUF_MASTER = 0x0C; 249 | /// Special fully nested (not) 250 | private const ubyte ICW4_SFNM = 0x10; 251 | 252 | /// 253 | void remap(ubyte offset1, ubyte offset2) { 254 | 255 | outp(PIC1_COMMAND, ICW1_INIT | ICW1_ICW4); // starts the initialization sequence (in cascade mode) 256 | outp(PIC2_COMMAND, ICW1_INIT | ICW1_ICW4); 257 | outp(PIC1_DATA, offset1); // ICW2: Master PIC vector offset 258 | outp(PIC2_DATA, offset2); // ICW2: Slave PIC vector offset 259 | outp(PIC1_DATA, 4); // ICW3: tell Master PIC that there is a slave PIC at IRQ2 (0000 0100) 260 | outp(PIC2_DATA, 2); // ICW3: tell Slave PIC its cascade identity (0000 0010) 261 | 262 | outp(PIC1_DATA, ICW4_8086); 263 | outp(PIC2_DATA, ICW4_8086); 264 | 265 | outp(PIC1_DATA, cast(ubyte)~0b0000_0001); 266 | outp(PIC2_DATA, cast(ubyte)~0b0000_0000); 267 | 268 | outp(0x43, 0x34); 269 | outp(0x40, cast(ubyte)(300 & 0xff)); 270 | outp(0x40, cast(ubyte)(300 >> 16)); 271 | 272 | } 273 | -------------------------------------------------------------------------------- /source/kernel/loafencode.d: -------------------------------------------------------------------------------- 1 | module kernel.loafencode; 2 | 3 | import kernel.io; 4 | import kernel.mm; 5 | import std.traits; 6 | 7 | private template isEnum(alias symb) { 8 | static if (is(symb == enum)) 9 | enum bool isEnum = true; 10 | else 11 | enum bool isEnum = false; 12 | } 13 | 14 | /// Loaf encoder 15 | void encode(T)(ref T v, ref byte[] data) { 16 | 17 | static if (isIntegral!(T)) { 18 | static if (isSigned!(T)) { 19 | if (v < 0) { 20 | push(data, 1); 21 | v = -v; 22 | } else { 23 | push(data, 0); 24 | } 25 | } 26 | while (v > 0) { 27 | byte nibble = cast(byte)(v & 0xff); 28 | // encoding: 0x01 29 | if (nibble == 0 || nibble == 1) { 30 | push(data, 1); 31 | } 32 | push(data, nibble); 33 | v = v >> 8; 34 | } 35 | push(data, 0); 36 | } else static if(isEnum!(T)) { 37 | encode(cast(ulong)v, data); 38 | } else { 39 | static foreach (member; __traits(allMembers, T)) { 40 | { 41 | static if (__traits(compiles, encode(__traits(getMember, v, member), data))) { 42 | encode(__traits(getMember, v, member), data); 43 | } else { 44 | static assert(0, "Can't print " ~ T.stringof ~ "." ~ member); 45 | } 46 | } 47 | } 48 | } 49 | 50 | } 51 | 52 | void decode(T)(ref T v, ref byte[] data) { 53 | ulong pos; 54 | decode(v, pos, data); 55 | } 56 | /// Loaf decoder 57 | void decode(T)(ref T v, ref ulong pos, ref byte[] data) { 58 | static if (isIntegral!(T)) { 59 | bool sign = false; 60 | static if (isSigned!(T)) { 61 | sign = data[pos++] == 1; 62 | } 63 | ulong val = 0; 64 | ulong bits = 0; 65 | while (data[pos] != 0) { 66 | byte nibble = data[pos++]; 67 | if (nibble == 1) { 68 | nibble = data[pos++]; 69 | } else if (nibble == 0) { 70 | break; 71 | } 72 | val >>= 8; 73 | val |= (cast(ulong)nibble) << 64 - 8; 74 | bits += 8; 75 | } 76 | val >>= 64 - bits; 77 | pos++; 78 | static if (isSigned!(T)) { 79 | if (sign) { 80 | v = -cast(T)val; 81 | return; 82 | } 83 | } 84 | v = cast(T)val; 85 | return; 86 | } else static if (isEnum!(T)) { 87 | static assert(0); 88 | } else { 89 | static foreach (member; __traits(allMembers, T)) { 90 | { 91 | decode(__traits(getMember, v, member), pos, data); 92 | } 93 | } 94 | } 95 | } -------------------------------------------------------------------------------- /source/kernel/main.d: -------------------------------------------------------------------------------- 1 | module kernel.main; 2 | 3 | import core.bitop; 4 | import core.volatile; 5 | import kernel.io; 6 | import kernel.mm; 7 | import kernel.elf; 8 | import kernel.irq; 9 | import kernel.cpio; 10 | import kernel.pmap; 11 | import kernel.port; 12 | import kernel.task; 13 | import kernel.rtti; 14 | import kernel.util; 15 | import kernel.refptr; 16 | import kernel.guards; 17 | import kernel.stivale; 18 | import kernel.platform; 19 | import kernel.optional; 20 | import kernel.ports.kbootstrap; 21 | 22 | unittest { 23 | 24 | } 25 | 26 | extern (C) private void fgdt(); 27 | 28 | private __gshared ulong test_global = 1; 29 | 30 | private extern (C) void load_tss(); 31 | private extern (C) ulong gettss(); 32 | private extern (C) ulong* gettssgdt(); 33 | private extern (C) ulong gettssgdtid(); 34 | 35 | private void test2() { 36 | __gshared ulong[8192] stack1; 37 | __gshared ulong[8192] stack2; 38 | task_create((void* eh) { 39 | asm { 40 | sti; 41 | } 42 | while (true) { 43 | printk("T1 {hex}", flags); 44 | } 45 | }, cast(void*) 0, (cast(void*) stack1) + stack1 46 | .sizeof); 47 | task_create((void* eh) { 48 | asm { 49 | sti; 50 | } 51 | while (true) { 52 | printk("T2 {hex}", flags); 53 | } 54 | }, cast(void*) 0, (cast(void*) stack2) + stack2 55 | .sizeof); 56 | printk("HEY 1!"); 57 | } 58 | 59 | private void test1(void* a) { 60 | // version (DiodeNoDebug) { 61 | // return; 62 | // } 63 | // printk("test1: we got this pointer: {}", a); 64 | // printk("test1: from RTTI i know it's of type {}", dynamic_typeinfo(a).name); 65 | // printk("test1: and it's HeapBlock is {}", mmGetHeapBlock(a)); 66 | 67 | // Option!(uint*) maybe_uint = dynamic_cast!(uint)(a); 68 | // Option!(ulong*) maybe_ulong = dynamic_cast!(ulong)(a); 69 | // if (maybe_uint.is_some()) { 70 | // printk("test1: as a uint, it's {}", *maybe_uint.unwrap()); 71 | // } 72 | // if (maybe_ulong.is_some()) { 73 | // printk("test1: as a ulong, it's {}", *maybe_ulong.unwrap()); 74 | // } 75 | } 76 | 77 | private ulong bits(ulong shiftup, ulong shiftdown, ulong mask, ulong val) { 78 | return ((val >> (shiftdown - mask)) & ((1 << mask) - 1)) << shiftup; 79 | } 80 | 81 | pragma(mangle, "_start") private extern (C) void kmain(StivaleHeader* info) { 82 | asm { 83 | mov RBX, 0; 84 | mov RAX, 0xdeadbeefdeadbeef; 85 | mov [RBX], RAX; 86 | } 87 | 88 | fgdt(); 89 | 90 | printk("Thank {} for blessing our ~flight~ operating system", info.brand); 91 | TagModules* m; 92 | TagRSDP* rsdptag; 93 | foreach (Tag* t; PtrTransformIter!Tag(info.tag0, function Tag* (Tag* e) { 94 | return e.next; 95 | })) { 96 | if (t.ident.inner == 0x968609d7af96b845) { 97 | printk(" - Display EDID"); 98 | } 99 | if (t.ident.inner == 0xe5e76a1b4597a781) { 100 | printk(" - Kernel Command Line: {:?}", (cast(TagCommandLine*) t).cmdline); 101 | } 102 | if (t.ident.inner == 0x2187f79e8612de07) { 103 | printk(" - Memory Map"); 104 | TagMemoryMap* mmap = cast(TagMemoryMap*) t; 105 | int i = 0; 106 | foreach (MemoryMapEntry ent; mmap.entries) { 107 | if (i++ >= mmap.entcount) 108 | break; 109 | if (ent.type != 1) 110 | continue; 111 | ulong start = ent.base; 112 | const ulong end = ent.size + start; 113 | if (start == 0) { 114 | start = 4096; 115 | } 116 | printk(DEBUG, " [{ptr}; {ptr}]", start, end); 117 | import kernel.mm : addpage; 118 | 119 | addpage(cast(ulong) start, cast(ulong)((end - start) / 4096), true); 120 | } 121 | } 122 | if (t.ident.inner == 0x506461d2950408fa) 123 | printk(" - Framebuffer"); 124 | if (t.ident.inner == 0x6bc1a78ebe871172) 125 | printk(" - FB MTRR"); 126 | if (t.ident.inner == 0x4b6fe466aade04ce) { 127 | printk(" - Modules"); 128 | m = cast(TagModules*) t; 129 | } 130 | if (t.ident.inner == 0x9e1786930a375e78) { 131 | printk(" - RSDP"); 132 | rsdptag = cast(TagRSDP*)t; 133 | } 134 | if (t.ident.inner == 0x566a7bed888e1407) 135 | printk(" - The Unix Epoch"); 136 | if (t.ident.inner == 0x359d837855e3858c) 137 | printk(" - Firmware Info"); 138 | if (t.ident.inner == 0x34d1d96339647025) 139 | printk(" - Symmetric Multiprocessing Information"); 140 | if (t.ident.inner == 0x29d1e96239247032) 141 | printk(" - PXE Boot Server Information"); 142 | } 143 | 144 | assert(m); 145 | printk(DEBUG, "Modules: "); 146 | CPIOFile banner, dkm, init; 147 | { 148 | Module mod = m.modules[0]; 149 | ubyte[] moddata = array(mod.begin, cast(ulong)(mod.end - mod.begin)); 150 | printk(DEBUG, " - {} ({hex} bytes)", mod.name, moddata.length); 151 | CPIOFile[] f = parse_cpio(moddata); 152 | if (try_find(banner, "./banner.txt", f)) { 153 | printk("\n{}", transmute!(ubyte[], string)(banner.data)); 154 | } 155 | if (try_find(dkm, "./symbol.map", f)) { 156 | import kernel.symbols; 157 | printk("Enabling symbolification..."); 158 | load(cast(byte*)dkm.data.ptr); 159 | } 160 | find(init, "./init.elf", f); 161 | } 162 | 163 | paging_fixups(); 164 | 165 | IDTR idtr = new_idtr(); 166 | asm { 167 | lea RAX, idtr; 168 | lidt [RAX]; 169 | } 170 | 171 | { 172 | import kernel.acpi.rsdp : load_rsdp; 173 | import kernel.pcie.mcfg : parse_mcfg, scan_pci; 174 | load_rsdp(rsdptag.rsdp); 175 | parse_mcfg(); 176 | // scan_pci(); 177 | } 178 | 179 | ensure_task_init(); 180 | remap(0x20, 0x28); 181 | 182 | ulong* a = alloc!ulong(); 183 | *a = 1234; 184 | test1(cast(void*) a); 185 | free!ulong(a); 186 | 187 | uint* b = alloc!uint(); 188 | *b = 5678; 189 | test1(cast(void*) b); 190 | free!uint(b); 191 | 192 | const ulong ptr = gettss; 193 | 194 | gettssgdt[0] = bits(16, 24, 24, ptr) | bits(56, 32, 8, ptr) | (103 & 0xff) | ( 195 | 0b1001UL << 40) | (1UL << 47); 196 | gettssgdt[1] = ptr >> 32; 197 | assert(gettssgdtid == 0x28, "Unable to load it in"); 198 | fgdt(); 199 | load_tss(); 200 | 201 | ist1 = alloc_stack(); 202 | uint outp; 203 | asm { 204 | mov EAX, 7; 205 | mov ECX, 0; 206 | cpuid; 207 | shr EBX, 20; 208 | and EBX, 1; 209 | mov outp, EBX; 210 | } 211 | printk(DEBUG, "cpuid leaf (7, 0)[20] = {hex}", outp); 212 | if (outp & 1) { 213 | asm { 214 | mov RAX, CR4; 215 | or RAX, 3 << 20; 216 | mov CR4, RAX; 217 | } 218 | printk(DEBUG, "SMAP/SMEP is enabled!"); 219 | } else { 220 | printk(WARN, "SMAP is not available on your machine"); 221 | } 222 | 223 | 224 | __gshared ulong[8192] stack1; 225 | task_create((CPIOFile* init) { 226 | asm { 227 | cli; 228 | } 229 | printk(DEBUG, "rsp0: {}", rsp0); 230 | ulong rip = 0; 231 | bool ok = load(rip, *init); 232 | assert(ok, "Init failed to load!"); 233 | 234 | cur_t.ports.insertElem(0, AnyPort(create_bootstrap())); 235 | 236 | user_branch(cast(ulong) rip, cast(void*) 0); 237 | 238 | }, alloc!(CPIOFile)(init), (cast(void*) stack1) + stack1 239 | .sizeof); 240 | 241 | // Port* p = alloc!(Port)(); 242 | // { 243 | // PortRights r = PortRights(p, PortRightsKind.RECV); 244 | // PortRights s = PortRights(p, PortRightsKind.SEND); 245 | // byte[3] arra = [0, 1, 2]; 246 | // byte[] outpt; 247 | // printk("result: {}", s.send(0, arra)); 248 | // printk("result: {}", r.recv(outpt)); 249 | // printk("out: {}", outpt); 250 | // } 251 | 252 | // jmp $ 253 | // mov di, 1 66bf0100 254 | // mov rsi, 0x4f0000040 48be400000f004000000 255 | // syscall 0f 05 256 | // jmp $ eb fe 257 | 258 | for (;;) { 259 | // the kernel idle task comes here 260 | sched_yield(); 261 | } 262 | } 263 | 264 | unittest { 265 | catch_assert(() { 266 | static foreach (u; __traits(getUnitTests, kernel.autoinit)) 267 | u(); 268 | return 0; 269 | }); 270 | catch_assert(() { 271 | static foreach (u; __traits(getUnitTests, kernel.irq)) 272 | u(); 273 | return 0; 274 | }); 275 | catch_assert(() { 276 | static foreach (u; __traits(getUnitTests, kernel.io)) 277 | u(); 278 | return 0; 279 | }); 280 | catch_assert(() { 281 | static foreach (u; __traits(getUnitTests, kernel.mm)) 282 | u(); 283 | return 0; 284 | }); 285 | catch_assert(() { 286 | static foreach (u; __traits(getUnitTests, kernel.pmap)) 287 | u(); 288 | return 0; 289 | }); 290 | catch_assert(() { 291 | static foreach (u; __traits(getUnitTests, kernel.platform)) 292 | u(); 293 | return 0; 294 | }); 295 | catch_assert(() { 296 | static foreach (u; __traits(getUnitTests, kernel.optional)) 297 | u(); 298 | return 0; 299 | }); 300 | catch_assert(() { 301 | static foreach (u; __traits(getUnitTests, kernel.util)) 302 | u(); 303 | return 0; 304 | }); 305 | } 306 | -------------------------------------------------------------------------------- /source/kernel/mm.d: -------------------------------------------------------------------------------- 1 | module kernel.mm; 2 | 3 | import kernel.io; 4 | import kernel.task; 5 | import kernel.rtti; 6 | import kernel.autoinit; 7 | import kernel.optional; 8 | import std.conv : emplace; 9 | import kernel.util : memset; 10 | import kernel.pmap : Phys, get_pte_ptr; 11 | import kernel.platform : rdweakrandom, rdrandom; 12 | 13 | private extern (C) struct MPageHeader { 14 | align(4) MPageHeader* next; 15 | ulong pagecount; 16 | } 17 | 18 | private __gshared MPageHeader* first = cast(MPageHeader*) 0; 19 | 20 | T[] array(T)(T* ptr, ulong len) { 21 | ulong[2] data; 22 | data[0] = len; 23 | data[1] = cast(ulong)ptr; 24 | return transmute!(ulong[2], T[])(data); 25 | } 26 | 27 | /// Allocate a phys 28 | Phys phys() { 29 | return Phys(cast(ulong) page()); 30 | } 31 | 32 | private __gshared ulong used = 0; 33 | private __gshared ulong total = 0; 34 | 35 | /// Out of memory! 36 | void oom_cond() { 37 | printk(FATAL, "OOM condition!"); 38 | printk(FATAL, " Kernel heap: {hex}/{hex} bytes used", heap_usage, heap_max); 39 | printk(FATAL, " Page frame allocator: {hex}/{hex} pages used", used, total); 40 | assert(false, "OOM"); 41 | } 42 | 43 | /// Allocate a page 44 | void* page() { 45 | if (first == cast(MPageHeader*) 0) 46 | oom_cond(); 47 | if (first.pagecount == 1) { 48 | void* result = cast(void*) first; 49 | first = first.next; 50 | return result; 51 | } 52 | first.pagecount -= 1; 53 | const ulong pc = first.pagecount; 54 | MPageHeader* n = first.next; 55 | void* data = cast(void*)(first); 56 | first = cast(MPageHeader*)(4096 + cast(ulong) first); 57 | first.next = &*n; 58 | first.pagecount = pc; 59 | used += 1; 60 | memset(cast(byte*) data, 0, 4096); 61 | return data; 62 | } 63 | 64 | /// Add a bunch of pages to the memory manager 65 | void addpage(ulong addr, ulong pagecount, bool isinital = false) { 66 | MPageHeader* next = first; 67 | first = cast(MPageHeader*) addr; 68 | import kernel.io : printk; 69 | 70 | first.next = next; 71 | first.pagecount = pagecount; 72 | if (isinital) { 73 | total += pagecount; 74 | } else { 75 | import kernel.util; 76 | 77 | used -= pagecount; 78 | memset16(cast(ushort*)first, 0xfeeb, pagecount * 2048); 79 | // hmmm 80 | first.next = next; 81 | first.pagecount = pagecount; 82 | } 83 | } 84 | 85 | private struct MMRefValue(T) { 86 | T value; 87 | ulong refcount = 1; 88 | } 89 | 90 | // Our allocator! 91 | // The algorithm is called RFAlloc, by me 92 | // It's essencialy an RNG that ensures resources get used nicely. 93 | // Each page committed lets us use a bit of RAM. 94 | private struct RFAllocPageState { 95 | private union { 96 | struct { 97 | byte[2048] bitmapA; 98 | byte[2048] bitmapB; 99 | } 100 | 101 | RFAllocPageState*[2] childs; 102 | } 103 | 104 | bool get_bitmap_offset_at(ulong offset, ulong bitmap_final_offset, ulong depth) { 105 | if (depth == 0) { 106 | return !!(bitmapA[bitmap_final_offset >> 3] & (1 << (bitmap_final_offset & 0x7))); 107 | } 108 | RFAllocPageState* target = childs[offset & 1]; 109 | if (target == cast(RFAllocPageState*) 0) { 110 | // This is a used-map 111 | // if it doesn't even own an RFAllocPageState 112 | // (literally the first step in getting a malloc from a region), 113 | // it's unused! 114 | return false; 115 | } 116 | return target.get_bitmap_offset_at(offset >> 1, bitmap_final_offset, depth - 1); 117 | } 118 | 119 | bool get_bitmap_offset_at_slotb(ulong offset, ulong bitmap_final_offset, ulong depth) { 120 | if (depth == 0) { 121 | return !!(bitmapB[bitmap_final_offset >> 3] & (1 << (bitmap_final_offset & 0x7))); 122 | } 123 | RFAllocPageState* target = childs[offset & 1]; 124 | if (target == cast(RFAllocPageState*) 0) { 125 | // This is a used-map 126 | // if it doesn't even own an RFAllocPageState 127 | // (literally the first step in getting a malloc from a region), 128 | // it's unused! 129 | return false; 130 | } 131 | return target.get_bitmap_offset_at(offset >> 1, bitmap_final_offset, depth - 1); 132 | } 133 | 134 | void set_bitmap_offset_at(ulong offset, ulong bitmap_final_offset, ulong depth, bool value) { 135 | if (depth == 0) { 136 | bitmapA[bitmap_final_offset >> 3] &= ~(1 << (bitmap_final_offset & 0x7)); 137 | bitmapA[bitmap_final_offset >> 3] |= ((cast(ulong) value) << (bitmap_final_offset & 0x7)); 138 | return; 139 | } 140 | RFAllocPageState* target = childs[offset & 1]; 141 | if (target == cast(RFAllocPageState*) 0) { 142 | target = childs[offset & 1] = create(); 143 | } 144 | target.set_bitmap_offset_at(offset >> 1, bitmap_final_offset, depth - 1, value); 145 | } 146 | 147 | void set_bitmap_offset_at_slotb(ulong offset, ulong bitmap_final_offset, ulong depth, bool value) { 148 | if (depth == 0) { 149 | bitmapB[bitmap_final_offset >> 3] &= ~(1 << (bitmap_final_offset & 0x7)); 150 | bitmapB[bitmap_final_offset >> 3] |= ((cast(ulong) value) << (bitmap_final_offset & 0x7)); 151 | return; 152 | } 153 | RFAllocPageState* target = childs[offset & 1]; 154 | if (target == cast(RFAllocPageState*) 0) { 155 | target = childs[offset & 1] = create(); 156 | } 157 | target.set_bitmap_offset_at(offset >> 1, bitmap_final_offset, depth - 1, value); 158 | } 159 | 160 | static RFAllocPageState* create() { 161 | import kernel.util : memset; 162 | 163 | RFAllocPageState* el = cast(RFAllocPageState*) page(); 164 | heap_max += 4096; 165 | memset(el.bitmapA.ptr, 0, 2048); 166 | memset(el.bitmapB.ptr, 0, 2048); 167 | el.childs[0] = cast(RFAllocPageState*) 0; 168 | el.childs[1] = cast(RFAllocPageState*) 0; 169 | 170 | return el; 171 | } 172 | } 173 | 174 | private __gshared AutoInit!(RFAllocPageState*) aps = AutoInit!(RFAllocPageState*)((() { 175 | return RFAllocPageState.create(); 176 | })); 177 | 178 | private __gshared ulong apsdims = 0; 179 | 180 | private __gshared ulong poolsize = 0; 181 | private __gshared ulong stackbase = 0xffff_8100_0000_0000; 182 | private const ulong poolbase = 0xffff_800f_f000_0000; 183 | private const ulong MM_ATTEMPTS_RFALLOC = 3000; 184 | 185 | /// Allocate a userland virtual address 186 | void* alloc_user_virt() { 187 | return cast(void*)(rdrandom() & 0x000_0fff_ffff_0000); 188 | } 189 | 190 | /// Allocate a stack 191 | void* alloc_stack(t* cur = cur_t) { 192 | stackbase += 0x2000; 193 | ulong base = stackbase; 194 | stackbase += 0x5000 + 0x4000; 195 | foreach (i; 0..8) { 196 | void* page = page(); 197 | ulong* a = get_pte_ptr(cast(void*)(base + (i << 12))).unwrap(); 198 | *a = 3 | cast(ulong)page; 199 | } 200 | return cast(void*)(base + 0x8000); 201 | } 202 | 203 | /// Free a stack 204 | void free_stack(void* a) { 205 | 206 | } 207 | 208 | private void increase_apsdims() { 209 | apsdims += 1; 210 | RFAllocPageState** apsref = aps.val(); 211 | RFAllocPageState* apsold = *apsref; 212 | *apsref = RFAllocPageState.create(); 213 | // this reborrow is awkward but makes dscanner shut up so :shrug: 214 | (*apsref).childs[0] = &*apsold; 215 | } 216 | 217 | private void commit_to_pool() { 218 | ulong* a = get_pte_ptr(cast(void*) poolbase + poolsize).unwrap(); 219 | *a = 3 | cast(ulong) page(); 220 | poolsize += 4096; 221 | heap_max += 4096; 222 | if ((1 << (apsdims + 16)) == poolsize) { 223 | increase_apsdims(); 224 | } 225 | } 226 | 227 | /// HeapBlock is a header of a heap-allocated object. 228 | /// It is located 16 bytes _before_ the pointer returned by mmIsHeap. 229 | extern (C) struct HeapBlock { 230 | /// The type of this object 231 | kernel.rtti.TypeInfo* typeinfo; 232 | /// The size of this object 233 | ulong size; 234 | 235 | /// Print it nicely 236 | void _prnt_value(string subarray, int prenest) { 237 | putsk("HeapBlock { "); 238 | typeinfo.print(cast(void*)((cast(ulong)&this) + 16), subarray, prenest, true); 239 | putsk(" }"); 240 | } 241 | } 242 | 243 | static assert(HeapBlock.sizeof == 16); 244 | 245 | /// How much bytes of the kernel heap are in use 246 | __gshared ulong heap_usage = 0; 247 | /// How much bytes of the kernel heap are committed 248 | __gshared ulong heap_max = 0; 249 | private __gshared ulong spillbits = 0; 250 | 251 | /// is `arg` on the heap? If yes, tells you where it starts. 252 | Option!(void*) mmIsHeap(void* a) { 253 | alias O = Option!(void*); 254 | ulong value = cast(ulong) a - poolbase; 255 | if (cast(ulong) a < poolbase) { 256 | return O(); 257 | } 258 | if (value > poolsize) { 259 | return O(); 260 | } 261 | ulong vs4 = (value >> 4) - 1; 262 | debug assert((*aps.val()).get_bitmap_offset_at(vs4 >> 14, vs4 & 0x3fff, 263 | apsdims), "Kernel: dangling pointer passed to `mmIsHeap`"); 264 | ulong nego = 0; 265 | vs4 += 1; 266 | if (!(*aps.val()).get_bitmap_offset_at(vs4 >> 14, vs4 & 0x3fff, 267 | apsdims)) { 268 | return O(a); 269 | } 270 | while (true) { 271 | debug assert((*aps.val()).get_bitmap_offset_at((vs4 - nego) >> 14, 272 | (vs4 - nego) & 0x3fff, apsdims)); 273 | if ((*aps.val()).get_bitmap_offset_at_slotb((vs4 - nego) >> 14, 274 | (vs4 - nego) & 0x3fff, apsdims)) 275 | return O(poolbase + 16 + cast(void*)(value - (nego << 4))); 276 | nego++; 277 | } 278 | } 279 | 280 | /// is `arg` on the heap? If yes, tells you where its corresponding HeapBlock is placed. 281 | Option!(HeapBlock*) mmGetHeapBlock(void* a) { 282 | return mmIsHeap(a).map!(HeapBlock*)((void* a) { 283 | return cast(HeapBlock*)(cast(ulong) a - 16); 284 | }); 285 | } 286 | 287 | private void* kalloc(ulong size) { 288 | size = (size + 15) & 0xffff_ffff_ffff_fff0; 289 | if (poolsize == 0) { 290 | commit_to_pool(); 291 | } 292 | if (size > 4096) { 293 | size = (size + 4095) / 4096; 294 | stackbase += 0x1000; 295 | ulong region = stackbase; 296 | foreach (i; 0..size) { 297 | stackbase += 0x1000; 298 | void* page = page(); 299 | ulong* a = get_pte_ptr(cast(void*)(region + (i << 12))).unwrap(); 300 | *a = 3 | cast(ulong)page; 301 | } 302 | return cast(void*)region; 303 | // stackbase 304 | } 305 | const ulong ss = size >> 4; 306 | while (true) { 307 | for (int i = 0; i < MM_ATTEMPTS_RFALLOC; i++) { 308 | const ulong rand = (rdweakrandom() % (poolsize - size)) & ~0xf; 309 | bool success = true; 310 | for (ulong j = 0; j < ss; j++) { 311 | const ulong v = j + (rand >> 4); 312 | const bool hit = (*aps.val()).get_bitmap_offset_at(v >> 14, v & 0x3fff, apsdims); 313 | if (hit) { 314 | success = false; 315 | break; 316 | } 317 | } 318 | if (!success) 319 | continue; 320 | for (ulong j = 0; j < ss; j++) { 321 | const ulong v = j + (rand >> 4); 322 | (*aps.val()).set_bitmap_offset_at(v >> 14, v & 0x3fff, apsdims, true); 323 | } 324 | (*aps.val()).set_bitmap_offset_at_slotb(rand >> 18, (rand >> 4) & 0x3fff, apsdims, true); 325 | heap_usage += size; 326 | heap_usage += ss / 8; 327 | spillbits += ss % 8; 328 | return cast(void*)(rand + poolbase); 329 | } 330 | commit_to_pool(); 331 | } 332 | assert(false); 333 | } 334 | 335 | private void kfree(void* value, ulong size) { 336 | if (cast(ulong)value < poolbase) { 337 | return; 338 | } 339 | const ulong addr = cast(ulong) value - poolbase; 340 | size = (size + 15) & 0xffff_ffff_ffff_fff0; 341 | const ulong ss = size >> 4; 342 | for (ulong j = 0; j < ss; j++) { 343 | const ulong v = j + (addr >> 4); 344 | (*aps.val()).set_bitmap_offset_at(v >> 14, v & 0x3fff, apsdims, false); 345 | } 346 | (*aps.val()).set_bitmap_offset_at_slotb((addr >> 4) >> 14, (addr >> 4) & 0x3fff, apsdims, false); 347 | heap_usage -= ss / 8; 348 | heap_usage -= size; 349 | spillbits -= ss % 8; 350 | while (spillbits < 0) { 351 | spillbits += 8; 352 | heap_usage -= 1; 353 | } 354 | } 355 | 356 | /// Allocate some stuff 357 | T* alloc(T, Args...)(Args arg) { 358 | const ulong size = (T.sizeof + 15) & 0xffff_ffff_ffff_fff0; 359 | HeapBlock* hblk = cast(HeapBlock*) kalloc(size + 16); 360 | hblk.typeinfo = typeinfo!T(); 361 | hblk.size = size; 362 | T* a = cast(T*)(16 + cast(ulong) hblk); 363 | emplace!(T)(a, arg); 364 | return a; 365 | } 366 | 367 | /// Type structure 1337 hax. Wildly unsafe. 368 | U transmute(T, U)(T a) { 369 | struct L { 370 | T v; 371 | } 372 | 373 | struct R { 374 | U v; 375 | } 376 | 377 | L l; 378 | l.v = a; 379 | return (cast(R*)&l).v; 380 | } 381 | 382 | private ulong storage_for_arr(T)(ulong n) { 383 | return ((T.sizeof * n) + 15) & 0xffff_ffff_ffff_fff0; 384 | } 385 | 386 | /// Unsafe alloca, needs a memcpy 387 | T[] alloca_unsafe(T)(ulong n) { 388 | const ulong size = storage_for_arr!T(n); 389 | HeapBlock* hblk = cast(HeapBlock*) kalloc(size + 16); 390 | hblk.typeinfo = typeinfo!(T[])(); 391 | hblk.size = size; 392 | memset(16 + cast(byte*) hblk, 0, size); 393 | T* hh = cast(T*)(16 + cast(ulong) hblk); 394 | struct fake_t { 395 | size_t length; 396 | T* ptr; 397 | } 398 | 399 | fake_t fake; 400 | fake.length = n; 401 | fake.ptr = hh; 402 | return transmute!(fake_t, T[])(fake); 403 | } 404 | 405 | /// Allocate a dynamically-sized array of 0 elements 406 | T[] alloca(T)() { 407 | return alloca_unsafe!T(0); 408 | } 409 | 410 | /// Allocate a dynamically-sized array of `n` elements 411 | T[] alloca(T, Args...)(ulong n, Args args) { 412 | T[] oa = alloca_unsafe!T(n); 413 | foreach (i; 0 .. n) { 414 | emplace(&oa[i], args); 415 | } 416 | return oa; 417 | } 418 | 419 | private T max(T)(T a, T b) { 420 | if (a < b) 421 | return b; 422 | return a; 423 | } 424 | 425 | /// Allocate a dynamically-sized array of `n` elements 426 | void push(T)(ref T[] arr, T e) { 427 | T[] newa = arr; 428 | bool skipalloc = false; 429 | if (mmIsHeap(cast(void*) arr.ptr).is_some()) { 430 | HeapBlock* hb = mmGetHeapBlock(cast(void*) arr.ptr).unwrap(); 431 | if (hb.size >= storage_for_arr!(T)(arr.length + 1)) { 432 | skipalloc = true; 433 | } 434 | } 435 | ulong oldlen = arr.length; 436 | if (!skipalloc) { 437 | newa = alloca_unsafe!T(max(arr.length * 2, arr.length + 1)); 438 | foreach (i; 0 .. arr.length) { 439 | emplace(&newa[i], arr[i]); 440 | } 441 | free(arr); 442 | } 443 | // HACK: manipulating the internal repr of an array is questionable at best 444 | (cast(ulong*)&arr)[0] = oldlen + 1; 445 | (cast(ulong*)&arr)[1] = cast(ulong) newa.ptr; 446 | emplace(&arr[oldlen], e); 447 | } 448 | 449 | /// Allocate a dynamically-sized array of `n` elements 450 | T[] realloca(T)(T[] old, ulong n) { 451 | // TODO: expand fast path 452 | 453 | T[] newa = alloca_unsafe!T(n); 454 | foreach (i; 0 .. n) { 455 | if (i < old.length) { 456 | emplace(&newa[i], old[i]); 457 | } else { 458 | emplace(&newa[i]); 459 | } 460 | } 461 | free(old); 462 | return newa; 463 | } 464 | 465 | /// Free memory 466 | void free(T)(T* value) { 467 | HeapBlock* allocbase = cast(HeapBlock*)((cast(ulong) value) - 16); 468 | kfree(cast(void*) allocbase, allocbase.size + 16); 469 | } 470 | 471 | /// Free memory 472 | void free(T)(T[] value) { 473 | if (mmGetHeapBlock(cast(void*) value.ptr).is_some()) { 474 | HeapBlock* allocbase = cast(HeapBlock*)((cast(ulong) value.ptr) - 16); 475 | kfree(cast(void*) allocbase, allocbase.size + 16); 476 | } 477 | } 478 | -------------------------------------------------------------------------------- /source/kernel/optional.d: -------------------------------------------------------------------------------- 1 | module kernel.optional; 2 | 3 | import kernel.util : memcpy; 4 | import std.conv : emplace; 5 | 6 | unittest { 7 | import kernel.io : printk; 8 | 9 | printk("[optional] Create, uninited"); 10 | Option!int uninited = Option!(int)(); 11 | printk("[optional] is_{some,none} on `none`"); 12 | assert(uninited.is_none()); 13 | assert(!uninited.is_some()); 14 | 15 | printk("[optional] Create, inited"); 16 | Option!int inited = Option!int(3); 17 | printk("[optional] is_{some,none} on `Some(3)`"); 18 | assert(!inited.is_none()); 19 | assert(inited.is_some()); 20 | printk("[optional] unwrap is 3 on `Some(3)"); 21 | assert(3 == inited.unwrap()); 22 | printk("[optional]: Some(3): {} | None: {}", inited, uninited); 23 | } 24 | 25 | /// Is it something? Is it nothing? It's unclear. 26 | struct Option(T) { 27 | align(T.alignof) private char[T.sizeof] buf; 28 | private bool is_something = false; 29 | private bool __invariant = false; 30 | /// Is it something? 31 | bool is_some() { 32 | assert(__invariant); 33 | return is_something; 34 | } 35 | /// Is it nothing? 36 | bool is_none() { 37 | assert(__invariant); 38 | return !is_something; 39 | } 40 | /// Unwrap it 41 | T unwrap() { 42 | assert(__invariant); 43 | assert(is_some()); 44 | return *cast(T*) buf.ptr; 45 | } 46 | /// Something 47 | static Option!T opCall(ref T t) { 48 | Option!(T) e; 49 | e.is_something = true; 50 | memcpy(cast(byte*) e.buf.ptr, cast(byte*)&t, t.sizeof); 51 | emplace(cast(T*) e.buf.ptr, t); 52 | e.__invariant = true; 53 | return e; 54 | } 55 | /// Something 56 | static Option!T opCall(T t) { 57 | Option!(T) e; 58 | e.is_something = true; 59 | memcpy(cast(byte*) e.buf.ptr, cast(byte*)&t, t.sizeof); 60 | emplace(cast(T*) e.buf.ptr, t); 61 | e.__invariant = true; 62 | return e; 63 | } 64 | /// Nothing 65 | static Option!T opCall() { 66 | Option!(T) e; 67 | e.__invariant = true; 68 | e.is_something = false; 69 | return e; 70 | } 71 | 72 | /// Map a value (if it's `some`) 73 | Option!U map(U)(U function(T t) f) { 74 | if (is_something) { 75 | return Option!U(f(unwrap())); 76 | } 77 | return Option!U(); 78 | } 79 | 80 | /// Map a value (if it's `some`, allowing it to return an option) 81 | Option!U and_then(U)(Option!U function(T t) f) { 82 | if (is_something) { 83 | return f(unwrap()); 84 | } 85 | return Option!U(); 86 | } 87 | 88 | ~this() { 89 | assert(__invariant); 90 | if (is_something) { 91 | destroy(*cast(T*) buf.ptr); 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /source/kernel/pcie/mcfg.d: -------------------------------------------------------------------------------- 1 | module kernel.pcie.mcfg; 2 | 3 | import kernel.io; 4 | import kernel.mm; 5 | import kernel.acpi.rsdp; 6 | 7 | uint load_u32(uint a) { 8 | return load_u32(cast(ulong) a); 9 | } 10 | 11 | uint load_u32(ulong a) { 12 | return *cast(uint*) a; 13 | } 14 | 15 | ulong load_u64(uint a) { 16 | return load_u64(cast(ulong) a); 17 | } 18 | 19 | ulong load_u64(ulong a) { 20 | return *cast(ulong*) a; 21 | } 22 | 23 | extern (C) struct MCFGEntry { 24 | align(1) ulong base; 25 | align(1) ushort group; 26 | align(1) ubyte buslo; 27 | align(1) ubyte bushi; 28 | align(1) uint _; 29 | } 30 | 31 | __gshared ulong[256] busbase; 32 | 33 | void parse_mcfg() { 34 | ulong table = cast(ulong) find_table("MCFG").unwrap(); 35 | uint entc = (load_u32(table + 4) - 44) >> 4; 36 | MCFGEntry[] e = array(cast(MCFGEntry*)(table + 44), entc); 37 | foreach (MCFGEntry ent; e) { 38 | foreach (i; cast(int)(ent.buslo) .. (cast(int)(ent.bushi) + 1)) { 39 | busbase[i] = ent.base + ((i - ent.buslo) << 20); 40 | } 41 | } 42 | } 43 | 44 | ubyte pci_readbyte(ulong bus, ulong dev, ulong fn, ulong offset) { 45 | return *cast(byte*)(busbase[bus] + ((dev << 15) | (fn << 12)) | offset); 46 | } 47 | 48 | ushort pci_readshort(ulong bus, ulong dev, ulong fn, ulong offset) { 49 | return *cast(ushort*)(busbase[bus] + ((dev << 15) | (fn << 12)) | offset); 50 | } 51 | 52 | uint pci_readint(ulong bus, ulong dev, ulong fn, ulong offset) { 53 | return *cast(uint*)(busbase[bus] + ((dev << 15) | (fn << 12)) | offset); 54 | } 55 | 56 | ulong pci_readlong(ulong bus, ulong dev, ulong fn, ulong offset) { 57 | return *cast(ulong*)(busbase[bus] + ((dev << 15) | (fn << 12)) | offset); 58 | } 59 | 60 | enum Class { 61 | MSC = 1 62 | } 63 | 64 | enum MSCSubClass { 65 | SCSI = 0, 66 | IDE = 1, 67 | FDC = 2, 68 | IPI = 3, 69 | RAID = 4, 70 | ATA = 5, 71 | SATA = 6, 72 | SAS = 7, 73 | NVM = 8, 74 | } 75 | 76 | enum MSCSATAProgif { 77 | VENDOR = 0, 78 | AHCI = 1, 79 | SSB = 2 80 | } 81 | 82 | enum HeaderType { 83 | REGULAR_MULTIFUNCTION = 0x80, 84 | PCI_TO_PCI_MULTIFUNCTION = 0x81, 85 | PCI_TO_CBUS_MULTIFUNCTION = 0x82, 86 | REGULAR = 0x00, 87 | PCI_TO_PCI = 0x01, 88 | PCI_TO_CBUS = 0x02, 89 | } 90 | 91 | void scan_pci() { 92 | printk("PCI devices:"); 93 | foreach (bus; 0 .. 256) { 94 | foreach (device; 0 .. 32) { 95 | foreach (fn; 0 .. 8) { 96 | ushort vendor = pci_readshort(bus, device, fn, 0); 97 | if (vendor != 0xFFFF) { 98 | ushort devid = pci_readshort(bus, device, fn, 2); 99 | printk(" - {}.{}.{}: {hex}:{hex}", bus, device, fn, vendor, devid); 100 | // printk("BAR0: {hex}", pci_readint(bus, device, fn, 0x10)); 101 | // printk("BAR1: {hex}", pci_readint(bus, device, fn, 0x14)); 102 | // printk("BAR2: {hex}", pci_readint(bus, device, fn, 0x18)); 103 | // printk("BAR3: {hex}", pci_readint(bus, device, fn, 0x1C)); 104 | // printk("BAR4: {hex}", pci_readint(bus, device, fn, 0x20)); 105 | // printk("BAR5: {hex}", pci_readint(bus, device, fn, 0x24)); 106 | if (vendor == 0x1af4) { 107 | if (devid == 0x1001) { 108 | import kernel.pcie.virtio; 109 | 110 | assert(pci_readint(bus, device, fn, 0x20) & 4, "Invalid BAR"); 111 | ulong ptr = pci_readlong(bus, device, fn, 0x20) & ~0xf; 112 | init_virtio_blk(ptr); 113 | } else { 114 | printk("Unimplemented virtio device {}", device - 0xfff); 115 | } 116 | } 117 | } 118 | } 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /source/kernel/pcie/virtio.d: -------------------------------------------------------------------------------- 1 | module kernel.pcie.virtio; 2 | 3 | import core.volatile; 4 | import kernel.io; 5 | 6 | ulong load_u32(uint a) { 7 | return load_u32(cast(ulong) a); 8 | } 9 | 10 | ulong load_u32(ulong a) { 11 | return cast(ulong)volatileLoad(cast(uint*) a); 12 | } 13 | 14 | void store_u32(ulong a, uint v) { 15 | volatileStore(cast(uint*) a, v); 16 | } 17 | 18 | ulong load_u64(uint a) { 19 | return load_u64(cast(ulong) a); 20 | } 21 | 22 | ulong load_u64(ulong a) { 23 | return volatileLoad(cast(ulong*) a); 24 | } 25 | 26 | enum VirtIOPorts { 27 | MagicValue = 0x000, 28 | Version = 0x004, 29 | DeviceId = 0x008, 30 | VendorId = 0x00c, 31 | HostFeatures = 0x010, 32 | HostFeaturesSel = 0x014, 33 | GuestFeatures = 0x020, 34 | GuestFeaturesSel = 0x024, 35 | GuestPageSize = 0x028, 36 | QueueSel = 0x030, 37 | QueueNumMax = 0x034, 38 | QueueNum = 0x038, 39 | QueueAlign = 0x03c, 40 | QueuePfn = 0x040, 41 | QueueNotify = 0x050, 42 | InterruptStatus = 0x060, 43 | InterruptAck = 0x064, 44 | Status = 0x070, 45 | Config = 0x100, 46 | } 47 | 48 | void init_virtio_blk(ulong ptr) { 49 | printk("Initializing virtio-blk @ {ptr}", ptr); 50 | assert(load_u32(ptr + VirtIOPorts.MagicValue) != 0x74_72_69_76); 51 | assert(load_u32(ptr + 8) != 0); 52 | debug assert(load_u32(ptr + 8) == 1); 53 | store_u32(ptr + VirtIOPorts.Status, 0); 54 | store_u32(ptr + VirtIOPorts.Status, 1 << 0); 55 | printk("{hex}", load_u32(ptr + VirtIOPorts.Status)); 56 | store_u32(ptr + VirtIOPorts.Status, 1 << 0 | 1 << 3); 57 | printk("{hex}", load_u32(ptr + VirtIOPorts.Version)); 58 | printk("Features: {hex}", load_u32(ptr + VirtIOPorts.HostFeatures)); 59 | store_u32(ptr + VirtIOPorts.GuestFeatures, 0); 60 | store_u32(ptr + VirtIOPorts.Status, 1 << 1 | 1 << 4 | 1 << 8); 61 | printk("{hex}", load_u32(ptr + VirtIOPorts.Status)); 62 | assert(load_u32(ptr + VirtIOPorts.Status) & (1 << 8), "Guest can't support us!"); 63 | } 64 | -------------------------------------------------------------------------------- /source/kernel/platform.d: -------------------------------------------------------------------------------- 1 | module kernel.platform; 2 | 3 | import kernel.optional; 4 | import ldc.attributes; 5 | import kernel.symbols; 6 | 7 | 8 | /// Call a system call 9 | extern (C) void platform_sc(ulong sysno, void* data); 10 | /// Branch to userland 11 | extern (C) @safe void user_branch(ulong tgd, void* stack); 12 | /// Set Jump 13 | extern (C) @(llvmAttr("returns-twice")) 14 | ulong setjmp(jmpbuf* buf); 15 | /// Long Jump 16 | extern (C) @(llvmAttr("noreturn")) 17 | void longjmp(jmpbuf* buf, ulong value); 18 | private __gshared Option!(jmpbuf*) _catch_assert = Option!(jmpbuf*)(); 19 | /// manipulate SMAP enable 20 | pragma(mangle, "_stac") extern (C) void stac(); 21 | /// manipulate SMAP enable 22 | pragma(mangle, "_clac") extern (C) void clac(); 23 | 24 | /// A nothing 25 | struct nothing { 26 | } 27 | 28 | /// halt the CPU until an interrupt arrives 29 | void hlt() { 30 | asm { 31 | hlt; 32 | } 33 | } 34 | 35 | /// are interrupts on? 36 | bool intr() { 37 | ulong flags; 38 | asm { 39 | pushfq; 40 | pop RBX; 41 | mov flags, RBX; 42 | } 43 | return !!(flags & 0x200); 44 | } 45 | 46 | /// The I/O port mapped to QEMU's stdout 47 | public const DEBUG_IO_PORT_NUM = 0xe9; 48 | 49 | /// A stack frame 50 | extern (C) struct Stackframe { 51 | Stackframe* rbp; 52 | ulong rip; 53 | } 54 | /// Do stack unwinding 55 | void backtrace() { 56 | Stackframe* stk; 57 | asm { 58 | mov stk, RBP; 59 | } 60 | import kernel.io : printk, FATAL; 61 | 62 | printk(FATAL, "Stack trace:"); 63 | for (;;) { 64 | Symbol sym = symbolify(stk.rip); 65 | // Unwind to previous stack frame 66 | printk(FATAL, " {} + {}", sym.name, sym.off); 67 | if (stk.rbp == cast(Stackframe*) 0 || stk.rip == 0) 68 | break; 69 | if (stk.rip < 0xffffffff80000000) 70 | break; 71 | if ((cast(ulong) stk.rbp) < 0xfff8_0000_0000_0000) 72 | break; 73 | stk = stk.rbp; 74 | } 75 | } 76 | 77 | /// IA32_EFER 78 | public const uint IA32_EFER = 0xC0000080; 79 | 80 | /// IA32_EFER System Call Extensions 81 | public const uint IA32_EFER_SCE = 1 << 0; 82 | /// IA32_EFER Long Mode Enable 83 | public const uint IA32_EFER_LME = 1 << 8; 84 | /// IA32_EFER Long Mode Active 85 | public const uint IA32_EFER_LMA = 1 << 10; 86 | /// IA32_EFER No Execute Enable 87 | public const uint IA32_EFER_NXE = 1 << 11; 88 | 89 | /// 90 | public const uint IA32_STAR = 0xC0000081; 91 | /// 92 | public const uint IA32_LSTAR = 0xC0000082; 93 | /// 94 | public const uint IA32_SFMASK = 0xC0000084; 95 | 96 | extern (C) long d_syscall(ulong a, ulong b) { 97 | import kernel.syscall.dispatch; 98 | 99 | return syscall(a, b); 100 | } 101 | 102 | /// Read an MSR 103 | ulong rdmsr(uint msr) { 104 | ulong outp; 105 | asm { 106 | mov ECX, msr; 107 | rdmsr; 108 | shr RDX, 32; 109 | or RDX, RAX; 110 | mov outp, RDX; 111 | } 112 | return outp; 113 | } 114 | 115 | /// Write an MSR 116 | void wrmsr(uint msr, ulong value) { 117 | uint lo = cast(uint) value; 118 | uint hi = cast(uint)(value >> 32); 119 | asm { 120 | mov ECX, msr; 121 | mov EAX, lo; 122 | mov EDX, hi; 123 | wrmsr; 124 | } 125 | } 126 | 127 | /// Reload CS 128 | extern (C) void reload_cs(); 129 | private extern (C) ulong _rdrand(); 130 | private ulong _rdrand2() { 131 | ulong raw = 0; 132 | while (!raw) 133 | raw = _rdrand(); 134 | return raw; 135 | } 136 | /// Get a random number 137 | ulong rdrandom() { 138 | const ulong lo = _rdrand2(); 139 | const ulong hi = _rdrand2(); 140 | return (lo >> 32) | (hi & 0xffff_ffff_0000_0000); 141 | } 142 | 143 | private __gshared ulong seed = 0; 144 | private __gshared ulong bk2 = 0; 145 | private __gshared ulong step = 63; 146 | /// Get a (cryptographicaly weak) random number 147 | uint rdshortweakrandom() { 148 | step += 1; 149 | if (step == 64) { 150 | step = 0; 151 | seed ^= bk2 = rdrandom(); 152 | } 153 | seed ^= bk2 << 1; 154 | const ulong bk2lsb = bk2 >> 63; 155 | bk2 <<= 1; 156 | bk2 |= bk2lsb; 157 | return seed & 0xffff_ffff; 158 | } 159 | /// Get a (cryptographicaly shit) random number 160 | ulong rdweakrandom() { 161 | return (cast(ulong) rdshortweakrandom()) | ((cast(ulong) rdshortweakrandom()) << 32); 162 | } 163 | 164 | extern (C) private __gshared int kend; 165 | /// Get kernel end 166 | ulong get_kend() { 167 | return cast(ulong)&kend - 0xffffffff80000000; 168 | } 169 | 170 | /// Atomically add 171 | void atomic_add(ulong* target, ulong val) { 172 | asm { 173 | mov RAX, target; 174 | mov RBX, val; 175 | lock; 176 | add [RAX], RBX; 177 | } 178 | } 179 | 180 | /// Atomically sub 181 | void atomic_sub(ulong* target, ulong val) { 182 | asm { 183 | mov RAX, target; 184 | mov RBX, val; 185 | lock; 186 | sub [RAX], RBX; 187 | } 188 | } 189 | 190 | /// Atomically xchg 191 | ulong atomic_xchg(ulong* target, ulong val) { 192 | asm { 193 | mov RAX, target; 194 | mov RBX, val; 195 | lock; 196 | xchg [RAX], RBX; 197 | mov val, RBX; 198 | } 199 | return val; 200 | } 201 | 202 | /// A lock! 203 | struct lock { 204 | private ulong l = 0; 205 | /// Grab a lock 206 | void lock() { 207 | while (atomic_xchg(&this.l, 1)) { 208 | } 209 | } 210 | /// Unlock 211 | void unlock() { 212 | assert(atomic_xchg(&this.l, 0)); 213 | } 214 | } 215 | 216 | /// A jump buffer 217 | struct jmpbuf { 218 | private ulong rbx; 219 | private ulong rbp; 220 | private ulong r12; 221 | private ulong r13; 222 | private ulong r14; 223 | private ulong r15; 224 | private ulong rsp; 225 | private ulong rip; 226 | private ulong rsi; 227 | } 228 | 229 | /// Catch assertions from `fn` 230 | Option!T catch_assert(T, Args...)(T function(Args) fn, Args args) { 231 | Option!(jmpbuf*) _catch_assert_bak = _catch_assert; 232 | jmpbuf j; 233 | if (setjmp(&j)) { 234 | _catch_assert = Option!(jmpbuf*)(); 235 | return Option!(T)(); 236 | } 237 | _catch_assert = Option!(jmpbuf*)(&j); 238 | T v = fn(args); 239 | _catch_assert = *&_catch_assert_bak; 240 | return Option!(T)(v); 241 | } 242 | 243 | /// Internal assetion code 244 | extern (C) void __assert(char* assertion, char* file, int line) { 245 | 246 | import kernel.io : FATAL, printk; 247 | import kernel.util : intToString; 248 | 249 | const ulong f = flags; 250 | cli(); 251 | 252 | printk(FATAL, "Kernel assertion failed: {} at {}:{}", assertion, file + 3, line); 253 | backtrace(); 254 | if (_catch_assert.is_some()) { 255 | flags = f; 256 | longjmp(_catch_assert.unwrap(), 1); 257 | } 258 | for (;;) { 259 | hlt(); 260 | } 261 | } 262 | 263 | /// Get the contents of the `rflags` register 264 | ulong flags() { 265 | ulong f; 266 | asm { 267 | pushf; 268 | pop RAX; 269 | mov f, RAX; 270 | } 271 | return f; 272 | } 273 | 274 | /// Update the contents of the `rflags` register 275 | void flags(ulong flags) { 276 | asm { 277 | mov RAX, flags; 278 | push RAX; 279 | popf; 280 | } 281 | } 282 | 283 | private extern (C) void* getrsp0(); 284 | private extern (C) void setrsp0(void* v); 285 | private extern (C) void setist1(void* v); 286 | 287 | /// Get the contents of `rsp0` 288 | void* rsp0() { 289 | return getrsp0(); 290 | } 291 | /// Update the contents of `ist1` 292 | void ist1(void* v) { 293 | setist1(v); 294 | } 295 | 296 | /// Update the contents of `rsp0` 297 | void rsp0(void* rsp0nv) { 298 | setrsp0(rsp0nv); 299 | } 300 | 301 | /// Clear the interrupts 302 | void cli() { 303 | asm { 304 | cli; 305 | } 306 | } 307 | 308 | /// Output a byte 309 | void outp(ushort port, ubyte b) { 310 | asm { 311 | mov DX, port; 312 | mov AL, b; 313 | out DX, AL; 314 | } 315 | } 316 | -------------------------------------------------------------------------------- /source/kernel/pmap.d: -------------------------------------------------------------------------------- 1 | module kernel.pmap; 2 | 3 | import kernel.io; 4 | import kernel.optional; 5 | 6 | private ulong* read_cr3() { 7 | ulong* outval; 8 | asm { 9 | mov RAX, CR3; 10 | mov outval, RAX; 11 | } 12 | return outval; 13 | } 14 | 15 | /// A physical address 16 | struct Phys { 17 | private ulong _addr; 18 | 19 | /// Access this phys, temporarily, mapping at least 4K of memory 20 | void quickmap(T, Args...)(void function(T*, Args) func, Args args) { 21 | void* addr = cast(void*) 0x0000_0800_8000_000; 22 | ulong* pte_addr = get_pte_ptr(addr).unwrap(); 23 | assert(pte_addr[0] == 0); 24 | debug assert(pte_addr[1] == 0); 25 | pte_addr[0] = 0x3 | (cast(ulong) _addr & 0x000f_ffff_ffff_f000); 26 | pte_addr[1] = 0x3 | (cast(ulong)(_addr + 0x1000) & 0x000f_ffff_ffff_f000); 27 | flush_tlb(); 28 | func(cast(T*)(addr + (_addr & 0x1ff)), args); 29 | pte_addr[0] = 0; 30 | pte_addr[1] = 0; 31 | flush_tlb(); 32 | } 33 | 34 | /// Drop access to this phys, returning it to the memory manager 35 | void drop() { 36 | import kernel.mm : addpage; 37 | 38 | assert((this._addr & 0x1ff) == 0); 39 | addpage(this._addr, 1); 40 | } 41 | 42 | /// Create a new phys, 4096 bytex long, via the memory manager 43 | static Phys alloc() { 44 | import kernel.mm : phys; 45 | 46 | return phys(); 47 | } 48 | 49 | /// Make it a long 50 | ulong addr() { 51 | return _addr; 52 | } 53 | 54 | /// O-Metas 55 | OMeta _ometa_addr() { 56 | return ptr_ometa(); 57 | } 58 | } 59 | 60 | /// Flush the TLB 61 | void flush_tlb() { 62 | asm { 63 | mov RAX, CR3; 64 | mov CR3, RAX; 65 | } 66 | } 67 | 68 | Option!Phys get_page_for(void* va) { 69 | ulong* page_table = read_cr3(); 70 | ulong va_val = (cast(ulong) va) & 0x000f_ffff_ffff_f000; 71 | const ushort[4] offsets = [ 72 | (va_val >> 12 >> 9 >> 9 >> 9) & 0x1ff, (va_val >> 12 >> 9 >> 9) & 0x1ff, 73 | (va_val >> 12 >> 9) & 0x1ff, (va_val >> 12) & 0x1ff 74 | ]; 75 | foreach (ushort key; offsets) { 76 | if (page_table[key] & 0x80) { 77 | return Option!Phys(Phys(page_table[key] & 0x000f_ffff_ffff_f000)); 78 | } 79 | if (!(page_table[key] & 1)) { 80 | return Option!(Phys)(); 81 | } 82 | page_table = cast(ulong*)(page_table[key] & 0x000f_ffff_ffff_f000); 83 | } 84 | 85 | return Option!Phys(Phys(cast(ulong) page_table)); 86 | } 87 | 88 | Option!(ulong*) get_user_pte_ptr(void* va) { 89 | ulong* page_table = read_cr3(); 90 | ulong* control_pte = cast(ulong*) 0; 91 | ulong va_val = (cast(ulong) va) & 0x000f_ffff_ffff_f000; 92 | const ushort[4] offsets = [ 93 | (va_val >> 12 >> 9 >> 9 >> 9) & 0x1ff, (va_val >> 12 >> 9 >> 9) & 0x1ff, 94 | (va_val >> 12 >> 9) & 0x1ff, (va_val >> 12) & 0x1ff 95 | ]; 96 | int i = -1; 97 | foreach (ushort key; offsets) { 98 | i++; 99 | if (page_table[key] & 0x80) { 100 | return Option!(ulong*)(); 101 | } 102 | if (!(page_table[key] & 1) && i != 3) { 103 | import kernel.mm : page, push; 104 | import kernel.task : cur_t; 105 | import kernel.util : memset; 106 | 107 | void* new_page_table = page(); 108 | printk(DEBUG, "Paving a new (user) memory page, index {hex} into PTE at {}, paving {}", 109 | key, &page_table[key], new_page_table); 110 | memset(cast(byte*) new_page_table, 0, 4096); 111 | push(cur_t.memoryowned, cast(ulong) new_page_table); 112 | page_table[key] = 0x7 | cast(ulong) new_page_table; 113 | } 114 | control_pte = &page_table[key]; 115 | page_table = cast(ulong*)(page_table[key] & 0x000f_ffff_ffff_f000); 116 | } 117 | 118 | return Option!(ulong*)(control_pte); 119 | } 120 | 121 | Option!(ulong*) get_pte_ptr(void* va) { 122 | ulong* page_table = read_cr3(); 123 | ulong* control_pte = cast(ulong*) 0; 124 | ulong va_val = (cast(ulong) va) & 0x000f_ffff_ffff_f000; 125 | const ushort[4] offsets = [ 126 | (va_val >> 12 >> 9 >> 9 >> 9) & 0x1ff, (va_val >> 12 >> 9 >> 9) & 0x1ff, 127 | (va_val >> 12 >> 9) & 0x1ff, (va_val >> 12) & 0x1ff 128 | ]; 129 | int i = -1; 130 | foreach (ushort key; offsets) { 131 | i++; 132 | if (page_table[key] & 0x80) { 133 | return Option!(ulong*)(); 134 | } 135 | if (!(page_table[key] & 1) && i != 3) { 136 | import kernel.mm : page; 137 | import kernel.util : memset; 138 | 139 | void* new_page_table = page(); 140 | debug printk(DEBUG, "Paving a new memory page, index {hex} into PTE at {}, paving {}", 141 | key, &page_table[key], new_page_table); 142 | memset(cast(byte*) new_page_table, 0, 4096); 143 | debug printk(DEBUG, "paved"); 144 | page_table[key] = 0x7 | cast(ulong) new_page_table; 145 | } 146 | control_pte = &page_table[key]; 147 | page_table = cast(ulong*)(page_table[key] & 0x000f_ffff_ffff_f000); 148 | } 149 | 150 | return Option!(ulong*)(control_pte); 151 | } 152 | /// Initial paging fixups 153 | void paging_fixups() { 154 | get_pte_ptr(cast(void*) 0x0000_0800_8000_000); 155 | get_pte_ptr(cast(void*) 0x0000_0800_8001_000); 156 | } 157 | 158 | unittest { 159 | import kernel.mm : page, addpage; 160 | 161 | printk("[pmap] get_pte_ptr so we can map stuff"); 162 | void* addr = cast(void*) 0x0000_0800_8000_000; 163 | long* addrl = cast(long*) addr; 164 | long* target = cast(long*) page(); 165 | ulong* pte_addr = get_pte_ptr(addr).unwrap(); 166 | 167 | printk("[pmap] mapping {ptr} to {ptr}", cast(ulong) target, cast(ulong) addr); 168 | *pte_addr = 0x3 | cast(ulong) target; 169 | 170 | printk("[pmap] flushing TLB"); 171 | flush_tlb(); 172 | 173 | printk("[pmap] assert mapping worked"); 174 | assert(*addrl == *target); 175 | *addrl = 0; 176 | assert(*addrl == 0); 177 | assert(*addrl == *target); 178 | *target = 3; 179 | assert(*addrl == 3); 180 | assert(*addrl == *target); 181 | 182 | printk("[pmap] get_page_for does work"); 183 | assert(get_page_for(addr).unwrap().addr() == cast(ulong) target); 184 | 185 | printk("[pmap] freeing resources"); 186 | *pte_addr = 0; 187 | addpage(cast(ulong) target, 1); 188 | 189 | printk("[pmap] flushing TLB"); 190 | flush_tlb(); 191 | 192 | printk("[pmap] allocating a phys"); 193 | Phys p = Phys.alloc(); 194 | Phys q = Phys.alloc(); 195 | 196 | printk("[pmap] quickmapping a phys"); 197 | p.quickmap((void* data, Phys q) { 198 | import kernel.platform : catch_assert; 199 | 200 | printk("[pmap] writing to quickmap"); 201 | long* data_long = cast(long*) data; 202 | *data_long = 1235; 203 | assert(catch_assert((Phys q) { 204 | printk("[pmap] asserting quickmap re-mapping fails"); 205 | q.quickmap((void*) {}); 206 | return 0; 207 | }, q).is_none()); 208 | printk("[pmap] unmapping a quickmap"); 209 | }, q); 210 | 211 | printk("[pmap] dropping physes"); 212 | p.drop(); 213 | q.drop(); 214 | 215 | } 216 | -------------------------------------------------------------------------------- /source/kernel/port.d: -------------------------------------------------------------------------------- 1 | module kernel.port; 2 | 3 | import kernel.io; 4 | import kernel.mm; 5 | import kernel.util; 6 | import vshared.share; 7 | 8 | /// Port message 9 | struct PortMessage { 10 | /// The next port message 11 | PortMessage* next; 12 | /// The data, on the heap 13 | byte[] data; 14 | /// Source: 0 - kernel, -1 - anonymous, positive - that pid 15 | long sourcepid; 16 | } 17 | 18 | /// Port right kind 19 | enum PortRightsKind { 20 | NONE = 0, 21 | RECV = 1, 22 | SEND = 2, 23 | ANON = 4, 24 | ANON_ONLY = 8 25 | } 26 | 27 | /// Userland port rights 28 | struct PortRights { 29 | /// What kind of rights this is? 30 | PortRightsKind kind = PortRightsKind.NONE; 31 | 32 | /// The port 33 | Port* port; 34 | 35 | /// Send data 36 | PortError send(long pid, byte[] data) { 37 | if (!(kind & PortRightsKind.SEND)) { 38 | printk(ERROR, "Attempted to send from a recieve port (kind = {hex})", kind); 39 | static import kernel.platform; kernel.platform.backtrace(); 40 | return PortError.EINVAL; 41 | } 42 | if (pid == -1) { 43 | if (!(kind & PortRightsKind.ANON)) { 44 | printk(WARN, "Permission denied to anonymously send from a regular port (kind = {hex})", kind); 45 | return PortError.EPERM; 46 | } 47 | } else { 48 | if (kind & PortRightsKind.ANON_ONLY) { 49 | printk(WARN, "Permission denied to non-anonymously send from an anon-only port (kind = {hex})", kind); 50 | return PortError.EPERM; 51 | } 52 | } 53 | byte[] heapdata = alloca_unsafe!(byte)(data.length); 54 | memcpy(heapdata.ptr, data.ptr, data.length); 55 | this.port.message = alloc!(PortMessage)(this.port.message, heapdata, pid); 56 | return PortError.EOK; 57 | } 58 | 59 | /// Recieve data 60 | PortError recv(ref byte[] data) { 61 | if (!(kind & PortRightsKind.RECV)) { 62 | printk(ERROR, "Attempted to recieve from a send port (kind = {hex})", kind); 63 | return PortError.EINVAL; 64 | } 65 | PortMessage* msg = this.port.message; 66 | if (msg == cast(PortMessage*) 0) 67 | return PortError.EEMPTY; 68 | this.port.message = msg.next; 69 | data = alloca!(byte)(msg.data.length); 70 | memcpy(data.ptr, msg.data.ptr, msg.data.length); 71 | free(msg.data); 72 | free(msg); 73 | return PortError.EOK; 74 | } 75 | 76 | @disable this(); 77 | /// Make it from a port 78 | this(Port* port, PortRightsKind kind) { 79 | this.port = port; 80 | this.port.rc += 1; 81 | this.kind = kind; 82 | } 83 | /// Copy 84 | this(ref PortRights rhs) { 85 | import kernel.platform; 86 | this.port = rhs.port; 87 | this.port.rc += 1; 88 | this.kind = rhs.kind; 89 | } 90 | /// Dtor 91 | ~this() { 92 | import kernel.platform; 93 | this.port.rc -= 1; 94 | if (this.port.rc == 0) { 95 | printk(DEBUG, "Letting go of a port!"); 96 | free(this.port); 97 | } 98 | } 99 | } 100 | 101 | /// A port 102 | struct Port { 103 | /// Port's refcount 104 | ulong rc = 0; 105 | 106 | /// Port's message, if any 107 | PortMessage* message = cast(PortMessage*)0; 108 | } 109 | 110 | /// A fake (hax/kernel) port 111 | struct FakePort { 112 | /// Recieve 113 | PortError function (FakePort*, long pid, ref byte[] data) _recv; 114 | /// Send 115 | PortError function (FakePort*, long pid, byte[] data) _send; 116 | 117 | /// Recieve 118 | PortError recv(long pid, ref byte[] data) { 119 | return this._recv(&this, pid, data); 120 | } 121 | /// Send 122 | PortError send(long pid, byte[] data) { 123 | return this._send(&this, pid, data); 124 | } 125 | } 126 | private enum PType { 127 | realp, 128 | fake 129 | } 130 | struct AnyPort { 131 | private PType p; 132 | private union { 133 | FakePort fake; 134 | PortRights realp; 135 | } 136 | @disable this(); 137 | 138 | this(FakePort src) { 139 | this.p = PType.fake; 140 | this.fake = src; 141 | } 142 | 143 | this(PortRights src) { 144 | this.p = PType.realp; 145 | this.realp = src; 146 | } 147 | 148 | /// Copy 149 | this(ref AnyPort rhs) { 150 | if (rhs.p == PType.realp) this.realp = rhs.realp; 151 | else this.fake = rhs.fake; 152 | this.p = rhs.p; 153 | } 154 | 155 | /// Dtor 156 | ~this() { 157 | if (this.p == PType.realp) destroy(this.realp); 158 | } 159 | 160 | /// Recieve 161 | PortError recv(long pid, ref byte[] data) { 162 | if (this.p == PType.realp) return this.realp.recv(data); 163 | return this.fake.recv(pid, data); 164 | } 165 | 166 | /// Send 167 | PortError send(long pid, byte[] data) { 168 | if (this.p == PType.realp) return this.realp.send(pid, data); 169 | return this.fake.send(pid, data); 170 | } 171 | } -------------------------------------------------------------------------------- /source/kernel/ports/kbootstrap.d: -------------------------------------------------------------------------------- 1 | module kernel.ports.kbootstrap; 2 | 3 | import kernel.io; 4 | import kernel.port; 5 | import kernel.loafencode; 6 | import vshared.share; 7 | 8 | enum BootstrapCmd { 9 | NOTIFY_EXIT = 1, 10 | NOTIFY_PING = 2, 11 | NOTIFY_PROXIED_EXIT = 3, 12 | } 13 | 14 | struct BootstrapCmdExit { 15 | BootstrapCmd cmd; 16 | ulong code; 17 | } 18 | struct BootstrapCmdProxiedExit { 19 | BootstrapCmd cmd; 20 | ulong pid; 21 | ulong code; 22 | } 23 | 24 | PortError bootstrap_send(FakePort*, long pid, byte[] data) { 25 | BootstrapCmd c; 26 | decode(c, data); 27 | if (c == BootstrapCmd.NOTIFY_EXIT) { 28 | BootstrapCmdExit c2; 29 | decode(c2, data); 30 | printk("Pid {} exited with code {}", pid, c2.code); 31 | return PortError.EOK; 32 | } else if (c == BootstrapCmd.NOTIFY_PING) { 33 | printk(">>>>>> Ping! <<<<<<"); 34 | return PortError.EOK; 35 | } else if (c == BootstrapCmd.NOTIFY_PROXIED_EXIT) { 36 | BootstrapCmdProxiedExit c2; 37 | decode(c2, data); 38 | printk("Pid {} exited with code {}", c2.pid, c2.code); 39 | return PortError.EOK; 40 | } else { 41 | printk(WARN, "Unimplemented bootstrap CMD: {hex} (aka {hex})", c, cast(ulong) c); 42 | } 43 | return PortError.EINVAL; 44 | } 45 | 46 | PortError bootstrap_recv(FakePort*, long pid, ref byte[] data) { 47 | printk(WARN, "User({}) EINVAL: Invalid operation on bootstrap port, recv", pid); 48 | return PortError.EINVAL; 49 | } 50 | 51 | FakePort create_bootstrap() { 52 | return FakePort(&bootstrap_recv, &bootstrap_send); 53 | } 54 | -------------------------------------------------------------------------------- /source/kernel/refptr.d: -------------------------------------------------------------------------------- 1 | module kernel.refptr; 2 | 3 | import kernel.io; 4 | import kernel.mm; 5 | 6 | 7 | private struct Marker {} 8 | 9 | /// A smart pointer 10 | struct Ref(T) { 11 | private ulong* rc; 12 | private T* v; 13 | 14 | @disable this(); 15 | 16 | /// Construct 17 | this(Args...)(Args args) { 18 | rc = alloc!(ulong)(1); 19 | v = alloc!(T, Args)(args); 20 | } 21 | 22 | /// Construct 23 | this(ref Ref!(T) rhs) { 24 | rc = rhs.rc; 25 | v = rhs.v; 26 | (*rc)++; 27 | } 28 | 29 | private this(Marker m) {} 30 | 31 | /// Construct 32 | static Ref!T mk() { 33 | Marker m; 34 | auto a = Ref!(T)(m); 35 | a.rc = alloc!(ulong)(1); 36 | a.v = alloc!(T)(); 37 | return a; 38 | } 39 | 40 | ~this() { 41 | (*rc)--; 42 | if (!*rc) { 43 | printk(DEBUG, "Freeing an rc"); 44 | } 45 | } 46 | 47 | invariant() { 48 | assert(*rc, "Argh!"); 49 | } 50 | 51 | /// Data 52 | @property ref T data() { 53 | return *v; 54 | } 55 | 56 | /// 57 | void __noshow_rc() {} 58 | /// 59 | void __noshow_v() {} 60 | /// 61 | void _prnt_refcount(string subarray, int prenest) { 62 | // We make 3 copies inside of printk. 63 | // TODO: pass-by-value??? 64 | putdyn(subarray, *rc - 3, prenest); 65 | } 66 | /// 67 | void _prnt_value(string subarray, int prenest) { 68 | putdyn(subarray, *v, prenest); 69 | } 70 | } -------------------------------------------------------------------------------- /source/kernel/rtti.d: -------------------------------------------------------------------------------- 1 | module kernel.rtti; 2 | 3 | import kernel.mm; 4 | import kernel.io; 5 | import kernel.optional; 6 | 7 | /// Member info 8 | struct MemberInfo { 9 | /// Name 10 | string name; 11 | /// Offset 12 | ulong offset; 13 | /// Type 14 | TypeInfo* info; 15 | } 16 | 17 | /// Type information 18 | struct TypeInfo { 19 | /// The type ID 20 | ulong type_id; 21 | 22 | /// Printer 23 | void function(void* self, string subarray, int prenest = 0, bool is_field = false) print; 24 | 25 | /// Name 26 | string name; 27 | 28 | /// Members 29 | MemberInfo[] members; 30 | 31 | /// opFormatter 32 | void opFormatter(string subarray, int _prenest) { 33 | debug assert(subarray == ""); 34 | putsk("[TypeInfo for "); 35 | putsk(name); 36 | putsk("]"); 37 | } 38 | 39 | /// Hide derefs so that TypeInfo* works nicely 40 | static void __hide_deref() { 41 | } 42 | } 43 | 44 | /// TypeInfo for an unknown type aka void* 45 | TypeInfo* unknown_typeinfo() { 46 | struct TypeinfoInternal { 47 | void typeidgen(bool _q1, bool _q2) { 48 | // cool! 49 | } 50 | 51 | static void printer(void* self, string subarray, int prenest = 0, bool is_field = false) { 52 | putdyn(subarray, self, prenest, is_field); 53 | } 54 | } 55 | 56 | __gshared TypeInfo tyi; 57 | tyi.type_id = cast(ulong) cast(void*)(&TypeinfoInternal.typeidgen); 58 | tyi.print = &TypeinfoInternal.printer; 59 | tyi.name = "void*"; 60 | return &tyi; 61 | } 62 | 63 | /// Get TypeInfo for `T` 64 | TypeInfo* typeinfo(T)() { 65 | static if (is(T == void*)) { 66 | return unknown_typeinfo(); 67 | } else { 68 | struct TypeinfoInternal { 69 | void typeidgen(bool _q1, bool _q2) { 70 | // cool! 71 | } 72 | 73 | static void printer(void* self, string subarray, int prenest = 0, bool is_field = false) { 74 | static if (__traits(compiles, putdyn(subarray, *cast(T*) self, prenest, is_field))) { 75 | putdyn(subarray, *cast(T*) self, prenest, is_field); 76 | } else { 77 | putsk(""); 78 | } 79 | } 80 | } 81 | 82 | __gshared TypeInfo tyi; 83 | tyi.type_id = cast(ulong) cast(void*)(&TypeinfoInternal.typeidgen); 84 | tyi.print = &TypeinfoInternal.printer; 85 | tyi.name = T.stringof; 86 | static if (__traits(compiles, __traits(allMembers, *(cast(T*)0)))) { 87 | enum members = __traits(allMembers, *(cast(T*)0)); 88 | MemberInfo[members.length] mem; 89 | int i = 0; 90 | foreach (member; members) { 91 | mem[i].name = member; 92 | mem[i].offset = cast(ulong)&__traits(getMember, (cast(T*)0), member); 93 | mem[i++].info = typeinfo!(typeof(__traits(getMember, (cast(T*)0), member)))(); 94 | } 95 | tyi.members = mem; 96 | } else { 97 | MemberInfo[0] mem; 98 | tyi.members = mem; 99 | } 100 | return &tyi; 101 | } 102 | } 103 | 104 | /// Get TypeInfo for `T` 105 | TypeInfo* dynamic_typeinfo(void* of) { 106 | Option!(TypeInfo*) maybeti = mmGetHeapBlock(of).map!(TypeInfo*)((HeapBlock* hb) { 107 | return hb.typeinfo; 108 | }); 109 | if (maybeti.is_none()) 110 | return unknown_typeinfo(); 111 | return maybeti.unwrap(); 112 | } 113 | 114 | /// Dynamic Cast 115 | Option!(T*) dynamic_cast(T)(TypeInfo* tyi, void* v) { 116 | if (tyi == typeinfo!(T)()) 117 | return Option!(T*)(cast(T*) v); 118 | return Option!(T*)(); 119 | } 120 | 121 | /// Dynamic Cast 122 | Option!(T*) dynamic_cast(T)(void* v) { 123 | return dynamic_cast!(T)(dynamic_typeinfo(v), v); 124 | } 125 | -------------------------------------------------------------------------------- /source/kernel/stivale.d: -------------------------------------------------------------------------------- 1 | module kernel.stivale; 2 | 3 | import kernel.io : Hex; 4 | import kernel.optional; 5 | 6 | /// A tag 7 | extern (C) struct Tag { 8 | /// Tag ID 9 | align(8) Hex!ulong ident; 10 | /// Next tag pointer 11 | align(8) Tag* next; 12 | } 13 | /// The header 14 | extern (C) struct StivaleHeader { 15 | /// The bootloader brand 16 | char[64] brand; 17 | /// The bootloader name? 18 | char[64] loader; 19 | /// The tags! 20 | Tag* tag0; 21 | } 22 | 23 | private const ulong HEADER_TAG_FRAMEBUFFER_ID = 0x3ecc1bc43d0f7971; 24 | private const ulong HEADER_TAG_FB_MTRR_ID = 0x4c7bb07731282e00; 25 | private const ulong HEADER_TAG_SMP_ID = 0x1ab015085f3273df; 26 | private const ulong HEADER_TAG_5LV_PAGING_ID = 0x932f477032007e8f; 27 | private const ulong STRUCT_TAG_CMDLINE_ID = 0xe5e76a1b4597a781; 28 | 29 | /// Command line stivale2 tag 30 | extern (C) struct TagCommandLine { 31 | /// the tag struct 32 | align(8) Tag tag; 33 | /// The command line 34 | char* cmdline; 35 | } 36 | 37 | /// RSDP stivale2 tag 38 | extern (C) struct TagRSDP { 39 | /// the tag struct 40 | align(8) Tag tag; 41 | /// The RSDP 42 | ulong rsdp; 43 | } 44 | 45 | private const ulong STRUCT_TAG_MEMMAP_ID = 0x2187f79e8612de07; 46 | /// Memory map entry 47 | extern (C) struct MemoryMapEntry { 48 | /// Base address 49 | align(8) ulong base; 50 | /// Size 51 | align(8) ulong size; 52 | /// The type of this entry, 1 = usable ram 53 | align(4) uint type; 54 | /// Padding 55 | align(4) uint pad; 56 | } 57 | /// Memory map stivale2 tag 58 | extern (C) struct TagMemoryMap { 59 | /// the tag struct 60 | align(8) Tag tag; 61 | //// Amount of entries 62 | align(8) ulong entcount; 63 | /// The memory map entries 64 | align(8) MemoryMapEntry[0x1000] entries; 65 | 66 | } 67 | 68 | /// A module 69 | extern (C) struct Module { 70 | /// 71 | ubyte* begin; 72 | /// 73 | ubyte* end; 74 | /// 75 | char[128] name; 76 | } 77 | 78 | /// Memory map stivale2 tag 79 | extern (C) struct TagModules { 80 | /// the tag struct 81 | align(8) Tag tag; 82 | /// Amount of modules 83 | align(8) ulong modulecount; 84 | /// Modules 85 | align(8) Module[0x8] modules; 86 | 87 | } 88 | 89 | private const ulong STRUCT_TAG_FRAMEBUFFER_ID = 0x506461d2950408fa; 90 | private const ulong STRUCT_TAG_FB_MTRR_ID = 0x6bc1a78ebe871172; 91 | private const ulong STRUCT_TAG_MODULES_ID = 0x4b6fe466aade04ce; 92 | private const ulong STRUCT_TAG_RSDP_ID = 0x9e1786930a375e78; 93 | private const ulong STRUCT_TAG_EPOCH_ID = 0x566a7bed888e1407; 94 | private const ulong STRUCT_TAG_FIRMWARE_ID = 0x359d837855e3858c; 95 | private const ulong STRUCT_TAG_SMP_ID = 0x34d1d96339647025; 96 | private const ulong STRUCT_TAG_PXE_SERVER_INFO = 0x29d1e96239247032; 97 | -------------------------------------------------------------------------------- /source/kernel/symbols.d: -------------------------------------------------------------------------------- 1 | module kernel.symbols; 2 | 3 | import kernel.hashmap; 4 | import kernel.io; 5 | 6 | struct Symbol { 7 | immutable(char)* name; 8 | ulong off; 9 | } 10 | 11 | struct SymbolTable { 12 | BetterHashMap!(immutable(char)*) symtab; 13 | } 14 | 15 | __gshared SymbolTable symtab; 16 | 17 | Symbol symbolify(ulong addr) { 18 | ulong the_addr = addr; 19 | while (!(addr in symtab.symtab)) { 20 | addr--; 21 | if (addr > 0xffffffff81000000 || addr < 0xffffffff80200000) return Symbol("".ptr, 0); 22 | } 23 | return Symbol(*symtab.symtab[addr], the_addr - addr); 24 | } 25 | 26 | void load(byte* diode_kernel_map) { 27 | printk("Loading DKM @ {}", cast(void*)diode_kernel_map); 28 | ulong size = *cast(ulong*)diode_kernel_map; 29 | printk(" - size = {}", size); 30 | ulong idx = 8; 31 | ulong prev = 0; 32 | foreach (i; 0 .. size) { 33 | if (((i * 100) / size) > prev) { printk("Loading: {}%", i * 100 / size); prev = (i * 100) / size; } 34 | ulong tgd = *cast(ulong*)(diode_kernel_map + idx); 35 | idx += 8; 36 | ulong sz = *cast(ulong*)(diode_kernel_map + idx); 37 | idx += 8; 38 | char* name = cast(char*)(diode_kernel_map + idx); 39 | idx += sz + 1; 40 | symtab.symtab.insertElem(tgd, cast(immutable(char)*)name); 41 | } 42 | printk("Loaded!"); 43 | } -------------------------------------------------------------------------------- /source/kernel/syscall/dispatch.d: -------------------------------------------------------------------------------- 1 | module kernel.syscall.dispatch; 2 | 3 | 4 | import kernel.syscall.generic; 5 | import kernel.syscall.exec; 6 | import kernel.syscall.exit; 7 | import kernel.syscall.map; 8 | import kernel.syscall.io; 9 | import vshared.share; 10 | import kernel.io; 11 | 12 | /// Handle a syscall 13 | long syscall(ulong sysno, ulong data) { 14 | return syscall(sysno, cast(void*)data); 15 | } 16 | 17 | /// Handle a syscall 18 | long syscall(ulong sysno, void* data) { 19 | printk(DEBUG, "Syscall: {hex}({})", sysno, data); 20 | switch (sysno) { 21 | case Syscall.EXIT: 22 | return sys_exit(data); 23 | case Syscall.MAP: 24 | return sys_map(data); 25 | case Syscall.SEND: 26 | return sys_send(data); 27 | case Syscall.RECV: 28 | return sys_recv(data); 29 | case Syscall.CREATE_PORT: 30 | return sys_create_port(data); 31 | case Syscall.EXEC: 32 | return sys_exec(data); 33 | case Syscall.KPRINT: 34 | return sys_kprint(data); 35 | case Syscall.GET_TID: 36 | return sys_get_tid(data); 37 | case Syscall.FORK: 38 | return sys_fork(data); 39 | case Syscall.GET_STACK_BOUNDS: 40 | return sys_get_stack_bounds(data); 41 | case Syscall.DESTROY_PORT: 42 | return sys_destroy_port(data); 43 | case cast(Syscall)0xC000: 44 | return sys_make_user_stack(data); 45 | default: 46 | printk(WARN, "Invalid syscall performed: sys={hex}({}) data={}", sysno, cast(Syscall)sysno, data); 47 | // assert(0, "Invalid syscall"); 48 | return ENOSYS; 49 | } 50 | } -------------------------------------------------------------------------------- /source/kernel/syscall/exec.d: -------------------------------------------------------------------------------- 1 | module kernel.syscall.exec; 2 | 3 | import kernel.io; 4 | import kernel.mm; 5 | import kernel.pmap; 6 | import kernel.task; 7 | import kernel.util; 8 | import kernel.guards; 9 | import kernel.hashmap; 10 | import kernel.platform; 11 | 12 | /// Exec 13 | long sys_exec(void* data) { 14 | assert(0, "sys_exec"); 15 | } 16 | 17 | /// Fork 18 | long sys_fork(void* data) { 19 | struct ForkShmemPage { 20 | void* virt; 21 | byte* data; 22 | } 23 | struct ForkShmem { 24 | ulong spin; 25 | ulong tid; 26 | ulong stktop; 27 | ulong stkbtm; 28 | void* data; 29 | ForkShmemPage[] pgs; 30 | t* old; 31 | } 32 | ForkShmemPage[] pgs = []; 33 | foreach (e; cur_t.pages_that_we_own.data) { 34 | if (!e.isthere) continue; 35 | void* virt = cast(void*)e.addr; 36 | byte* datap = cast(byte*)(*get_user_pte_ptr(cast(void*)e.addr).unwrap() & 0x000f_ffff_ffff_f000); 37 | push(pgs, ForkShmemPage(virt, datap)); 38 | } 39 | ForkShmem fosh = ForkShmem(0, 0, cur_t.user_stack_top, cur_t.user_stack_bottom, data, pgs, cur_t); 40 | task_create(function (ForkShmem* fkshmem) { 41 | ulong* cr3; 42 | asm { 43 | mov RAX, CR3; 44 | mov cr3, RAX; 45 | } 46 | foreach (i; 1..256) { 47 | cr3[i] = 0; 48 | } 49 | auto g = no_smap(); 50 | foreach (pg; fkshmem.pgs) { 51 | // pg.data 52 | user_map(pg.virt); 53 | import kernel.io; 54 | cur_t.user_stack_top = fkshmem.stktop; 55 | cur_t.user_stack_bottom = fkshmem.stkbtm; 56 | memcpy(cast(byte*)pg.virt, pg.data, 4096); 57 | } 58 | g.die(); 59 | fkshmem.tid = cur_t.tid; 60 | ulong data = cast(ulong)fkshmem.data; 61 | foreach (ref p; fkshmem.old.ports.iter()) { 62 | cur_t.ports.insertElem(p[0], *p[1]); 63 | printk("port: {}", p[0]); 64 | } 65 | asm { lfence; } 66 | fkshmem.spin = 1; 67 | asm { lfence; } 68 | 69 | user_branch(data, cast(void*)0); 70 | }, &fosh, alloc_stack()); 71 | while (fosh.spin == 0) { 72 | sched_yield(); 73 | } 74 | 75 | free(pgs); 76 | return fosh.tid; 77 | } -------------------------------------------------------------------------------- /source/kernel/syscall/exit.d: -------------------------------------------------------------------------------- 1 | module kernel.syscall.exit; 2 | 3 | import kernel.io; 4 | import kernel.mm; 5 | import kernel.loafencode; 6 | import kernel.pmap; 7 | import kernel.task; 8 | import kernel.syscall.util; 9 | import kernel.ports.kbootstrap; 10 | 11 | /// Exit 12 | long sys_exit(void* data) { 13 | 14 | BootstrapCmdExit e; 15 | e.cmd = BootstrapCmd.NOTIFY_EXIT; 16 | e.code = 0; 17 | copy_from_user(data, cast(void*)&e.code, ulong.sizeof); 18 | printk(DEBUG, "pid({}) Exiting with code: {}", cur_t.tid, e.code); 19 | ulong* pt; 20 | asm { 21 | mov RAX, CR3; 22 | mov pt, RAX; 23 | } 24 | 25 | foreach (i; 1..256) { 26 | pt[i] = 0; 27 | } 28 | 29 | flush_tlb(); 30 | 31 | // freet(pt); 32 | foreach (region; cur_t.memoryowned) { 33 | addpage(region, 1); 34 | } 35 | 36 | byte[] dat = alloca!(byte)(0); 37 | encode(e, dat); 38 | 39 | // If they have the bootstrap port, 40 | if (0 in cur_t.ports) { 41 | printk(DEBUG, "Sending exit info to the BSP"); 42 | // then signal on the bootstrap port 43 | cur_t.ports[0].send(/* ktid == pid */ cur_t.tid, dat); 44 | } 45 | 46 | 47 | task_exit(); 48 | } 49 | -------------------------------------------------------------------------------- /source/kernel/syscall/generic.d: -------------------------------------------------------------------------------- 1 | module kernel.syscall.generic; 2 | 3 | import kernel.io; 4 | import kernel.mm; 5 | import kernel.task; 6 | import vshared.share; 7 | import kernel.syscall.util; 8 | 9 | 10 | /// KPrint 11 | long sys_kprint(void* data) { 12 | KPrintBuffer buf; 13 | copy_from_user(data, cast(void*)&buf, KPrintBuffer.sizeof); 14 | immutable(char)[] s = alloca!(immutable(char))(buf.len + 1, '\0'); 15 | copy_from_user(cast(void*)buf.ptr, cast(void*)s.ptr, buf.len); 16 | putsk(s); 17 | free(s); 18 | return 0; 19 | } 20 | 21 | long sys_get_tid(void* data) { 22 | copy_to_user(data, &cur_t.tid, long.sizeof); 23 | return 0; 24 | } -------------------------------------------------------------------------------- /source/kernel/syscall/io.d: -------------------------------------------------------------------------------- 1 | module kernel.syscall.io; 2 | 3 | import kernel.io; 4 | import kernel.mm; 5 | import kernel.port; 6 | import kernel.task; 7 | import vshared.share; 8 | import kernel.syscall.util; 9 | 10 | 11 | __gshared ulong portassocbase = 0x1000; 12 | 13 | /// Send 14 | long sys_send(void* data) { 15 | KPortIOOp buf; 16 | copy_from_user(data, &buf, buf.sizeof); 17 | if (buf.kind == IOOpKind.KIND_RECV) /* Invalid operation */ return EINVAL; 18 | if (buf.port in cur_t.ports) { 19 | byte[] bufr = alloca_unsafe!(byte)(buf.len); 20 | copy_from_user(buf.data, bufr.ptr, buf.len); 21 | PortError e = cur_t.ports[buf.port].send(/* ktid == pid */ buf.kind == IOOpKind.KIND_SEND ? cur_t.tid : -1, bufr); 22 | free(bufr); 23 | if (e == PortError.EOK) return 0; 24 | if (e == PortError.EPERM) return /* Permission denied */ EPERM; 25 | if (e == PortError.EINVAL) return /* Invalid operation */ EINVAL; 26 | if (e == PortError.EEMPTY) return /* Empty */ EEMPTY; 27 | } else { 28 | return /* Bad file descriptor */ EBADF; 29 | } 30 | 31 | assert(0, "sys_send"); 32 | } 33 | /// Recieve 34 | long sys_recv(void* data) { 35 | KPortIOOp op; 36 | copy_from_user(data, &op, op.sizeof); 37 | if (op.kind != IOOpKind.KIND_RECV) return EINVAL; 38 | if (op.port !in cur_t.ports) return EBADF; 39 | AnyPort* p = cur_t.ports[op.port]; 40 | byte[] arr = []; 41 | PortError e = p.recv(cur_t.tid, arr); 42 | while (e == PortError.EEMPTY) { 43 | sched_yield(); 44 | e = p.recv(cur_t.tid, arr); 45 | } 46 | switch (e) { 47 | case PortError.EPERM: 48 | return EPERM; 49 | case PortError.EINVAL: 50 | return EINVAL; 51 | case PortError.EEMPTY: 52 | return EEMPTY; 53 | default: 54 | } 55 | assert(arr.length <= 4096); 56 | void* tgd = alloc_user_virt(); 57 | user_map(tgd); 58 | copy_to_user(tgd, arr.ptr, arr.length); 59 | op.data = tgd; 60 | op.len = arr.length; 61 | free(arr); 62 | copy_to_user(data, &op, op.sizeof); 63 | return 0; 64 | } 65 | /// Create a port 66 | long sys_create_port(void* data) { 67 | KPortCreate buf; 68 | // buf.outp 69 | // assert(0, "sys_create_port"); 70 | ulong x = portassocbase++; 71 | Port* p = alloc!(Port)(); 72 | cur_t.ports.insertElem(x, AnyPort(PortRights(p, PortRightsKind.ANON | PortRightsKind.SEND | PortRightsKind.RECV))); 73 | buf.outp = x; 74 | printk("port allocated: {} {}", x, x in cur_t.ports); 75 | copy_to_user(data, &buf, buf.sizeof); 76 | return 0; 77 | } 78 | /// Destroy a port 79 | long sys_destroy_port(void* data) { 80 | return ENOSYS; 81 | } -------------------------------------------------------------------------------- /source/kernel/syscall/map.d: -------------------------------------------------------------------------------- 1 | module kernel.syscall.map; 2 | 3 | import kernel.io; 4 | import kernel.mm; 5 | import kernel.pmap; 6 | import kernel.util; 7 | import kernel.task; 8 | import vshared.share; 9 | import kernel.syscall.util; 10 | 11 | /// Get stack bounds 12 | long sys_get_stack_bounds(void* data) { 13 | copy_to_user(data, &cur_t.user_stack_top, 8); 14 | return 0; 15 | } 16 | 17 | /// Map 18 | long sys_map(void* data) { 19 | MMap map; 20 | copy_from_user(data, &map, MMap.sizeof); 21 | if (map.flags & MMapFlags.MAP_FIXED && (cast(ulong)map.addr) < (1UL << 32)) return EINVAL; 22 | if (map.flags & MMapFlags.MAP_UNMAP) { 23 | *get_user_pte_ptr(map.addr).unwrap() = 0; 24 | return 0; 25 | } 26 | if (!(map.flags & MMapFlags.MAP_PRIVATE)) return EINVAL; 27 | 28 | if (map.flags & MMapFlags.MAP_FIXED) { 29 | printk(WARN, "TODO: mmap(MAP_FIXED)"); 30 | return ENOSYS; 31 | } else { 32 | void* addr = alloc_user_virt(); 33 | auto ptep = get_user_pte_ptr(cast(void*) addr); 34 | while (ptep.is_none()) { 35 | addr = alloc_user_virt(); 36 | ptep = get_user_pte_ptr(cast(void*) addr); 37 | } 38 | map.addr = addr; 39 | user_map(addr); 40 | copy_to_user(data, &map, map.sizeof); 41 | return 0; 42 | } 43 | 44 | assert(0); 45 | } 46 | /// sbrk 47 | long sys_sbrk(void* data) { 48 | assert(0, "sys_sbrk"); 49 | } 50 | /// set up stack 51 | long sys_make_user_stack(void* data) { 52 | ulong top = cur_t.user_stack_top = cast(ulong)alloc_user_virt(); 53 | cur_t.user_stack_bottom = top - 0x1000; 54 | user_map(cast(void*)(cur_t.user_stack_bottom)); 55 | printk(DEBUG, "Allocated user stack: {ptr}", cur_t.user_stack_top); 56 | return cast(long)cur_t.user_stack_top; 57 | } 58 | -------------------------------------------------------------------------------- /source/kernel/syscall/util.d: -------------------------------------------------------------------------------- 1 | module kernel.syscall.util; 2 | 3 | import kernel.guards; 4 | 5 | 6 | /// Is we safe now? 7 | __gshared bool is_safe_function = false; 8 | 9 | /// Copy from user to your area 10 | void copy_from_user(void* user, void* buf, ulong len) { 11 | if ((cast(ulong)user) >= 0xffff_8000_0000_0000) { 12 | return; 13 | } 14 | is_safe_function = true; 15 | const auto g = no_smap(); 16 | asm { 17 | mov R8, fail; 18 | mov RSI, user; 19 | mov RDI, buf; 20 | xor RCX, RCX; 21 | mov RDX, len; 22 | 23 | loop: 24 | cmp RCX, RDX; 25 | jge fail; 26 | 27 | mov AL, [RCX + RSI]; 28 | mov [RCX + RDI], AL; 29 | inc RCX; 30 | jmp loop; 31 | fail: ; 32 | } 33 | is_safe_function = false; 34 | } 35 | 36 | /// Copy to user from your area 37 | void copy_to_user(void* user, void* buf, ulong len) { 38 | if ((cast(ulong)user) >= 0xffff_8000_0000_0000) { 39 | return; 40 | } 41 | is_safe_function = true; 42 | const auto g = no_smap(); 43 | asm { 44 | mov R8, fail; 45 | mov RSI, buf; 46 | mov RDI, user; 47 | xor RCX, RCX; 48 | mov RDX, len; 49 | 50 | loop: 51 | cmp RCX, RDX; 52 | jge fail; 53 | 54 | mov AL, [RCX + RSI]; 55 | mov [RCX + RDI], AL; 56 | 57 | inc RCX; 58 | jmp loop; 59 | fail: ; 60 | } 61 | is_safe_function = false; 62 | } -------------------------------------------------------------------------------- /source/kernel/task.d: -------------------------------------------------------------------------------- 1 | module kernel.task; 2 | 3 | import kernel.mm; 4 | import kernel.io; 5 | import kernel.irq; 6 | import kernel.port; 7 | import kernel.util; 8 | import ldc.attributes; 9 | import kernel.hashmap; 10 | import kernel.platform; 11 | 12 | /// A `t` - a DIOS task. 13 | struct t { 14 | private ulong[5] state; 15 | private ulong juice; 16 | private t* prev; 17 | private t* next; 18 | /// The niceness value 19 | ulong niceness; 20 | /// the TID 21 | ulong tid; 22 | /// The meh ports owned by this `t` 23 | BetterHashMap!AnyPort ports; 24 | /// The low (user) pages owned by this `t` 25 | ulong[256] pages; 26 | /// Pages mapped 27 | ulong[] memoryowned; 28 | /// rsp0 29 | ulong rsp0val; 30 | 31 | HashMap pages_that_we_own; 32 | 33 | ulong user_stack_top = 0; 34 | ulong user_stack_bottom = 0; 35 | } 36 | 37 | /// A task creation request 38 | private struct r { 39 | void function(void*) func; 40 | void* arg; 41 | } 42 | 43 | /// Current task 44 | __gshared t* cur_t; 45 | private __gshared t zygote_t; 46 | private __gshared lock tidlock; 47 | private __gshared ulong tidbase = 1; 48 | private __gshared bool is_task_inited = false; 49 | 50 | static assert(t.sizeof < 4096); 51 | 52 | /// Ensure tasks are ready 53 | void ensure_task_init() { 54 | if (!is_task_inited) { 55 | printk(DEBUG, "Initing task structures"); 56 | is_task_inited = true; 57 | zygote_t.juice = 1; 58 | zygote_t.niceness = 20; 59 | zygote_t.tid = 1; 60 | zygote_t.next = &zygote_t; 61 | zygote_t.prev = &zygote_t; 62 | cur_t = &zygote_t; 63 | } 64 | } 65 | 66 | private extern (C) void task_trampoline(r* the_r) { 67 | void function(void*) func = the_r.func; 68 | void* arg = the_r.arg; 69 | free(the_r); 70 | rsp0 = cast(void*)cur_t.rsp0val; 71 | func(arg); 72 | task_exit(); 73 | assert(0); 74 | } 75 | 76 | private extern(C) void altstack_task_setup(void* data, ulong* target_buf); 77 | 78 | /// Create a task 79 | void task_create(T)(void function(T*) func, T* arg, void* stack) { 80 | r the_r; 81 | the_r.func = cast(void function(void*))func; 82 | the_r.arg = cast(void*)arg; 83 | const r* borrow_r = alloc!r(the_r); 84 | void* argg = cast(void*)borrow_r; 85 | 86 | t* the_t = cast(t*)page(); 87 | memset(cast(byte*)the_t, 0, 4096); 88 | void*[0] a; 89 | 90 | ulong* s = the_t.state.ptr; 91 | asm { 92 | mov R13, stack; 93 | mov R14, s; 94 | mov R15, argg; 95 | xchg R13, RSP; 96 | mov RDI, R15; 97 | mov RSI, s; 98 | call altstack_task_setup; 99 | xchg R13, RSP; 100 | } 101 | the_t.next = cur_t.next; 102 | cur_t.next.prev = the_t; 103 | the_t.prev = cur_t; 104 | cur_t.next = the_t; 105 | the_t.tid = ++tidbase; 106 | the_t.user_stack_top = 0; 107 | the_t.user_stack_bottom = 0; 108 | the_t.rsp0val = cast(ulong)alloc_stack(); 109 | } 110 | 111 | private ulong ncn_to_juice(ulong u) { 112 | return 41 - (20 + u); 113 | } 114 | 115 | /// Goodbye, cruel world! 116 | void task_exit() { 117 | assert(cur_t.tid != 1, "Bootstrap task cannot be killed!"); 118 | asm { 119 | cli; 120 | } 121 | t* self = cur_t; 122 | self.prev.next = self.next; 123 | self.next.prev = self.prev; 124 | cur_t = cur_t.next; 125 | free(cur_t.pages_that_we_own.data); 126 | free(self); 127 | task_pls_die(cur_t.state.ptr); 128 | assert(0); 129 | } 130 | 131 | private extern(C) void task_switch(ulong* buf); 132 | private extern(C) void task_pls_die(ulong* buf); 133 | 134 | private extern(C) void task_enqueue(ulong* buf) { 135 | ensure_task_init(); 136 | cur_t.state[0] = buf[0]; 137 | cur_t.state[1] = buf[1]; 138 | cur_t.state[2] = buf[2]; 139 | cur_t.state[3] = buf[3]; 140 | cur_t.state[4] = buf[4]; 141 | cur_t = cur_t.next; 142 | } 143 | 144 | /// Force a yield. 145 | void sched_yield() { 146 | const ulong flg = flags; 147 | cli(); 148 | ulong* cr3; 149 | asm { 150 | mov RAX, CR3; 151 | mov cr3, RAX; 152 | } 153 | foreach (i; 1..256) { 154 | cur_t.pages[i] = cr3[i]; 155 | // if (cr3[i]) printk("i={} (tid={})", i, cur_t.tid); 156 | } 157 | asm { 158 | mov RAX, CR3; 159 | mov CR3, RAX; 160 | } 161 | task_switch(cur_t.next.state.ptr); 162 | rsp0 = cast(void*)cur_t.rsp0val; 163 | foreach (i; 1..256) { 164 | cr3[i] = cur_t.pages[i]; 165 | // if (cr3[i]) printk("i={} (tid={})", i, cur_t.tid); 166 | } 167 | asm { 168 | mov RAX, CR3; 169 | mov CR3, RAX; 170 | } 171 | 172 | flags = flg; 173 | } 174 | 175 | /// Fake a yield, resetting the juice. Useful after changing the niceness in the kernel. 176 | void fake_yield() { 177 | cur_t.juice = ncn_to_juice(cur_t.niceness); 178 | } 179 | 180 | /// Attempt to yield. Does not guarantee an actual yield. 181 | void sched_mayyield() { 182 | cur_t.juice -= 1; 183 | if (cur_t.juice) 184 | return; 185 | sched_yield(); 186 | } 187 | 188 | /// Map 189 | void user_map(void* user) { 190 | import kernel.mm; 191 | import kernel.pmap; 192 | 193 | void* raw = page(); 194 | memset(cast(byte*)raw, 0, 4096); 195 | *get_user_pte_ptr(user).unwrap() = 7 | cast(ulong) raw; 196 | push(cur_t.memoryowned, cast(ulong)raw); 197 | cur_t.pages_that_we_own.insertElem(cast(ulong)user, cast(ulong*)0); 198 | } 199 | -------------------------------------------------------------------------------- /source/kernel/util.d: -------------------------------------------------------------------------------- 1 | module kernel.util; 2 | 3 | import kernel.io; 4 | 5 | /// Int -> String 6 | char* intToString(long value, char* str, int base) { 7 | char* rc; 8 | char* ptr; 9 | char* low; 10 | // Check for supported base. 11 | if (base < 2 || base > 36) { 12 | *str = '\0'; 13 | return str; 14 | } 15 | rc = ptr = str; 16 | // Set '-' for negative decimals. 17 | if (value < 0 && base == 10) { 18 | *ptr++ = '-'; 19 | } 20 | // Remember where the numbers start. 21 | low = ptr; 22 | // The actual conversion. 23 | do { 24 | // Modulo is negative for negative value. This trick makes abs() unnecessary. 25 | *ptr++ = "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz"[cast( 26 | int)(35 + value % base)]; 27 | value /= base; 28 | } 29 | while (value); 30 | // Terminating the string. 31 | *ptr-- = '\0'; 32 | // Invert the numbers. 33 | while (low < ptr) { 34 | const char tmp = *low; 35 | *low++ = *ptr; 36 | *ptr-- = tmp; 37 | } 38 | return rc; 39 | } 40 | 41 | /// Int -> String 42 | char* intToString(ulong value, char* str, int base) { 43 | char* rc; 44 | char* ptr; 45 | char* low; 46 | // Check for supported base. 47 | if (base < 2 || base > 36) { 48 | *str = '\0'; 49 | return str; 50 | } 51 | rc = ptr = str; 52 | // Set '-' for negative decimals. 53 | if (value < 0 && base == 10) { 54 | *ptr++ = '-'; 55 | } 56 | // Remember where the numbers start. 57 | low = ptr; 58 | // The actual conversion. 59 | do { 60 | // Modulo is negative for negative value. This trick makes abs() unnecessary. 61 | *ptr++ = "0123456789abcdefghijklmnopqrstuvwxyz"[cast( 62 | uint)(value % base)]; 63 | value /= base; 64 | } 65 | while (value); 66 | // Terminating the string. 67 | *ptr-- = '\0'; 68 | // Invert the numbers. 69 | while (low < ptr) { 70 | const char tmp = *low; 71 | *low++ = *ptr; 72 | *ptr-- = tmp; 73 | } 74 | return rc; 75 | } 76 | 77 | /// Int -> String 78 | char* intToString(uint value, char* str, int base) { 79 | return intToString(cast(ulong) value, str, base); 80 | } 81 | /// Int -> String 82 | char* intToString(int value, char* str, int base) { 83 | return intToString(cast(long) value, str, base); 84 | } 85 | 86 | /// Print some 1337 h4x0r hex 87 | void puthex(int value) { 88 | char[32] arr; 89 | arr[0] = 0; 90 | char* rc; 91 | char* ptr; 92 | char* low; 93 | rc = ptr = arr.ptr; 94 | // Remember where the numbers start. 95 | low = ptr; 96 | // The actual conversion. 97 | do { 98 | // Modulo is negative for negative value. This trick makes abs() unnecessary. 99 | *ptr++ = "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz"[35 100 | + value % 16]; 101 | value /= 16; 102 | } 103 | while (value); 104 | // Terminating the string. 105 | *ptr-- = '\0'; 106 | // Invert the numbers. 107 | while (low < ptr) { 108 | const char tmp = *low; 109 | *low++ = *ptr; 110 | *ptr-- = tmp; 111 | } 112 | putsk(rc); 113 | } 114 | 115 | /// Print a decimal 116 | void putdec(int value) { 117 | char[32] arr; 118 | arr[0] = 0; 119 | char* rc; 120 | char* ptr; 121 | char* low; 122 | rc = ptr = arr.ptr; 123 | // Remember where the numbers start. 124 | low = ptr; 125 | // The actual conversion. 126 | if (value < 0) { 127 | *ptr++ = '-'; 128 | } 129 | do { 130 | // Modulo is negative for negative value. This trick makes abs() unnecessary. 131 | *ptr++ = "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz"[35 132 | + value % 10]; 133 | value /= 10; 134 | } 135 | while (value); 136 | // Terminating the string. 137 | *ptr-- = '\0'; 138 | // Invert the numbers. 139 | while (low < ptr) { 140 | const char tmp = *low; 141 | *low++ = *ptr; 142 | *ptr-- = tmp; 143 | } 144 | putsk(rc); 145 | } 146 | 147 | /// A (pointer; len) tuple to iterator translation layer 148 | struct PtrRange(T) { 149 | private int begin; 150 | private int end; 151 | private T* data; 152 | 153 | @disable this(); 154 | /// Create a PtrRange 155 | this(int len, T* argdata) { 156 | this.begin = 0; 157 | this.data = argdata; 158 | this.end = len; 159 | } 160 | 161 | invariant () { 162 | // There is a bug if begin is greater than end 163 | assert(begin <= end); 164 | } 165 | 166 | /// Is it empty? 167 | bool empty() const { 168 | // The range is consumed when begin equals end 169 | return begin == end; 170 | } 171 | 172 | /// Next element pls 173 | void popFront() { 174 | // Skipping the first element is achieved by 175 | // incrementing the beginning of the range 176 | ++begin; 177 | } 178 | 179 | /// First element ptr (reborrowed) 180 | T* front() const { 181 | // The front element is the one at the beginning 182 | return cast(T*)&data[begin]; 183 | } 184 | } 185 | 186 | /// An iterator based on pointer transforms 187 | struct PtrTransformIter(T) { 188 | private T* function(T*) transformer; 189 | private T* data; 190 | 191 | @disable this(); 192 | /// Create a PtrRange 193 | this(T* data, T* function(T*) transformer) { 194 | this.data = data; 195 | this.transformer = transformer; 196 | } 197 | 198 | /// Is it empty? 199 | bool empty() const { 200 | // The range is consumed when begin equals end 201 | return this.data == cast(T*) 0; 202 | } 203 | 204 | /// Next element pls 205 | void popFront() { 206 | // Skipping the first element is achieved by 207 | // incrementing the beginning of the range 208 | this.data = this.transformer(this.data); 209 | } 210 | 211 | /// First element ptr (reborrowed) 212 | T* front() const { 213 | return cast(T*) this.data; 214 | } 215 | } 216 | 217 | /// memcmp - compare memory areas 218 | /// 219 | /// # Description 220 | /// The memcmp() function compares the first n bytes (each interpreted as unsigned char) of the memory areas s1 and s2. 221 | /// 222 | /// # Return Value 223 | /// The memcmp() function returns an integer less than, equal to, or greater than zero if the first n bytes of s1 is found, respectively, to be less than, to match, or be greater than the 224 | /// first n bytes of s2. 225 | /// 226 | /// Not the above guarantee about memcmp's return value is guaranteed by linux and not the standard, as seen in CVE-2012-2122 "optimized memcmp" 227 | /// glibc memcmp when SSE/AVX is on will return whatever. 228 | /// So we return 1/0/-1. 229 | /// 230 | /// For a nonzero return value, the sign is determined by the sign of the difference between the first pair of bytes (interpreted as unsigned char) that differ in s1 and s2. 231 | /// 232 | /// If n is zero, the return value is zero. 233 | extern (C) int memcmp(const byte* s1, const byte* s2, size_t n) { 234 | foreach (i; 0 .. n) { 235 | if (s1[i] < s2[i]) 236 | return -1; 237 | if (s1[i] > s2[i]) 238 | return 1; 239 | } 240 | return 0; 241 | } 242 | 243 | /// strlen - calculate the length of a string 244 | /// 245 | /// The strlen() function calculates the length of the string pointed to by s, excluding the terminating null byte ('\0'). 246 | /// 247 | /// The strlen() function returns the number of bytes in the string pointed to by s. 248 | extern (C) size_t strlen(const char* s) { 249 | size_t i = 0; 250 | for (; s[i] != 0; i++) { 251 | } 252 | return i; 253 | } 254 | 255 | /// memcpy - copy memory area 256 | /// 257 | /// The memcpy() function copies n bytes from memory area src to memory area dest. 258 | /// The memory areas must not overlap. Use memmove(3) if the memory 259 | /// areas do overlap. 260 | /// 261 | /// The memcpy() function returns a pointer to dest. 262 | extern (C) byte* memcpy(byte* dst, const byte* src, size_t n) { 263 | size_t i = 0; 264 | while (i + 8 <= n) { 265 | *(cast(ulong*)(&dst[i])) = *(cast(ulong*)(&src[i])); 266 | i += 8; 267 | } 268 | while (i + 4 <= n) { 269 | *(cast(uint*)(&dst[i])) = *(cast(uint*)(&src[i])); 270 | i += 4; 271 | } 272 | while (i + 2 <= n) { 273 | *(cast(ushort*)(&dst[i])) = *(cast(ushort*)(&src[i])); 274 | i += 2; 275 | } 276 | while (i + 1 <= n) { 277 | *(cast(byte*)(&dst[i])) = *(cast(byte*)(&src[i])); 278 | i += 1; 279 | } 280 | return dst; 281 | } 282 | 283 | /// memset - fill memory with a constant byte 284 | /// 285 | /// The memset() function fills the first len bytes of the memory 286 | /// area pointed to by mem with the constant byte data. 287 | /// 288 | /// The memset() function returns a pointer to the memory area mem. 289 | extern (C) byte* memset(byte* mem, int data, size_t len) { 290 | for (size_t i = 0; i < len; i++) { 291 | mem[i] = cast(byte)data; 292 | } 293 | return mem; 294 | } 295 | 296 | extern (C) ushort* memset16(ushort* mem, ushort data, size_t len) { 297 | for (size_t i = 0; i < len; i++) { 298 | mem[i] = cast(ushort)data; 299 | } 300 | return mem; 301 | } 302 | -------------------------------------------------------------------------------- /source/libled/except.d: -------------------------------------------------------------------------------- 1 | module libled.except; 2 | 3 | import vshared.share; 4 | import libsys.syscall; 5 | import libsys.io; 6 | 7 | 8 | ///Deprecated: idk why 9 | deprecated extern (C) __gshared void* _Dmodule_ref; 10 | 11 | extern (C) Throwable _d_eh_enter_catch(void*) { 12 | assert(0, "_d_eh_enter_catch"); 13 | } 14 | 15 | extern (C) void _d_arraybounds(string file, uint line) { 16 | printf(line, file, FATAL, "Out of bounds access to an array!"); 17 | assert(0, "assertion failed"); 18 | } 19 | 20 | extern (C) void _d_assert(string file, uint line) { 21 | printf(line, file, FATAL, "Assertion failed!"); 22 | assert(0, "assertion failed"); 23 | } -------------------------------------------------------------------------------- /source/libsys/entry.d: -------------------------------------------------------------------------------- 1 | module libsys.entry; 2 | 3 | import libsys.io; 4 | import libsys.mem; 5 | import std.traits; 6 | import libsys.util; 7 | import libsys.syscall; 8 | import vshared.share; 9 | 10 | extern (C) int ___emain(); 11 | 12 | pragma(mangle, "_main") private extern (C) void usermain(ulong bgn, ulong end, void** elfbgn, void** elfend) { 13 | foreach (i; 0 .. ((end - bgn) / 8)) { 14 | alias initfn = extern(C) void function(); 15 | (*cast(initfn*)((i * 8) + bgn))(); 16 | } 17 | elfbase = elfbgn; 18 | elftop = elfend; 19 | 20 | commit_bigblk(); 21 | ulong ec = cast(ulong) ___emain(); 22 | sweep(); 23 | syscall(Syscall.EXIT, &ec); 24 | assert(0); 25 | } 26 | 27 | mixin template entry(alias f) { 28 | static if (is(typeof(&f) == int function())) { 29 | private extern (C) int ___emain() { 30 | return f(); 31 | } 32 | } else { 33 | private extern (C) int ___emain() { 34 | printf("t={}", f.stringof); 35 | f(); 36 | return 0; 37 | } 38 | } 39 | } 40 | 41 | private void write(ref char* buf, char* s) { 42 | while (*s) 43 | write(buf, *(s++)); 44 | } 45 | 46 | private void write(ref char* buf, immutable(char)* s) { 47 | while (*s) 48 | write(buf, *(s++)); 49 | } 50 | 51 | private void write(ref char* buf, char c) { 52 | *(buf++) = c; 53 | } 54 | 55 | /// memset - fill memory with a constant byte 56 | /// 57 | /// The memset() function fills the first len bytes of the memory 58 | /// area pointed to by mem with the constant byte data. 59 | /// 60 | /// The memset() function returns a pointer to the memory area mem. 61 | extern (C) byte* memset(byte* mem, int data, size_t len) { 62 | for (size_t i = 0; i < len; i++) 63 | mem[i] = cast(byte) data; 64 | return mem; 65 | } 66 | 67 | /// Do stack unwinding 68 | private void do_backtrace() { 69 | import kernel.platform : Stackframe; 70 | Stackframe* stk; 71 | asm { 72 | mov stk, RBP; 73 | } 74 | import libsys.io : printf, FATAL; 75 | 76 | printf(FATAL, "Stack trace:"); 77 | for (;;) { 78 | // Unwind to previous stack frame 79 | printf(FATAL, " {hex}", stk.rip); 80 | if (stk.rbp == cast(Stackframe*) 0 || stk.rip == 0) 81 | break; 82 | if (stk.rip > 0xffffffff80000000) 83 | break; 84 | if ((cast(ulong) stk.rbp) > 0xfff8_0000_0000_0000) 85 | break; 86 | stk = stk.rbp; 87 | } 88 | } 89 | 90 | pragma(mangle, "__assert") extern (C) void cause_assert(immutable(char)* assertion, immutable(char)* file, int line) { 91 | char[512] buf; 92 | char[512] buf2; 93 | char* b = buf.ptr; 94 | write(b, ("[\x1b[31;1mFATAL\x1b[0m] " ~ __FILE__[3 .. __FILE__.length] ~ ":" 95 | ~ __LINE__.stringof ~ " User assertion failed: \0") 96 | .ptr); 97 | write(b, assertion); 98 | write(b, " at \0".ptr); 99 | write(b, file + 3); 100 | write(b, ":\0".ptr); 101 | write(b, intToString(line, buf2.ptr, 10)); 102 | write(b, "\n\0".ptr); 103 | int le = cast(int)(b - buf.ptr); 104 | KPrintBuffer kbuf; 105 | kbuf.len = le; 106 | kbuf.ptr = buf.ptr; 107 | ulong exitcode = 127; 108 | syscall(Syscall.KPRINT, cast(void*)&kbuf); 109 | do_backtrace(); 110 | syscall(Syscall.EXIT, &exitcode); 111 | while (1) { 112 | } 113 | } 114 | 115 | 116 | /// memcpy - copy memory area 117 | /// 118 | /// The memcpy() function copies n bytes from memory area src to memory area dest. 119 | /// The memory areas must not overlap. Use memmove(3) if the memory 120 | /// areas do overlap. 121 | /// 122 | /// The memcpy() function returns a pointer to dest. 123 | extern (C) byte* memcpy(byte* dst, const byte* src, size_t n) { 124 | size_t i = 0; 125 | while (i + 8 <= n) { 126 | *(cast(ulong*)(&dst[i])) = *(cast(ulong*)(&src[i])); 127 | i += 8; 128 | } 129 | while (i + 4 <= n) { 130 | *(cast(uint*)(&dst[i])) = *(cast(uint*)(&src[i])); 131 | i += 4; 132 | } 133 | while (i + 2 <= n) { 134 | *(cast(ushort*)(&dst[i])) = *(cast(ushort*)(&src[i])); 135 | i += 2; 136 | } 137 | while (i + 1 <= n) { 138 | *(cast(byte*)(&dst[i])) = *(cast(byte*)(&src[i])); 139 | i += 1; 140 | } 141 | return dst; 142 | } -------------------------------------------------------------------------------- /source/libsys/errno.d: -------------------------------------------------------------------------------- 1 | module libsys.errno; 2 | 3 | static import vshared.share; 4 | 5 | const EPERM = vshared.share.EPERM; 6 | const ENOENT = vshared.share.ENOENT; 7 | const ESRCH = vshared.share.ESRCH; 8 | const EINTR = vshared.share.EINTR; 9 | const EIO = vshared.share.EIO; 10 | const ENXIO = vshared.share.ENXIO; 11 | const E2BIG = vshared.share.E2BIG; 12 | const ENOEXEC = vshared.share.ENOEXEC; 13 | const EBADF = vshared.share.EBADF; 14 | const ECHILD = vshared.share.ECHILD; 15 | const EAGAIN = vshared.share.EAGAIN; 16 | const ENOMEM = vshared.share.ENOMEM; 17 | const EACCES = vshared.share.EACCES; 18 | const EFAULT = vshared.share.EFAULT; 19 | const ENOTBLK = vshared.share.ENOTBLK; 20 | const EBUSY = vshared.share.EBUSY; 21 | const EEXIST = vshared.share.EEXIST; 22 | const EXDEV = vshared.share.EXDEV; 23 | const ENODEV = vshared.share.ENODEV; 24 | const ENOTDIR = vshared.share.ENOTDIR; 25 | const EISDIR = vshared.share.EISDIR; 26 | const EINVAL = vshared.share.EINVAL; 27 | const ENFILE = vshared.share.ENFILE; 28 | const EMFILE = vshared.share.EMFILE; 29 | const ENOTTY = vshared.share.ENOTTY; 30 | const ETXTBSY = vshared.share.ETXTBSY; 31 | const EFBIG = vshared.share.EFBIG; 32 | const ENOSPC = vshared.share.ENOSPC; 33 | const ESPIPE = vshared.share.ESPIPE; 34 | const EROFS = vshared.share.EROFS; 35 | const EMLINK = vshared.share.EMLINK; 36 | const EPIPE = vshared.share.EPIPE; 37 | const EDOM = vshared.share.EDOM; 38 | const ERANGE = vshared.share.ERANGE; 39 | const EDEADLK = vshared.share.EDEADLK; 40 | const ENAMETOOLONG = vshared.share.ENAMETOOLONG; 41 | const ENOLCK = vshared.share.ENOLCK; 42 | const ENOSYS = vshared.share.ENOSYS; 43 | const ENOTEMPTY = vshared.share.ENOTEMPTY; 44 | const ELOOP = vshared.share.ELOOP; 45 | const ENOMSG = vshared.share.ENOMSG; 46 | const EEMPTY = vshared.share.EEMPTY; 47 | 48 | /// The errno 49 | __gshared long errno = 0; 50 | 51 | long error_out(long e) { 52 | errno = 0; 53 | if (e) errno = e; 54 | return e; 55 | } 56 | 57 | T must_succeed(string F = __FILE__, string fn = __FUNCTION__, int L = __LINE__, T)(T v) { 58 | if (errno) { 59 | import libsys.entry; 60 | 61 | perror!(F, L)(fn); 62 | cause_assert("must_succeed failed.".ptr, F.ptr, L); 63 | } 64 | return v; 65 | } 66 | 67 | void perror(string F = __FILE__, int L = __LINE__)(string e) { 68 | import libsys.io; 69 | string err = "Unknown error"; 70 | 71 | switch (errno) { 72 | case EPERM: err = "Permission denied"; break; 73 | case ENOENT: err = "No such file or directory"; break; 74 | case ESRCH: err = "No such process"; break; 75 | case EINTR: err = "Interrupted system call"; break; 76 | case EIO: err = "IO error"; break; 77 | case ENXIO: err = "The device configuration is missing or is invalid"; break; 78 | case E2BIG: err = "Too many arguments!"; break; 79 | case ENOEXEC: err = "Executable format incorrect"; break; 80 | case EBADF: err = "An invalid file descriptor was used"; break; 81 | case ECHILD: err = "No child processes"; break; 82 | case EAGAIN: err = "Resource temporarily unavailable, try again later"; break; 83 | case ENOMEM: err = "No memory left"; break; 84 | case EACCES: err = "Permission denied"; break; 85 | case EFAULT: err = "Bad addresss (memory operand passed is invalid)"; break; 86 | case ENOTBLK: err = "The target device is not a block device"; break; 87 | case EBUSY: err = "The target resource is busy"; break; 88 | case EEXIST: err = "The file cannot be created as it already exists"; break; 89 | case EXDEV: err = "It is prohibited to hardlink across different devices (use symbolic links?)"; break; 90 | case ENODEV: err = "The desired operation is not supported by the device"; break; 91 | case ENOTDIR: err = "The target is a file not a directory"; break; 92 | case EISDIR: err = "The target is not a regular file but instead a directory"; break; 93 | case EINVAL: err = "Invalid argument"; break; 94 | case ENFILE: err = "Too many open files"; break; 95 | case EMFILE: err = "Too many open files"; break; 96 | case ENOTTY: err = "Inappropriate ioctl for device"; break; 97 | case ETXTBSY: err = "Target text file is busy; try again later"; break; 98 | case EFBIG: err = "The file is too large"; break; 99 | case ENOSPC: err = "No space left on the device"; break; 100 | case ESPIPE: err = "Attempt to illegaly seek out-of-bounds"; break; 101 | case EROFS: err = "This file system is not writable"; break; 102 | case EMLINK: err = "Too many hard links to a single file; consider using symbolic links instead"; break; 103 | case EPIPE: err = "Broken pipe"; break; 104 | case ERANGE: err = "The result is too large"; break; 105 | case EDEADLK: err = "This operation would cause a deadlock, which was avoided"; break; 106 | case ENAMETOOLONG: err = "This file name is too long"; break; 107 | case ENOLCK: err = "No locks are available"; break; 108 | case ENOSYS: err = "Not implemented"; break; 109 | case ENOTEMPTY: err = "The target directory not empty"; break; 110 | case ELOOP: err = "Too much symbolic link nesting, i give up"; break; 111 | case ENOMSG: err = "You asked me not to wait, and i can't find the message you want"; break; 112 | case EEMPTY: err = "The target file is empty"; break; 113 | default: 114 | printf(WARN, "unknown error {}", errno); 115 | } 116 | 117 | errno = 0; 118 | 119 | printf!(F, L)("{}: {}", e, err); 120 | } -------------------------------------------------------------------------------- /source/libsys/io.d: -------------------------------------------------------------------------------- 1 | module libsys.io; 2 | 3 | import core.vararg; 4 | import core.volatile; 5 | import std.traits; 6 | import std.algorithm; 7 | import libsys.syscall; 8 | import vshared.share; 9 | import libsys.util; 10 | 11 | /// puts_const_str is like puts but for const(string) 12 | void puts_const_str(const(string) s) { 13 | foreach (chr; s) { 14 | if (chr == 0) 15 | break; 16 | putc(chr); 17 | } 18 | } 19 | 20 | /// puts_const_str is like puts but for const(string) 21 | void puts_const_str(string s) { 22 | foreach (chr; s) { 23 | if (chr == 0) 24 | break; 25 | putc(chr); 26 | } 27 | } 28 | 29 | /// puts is a dumb version of printf (with no newline!) 30 | void puts(char* s) { 31 | for (int i = 0; s[i] != 0; i++) { 32 | putc(s[i]); 33 | } 34 | } 35 | 36 | /// puts is a dumb version of printf (with no newline!) 37 | void puts(immutable(char)[] s) { 38 | foreach (chr; s) { 39 | putc(chr); 40 | } 41 | } 42 | 43 | /// puts is a dumb version of printf (with no newline!) 44 | private void puts_string(T)(T s) { 45 | foreach (chr; s) { 46 | putc(chr); 47 | } 48 | } 49 | 50 | private __gshared char[128] buf; 51 | private __gshared int buf_idx = 0; 52 | 53 | /// Flush the I/O buffer 54 | void flush() { 55 | KPrintBuffer kbuf; 56 | kbuf.len = buf_idx; 57 | kbuf.ptr = buf.ptr; 58 | syscall(Syscall.KPRINT, cast(void*)&kbuf); 59 | buf_idx = 0; 60 | } 61 | 62 | /// putc prints a char (buffered) 63 | extern(C) void putc(char c) { 64 | if (c != 0) { 65 | if (buf_idx >= 128) flush(); 66 | buf[buf_idx++] = c; 67 | if (c == '\n') flush(); 68 | } 69 | } 70 | 71 | /// puts is a dumb version of printf (with no newline!) 72 | void puts(T)(T s) { 73 | static assert(is(Unqual!(T) : ArrayMarker!char)); 74 | foreach (char chr; s) { 75 | putc(chr); 76 | } 77 | } 78 | 79 | /// puts is a dumb version of printf (with no newline!) 80 | void puts(char s) { 81 | putc(s); 82 | } 83 | 84 | private struct EnumMarker { 85 | 86 | } 87 | 88 | private struct ArrayMarker(T) { 89 | } 90 | 91 | private struct IOIterMarker(T) { 92 | } 93 | 94 | /// A hex printer for `T` 95 | extern (C) struct Hex(T) { 96 | /// The value of the `hex` 97 | align(T.alignof) T inner; 98 | /// Create a new Hex 99 | this(T inner) { 100 | this.inner = inner; 101 | } 102 | } 103 | 104 | private template isEnum(alias symb) { 105 | static if (is(symb == enum)) 106 | enum bool isEnum = true; 107 | else 108 | enum bool isEnum = false; 109 | } 110 | 111 | private template Unqual(T) { 112 | static if (is(T U == shared(const U))) 113 | alias Unqual = U; 114 | else static if (is(T U == const U)) 115 | alias Unqual = U; 116 | else static if (is(T U == immutable U)) 117 | alias Unqual = U; 118 | else static if (is(T U == shared U)) 119 | alias Unqual = U; 120 | else static if (is(T == string)) 121 | alias Unqual = ArrayMarker!char; 122 | else static if (__traits(hasMember, T, "ioiter")) 123 | alias Unqual = IOIterMarker!(ReturnType!(__traits(getMember, T, "ioiter"))); 124 | else static if (isArray!(T)) { 125 | alias Unqual = ArrayMarker!(typeof(T.init[0])); 126 | } else static if (isEnum!(T)) 127 | alias Unqual = EnumMarker; 128 | else 129 | alias Unqual = T; 130 | } 131 | 132 | private template UnPtr(T) { 133 | static if (is(T U == U*)) 134 | alias UnPtr = void*; 135 | else 136 | alias UnPtr = T; 137 | } 138 | 139 | private template Deref(T) { 140 | static if (is(T U == U*)) 141 | alias Deref = Deref(U); 142 | else 143 | alias Deref = T; 144 | } 145 | 146 | private void _printf_outint(string subarray, ulong arg, bool bare = false) { 147 | int pad = 0; 148 | int base = 10; 149 | switch (subarray) { 150 | case "_chr_oob": 151 | base = 16; 152 | pad = 2; 153 | break; 154 | case "hex": 155 | if (!bare) 156 | puts("0x"); 157 | base = 16; 158 | break; 159 | case "ptr": 160 | if (!bare) 161 | puts("0x"); 162 | base = 16; 163 | pad = 16; 164 | break; 165 | case "": 166 | break; 167 | case "oct": 168 | if (!bare) 169 | puts("0o"); 170 | base = 8; 171 | break; 172 | case "bin": 173 | if (!bare) 174 | puts("0b"); 175 | base = 2; 176 | break; 177 | default: 178 | assert(false); 179 | } 180 | char[70] arr; 181 | char* arr_offset_correctly = intToString(arg, arr.ptr, base); 182 | const long arr_offset_correctly_len = strlen(arr_offset_correctly); 183 | const long pad_needed = max(0, pad - arr_offset_correctly_len); 184 | for (int i = 0; i < pad_needed; i++) 185 | puts('0'); 186 | puts(arr_offset_correctly); 187 | } 188 | 189 | private void _printf_outint(string subarray, long arg) { 190 | int pad = 0; 191 | int base = 10; 192 | switch (subarray) { 193 | case "hex": 194 | puts("0x"); 195 | base = 16; 196 | break; 197 | case "ptr": 198 | puts("0x"); 199 | base = 16; 200 | pad = 8; 201 | break; 202 | case "": 203 | break; 204 | case "oct": 205 | puts("0o"); 206 | base = 8; 207 | break; 208 | case "bin": 209 | base = 2; 210 | puts("0b"); 211 | break; 212 | default: 213 | assert(false); 214 | } 215 | char[70] arr; 216 | char* arr_offset_correctly = intToString(arg, arr.ptr, base); 217 | const long arr_offset_correctly_len = strlen(arr_offset_correctly); 218 | const long pad_needed = max(0, pad - arr_offset_correctly_len); 219 | for (int i = 0; i < pad_needed; i++) 220 | puts('0'); 221 | puts(arr_offset_correctly); 222 | } 223 | 224 | private template GetOMeta(string Target) { 225 | const char[] GetOMeta = "OMeta meta = arg._ometa_" ~ Target ~ ";"; 226 | } 227 | 228 | private template HasOMeta(string Target) { 229 | const char[] HasOMeta = "__traits(compiles, arg._ometa_" ~ Target ~ ")"; 230 | } 231 | /// An O-Meta 232 | struct OMeta { 233 | /// Should we ignore the value? If yes, print `fmt` 234 | bool ignore = false; 235 | /// Should we _fully_ ignore the value? 236 | bool nuke = false; 237 | /// The format string for this O-Meta 238 | string fmt = ""; 239 | /// Should we print it raw? 240 | bool print_raw = false; 241 | /// Internal print skipper 242 | byte _oskip = 0; 243 | } 244 | /// Get a hex printer O-Meta 245 | OMeta hex_ometa() { 246 | OMeta m; 247 | m.fmt = "hex"; 248 | m.print_raw = false; 249 | return m; 250 | } 251 | /// Get a hex printer O-Meta 252 | OMeta disabler_ometa() { 253 | OMeta m; 254 | m.nuke = true; 255 | return m; 256 | } 257 | /// Get a pointer printer O-Meta 258 | OMeta ptr_ometa() { 259 | OMeta m; 260 | m.fmt = "ptr"; 261 | m.print_raw = false; 262 | return m; 263 | } 264 | 265 | unittest { 266 | printf("Test printf [short]: {}", cast(short) 3); 267 | printf("Test printf [ushort]: {}", cast(ushort) 3); 268 | printf("Test printf [int]: {}", cast(int) 3); 269 | printf("Test printf [uint]: {}", cast(uint) 3); 270 | printf("Test printf [long]: {}", cast(long) 3); 271 | printf("Test printf [ulong]: {}", cast(ulong) 3); 272 | printf("Test printf [string]: {}", "asdf"); 273 | printf("Test printf [char*]: {}", "asdf".ptr); 274 | } 275 | 276 | /// Print an object! 277 | void putdyn(ObjTy)(string subarray, ObjTy arg, int prenest = 0, bool is_field = false) { 278 | ObjTy arg2 = arg; 279 | putdyn(subarray, arg2, prenest, is_field); 280 | } 281 | 282 | /// Print an object! 283 | void putdyn(ObjTy)(string subarray, ref ObjTy arg, int prenest = 0, bool is_field = false) { 284 | if (subarray == ":?") { 285 | subarray = ""; 286 | is_field = true; 287 | } 288 | alias T = Unqual!(typeof(arg)); 289 | static if (is(T : const char[])) { 290 | assert(subarray == ""); 291 | if (is_field) { 292 | puts('"'); 293 | foreach (chr; arg) { 294 | if (chr == '\n') { 295 | puts("\\n"); 296 | } else if (chr > 0x7e || chr < 0x20) { 297 | puts("\\x"); 298 | _printf_outint("_chr_oob", cast(ulong) chr); 299 | } else { 300 | puts(chr); 301 | } 302 | } 303 | puts('"'); 304 | } else { 305 | puts_string(arg); 306 | } 307 | } 308 | static if (is(T == EnumMarker)) { 309 | static foreach (k; __traits(allMembers, ObjTy)) { 310 | { 311 | if (__traits(getMember, ObjTy, k) == arg) { 312 | puts_string(k); 313 | return; 314 | } 315 | } 316 | } 317 | // it's a bitfield 318 | putc('['); 319 | bool do_space = true; 320 | static foreach (k; __traits(allMembers, ObjTy)) { 321 | { 322 | if (__traits(getMember, ObjTy, k) & arg) { 323 | if (do_space) { 324 | do_space = false; 325 | putc(' '); 326 | } 327 | puts_string(k); 328 | putc(' '); 329 | } 330 | } 331 | } 332 | putc(']'); 333 | } else static if (is(T : const char*)) { 334 | assert(subarray == ""); 335 | if (is_field) { 336 | puts('"'); 337 | for (int i = 0; arg[i]; i++) { 338 | if (arg[i] == '\n') { 339 | puts("\\n"); 340 | } else if (arg[i] > 0x7e || arg[i] < 0x20) { 341 | puts("\\x"); 342 | _printf_outint("_chr_oob", cast(ulong) arg[i]); 343 | } else { 344 | puts(arg[i]); 345 | } 346 | } 347 | puts('"'); 348 | } else { 349 | for (int i = 0; arg[i]; i++) { 350 | putc(arg[i]); 351 | } 352 | } 353 | } else static if (is(T : ArrayMarker!char)) { 354 | if (is_field) { 355 | puts('"'); 356 | foreach (chr; arg) { 357 | if (chr == '\n') { 358 | puts("\\n"); 359 | } else if (chr > 0x7e || chr < 0x20) { 360 | puts("\\x"); 361 | _printf_outint("_chr_oob", cast(ulong) chr); 362 | } else { 363 | puts(chr); 364 | } 365 | } 366 | puts('"'); 367 | } else { 368 | puts_string(arg); 369 | } 370 | } else static if (is(T == char)) { 371 | puts("'"); 372 | if (arg == '\n') { 373 | puts("\\n"); 374 | } else if (arg > 0x7e || arg < 0x20) { 375 | puts("\\x"); 376 | _printf_outint("_chr_oob", cast(ulong) arg); 377 | } else { 378 | puts(arg); 379 | } 380 | puts("'"); 381 | } else static if (is(T U == Hex!U)) { 382 | assert(subarray == ""); 383 | putdyn("hex", arg.inner, prenest, is_field); 384 | } else static if (is(T == byte)) { 385 | _printf_outint(subarray, cast(long) arg); 386 | } else static if (is(T == ubyte)) { 387 | _printf_outint(subarray, cast(ulong) arg); 388 | } else static if (is(T == int)) { 389 | _printf_outint(subarray, cast(long) arg); 390 | } else static if (is(T == uint)) { 391 | _printf_outint(subarray, cast(ulong) arg); 392 | } else static if (is(T == short)) { 393 | _printf_outint(subarray, cast(long) arg); 394 | } else static if (is(T == ushort)) { 395 | _printf_outint(subarray, cast(ulong) arg); 396 | } else static if (is(T == long)) { 397 | _printf_outint(subarray, arg); 398 | } else static if (is(T == ulong)) { 399 | _printf_outint(subarray, arg); 400 | } else static if (is(T == bool)) { 401 | puts(arg ? "true" : "false"); 402 | } else static if (is(T == void)) { 403 | puts("void"); 404 | } else static if (is(T == void*)) { 405 | assert(subarray == ""); 406 | _printf_outint("ptr", cast(ulong) arg); 407 | } else static if (is(T U == ArrayMarker!U)) { 408 | puts('['); 409 | bool is_first = true; 410 | foreach (member; arg) { 411 | if (is_first) { 412 | puts('\n'); 413 | for (int i = 0; i < prenest; i++) { 414 | puts(" "); 415 | } 416 | is_first = false; 417 | } 418 | puts(" "); 419 | { 420 | putdyn(subarray, member, prenest + 4, true); 421 | } 422 | puts('\n'); 423 | for (int i = 0; i < prenest; i++) { 424 | puts(" "); 425 | } 426 | } 427 | puts(']'); 428 | } else static if (is(T U == IOIterMarker!U)) { 429 | puts('['); 430 | bool is_first = true; 431 | int max_counter = 0; 432 | foreach (member; arg.ioiter()) { 433 | max_counter++; 434 | if (max_counter > 15) { 435 | puts(" ... snip"); 436 | puts('\n'); 437 | for (int i = 0; i < prenest; i++) { 438 | puts(" "); 439 | } 440 | break; 441 | } 442 | if (is_first) { 443 | puts('\n'); 444 | for (int i = 0; i < prenest; i++) { 445 | puts(" "); 446 | } 447 | is_first = false; 448 | } 449 | puts(" "); 450 | { 451 | putdyn(subarray, member, prenest + 4, true); 452 | } 453 | puts('\n'); 454 | for (int i = 0; i < prenest; i++) { 455 | puts(" "); 456 | } 457 | } 458 | puts(']'); 459 | } else static if (is(T U == Option!U)) { 460 | if (arg.is_some()) { 461 | puts("Some("); 462 | putdyn(subarray, arg.unwrap(), prenest, true); 463 | puts(")"); 464 | } else { 465 | assert(arg.is_none()); 466 | puts("None"); 467 | } 468 | } else { 469 | alias U = UnPtr!(T); 470 | static if (is(U == void*) && __traits(hasMember, T, "__hide_deref")) { 471 | if (cast(ulong) arg != 0) { 472 | putdyn(subarray, *arg, prenest); 473 | } else { 474 | puts("(null)"); 475 | } 476 | } else static if (is(U == void*)) { 477 | string asdf = T.stringof; 478 | puts_const_str(asdf); 479 | puts(" @ "); 480 | _printf_outint("ptr", cast(ulong) arg); 481 | if (cast(ulong) arg != 0) { 482 | puts(" "); 483 | putdyn(subarray, *arg, prenest); 484 | } 485 | } else { 486 | static if (__traits(hasMember, T, "opFormat")) { 487 | putdyn(subarray, T.opFormat(), prenest); 488 | } else static if (__traits(hasMember, T, "opFormatter")) { 489 | arg.opFormatter(subarray, prenest); 490 | } else { 491 | puts('{'); 492 | bool is_first = true; 493 | static foreach (member; [__traits(allMembers, T)]) { 494 | static if (member.length > 6 && member[0 .. 6] == "_prnt_") { 495 | if (is_first) { 496 | puts('\n'); 497 | for (int i = 0; i < prenest; i++) { 498 | puts(" "); 499 | } 500 | is_first = false; 501 | } 502 | puts(" "); 503 | puts(member[6 .. member.length]); 504 | puts(": "); 505 | __traits(getMember, arg, member)(subarray, prenest + 4); 506 | puts('\n'); 507 | for (int i = 0; i < prenest; i++) { 508 | puts(" "); 509 | } 510 | } else static if (!isCallable!(__traits(getMember, arg, member)) && __traits(compiles, putdyn( 511 | subarray, __traits(getMember, arg, 512 | member), prenest + 4, true))) { 513 | static if (!__traits(compiles, __traits(getMember, arg, member) 514 | ._oskip) && !__traits(hasMember, arg, "__noshow_" ~ member)) { 515 | if (is_first) { 516 | puts('\n'); 517 | for (int i = 0; i < prenest; i++) { 518 | puts(" "); 519 | } 520 | is_first = false; 521 | } 522 | puts(" "); 523 | puts(member); 524 | puts(": "); 525 | static if (mixin(HasOMeta!(member))) { 526 | { 527 | mixin(GetOMeta!(member)); 528 | putdyn(meta.fmt, __traits(getMember, arg, 529 | member), prenest + 4, meta.print_raw); 530 | } 531 | } else { 532 | putdyn(subarray, __traits(getMember, arg, 533 | member), prenest + 4, true); 534 | } 535 | puts('\n'); 536 | for (int i = 0; i < prenest; i++) { 537 | puts(" "); 538 | } 539 | } 540 | } 541 | } 542 | puts('}'); 543 | } 544 | } 545 | } 546 | } 547 | 548 | /// Log level 549 | enum Log { 550 | DEBUG, 551 | INFO, 552 | WARN, 553 | ERROR, 554 | FATAL, 555 | } 556 | 557 | /// Debug 558 | public const Log DEBUG = Log.DEBUG; 559 | /// Info 560 | public const Log INFO = Log.INFO; 561 | /// Warn 562 | public const Log WARN = Log.WARN; 563 | /// Error 564 | public const Log ERROR = Log.ERROR; 565 | /// Fatal 566 | public const Log FATAL = Log.FATAL; 567 | 568 | private template Digit(uint n) { 569 | public __gshared enum char[] Digit = [("0123456789"[n .. n + 1])[0]]; 570 | } 571 | 572 | private template Itoa(uint n) { 573 | static if (n < 0) 574 | public __gshared const char[] Itoa = "-" ~ Itoa!(-n); 575 | else static if (n < 10) 576 | public __gshared const char[] Itoa = Digit!(n); 577 | else 578 | public __gshared const char[] Itoa = Itoa!(n / 10) ~ Digit!(n % 10); 579 | } 580 | 581 | private __gshared ulong lineno_max = 3; 582 | 583 | /// Print a string 584 | void printf(string A = __FILE__, int L = __LINE__, Args...)(Log l, string s, Args args) { 585 | printf(L, A, l, s, args); 586 | } 587 | /// Print a string 588 | void printf(Args...)(int L, string A, Log l, string s, Args args) { 589 | version(DiodeNoDebug) { 590 | if (l == Log.DEBUG) return; 591 | } 592 | ulong maxl = 4; 593 | putc('['); 594 | switch (l) { 595 | case DEBUG: 596 | puts("\x1b[30;1mDEBUG"); 597 | maxl = 5; 598 | break; 599 | case INFO: 600 | puts("\x1b[32;1mINFO"); 601 | break; 602 | case WARN: 603 | puts("\x1b[33;1mWARN"); 604 | break; 605 | case ERROR: 606 | puts("\x1b[31;1mERROR"); 607 | maxl = 5; 608 | break; 609 | case FATAL: 610 | puts("\x1b[31;1mFATAL"); 611 | maxl = 5; 612 | break; 613 | default: 614 | printf(FATAL, "Invalid level: {}", cast(int) l); 615 | assert(0); 616 | } 617 | puts("\x1b[0m] "); 618 | puts_string(A[3 .. A.length]); 619 | puts(":"); 620 | char[32] buffer; 621 | char* asds = intToString(L, buffer.ptr, 10); 622 | puts(asds); 623 | putc(' '); 624 | ulong asdslength = strlen(asds); 625 | if (lineno_max < asdslength) 626 | lineno_max = asdslength; 627 | for (ulong i = asdslength; i < lineno_max; i++) 628 | putc(' '); 629 | 630 | int offinit = cast(int)(lineno_max + A.length - 5 + maxl); 631 | 632 | int idx_into_s = 0; 633 | foreach (arg; args) { 634 | 635 | // advance s 636 | while (s[idx_into_s] != '{') 637 | putc(s[idx_into_s++]); 638 | const int og_idx = idx_into_s + 1; 639 | while (s[idx_into_s++] != '}') { 640 | } 641 | const string subarray = s[og_idx .. idx_into_s - 1]; 642 | 643 | putdyn(subarray, arg, offinit); 644 | } 645 | while (idx_into_s < s.length) 646 | putc(s[idx_into_s++]); 647 | putc('\n'); 648 | } 649 | 650 | /// Print a string 651 | void printf(string A = __FILE__, int L = __LINE__, Args...)(string s, Args args) { 652 | printf!(A, L, Args)(INFO, s, args); 653 | } 654 | -------------------------------------------------------------------------------- /source/libsys/loaf.d: -------------------------------------------------------------------------------- 1 | module libsys.loaf; 2 | 3 | import std.traits; 4 | import libsys.mem; 5 | 6 | private template isEnum(alias symb) { 7 | static if (is(symb == enum)) 8 | enum bool isEnum = true; 9 | else 10 | enum bool isEnum = false; 11 | } 12 | 13 | byte[] loaf_encode(T)(T v) { 14 | return loaf_encode(v); 15 | } 16 | /// Loaf encoder 17 | byte[] loaf_encode(T)(ref T v) { 18 | static if (isIntegral!(T)) { 19 | byte[] data = arr!(byte)(); 20 | static if (isSigned!(T)) { 21 | if (v < 0) { 22 | data = concat(data, 1); 23 | v = -v; 24 | } else { 25 | data = concat(data, 0); 26 | } 27 | } 28 | while (v > 0) { 29 | byte nibble = cast(byte)(v & 0xff); 30 | // encoding: 0x01 31 | if (nibble == 0 || nibble == 1) { 32 | data = concat(data, 1); 33 | } 34 | data = concat(data, nibble); 35 | v = v >> 8; 36 | } 37 | return concat(data, 0); 38 | } else static if(isEnum!(T)) { 39 | return encode(cast(ulong)v); 40 | } else { 41 | byte[] data = arr!byte(); 42 | static foreach (member; __traits(allMembers, T)) { 43 | { 44 | static if (__traits(compiles, loaf_encode(__traits(getMember, v, member)))) { 45 | data = concat(data, loaf_encode(__traits(getMember, v, member))); 46 | } else { 47 | loaf_encode(__traits(getMember, v, member)); 48 | static assert(0, "Can't print " ~ T.stringof ~ "." ~ member); 49 | } 50 | } 51 | } 52 | return data; 53 | } 54 | 55 | } 56 | 57 | void loaf_decode(T)(ref T v, ref byte[] data) { 58 | ulong pos; 59 | loaf_decode(v, pos, data); 60 | } 61 | /// Loaf decoder 62 | void loaf_decode(T)(ref T v, ref ulong pos, ref byte[] data) { 63 | static if (isIntegral!(T)) { 64 | bool sign = false; 65 | static if (isSigned!(T)) { 66 | sign = data[pos++] == 1; 67 | } 68 | ulong val = 0; 69 | ulong bits = 0; 70 | while (data[pos] != 0) { 71 | byte nibble = data[pos++]; 72 | if (nibble == 1) { 73 | nibble = data[pos++]; 74 | } else if (nibble == 0) { 75 | break; 76 | } 77 | val >>= 8; 78 | val |= (cast(ulong)nibble) << 64 - 8; 79 | bits += 8; 80 | } 81 | val >>= 64 - bits; 82 | pos++; 83 | static if (isSigned!(T)) { 84 | if (sign) { 85 | v = -cast(T)val; 86 | return; 87 | } 88 | } 89 | v = cast(T)val; 90 | return; 91 | } else static if (isEnum!(T)) { 92 | static assert(0); 93 | } else { 94 | static foreach (member; __traits(allMembers, T)) { 95 | { 96 | loaf_decode(__traits(getMember, v, member), pos, data); 97 | } 98 | } 99 | } 100 | } -------------------------------------------------------------------------------- /source/libsys/mem.d: -------------------------------------------------------------------------------- 1 | module libsys.mem; 2 | 3 | import libsys.syscall; 4 | import vshared.share; 5 | import libsys.errno; 6 | import libsys.io; 7 | import std.conv; 8 | 9 | // My memory manager 10 | // cool stuff 11 | // 12 | // the concept: 13 | // we have three zones: 14 | // slab64's - 64-byte slabs for anything <= 64 15 | // bigblk's - a bitmap allocator 16 | // huge8k's - large >8k zones from mmap(). Aligned to 64 bytes (read: beginning_of_page+64) 17 | 18 | // slab64 stuff 19 | private struct Slab64 { 20 | Slab64* next; 21 | // amount of slabs 22 | ulong count; 23 | } 24 | 25 | __gshared ulong slab64_mem_low = 0; 26 | __gshared ulong slab64_mem_size = 0; 27 | __gshared Slab64* first_slab64 = cast(Slab64*) 0; 28 | bool is_slab64(void* addr) { 29 | return is_slab64(cast(ulong) addr); 30 | } 31 | 32 | bool is_slab64(ulong addr) { 33 | if (!slab64_mem_low) 34 | return false; 35 | if (addr < slab64_mem_low) 36 | return false; 37 | if (addr > slab64_mem_size + slab64_mem_low) 38 | return false; 39 | return true; 40 | } 41 | 42 | void slab64_commit() { 43 | if (!slab64_mem_low) { 44 | slab64_mem_low = cast(ulong) must_succeed(mmap(cast(void*) 0, 4096, MMapFlags.MAP_PRIVATE)); 45 | } else { 46 | must_succeed(mmap(cast(void*)(slab64_mem_low + slab64_mem_size), 4096, MMapFlags.MAP_FIXED | MMapFlags 47 | .MAP_PRIVATE)); 48 | } 49 | Slab64* slab = cast(Slab64*)(slab64_mem_low + slab64_mem_size); 50 | slab.count = 64; 51 | slab.next = first_slab64; 52 | first_slab64 = slab; 53 | slab64_mem_size += 4096; 54 | } 55 | 56 | void* alloc_slab64(ulong size) { 57 | assert(size <= 64); 58 | size = 64; 59 | if (first_slab64 == cast(Slab64*) 0) { 60 | slab64_commit(); 61 | } 62 | if (first_slab64.count > 1) { 63 | first_slab64.count -= 1; 64 | return cast(void*)(first_slab64.count * 64 + cast(ulong) first_slab64); 65 | } 66 | void* data = cast(void*) first_slab64; 67 | first_slab64 = first_slab64.next; 68 | return data; 69 | } 70 | 71 | void free_slab64(void* mem) { 72 | debug assert(is_slab64(cast(ulong) mem)); 73 | Slab64* new_head = cast(Slab64*) mem; 74 | new_head.count = 1; 75 | new_head.next = first_slab64; 76 | first_slab64 = new_head; 77 | } 78 | 79 | // bigblk stuff 80 | __gshared ulong bigblk_mem_low = 0; 81 | __gshared ulong bigblk_bitmap_low = 0; 82 | __gshared ulong bigblk_mem_size = 0; 83 | __gshared ulong bigblk_bitmap_size = 0; 84 | __gshared ulong bigblk_bitmap_off = 0; 85 | 86 | bool is_bigblk(ulong addr) { 87 | if (!bigblk_mem_low) 88 | return false; 89 | if (addr < bigblk_mem_low) 90 | return false; 91 | if (addr > bigblk_mem_size + bigblk_mem_low) 92 | return false; 93 | return true; 94 | } 95 | 96 | bool testbits(ulong* mem, ulong offset, ulong count) { 97 | foreach (i; 0 .. count) { 98 | if ((mem[(offset + i) >> 6] >> ((offset + i) & 0x3f)) & 1) 99 | return false; 100 | } 101 | return true; 102 | } 103 | 104 | void setbits(ulong* mem, ulong offset, ulong count) { 105 | foreach (i; 0 .. count) { 106 | mem[(offset + i) >> 6] |= 1 << ((offset + i) & 0x3f); 107 | } 108 | } 109 | 110 | void clearbits(ulong* mem, ulong offset, ulong count) { 111 | foreach (i; 0 .. count) { 112 | mem[(offset + i) >> 6] &= ~(1 << ((offset + i) & 0x3f)); 113 | } 114 | } 115 | 116 | void commit_bigblk() { 117 | if (!bigblk_bitmap_low) { 118 | bigblk_bitmap_low = cast(ulong) must_succeed(mmap(cast(void*) 0, 4096, MMapFlags 119 | .MAP_PRIVATE)); 120 | } else if (bigblk_bitmap_size & 0x1000) { 121 | ulong tgd = bigblk_bitmap_low + bigblk_bitmap_size; 122 | must_succeed(mmap(cast(void*) tgd, 4096, MMapFlags.MAP_FIXED | MMapFlags.MAP_PRIVATE)); 123 | } 124 | bigblk_bitmap_size += 0x20; 125 | if (!bigblk_mem_low) { 126 | bigblk_mem_low = cast(ulong) must_succeed(mmap(cast(void*) 0, 4096, MMapFlags.MAP_PRIVATE)); 127 | } else { 128 | must_succeed(mmap(cast(void*)(bigblk_mem_low + bigblk_mem_size), 4096, MMapFlags.MAP_FIXED | MMapFlags 129 | .MAP_PRIVATE)); 130 | } 131 | bigblk_mem_size += 4096; 132 | } 133 | 134 | void* alloc_bigblk(ulong size) { 135 | void* m = _alloc_bigblk(size + 8); 136 | *(cast(ulong*) m) = size; 137 | return m + 8; 138 | } 139 | 140 | void* _alloc_bigblk(ulong size) { 141 | import core.bitop; 142 | 143 | assert(size > 64); 144 | ulong sizex = size >> 4; 145 | ulong off_start = bigblk_bitmap_off; 146 | while (true) { 147 | ulong* ptr = cast(ulong*)(bigblk_bitmap_low + bigblk_bitmap_off); 148 | 149 | foreach (i; 0 .. 64) 150 | if (testbits(ptr, i, sizex)) { 151 | setbits(ptr, i, sizex); 152 | return cast(void*)(((i + (bigblk_bitmap_off << 3)) << 4) + bigblk_mem_low); 153 | } 154 | 155 | bigblk_bitmap_off += 8; 156 | if (off_start == bigblk_bitmap_off) { 157 | bigblk_bitmap_off = bigblk_bitmap_size; 158 | commit_bigblk(); 159 | } 160 | bigblk_bitmap_off %= bigblk_bitmap_size; 161 | } 162 | } 163 | 164 | void dealloc_bigblk(void* mem) { 165 | ulong size = *(cast(ulong*) mem - 1); 166 | ulong m = cast(ulong)(mem - 1); 167 | ulong sizex = size >> 4; 168 | ulong off = ((m - bigblk_mem_low) >> 7); 169 | ulong i = ((m - bigblk_mem_low) >> 4) & 7; 170 | bigblk_bitmap_off = off; 171 | ulong* ptr = cast(ulong*)(bigblk_bitmap_low + off); 172 | clearbits(ptr, i, sizex); 173 | } 174 | 175 | // general malloc 176 | extern (C) void* malloc(ulong size) { 177 | if (size <= 64) 178 | return alloc_slab64(size); 179 | return alloc_bigblk(size); 180 | } 181 | 182 | extern (C) void free(void* mem) { 183 | if (is_slab64(mem)) 184 | dealloc_bigblk(mem); 185 | else { 186 | debug assert(is_bigblk(cast(ulong) mem)); 187 | dealloc_bigblk(mem); 188 | } 189 | } 190 | 191 | // mark & sweep 192 | private enum SweepColor { 193 | // note that scanned and not scanned are flipped around if `is_flipped` is set 194 | 195 | BLACK, // not scanned 196 | GRAY, // in process of being scanned 197 | WHITE, // scanned 198 | } 199 | 200 | private struct BlockHeader { 201 | ulong size; 202 | BlockHeader* next; 203 | BlockHeader* prev; 204 | SweepColor color; 205 | } 206 | 207 | private struct Block(T) { 208 | ulong magic; 209 | BlockHeader header; 210 | T data; 211 | } 212 | 213 | private __gshared BlockHeader* first = cast(BlockHeader*) 0; 214 | private __gshared bool is_flipped = cast(BlockHeader*) 0; 215 | __gshared ulong maysweep = 0; 216 | __gshared void** elfbase; 217 | __gshared void** elftop; 218 | 219 | private SweepColor _swept() { 220 | return is_flipped ? SweepColor.BLACK : SweepColor.WHITE; 221 | } 222 | 223 | private SweepColor _not_swept() { 224 | return is_flipped ? SweepColor.WHITE : SweepColor.BLACK; 225 | } 226 | 227 | private T[] array(T)(T* ptr, ulong len) { 228 | union U { 229 | ulong[2] a; 230 | T[] b; 231 | } 232 | U data; 233 | data.a[0] = len; 234 | data.a[1] = cast(ulong)ptr; 235 | return data.b; 236 | } 237 | 238 | T[] alloc_array(T)(ulong n) { 239 | maysweep += 1; 240 | if (maysweep == 5) { 241 | sweep(); 242 | maysweep = 0; 243 | } 244 | 245 | alias Blk = Block!(T); 246 | Blk* b = cast(Blk*) malloc(Blk.sizeof); 247 | b.magic = *cast(ulong*) "TRICOLOR".ptr; 248 | b.header.color = _swept; 249 | b.header.next = first; 250 | b.header.size = n * T.sizeof; 251 | b.header.prev = cast(BlockHeader*) 0; 252 | if (first) first.prev = &b.header; 253 | b.header.size = T.sizeof; 254 | first = &b.header; 255 | return array(&b.data, n); 256 | } 257 | 258 | T[] arr(T)(T[] args...) { 259 | return args; 260 | } 261 | T[] concat(T)(T[] arra, T[] args...) { 262 | T[] arr = alloc_array!(T)(arra.length + args.length); 263 | foreach (i; 0 .. arr.length) { 264 | if (arra.length > i) emplace(&arr.ptr[i], arra[i]); 265 | else emplace(&arr.ptr[i], args[i - arra.length]); 266 | } 267 | return arr; 268 | } 269 | T[] concat(T)(T[] arra, T[] args) { 270 | T[] arr = alloc_array!(T)(arra.length + args.length); 271 | foreach (i; 0 .. arr.length) { 272 | if (arra.length > i) emplace(&arr.ptr[i], arra[i]); 273 | else emplace(&arr.ptr[i], args[i - arra.length]); 274 | } 275 | return arr; 276 | } 277 | 278 | 279 | 280 | T* alloc(T)() { 281 | maysweep += 1; 282 | if (maysweep == 5) { 283 | sweep(); 284 | maysweep = 0; 285 | } 286 | 287 | alias Blk = Block!(T); 288 | Blk* b = cast(Blk*) malloc(Blk.sizeof); 289 | b.magic = *cast(ulong*) "TRICOLOR".ptr; 290 | b.header.color = _swept; 291 | b.header.next = first; 292 | b.header.size = T.sizeof; 293 | b.header.prev = cast(BlockHeader*) 0; 294 | if (first) first.prev = &b.header; 295 | b.header.size = T.sizeof; 296 | first = &b.header; 297 | return &b.data; 298 | } 299 | 300 | private void do_dealloc(BlockHeader* v) { 301 | if (v.next) 302 | v.next.prev = v.prev; 303 | if (v.prev) 304 | v.prev.next = v.next; 305 | if (first == v) 306 | first = v.next; 307 | // printf(DEBUG, "Freeing {}", (cast(void*) v) - 8); 308 | free((cast(void*) v) - 8); 309 | } 310 | 311 | private void do_sweep_of(void* d) { 312 | void* ogptr = d; 313 | 314 | if (!is_bigblk(cast(ulong) d) && !is_slab64(d)) /* not a slab64 or a bigblk */ return; 315 | d = d - BlockHeader.sizeof - ulong.sizeof; 316 | if (!is_bigblk(cast(ulong) d) && !is_slab64(d)) /* also not a slab64 or a bigblk */ return; 317 | 318 | ulong magic = *cast(ulong*)(d); 319 | if (magic != *cast(ulong*) "TRICOLOR".ptr) /* wrong magic */ return; 320 | 321 | // we are pretty sure this is a valid object. sweep it. 322 | BlockHeader* hdr = cast(BlockHeader*)(d + ulong.sizeof); 323 | 324 | if (hdr.color == SweepColor.GRAY) /* being sweeped */ return; 325 | 326 | hdr.color = SweepColor.GRAY; 327 | 328 | ulong size = hdr.size; 329 | ulong ptrcnt = size / 8; 330 | foreach (i; 0 .. ptrcnt) { 331 | do_sweep_of(cast(void*)*cast(ulong*)(i * 8 + ogptr)); 332 | } 333 | 334 | hdr.color = _swept; 335 | } 336 | 337 | void sweep() { 338 | ulong[15] regs; 339 | ulong* rp = regs.ptr; 340 | asm { 341 | mov RAX, rp; 342 | 343 | mov [RAX + 0x0], RBX; 344 | mov [RAX + 0x8], RCX; 345 | mov [RAX + 0x10], RDX; 346 | mov [RAX + 0x18], RBP; 347 | mov [RAX + 0x20], RSI; 348 | mov [RAX + 0x28], RDI; 349 | mov [RAX + 0x30], R8; 350 | mov [RAX + 0x38], R9; 351 | mov [RAX + 0x40], R10; 352 | mov [RAX + 0x48], R11; 353 | mov [RAX + 0x50], R12; 354 | mov [RAX + 0x58], R13; 355 | mov [RAX + 0x60], R14; 356 | mov [RAX + 0x68], R15; 357 | mov [RAX + 0x70], RSP; 358 | } 359 | ulong stack_top; 360 | must_succeed(esyscall(Syscall.GET_STACK_BOUNDS, &stack_top)); 361 | ulong stack_bottom = regs[14]; 362 | is_flipped = !is_flipped; 363 | foreach (i; stack_bottom .. stack_top) { 364 | if (i & 7) continue; 365 | do_sweep_of(*(cast(void**) i)); 366 | } 367 | foreach (ulong reg; regs) { 368 | do_sweep_of(cast(void*) reg); 369 | } 370 | foreach (void** ee; elfbase .. elftop) { 371 | do_sweep_of(*ee); 372 | } 373 | BlockHeader* h = first; 374 | while (h) { 375 | if (h.color == _not_swept) { 376 | BlockHeader* n = h.next; 377 | do_dealloc(h); 378 | h = n; 379 | } else 380 | h = h.next; 381 | } 382 | } 383 | -------------------------------------------------------------------------------- /source/libsys/port.d: -------------------------------------------------------------------------------- 1 | module libsys.port; 2 | 3 | import libsys.mem; 4 | import libsys.loaf; 5 | import libsys.errno; 6 | import vshared.share; 7 | import libsys.syscall; 8 | 9 | extern(C) struct port_t { 10 | ulong fd; 11 | long send(byte[] data) { 12 | return meh_port_send(this, data); 13 | } 14 | long send_fat(T)(T data) { 15 | byte[] buf = loaf_encode(data); 16 | return send(buf); 17 | } 18 | long send_anon(byte[] data) { 19 | return meh_port_send_anon(this, data.ptr, data.length); 20 | } 21 | long send_fat_anon(T)(T data) { 22 | byte[] buf = loaf_encode(data); 23 | return send_anon(buf); 24 | } 25 | } 26 | 27 | const PORT_RECV = cast(ulong)PortCloneFlags.RECV; 28 | const PORT_SEND = cast(ulong)PortCloneFlags.SEND; 29 | const PORT_SEND_ANON = cast(ulong)PortCloneFlags.SEND_ANON; 30 | 31 | long meh_port_create(out port_t p) { 32 | KPortCreate buf; 33 | long e = esyscall(Syscall.CREATE_PORT, &buf); 34 | if (e) return e; 35 | p.fd = buf.outp; 36 | return 0; 37 | } 38 | long meh_port_delete(ref port_t p) { 39 | KPortDestroy buf; 40 | buf.inp = p.fd; 41 | long e = esyscall(Syscall.DESTROY_PORT, &buf); 42 | if (e) return e; 43 | p.fd = cast(ulong)/* sign extend */cast(long)-1; 44 | return 0; 45 | } 46 | long meh_port_send_anon(ref port_t p, void* data, ulong len) { 47 | return meh_port_send(p, data, len, true); 48 | } 49 | long meh_port_send(ref port_t p, byte[] data) { 50 | return meh_port_send(p, data.ptr, data.length, false); 51 | } 52 | long meh_port_send(ref port_t p, void* data, ulong len, bool hidepid = false) { 53 | KPortIOOp buf; 54 | buf.kind = hidepid ? IOOpKind.KIND_SEND_ANON : IOOpKind.KIND_SEND; 55 | buf.data = data; 56 | buf.len = len; 57 | buf.port = p.fd; 58 | long e = esyscall(Syscall.SEND, &buf); 59 | if (e) return e; 60 | return 0; 61 | } 62 | long meh_port_send(ref port_t p, byte[] data, bool hidepid = false) { 63 | return meh_port_send(p, data.ptr, data.length, hidepid); 64 | } 65 | long meh_port_recv(ref port_t p, out byte[] data) { 66 | import libsys.entry; 67 | 68 | void* dat; 69 | ulong le; 70 | long da = meh_port_recv(p, dat, le); 71 | if (da) return da; 72 | byte[] d = alloc_array!(byte)(le); 73 | data = d; 74 | memcpy(d.ptr, cast(byte*)dat, le); 75 | free(dat); 76 | return 0; 77 | } 78 | long meh_port_recv(ref port_t p, out void* data, out ulong len) { 79 | import libsys.mem; 80 | import libsys.entry; 81 | 82 | KPortIOOp buf; 83 | buf.kind = IOOpKind.KIND_RECV; 84 | buf.port = p.fd; 85 | long e = esyscall(Syscall.RECV, &buf); 86 | if (e) return e; 87 | void* dat = malloc(buf.len); 88 | len = buf.len; 89 | memcpy(cast(byte*)dat, cast(byte*)buf.data, buf.len); 90 | buf.len = (buf.len + 4095) & ~0xfff; 91 | if (munmap(buf.data, buf.len)) { 92 | perror("meh_port_recv: munmap"); 93 | exit(1); 94 | } 95 | data = dat; 96 | return 0; 97 | } 98 | long meh_port_clone(ref port_t pin, ulong flags, out port_t pout) { 99 | KPortClone buf; 100 | buf.port_in = pin.fd; 101 | buf.flags = cast(PortCloneFlags)flags; 102 | long e = esyscall(Syscall.CLONE_PORT, &buf); 103 | if (e) return e; 104 | pout.fd = buf.port_out; 105 | return 0; 106 | } -------------------------------------------------------------------------------- /source/libsys/syscall.d: -------------------------------------------------------------------------------- 1 | module libsys.syscall; 2 | 3 | import vshared.share; 4 | import libsys.errno; 5 | 6 | long syscall(Syscall sysno, void* arg) { 7 | long o; 8 | ulong sys = cast(ulong) sysno; 9 | asm { 10 | mov RDI, sys; 11 | mov RSI, arg; 12 | syscall; 13 | mov o, RAX; 14 | } 15 | return o; 16 | } 17 | long getpid() { 18 | long v; 19 | if (!esyscall(Syscall.GET_TID, &v)) return v; 20 | return -1; 21 | } 22 | extern(C) long __psys_fork(ulong sys); 23 | extern(C) long fork() { 24 | return __psys_fork(cast(ulong) Syscall.FORK); 25 | } 26 | 27 | long esyscall(Syscall sysno, void* arg) { 28 | return error_out(syscall(sysno, arg)); 29 | } 30 | 31 | void* mmap(void* addr, ulong len, MMapFlags flags) { 32 | import libsys.errno; 33 | 34 | MMap map; 35 | map.addr = addr; 36 | assert(len == 4096); 37 | map.flags = flags; 38 | map.addr = addr; 39 | long e = esyscall(Syscall.MAP, &map); 40 | if (e) return cast(void*)0; 41 | assert(map.addr != cast(void*)0); 42 | return map.addr; 43 | } 44 | 45 | long munmap(void* addr, ulong len) { 46 | import libsys.errno; 47 | 48 | MMap map; 49 | map.addr = addr; 50 | assert(len == 4096); 51 | map.flags = MMapFlags.MAP_UNMAP; 52 | map.addr = addr; 53 | long e = esyscall(Syscall.MAP, &map); 54 | return e; 55 | } 56 | 57 | 58 | void exit(long ec) { 59 | syscall(Syscall.EXIT, &ec); 60 | perror("exit"); 61 | exit(0xffffffff); 62 | } -------------------------------------------------------------------------------- /source/libsys/util.d: -------------------------------------------------------------------------------- 1 | module libsys.util; 2 | 3 | 4 | /// memcmp - compare memory areas 5 | /// 6 | /// # Description 7 | /// The memcmp() function compares the first n bytes (each interpreted as unsigned char) of the memory areas s1 and s2. 8 | /// 9 | /// # Return Value 10 | /// The memcmp() function returns an integer less than, equal to, or greater than zero if the first n bytes of s1 is found, respectively, to be less than, to match, or be greater than the 11 | /// first n bytes of s2. 12 | /// 13 | /// Not the above guarantee about memcmp's return value is guaranteed by linux and not the standard, as seen in CVE-2012-2122 "optimized memcmp" 14 | /// glibc memcmp when SSE/AVX is on will return whatever. 15 | /// So we return 1/0/-1. 16 | /// 17 | /// For a nonzero return value, the sign is determined by the sign of the difference between the first pair of bytes (interpreted as unsigned char) that differ in s1 and s2. 18 | /// 19 | /// If n is zero, the return value is zero. 20 | extern (C) int memcmp(const byte* s1, const byte* s2, size_t n) { 21 | foreach (i; 0 .. n) { 22 | if (s1[i] < s2[i]) 23 | return -1; 24 | if (s1[i] > s2[i]) 25 | return 1; 26 | } 27 | return 0; 28 | } 29 | 30 | /// strlen - calculate the length of a string 31 | /// 32 | /// The strlen() function calculates the length of the string pointed to by s, excluding the terminating null byte ('\0'). 33 | /// 34 | /// The strlen() function returns the number of bytes in the string pointed to by s. 35 | extern (C) size_t strlen(const char* s) { 36 | size_t i = 0; 37 | for (; s[i] != 0; i++) { 38 | } 39 | return i; 40 | } 41 | 42 | /// Int -> String 43 | char* intToString(long value, char* str, int base) { 44 | char* rc; 45 | char* ptr; 46 | char* low; 47 | // Check for supported base. 48 | if (base < 2 || base > 36) { 49 | *str = '\0'; 50 | return str; 51 | } 52 | rc = ptr = str; 53 | // Set '-' for negative decimals. 54 | if (value < 0 && base == 10) { 55 | *ptr++ = '-'; 56 | } 57 | // Remember where the numbers start. 58 | low = ptr; 59 | // The actual conversion. 60 | do { 61 | // Modulo is negative for negative value. This trick makes abs() unnecessary. 62 | *ptr++ = "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz"[cast( 63 | int)(35 + value % base)]; 64 | value /= base; 65 | } 66 | while (value); 67 | // Terminating the string. 68 | *ptr-- = '\0'; 69 | // Invert the numbers. 70 | while (low < ptr) { 71 | const char tmp = *low; 72 | *low++ = *ptr; 73 | *ptr-- = tmp; 74 | } 75 | return rc; 76 | } -------------------------------------------------------------------------------- /source/progs/init.d: -------------------------------------------------------------------------------- 1 | module progs.init; 2 | 3 | import libsys.errno; 4 | import libsys.entry; 5 | import libsys.port; 6 | import libsys.loaf; 7 | import libsys.mem; 8 | import libsys.io; 9 | import libsys.syscall; 10 | import vshared.share; 11 | 12 | enum BootstrapCmd { 13 | NOTIFY_EXIT = 1, 14 | NOTIFY_PING = 2, 15 | NOTIFY_PROXIED_MSG = 3, 16 | } 17 | 18 | struct BootstrapCmdExit { 19 | BootstrapCmd cmd; 20 | ulong code; 21 | } 22 | 23 | struct BootstrapCmdProxiedExit { 24 | BootstrapCmd cmd; 25 | ulong pid; 26 | ulong code; 27 | } 28 | 29 | struct Meme { 30 | ulong x; 31 | ulong y; 32 | ulong z; 33 | } 34 | 35 | int app() { 36 | printf("Hello, world!"); 37 | port_t p; 38 | if (meh_port_create(p)) { 39 | perror("meh_port_create"); 40 | exit(1); 41 | } 42 | long f = fork(); 43 | printf("fork(): {} (pid={})", f, getpid()); 44 | if (f) { 45 | // parent 46 | Meme x = Meme(69, 420, 1337); 47 | meh_port_send(p, loaf_encode(x)); 48 | if (meh_port_delete(p)) { perror("meh_port_delete"); return 1; } 49 | } else { 50 | // child 51 | byte[] da; 52 | if (meh_port_recv(p, da)) { perror("meh_port_recv"); return 1; } 53 | Meme x; 54 | loaf_decode(x, da); 55 | printf("m: {}", x); 56 | if (meh_port_delete(p)) { perror("meh_port_delete"); return 1; } 57 | } 58 | return 3; 59 | } 60 | 61 | mixin entry!app; 62 | -------------------------------------------------------------------------------- /source/progs/init_.d: -------------------------------------------------------------------------------- 1 | module progs.init_; 2 | 3 | import libsys.entry; 4 | import libsys.io; 5 | 6 | int app() { 7 | printf(DEBUG, "level=DEBUG"); 8 | printf(INFO, "level=INFO"); 9 | printf(WARN, "level=WARN"); 10 | printf(ERROR, "level=ERROR"); 11 | printf(FATAL, "level=FATAL"); 12 | return 1; 13 | } 14 | 15 | mixin entry!app; -------------------------------------------------------------------------------- /source/task.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | void __assert(char*, char*, int); 5 | #define assert(e) \ 6 | ((void) ((e) ? ((void)0) : __assert (#e, __FILE__, __LINE__))) 7 | 8 | void task_trampoline(void* data); 9 | void task_enqueue(void** buf); 10 | void task_switch(void** buf2) { 11 | void* buf1v[5]; 12 | if (__builtin_setjmp(buf1v)) { 13 | return; 14 | } 15 | task_enqueue(buf1v); 16 | __builtin_longjmp(buf2, 1); 17 | } 18 | void task_pls_die(void** buf) { 19 | __builtin_longjmp(buf, 1); 20 | } 21 | void altstack_task_setup_internal(void* data, void** target_buf, void** exit_buf) { 22 | if (!__builtin_setjmp(target_buf)) { 23 | __builtin_longjmp(exit_buf, 1); 24 | } 25 | task_trampoline(data); 26 | } 27 | void altstack_task_setup(void* data, void** target_buf) { 28 | void* buf1v[5]; 29 | if (__builtin_setjmp(buf1v)) { 30 | return; 31 | } 32 | altstack_task_setup_internal(data, target_buf, buf1v); 33 | /* never gets here */ 34 | assert(0); 35 | } -------------------------------------------------------------------------------- /source/user.ld: -------------------------------------------------------------------------------- 1 | SECTIONS 2 | { 3 | 4 | 5 | . = 0x000008000000000; 6 | 7 | __elf_begin = .; 8 | .text : ALIGN(4096) { 9 | *(.text*) 10 | } 11 | . = ALIGN(4096); 12 | 13 | .rodata : ALIGN(4096) { 14 | *(.rodata*) 15 | } 16 | 17 | .data : ALIGN(4096) { 18 | *(.data*) 19 | __init_begin = .; 20 | *(.init_array) 21 | *(.init_array*) 22 | __init_end = .; 23 | } 24 | 25 | .tdata : ALIGN(4096) { 26 | *(.tdata) 27 | } 28 | 29 | .tbss : ALIGN(4096) { 30 | *(.tbss) 31 | } 32 | 33 | .bss : ALIGN(4096) { 34 | *(COMMON) 35 | *(.bss*) 36 | } 37 | __elf_end = .; 38 | } 39 | -------------------------------------------------------------------------------- /source/vshared/share.d: -------------------------------------------------------------------------------- 1 | module vshared.share; 2 | 3 | enum PortCloneFlags { 4 | RECV = 1, 5 | SEND = 2, 6 | SEND_ANON = 4 7 | } 8 | 9 | /// Port errors 10 | enum PortError : byte { 11 | EOK = 0, 12 | EPERM = 1, 13 | EINVAL = 2, 14 | EEMPTY = -1, 15 | } 16 | enum Syscall { 17 | EXIT = 1, 18 | MAP = 2, 19 | MUNMAP = 3, 20 | SEND = 4, 21 | RECV = 5, 22 | CLOSE_PORT = 6, 23 | CLONE_PORT = 7, 24 | CREATE_PORT = 8, 25 | DESTROY_PORT = 9, 26 | EXEC = 10, 27 | FORK = 11, 28 | KPRINT = 12, 29 | GET_STACK_BOUNDS = 13, 30 | GET_TID = 14, 31 | } 32 | enum IOOpKind { 33 | KIND_SEND, 34 | KIND_SEND_ANON, 35 | KIND_RECV 36 | } 37 | alias KStackBounds = ulong; 38 | struct KPortCreate { 39 | ulong outp; 40 | } 41 | struct KPortDestroy { 42 | ulong inp; 43 | } 44 | struct KPortIOOp { 45 | ulong port; 46 | void* data; 47 | ulong len; 48 | IOOpKind kind; 49 | } 50 | struct KPortClone { 51 | ulong port_in; 52 | ulong port_out; 53 | PortCloneFlags flags; 54 | } 55 | struct KPrintBuffer { 56 | ulong len; 57 | char* ptr; 58 | } 59 | 60 | 61 | 62 | enum EPERM = 1; 63 | enum ENOENT = 2; 64 | enum ESRCH = 3; 65 | enum EINTR = 4; 66 | enum EIO = 5; 67 | enum ENXIO = 6; 68 | enum E2BIG = 7; 69 | enum ENOEXEC = 8; 70 | enum EBADF = 9; 71 | enum ECHILD = 10; 72 | enum EAGAIN = 11; 73 | enum ENOMEM = 12; 74 | enum EACCES = 13; 75 | enum EFAULT = 14; 76 | enum ENOTBLK = 15; 77 | enum EBUSY = 16; 78 | enum EEXIST = 17; 79 | enum EXDEV = 18; 80 | enum ENODEV = 19; 81 | enum ENOTDIR = 20; 82 | enum EISDIR = 21; 83 | enum EINVAL = 22; 84 | enum ENFILE = 23; 85 | enum EMFILE = 24; 86 | enum ENOTTY = 25; 87 | enum ETXTBSY = 26; 88 | enum EFBIG = 27; 89 | enum ENOSPC = 28; 90 | enum ESPIPE = 29; 91 | enum EROFS = 30; 92 | enum EMLINK = 31; 93 | enum EPIPE = 32; 94 | enum ERANGE = 33; 95 | enum EDEADLK = 34; 96 | enum ENAMETOOLONG = 35; 97 | enum ENOLCK = 36; 98 | enum ENOSYS = 37; 99 | enum ENOTEMPTY = 38; 100 | enum ELOOP = 39; 101 | enum ENOMSG = 40; 102 | enum EEMPTY = 41; 103 | 104 | enum MMapProt { 105 | PROT_EXEC = 1, 106 | PROT_READ = 2, 107 | PROT_WRITE = 4, 108 | } 109 | enum MMapFlags { 110 | MAP_UNMAP = 1, 111 | MAP_FIXED = 2, 112 | MAP_PRIVATE = 4, 113 | } 114 | struct MMap { 115 | MMapFlags flags; 116 | // MMapProt prot; 117 | void* addr; 118 | } -------------------------------------------------------------------------------- /trace.py: -------------------------------------------------------------------------------- 1 | # Instrument a VM 2 | import time 3 | 4 | gfr = [] 5 | 6 | def frame(line_info, fn): 7 | return { 'line': line_info, 'fn': fn } 8 | 9 | print(str(lldb.process.Continue())) 10 | while True: 11 | time.sleep(0.001) 12 | print(str(lldb.process.Stop())) 13 | frames = [f for f in [f for f in lldb.process][0]] 14 | for f in frames: 15 | gfr.append(frame(str(f.GetLineEntry()), str(f.GetFunctionName()))) 16 | print(str(lldb.process.Continue())) 17 | with open("frames.txt", "w") as file: file.write('\n'.join([a['fn'] for a in gfr])) -------------------------------------------------------------------------------- /x64-llvm.ini: -------------------------------------------------------------------------------- 1 | 2 | [binaries] 3 | c = 'clang' 4 | c_ld = 'ld.lld' 5 | d = 'diode-ldc2' 6 | as = 'nasm' 7 | ar = '/opt/homebrew/opt/llvm/bin/llvm-ar' 8 | 9 | [built-in options] 10 | d_ld_args = [ 11 | 'fake' ] 12 | 13 | c_args = [ 14 | '-target', 'x86_64-elf', 15 | '-ffreestanding', 16 | '-ggdb', 17 | '-fno-omit-frame-pointer', 18 | '-mno-red-zone' ] 19 | 20 | d_args = [ 21 | '--float-abi=soft', 22 | '-code-model=medium', 23 | '--relocation-model=static', 24 | '--disable-red-zone', 25 | '-mtriple', 'amd64-unknown-elf', 26 | '--frame-pointer=all', 27 | '-g', 28 | '--d-debug', 29 | '-mattr=-sse,-sse2,-sse3,-ssse3', 30 | '--enable-asserts=true', 31 | '--link-internally' ] 32 | 33 | cpp_args = [ '__cantbuildme_can_you' ] 34 | 35 | cpp_link_args = [ '___cantlinkme_can_you' ] 36 | 37 | [host_machine] 38 | system = 'diode' 39 | cpu_family = 'x86_64' 40 | endian = 'little' 41 | cpu = 'x64' 42 | --------------------------------------------------------------------------------