├── .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 |
--------------------------------------------------------------------------------