summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/assets/hyra.pngbin0 -> 645542 bytes
-rw-r--r--.github/workflows/cicd.yml4
-rw-r--r--.gitignore1
-rw-r--r--Makefile.in83
-rw-r--r--README.md104
-rwxr-xr-xbootstrap24
-rw-r--r--builddeps/limine.conf6
-rw-r--r--builddeps/user.mk3
-rw-r--r--builddeps/wallpaper.jpgbin0 -> 203867 bytes
-rw-r--r--configure.ac29
-rw-r--r--etc/hostname1
-rw-r--r--etc/motd4
-rw-r--r--etc/oemu/emutest.rc5
-rw-r--r--etc/oemu/test-00.s20
-rw-r--r--etc/passwd1
-rwxr-xr-xhyra-build.sh227
-rw-r--r--lib/Makefile10
-rw-r--r--lib/libc/Makefile8
-rw-r--r--lib/libc/include/arch/aarch64/syscall.h85
-rw-r--r--lib/libc/include/crypto/sha256.h62
-rw-r--r--lib/libc/include/ctype.h104
-rw-r--r--lib/libc/include/fenv.h61
-rw-r--r--lib/libc/include/math.h274
-rw-r--r--lib/libc/include/stdarg.h92
-rw-r--r--lib/libc/include/stdatomic.h119
-rw-r--r--lib/libc/include/stddef.h149
-rw-r--r--lib/libc/include/stdio.h106
-rw-r--r--lib/libc/include/stdlib.h97
-rw-r--r--lib/libc/include/string.h9
-rw-r--r--lib/libc/include/time.h37
-rw-r--r--lib/libc/include/unistd.h41
-rw-r--r--lib/libc/src/arch/aarch64/crti.S35
-rw-r--r--lib/libc/src/arch/amd64/crti.S (renamed from lib/libc/src/arch/amd64/crt0.S)4
-rw-r--r--lib/libc/src/crypto/sha256.c242
-rw-r--r--lib/libc/src/hyra/disk.c125
-rw-r--r--lib/libc/src/hyra/inject.c51
-rw-r--r--lib/libc/src/hyra/mmap.c47
-rw-r--r--lib/libc/src/hyra/reboot.c40
-rw-r--r--lib/libc/src/hyra/sleep.c43
-rw-r--r--lib/libc/src/hyra/socket.c86
-rw-r--r--lib/libc/src/hyra/spawn.c47
-rw-r--r--lib/libc/src/hyra/stat.c37
-rw-r--r--lib/libc/src/hyra/sysctl.c38
-rw-r--r--lib/libc/src/hyra/wait.c37
-rw-r--r--lib/libc/src/main.c46
-rw-r--r--lib/libc/src/musl-math/__cos.c71
-rw-r--r--lib/libc/src/musl-math/__cosdf.c35
-rw-r--r--lib/libc/src/musl-math/__cosl.c96
-rw-r--r--lib/libc/src/musl-math/__expo2.c16
-rw-r--r--lib/libc/src/musl-math/__expo2f.c16
-rw-r--r--lib/libc/src/musl-math/__fpclassify.c11
-rw-r--r--lib/libc/src/musl-math/__fpclassifyf.c11
-rw-r--r--lib/libc/src/musl-math/__fpclassifyl.c42
-rw-r--r--lib/libc/src/musl-math/__invtrigl.c63
-rw-r--r--lib/libc/src/musl-math/__invtrigl.h11
-rw-r--r--lib/libc/src/musl-math/__polevll.c93
-rw-r--r--lib/libc/src/musl-math/__rem_pio2.c177
-rw-r--r--lib/libc/src/musl-math/__rem_pio2_large.c442
-rw-r--r--lib/libc/src/musl-math/__rem_pio2f.c75
-rw-r--r--lib/libc/src/musl-math/__rem_pio2l.c141
-rw-r--r--lib/libc/src/musl-math/__signbit.c13
-rw-r--r--lib/libc/src/musl-math/__signbitf.c11
-rw-r--r--lib/libc/src/musl-math/__signbitl.c14
-rw-r--r--lib/libc/src/musl-math/__sin.c64
-rw-r--r--lib/libc/src/musl-math/__sindf.c36
-rw-r--r--lib/libc/src/musl-math/__sinl.c78
-rw-r--r--lib/libc/src/musl-math/__tan.c110
-rw-r--r--lib/libc/src/musl-math/__tandf.c54
-rw-r--r--lib/libc/src/musl-math/__tanl.c143
-rw-r--r--lib/libc/src/musl-math/acos.c101
-rw-r--r--lib/libc/src/musl-math/acosf.c71
-rw-r--r--lib/libc/src/musl-math/acosh.c24
-rw-r--r--lib/libc/src/musl-math/acoshf.c26
-rw-r--r--lib/libc/src/musl-math/acoshl.c29
-rw-r--r--lib/libc/src/musl-math/acosl.c67
-rw-r--r--lib/libc/src/musl-math/asin.c107
-rw-r--r--lib/libc/src/musl-math/asinf.c61
-rw-r--r--lib/libc/src/musl-math/asinh.c28
-rw-r--r--lib/libc/src/musl-math/asinhf.c28
-rw-r--r--lib/libc/src/musl-math/asinhl.c41
-rw-r--r--lib/libc/src/musl-math/asinl.c71
-rw-r--r--lib/libc/src/musl-math/atan.c116
-rw-r--r--lib/libc/src/musl-math/atan2.c107
-rw-r--r--lib/libc/src/musl-math/atan2f.c83
-rw-r--r--lib/libc/src/musl-math/atan2l.c85
-rw-r--r--lib/libc/src/musl-math/atanf.c94
-rw-r--r--lib/libc/src/musl-math/atanh.c29
-rw-r--r--lib/libc/src/musl-math/atanhf.c28
-rw-r--r--lib/libc/src/musl-math/atanhl.c35
-rw-r--r--lib/libc/src/musl-math/atanl.c184
-rw-r--r--lib/libc/src/musl-math/cbrt.c103
-rw-r--r--lib/libc/src/musl-math/cbrtf.c66
-rw-r--r--lib/libc/src/musl-math/cbrtl.c124
-rw-r--r--lib/libc/src/musl-math/ceil.c31
-rw-r--r--lib/libc/src/musl-math/ceilf.c27
-rw-r--r--lib/libc/src/musl-math/ceill.c34
-rw-r--r--lib/libc/src/musl-math/copysign.c8
-rw-r--r--lib/libc/src/musl-math/copysignf.c10
-rw-r--r--lib/libc/src/musl-math/copysignl.c16
-rw-r--r--lib/libc/src/musl-math/cos.c77
-rw-r--r--lib/libc/src/musl-math/cosf.c78
-rw-r--r--lib/libc/src/musl-math/cosh.c40
-rw-r--r--lib/libc/src/musl-math/coshf.c33
-rw-r--r--lib/libc/src/musl-math/coshl.c47
-rw-r--r--lib/libc/src/musl-math/cosl.c39
-rw-r--r--lib/libc/src/musl-math/erf.c273
-rw-r--r--lib/libc/src/musl-math/erff.c183
-rw-r--r--lib/libc/src/musl-math/erfl.c353
-rw-r--r--lib/libc/src/musl-math/exp.c134
-rw-r--r--lib/libc/src/musl-math/exp10.c26
-rw-r--r--lib/libc/src/musl-math/exp10f.c24
-rw-r--r--lib/libc/src/musl-math/exp10l.c34
-rw-r--r--lib/libc/src/musl-math/exp2.c375
-rw-r--r--lib/libc/src/musl-math/exp2f.c126
-rw-r--r--lib/libc/src/musl-math/exp2l.c619
-rw-r--r--lib/libc/src/musl-math/expf.c83
-rw-r--r--lib/libc/src/musl-math/expl.c128
-rw-r--r--lib/libc/src/musl-math/expm1.c201
-rw-r--r--lib/libc/src/musl-math/expm1f.c111
-rw-r--r--lib/libc/src/musl-math/expm1l.c123
-rw-r--r--lib/libc/src/musl-math/fabs.c9
-rw-r--r--lib/libc/src/musl-math/fabsf.c9
-rw-r--r--lib/libc/src/musl-math/fabsl.c15
-rw-r--r--lib/libc/src/musl-math/fdim.c10
-rw-r--r--lib/libc/src/musl-math/fdimf.c10
-rw-r--r--lib/libc/src/musl-math/fdiml.c18
-rw-r--r--lib/libc/src/musl-math/finite.c7
-rw-r--r--lib/libc/src/musl-math/finitef.c7
-rw-r--r--lib/libc/src/musl-math/floor.c31
-rw-r--r--lib/libc/src/musl-math/floorf.c27
-rw-r--r--lib/libc/src/musl-math/floorl.c34
-rw-r--r--lib/libc/src/musl-math/fmaf.c93
-rw-r--r--lib/libc/src/musl-math/fmal.c293
-rw-r--r--lib/libc/src/musl-math/fmax.c13
-rw-r--r--lib/libc/src/musl-math/fmaxf.c13
-rw-r--r--lib/libc/src/musl-math/fmaxl.c21
-rw-r--r--lib/libc/src/musl-math/fmin.c13
-rw-r--r--lib/libc/src/musl-math/fminf.c13
-rw-r--r--lib/libc/src/musl-math/fminl.c21
-rw-r--r--lib/libc/src/musl-math/fmod.c68
-rw-r--r--lib/libc/src/musl-math/fmodf.c65
-rw-r--r--lib/libc/src/musl-math/fmodl.c105
-rw-r--r--lib/libc/src/musl-math/frexp.c24
-rw-r--r--lib/libc/src/musl-math/frexpf.c24
-rw-r--r--lib/libc/src/musl-math/frexpl.c30
-rw-r--r--lib/libc/src/musl-math/hypot.c67
-rw-r--r--lib/libc/src/musl-math/hypotf.c35
-rw-r--r--lib/libc/src/musl-math/hypotl.c66
-rw-r--r--lib/libc/src/musl-math/ilogb.c26
-rw-r--r--lib/libc/src/musl-math/ilogbf.c26
-rw-r--r--lib/libc/src/musl-math/ilogbl.c55
-rw-r--r--lib/libc/src/musl-math/j0.c375
-rw-r--r--lib/libc/src/musl-math/j0f.c314
-rw-r--r--lib/libc/src/musl-math/j1.c362
-rw-r--r--lib/libc/src/musl-math/j1f.c310
-rw-r--r--lib/libc/src/musl-math/ldexp.c6
-rw-r--r--lib/libc/src/musl-math/ldexpf.c6
-rw-r--r--lib/libc/src/musl-math/ldexpl.c6
-rw-r--r--lib/libc/src/musl-math/lgamma.c9
-rw-r--r--lib/libc/src/musl-math/lgamma_r.c285
-rw-r--r--lib/libc/src/musl-math/lgammaf.c9
-rw-r--r--lib/libc/src/musl-math/lgammaf_r.c220
-rw-r--r--lib/libc/src/musl-math/lgammal.c361
-rw-r--r--lib/libc/src/musl-math/libm.h198
-rw-r--r--lib/libc/src/musl-math/llrint.c8
-rw-r--r--lib/libc/src/musl-math/llrintf.c8
-rw-r--r--lib/libc/src/musl-math/llrintl.c36
-rw-r--r--lib/libc/src/musl-math/llround.c6
-rw-r--r--lib/libc/src/musl-math/llroundf.c6
-rw-r--r--lib/libc/src/musl-math/llroundl.c6
-rw-r--r--lib/libc/src/musl-math/log.c118
-rw-r--r--lib/libc/src/musl-math/log10.c101
-rw-r--r--lib/libc/src/musl-math/log10f.c77
-rw-r--r--lib/libc/src/musl-math/log10l.c191
-rw-r--r--lib/libc/src/musl-math/log1p.c122
-rw-r--r--lib/libc/src/musl-math/log1pf.c77
-rw-r--r--lib/libc/src/musl-math/log1pl.c177
-rw-r--r--lib/libc/src/musl-math/log2.c122
-rw-r--r--lib/libc/src/musl-math/log2f.c74
-rw-r--r--lib/libc/src/musl-math/log2l.c182
-rw-r--r--lib/libc/src/musl-math/logb.c17
-rw-r--r--lib/libc/src/musl-math/logbf.c10
-rw-r--r--lib/libc/src/musl-math/logbl.c16
-rw-r--r--lib/libc/src/musl-math/logf.c69
-rw-r--r--lib/libc/src/musl-math/logl.c175
-rw-r--r--lib/libc/src/musl-math/lrint.c46
-rw-r--r--lib/libc/src/musl-math/lrintf.c8
-rw-r--r--lib/libc/src/musl-math/lrintl.c36
-rw-r--r--lib/libc/src/musl-math/lround.c6
-rw-r--r--lib/libc/src/musl-math/lroundf.c6
-rw-r--r--lib/libc/src/musl-math/lroundl.c6
-rw-r--r--lib/libc/src/musl-math/modf.c34
-rw-r--r--lib/libc/src/musl-math/modff.c34
-rw-r--r--lib/libc/src/musl-math/modfl.c53
-rw-r--r--lib/libc/src/musl-math/nan.c6
-rw-r--r--lib/libc/src/musl-math/nanf.c6
-rw-r--r--lib/libc/src/musl-math/nanl.c6
-rw-r--r--lib/libc/src/musl-math/nearbyint.c20
-rw-r--r--lib/libc/src/musl-math/nearbyintf.c18
-rw-r--r--lib/libc/src/musl-math/nearbyintl.c26
-rw-r--r--lib/libc/src/musl-math/nextafter.c31
-rw-r--r--lib/libc/src/musl-math/nextafterf.c30
-rw-r--r--lib/libc/src/musl-math/nextafterl.c75
-rw-r--r--lib/libc/src/musl-math/nexttoward.c42
-rw-r--r--lib/libc/src/musl-math/nexttowardf.c35
-rw-r--r--lib/libc/src/musl-math/nexttowardl.c6
-rw-r--r--lib/libc/src/musl-math/pow.c328
-rw-r--r--lib/libc/src/musl-math/powf.c259
-rw-r--r--lib/libc/src/musl-math/powl.c522
-rw-r--r--lib/libc/src/musl-math/remainder.c11
-rw-r--r--lib/libc/src/musl-math/remainderf.c11
-rw-r--r--lib/libc/src/musl-math/remainderl.c15
-rw-r--r--lib/libc/src/musl-math/remquo.c82
-rw-r--r--lib/libc/src/musl-math/remquof.c82
-rw-r--r--lib/libc/src/musl-math/remquol.c124
-rw-r--r--lib/libc/src/musl-math/rint.c28
-rw-r--r--lib/libc/src/musl-math/rintf.c30
-rw-r--r--lib/libc/src/musl-math/rintl.c29
-rw-r--r--lib/libc/src/musl-math/round.c35
-rw-r--r--lib/libc/src/musl-math/roundf.c36
-rw-r--r--lib/libc/src/musl-math/roundl.c37
-rw-r--r--lib/libc/src/musl-math/scalb.c35
-rw-r--r--lib/libc/src/musl-math/scalbf.c32
-rw-r--r--lib/libc/src/musl-math/scalbln.c12
-rw-r--r--lib/libc/src/musl-math/scalblnf.c12
-rw-r--r--lib/libc/src/musl-math/scalblnl.c20
-rw-r--r--lib/libc/src/musl-math/scalbn.c33
-rw-r--r--lib/libc/src/musl-math/scalbnf.c31
-rw-r--r--lib/libc/src/musl-math/scalbnl.c36
-rw-r--r--lib/libc/src/musl-math/signgam.c5
-rw-r--r--lib/libc/src/musl-math/significand.c7
-rw-r--r--lib/libc/src/musl-math/significandf.c7
-rw-r--r--lib/libc/src/musl-math/sin.c78
-rw-r--r--lib/libc/src/musl-math/sincos.c69
-rw-r--r--lib/libc/src/musl-math/sincosf.c117
-rw-r--r--lib/libc/src/musl-math/sincosl.c60
-rw-r--r--lib/libc/src/musl-math/sinf.c76
-rw-r--r--lib/libc/src/musl-math/sinh.c39
-rw-r--r--lib/libc/src/musl-math/sinhf.c31
-rw-r--r--lib/libc/src/musl-math/sinhl.c43
-rw-r--r--lib/libc/src/musl-math/sinl.c41
-rw-r--r--lib/libc/src/musl-math/sqrt.c185
-rw-r--r--lib/libc/src/musl-math/sqrtf.c84
-rw-r--r--lib/libc/src/musl-math/sqrtl.c7
-rw-r--r--lib/libc/src/musl-math/tan.c70
-rw-r--r--lib/libc/src/musl-math/tanf.c64
-rw-r--r--lib/libc/src/musl-math/tanh.c45
-rw-r--r--lib/libc/src/musl-math/tanhf.c39
-rw-r--r--lib/libc/src/musl-math/tanhl.c48
-rw-r--r--lib/libc/src/musl-math/tanl.c29
-rw-r--r--lib/libc/src/musl-math/tgamma.c222
-rw-r--r--lib/libc/src/musl-math/tgammaf.c6
-rw-r--r--lib/libc/src/musl-math/tgammal.c281
-rw-r--r--lib/libc/src/musl-math/trunc.c19
-rw-r--r--lib/libc/src/musl-math/truncf.c19
-rw-r--r--lib/libc/src/musl-math/truncl.c34
-rw-r--r--lib/libc/src/musl-math/weak_alias.h7
-rw-r--r--lib/libc/src/posix/getopt.c100
-rw-r--r--lib/libc/src/stdio/fclose.c48
-rw-r--r--lib/libc/src/stdio/fgetc.c55
-rw-r--r--lib/libc/src/stdio/fgets.c73
-rw-r--r--lib/libc/src/stdio/file.c36
-rw-r--r--lib/libc/src/stdio/fopen.c74
-rw-r--r--lib/libc/src/stdio/fputc.c55
-rw-r--r--lib/libc/src/stdio/fputs.c68
-rw-r--r--lib/libc/src/stdio/fread.c61
-rw-r--r--lib/libc/src/stdio/fseek.c42
-rw-r--r--lib/libc/src/stdio/ftell.c37
-rw-r--r--lib/libc/src/stdio/fwrite.c61
-rw-r--r--lib/libc/src/stdio/init.c62
-rw-r--r--lib/libc/src/stdio/snprintf.c63
-rw-r--r--lib/libc/src/stdio/vsnprintf.c145
-rw-r--r--lib/libc/src/stdlib/_Exit.c38
-rw-r--r--lib/libc/src/stdlib/abort.c40
-rw-r--r--lib/libc/src/stdlib/exit.c40
-rw-r--r--lib/libc/src/stdlib/malloc.c219
-rw-r--r--lib/libc/src/stdlib/rand.c45
-rw-r--r--lib/libc/src/string/atoi.c51
-rw-r--r--lib/libc/src/string/itoa.c134
-rw-r--r--lib/libc/src/string/memcpy.c40
-rw-r--r--lib/libc/src/string/memset.c45
-rw-r--r--lib/libc/src/string/strdup.c51
-rw-r--r--lib/libc/src/string/strtok.c82
-rw-r--r--lib/libc/src/unistd/access.c37
-rw-r--r--lib/libc/src/unistd/dup.c44
-rw-r--r--lib/libc/src/unistd/fork.c37
-rw-r--r--lib/libc/src/unistd/getcwd.c52
-rw-r--r--lib/libc/src/unistd/getlogin.c109
-rw-r--r--lib/libc/src/unistd/getpid.c43
-rw-r--r--lib/libc/src/unistd/getuid.c38
-rw-r--r--lib/libc/src/unistd/hostname.c125
-rw-r--r--lib/libc/src/unistd/lseek.c37
-rw-r--r--lib/libc/src/unistd/setuid.c37
-rw-r--r--lib/libc/src/unistd/symlink.c53
-rw-r--r--lib/libc/src/unistd/sysconf.c42
-rw-r--r--lib/libc/src/unistd/unlink.c53
-rw-r--r--lib/libgfx/Makefile29
-rw-r--r--lib/libgfx/include/libgfx/draw.h133
-rw-r--r--lib/libgfx/include/libgfx/gfx.h74
-rw-r--r--lib/libgfx/src/draw.c282
-rw-r--r--lib/libgfx/src/gfx.c97
-rw-r--r--lib/liboda/Makefile29
-rw-r--r--lib/liboda/include/liboda/input.h74
-rw-r--r--lib/liboda/include/liboda/oda.h128
-rw-r--r--lib/liboda/include/liboda/odavar.h59
-rw-r--r--lib/liboda/include/liboda/types.h41
-rw-r--r--lib/liboda/src/input.c88
-rw-r--r--lib/liboda/src/oda.c77
-rw-r--r--lib/liboda/src/window.c461
-rw-r--r--rc/init.rc9
-rw-r--r--share/docs/hw/et131x.txt215
-rw-r--r--share/docs/kernel/ctlfs.md125
-rw-r--r--share/docs/kernel/disk.txt49
-rw-r--r--share/docs/lib/liboda.md229
-rw-r--r--share/man/man1/beep.145
-rw-r--r--share/man/man1/cat.152
-rw-r--r--share/man/man1/echo.147
-rw-r--r--share/man/man1/mex.148
-rw-r--r--share/man/man1/nerve.173
-rw-r--r--share/man/man1/omar.155
-rw-r--r--share/man/man1/osh.183
-rw-r--r--share/man/man2/exit.255
-rw-r--r--share/man/man9/kconf.981
-rw-r--r--share/man/man9/mmio.93
-rw-r--r--share/man/man9/vm.946
-rw-r--r--share/man/man9/vm_map.997
-rw-r--r--share/misc/contrib31
-rw-r--r--share/misc/tmpfs15
-rw-r--r--sys/arch/aarch64/aarch64/exception.c128
-rw-r--r--sys/arch/aarch64/aarch64/intr.c37
-rw-r--r--sys/arch/aarch64/aarch64/locore.S (renamed from sys/arch/amd64/isa/i8042.S)13
-rw-r--r--sys/arch/aarch64/aarch64/machdep.c32
-rw-r--r--sys/arch/aarch64/aarch64/pmap.c280
-rw-r--r--sys/arch/aarch64/aarch64/proc_machdep.c6
-rw-r--r--sys/arch/aarch64/aarch64/reboot.c18
-rw-r--r--sys/arch/aarch64/aarch64/vector.S96
-rw-r--r--sys/arch/aarch64/conf/GENERIC6
-rw-r--r--sys/arch/aarch64/conf/link.ld6
-rw-r--r--sys/arch/aarch64/pci/pci_machdep.c14
-rw-r--r--sys/arch/amd64/amd64/acpi_machdep.c3
-rw-r--r--sys/arch/amd64/amd64/gdt.c76
-rw-r--r--sys/arch/amd64/amd64/hpet.c17
-rw-r--r--sys/arch/amd64/amd64/intr.c72
-rw-r--r--sys/arch/amd64/amd64/ipi.c191
-rw-r--r--sys/arch/amd64/amd64/lapic.c9
-rw-r--r--sys/arch/amd64/amd64/lapic_intr.S3
-rw-r--r--sys/arch/amd64/amd64/machdep.c433
-rw-r--r--sys/arch/amd64/amd64/mp.c71
-rw-r--r--sys/arch/amd64/amd64/pmap.c75
-rw-r--r--sys/arch/amd64/amd64/proc_machdep.c134
-rw-r--r--sys/arch/amd64/amd64/reboot.c62
-rw-r--r--sys/arch/amd64/amd64/simd.S76
-rw-r--r--sys/arch/amd64/amd64/trap.c71
-rw-r--r--sys/arch/amd64/amd64/tsc.c109
-rw-r--r--sys/arch/amd64/amd64/vector.S221
-rw-r--r--sys/arch/amd64/conf/GENERIC22
-rw-r--r--sys/arch/amd64/conf/link.ld6
-rw-r--r--sys/arch/amd64/isa/i8042.c363
-rw-r--r--sys/arch/amd64/isa/mc1468.c281
-rw-r--r--sys/arch/amd64/isa/spkr.c53
-rw-r--r--sys/arch/amd64/pci/pci_machdep.c21
-rw-r--r--sys/conf/GENERIC10
-rw-r--r--sys/crypto/chacha20.c97
-rw-r--r--sys/crypto/siphash.c116
-rw-r--r--sys/dev/acpi/acpi_init.c8
-rw-r--r--sys/dev/acpi/acpi_sleep.c80
-rw-r--r--sys/dev/acpi/uacpi.c677
-rw-r--r--sys/dev/acpi/uacpi/default_handlers.c336
-rw-r--r--sys/dev/acpi/uacpi/event.c2449
-rw-r--r--sys/dev/acpi/uacpi/interpreter.c6053
-rw-r--r--sys/dev/acpi/uacpi/io.c1116
-rw-r--r--sys/dev/acpi/uacpi/mutex.c396
-rw-r--r--sys/dev/acpi/uacpi/namespace.c1081
-rw-r--r--sys/dev/acpi/uacpi/notify.c255
-rw-r--r--sys/dev/acpi/uacpi/opcodes.c272
-rw-r--r--sys/dev/acpi/uacpi/opregion.c1056
-rw-r--r--sys/dev/acpi/uacpi/osi.c388
-rw-r--r--sys/dev/acpi/uacpi/registers.c572
-rw-r--r--sys/dev/acpi/uacpi/resources.c2569
-rw-r--r--sys/dev/acpi/uacpi/shareable.c71
-rw-r--r--sys/dev/acpi/uacpi/sleep.c616
-rw-r--r--sys/dev/acpi/uacpi/stdlib.c728
-rw-r--r--sys/dev/acpi/uacpi/tables.c1399
-rw-r--r--sys/dev/acpi/uacpi/types.c1489
-rw-r--r--sys/dev/acpi/uacpi/uacpi.c998
-rw-r--r--sys/dev/acpi/uacpi/utilities.c1156
-rw-r--r--sys/dev/cons/cons.c427
-rw-r--r--sys/dev/cons/cons_ansi.c189
-rw-r--r--sys/dev/cons/cons_buf.c40
-rw-r--r--sys/dev/dcdr/cache.c14
-rw-r--r--sys/dev/dmi/dmi.c306
-rw-r--r--sys/dev/dmi/dmi_board.c104
-rw-r--r--sys/dev/ic/ahci.c912
-rw-r--r--sys/dev/ic/ahci_ctl.c63
-rw-r--r--sys/dev/ic/nvme.c62
-rw-r--r--sys/dev/pci/pci.c112
-rw-r--r--sys/dev/phy/e1000.c358
-rw-r--r--sys/dev/phy/et131x.c338
-rw-r--r--sys/dev/phy/rtl.c (renamed from sys/dev/phy/rt8139.c)210
-rw-r--r--sys/dev/random/entropy.c55
-rw-r--r--sys/dev/random/random.c88
-rw-r--r--sys/dev/usb/xhci.c136
-rw-r--r--sys/dev/video/fbdev.c85
-rw-r--r--sys/fs/ctlfs.c440
-rw-r--r--sys/fs/devfs.c59
-rw-r--r--sys/fs/initramfs.c117
-rw-r--r--sys/fs/tmpfs.c430
-rw-r--r--sys/include/arch/aarch64/board.h51
-rw-r--r--sys/include/arch/aarch64/cdefs.h1
-rw-r--r--sys/include/arch/aarch64/cpu.h3
-rw-r--r--sys/include/arch/aarch64/exception.h54
-rw-r--r--sys/include/arch/aarch64/frame.h82
-rw-r--r--sys/include/arch/aarch64/frameasm.h89
-rw-r--r--sys/include/arch/aarch64/intr.h57
-rw-r--r--sys/include/arch/aarch64/param.h35
-rw-r--r--sys/include/arch/aarch64/pci/pci.h40
-rw-r--r--sys/include/arch/amd64/asm.h10
-rw-r--r--sys/include/arch/amd64/bus.h8
-rw-r--r--sys/include/arch/amd64/cdefs.h12
-rw-r--r--sys/include/arch/amd64/cpu.h54
-rw-r--r--sys/include/arch/amd64/frame.h2
-rw-r--r--sys/include/arch/amd64/frameasm.h10
-rw-r--r--sys/include/arch/amd64/gdt.h79
-rw-r--r--sys/include/arch/amd64/intr.h60
-rw-r--r--sys/include/arch/amd64/ioapic.h3
-rw-r--r--sys/include/arch/amd64/ipi.h67
-rw-r--r--sys/include/arch/amd64/isa/i8042var.h6
-rw-r--r--sys/include/arch/amd64/lapic.h2
-rw-r--r--sys/include/arch/amd64/param.h35
-rw-r--r--sys/include/arch/amd64/pci/pci.h40
-rw-r--r--sys/include/arch/amd64/tsc.h55
-rw-r--r--sys/include/crypto/chacha20.h47
-rw-r--r--sys/include/crypto/siphash.h34
-rw-r--r--sys/include/dev/acpi/acpi.h7
-rw-r--r--sys/include/dev/acpi/tables.h98
-rw-r--r--sys/include/dev/acpi/uacpi.h35
-rw-r--r--sys/include/dev/acpi/uacpi/uacpi/acpi.h1430
-rw-r--r--sys/include/dev/acpi/uacpi/uacpi/context.h53
-rw-r--r--sys/include/dev/acpi/uacpi/uacpi/event.h286
-rw-r--r--sys/include/dev/acpi/uacpi/uacpi/helpers.h12
-rw-r--r--sys/include/dev/acpi/uacpi/uacpi/internal/compiler.h3
-rw-r--r--sys/include/dev/acpi/uacpi/uacpi/internal/context.h155
-rw-r--r--sys/include/dev/acpi/uacpi/uacpi/internal/dynamic_array.h185
-rw-r--r--sys/include/dev/acpi/uacpi/uacpi/internal/event.h25
-rw-r--r--sys/include/dev/acpi/uacpi/uacpi/internal/helpers.h7
-rw-r--r--sys/include/dev/acpi/uacpi/uacpi/internal/interpreter.h24
-rw-r--r--sys/include/dev/acpi/uacpi/uacpi/internal/io.h77
-rw-r--r--sys/include/dev/acpi/uacpi/uacpi/internal/log.h23
-rw-r--r--sys/include/dev/acpi/uacpi/uacpi/internal/mutex.h82
-rw-r--r--sys/include/dev/acpi/uacpi/uacpi/internal/namespace.h123
-rw-r--r--sys/include/dev/acpi/uacpi/uacpi/internal/notify.h13
-rw-r--r--sys/include/dev/acpi/uacpi/uacpi/internal/opcodes.h1390
-rw-r--r--sys/include/dev/acpi/uacpi/uacpi/internal/opregion.h49
-rw-r--r--sys/include/dev/acpi/uacpi/uacpi/internal/osi.h8
-rw-r--r--sys/include/dev/acpi/uacpi/uacpi/internal/registers.h7
-rw-r--r--sys/include/dev/acpi/uacpi/uacpi/internal/resources.h327
-rw-r--r--sys/include/dev/acpi/uacpi/uacpi/internal/shareable.h21
-rw-r--r--sys/include/dev/acpi/uacpi/uacpi/internal/stdlib.h131
-rw-r--r--sys/include/dev/acpi/uacpi/uacpi/internal/tables.h70
-rw-r--r--sys/include/dev/acpi/uacpi/uacpi/internal/types.h310
-rw-r--r--sys/include/dev/acpi/uacpi/uacpi/internal/utilities.h45
-rw-r--r--sys/include/dev/acpi/uacpi/uacpi/io.h36
-rw-r--r--sys/include/dev/acpi/uacpi/uacpi/kernel_api.h375
-rw-r--r--sys/include/dev/acpi/uacpi/uacpi/log.h40
-rw-r--r--sys/include/dev/acpi/uacpi/uacpi/namespace.h186
-rw-r--r--sys/include/dev/acpi/uacpi/uacpi/notify.h30
-rw-r--r--sys/include/dev/acpi/uacpi/uacpi/opregion.h47
-rw-r--r--sys/include/dev/acpi/uacpi/uacpi/osi.h125
-rw-r--r--sys/include/dev/acpi/uacpi/uacpi/platform/arch_helpers.h38
-rw-r--r--sys/include/dev/acpi/uacpi/uacpi/platform/atomic.h347
-rw-r--r--sys/include/dev/acpi/uacpi/uacpi/platform/compiler.h123
-rw-r--r--sys/include/dev/acpi/uacpi/uacpi/platform/config.h162
-rw-r--r--sys/include/dev/acpi/uacpi/uacpi/platform/libc.h28
-rw-r--r--sys/include/dev/acpi/uacpi/uacpi/platform/types.h64
-rw-r--r--sys/include/dev/acpi/uacpi/uacpi/registers.h105
-rw-r--r--sys/include/dev/acpi/uacpi/uacpi/resources.h740
-rw-r--r--sys/include/dev/acpi/uacpi/uacpi/sleep.h67
-rw-r--r--sys/include/dev/acpi/uacpi/uacpi/status.h57
-rw-r--r--sys/include/dev/acpi/uacpi/uacpi/tables.h141
-rw-r--r--sys/include/dev/acpi/uacpi/uacpi/types.h547
-rw-r--r--sys/include/dev/acpi/uacpi/uacpi/uacpi.h269
-rw-r--r--sys/include/dev/acpi/uacpi/uacpi/utilities.h188
-rw-r--r--sys/include/dev/cons/ansi.h75
-rw-r--r--sys/include/dev/cons/cons.h21
-rw-r--r--sys/include/dev/cons/consvar.h2
-rw-r--r--sys/include/dev/dmi/dmi.h42
-rw-r--r--sys/include/dev/dmi/dmivar.h41
-rw-r--r--sys/include/dev/ic/ahciregs.h36
-rw-r--r--sys/include/dev/ic/ahcivar.h198
-rw-r--r--sys/include/dev/ic/nvmevar.h84
-rw-r--r--sys/include/dev/mii/mii.h57
-rw-r--r--sys/include/dev/pci/pci.h6
-rw-r--r--sys/include/dev/phy/e1000regs.h119
-rw-r--r--sys/include/dev/phy/et131xregs.h275
-rw-r--r--sys/include/dev/phy/rtl.h (renamed from sys/include/dev/phy/rt8139.h)16
-rw-r--r--sys/include/dev/random/entropy.h40
-rw-r--r--sys/include/dev/timer.h6
-rw-r--r--sys/include/dev/usb/xhciregs.h8
-rw-r--r--sys/include/dev/usb/xhcivar.h5
-rw-r--r--sys/include/dev/video/fbdev.h1
-rw-r--r--sys/include/fs/ctlfs.h62
-rw-r--r--sys/include/fs/devfs.h2
-rw-r--r--sys/include/fs/tmpfs.h77
-rw-r--r--sys/include/lib/crc32.h37
-rw-r--r--sys/include/lib/stdbool.h38
-rw-r--r--sys/include/lib/stddef.h36
-rw-r--r--sys/include/lib/stdint.h36
-rw-r--r--sys/include/lib/string.h2
-rw-r--r--sys/include/net/ethertypes.h36
-rw-r--r--sys/include/net/if.h35
-rw-r--r--sys/include/net/if_arp.h51
-rw-r--r--sys/include/net/if_var.h82
-rw-r--r--sys/include/net/netbuf.h42
-rw-r--r--sys/include/netinet/if_ether.h56
-rw-r--r--sys/include/sys/atomic.h30
-rw-r--r--sys/include/sys/bitops.h52
-rw-r--r--sys/include/sys/cdefs.h2
-rw-r--r--sys/include/sys/console.h57
-rw-r--r--sys/include/sys/device.h9
-rw-r--r--sys/include/sys/devstat.h46
-rw-r--r--sys/include/sys/disk.h211
-rw-r--r--sys/include/sys/disklabel.h48
-rw-r--r--sys/include/sys/dmi.h63
-rw-r--r--sys/include/sys/driver.h69
-rw-r--r--sys/include/sys/elf.h66
-rw-r--r--sys/include/sys/endian.h54
-rw-r--r--sys/include/sys/exec.h3
-rw-r--r--sys/include/sys/fbdev.h40
-rw-r--r--sys/include/sys/fcntl.h1
-rw-r--r--sys/include/sys/filedesc.h17
-rw-r--r--sys/include/sys/krq.h40
-rw-r--r--sys/include/sys/limits.h7
-rw-r--r--sys/include/sys/mman.h24
-rw-r--r--sys/include/sys/mount.h4
-rw-r--r--sys/include/sys/mutex.h52
-rw-r--r--sys/include/sys/namei.h3
-rw-r--r--sys/include/sys/param.h16
-rw-r--r--sys/include/sys/proc.h74
-rw-r--r--sys/include/sys/queue.h5
-rw-r--r--sys/include/sys/reboot.h9
-rw-r--r--sys/include/sys/sched.h36
-rw-r--r--sys/include/sys/schedvar.h8
-rw-r--r--sys/include/sys/signal.h2
-rw-r--r--sys/include/sys/socket.h202
-rw-r--r--sys/include/sys/socketvar.h53
-rw-r--r--sys/include/sys/spawn.h39
-rw-r--r--sys/include/sys/spinlock.h3
-rw-r--r--sys/include/sys/stat.h4
-rw-r--r--sys/include/sys/syscall.h24
-rw-r--r--sys/include/sys/sysctl.h22
-rw-r--r--sys/include/sys/syslog.h2
-rw-r--r--sys/include/sys/systm.h1
-rw-r--r--sys/include/sys/termios.h29
-rw-r--r--sys/include/sys/time.h60
-rw-r--r--sys/include/sys/types.h33
-rw-r--r--sys/include/sys/ucred.h57
-rw-r--r--sys/include/sys/uio.h58
-rw-r--r--sys/include/sys/vfs.h1
-rw-r--r--sys/include/sys/vmstat.h48
-rw-r--r--sys/include/sys/vnode.h39
-rw-r--r--sys/include/sys/vsr.h165
-rw-r--r--sys/include/sys/wait.h37
-rw-r--r--sys/include/sys/workqueue.h101
-rw-r--r--sys/include/vm/physmem.h4
-rw-r--r--sys/include/vm/pmap.h16
-rw-r--r--sys/include/vm/stat.h39
-rw-r--r--sys/include/vm/vm_device.h43
-rw-r--r--sys/kern/disk_engine.c208
-rw-r--r--sys/kern/driver_blacklist.c170
-rw-r--r--sys/kern/driver_subr.c76
-rw-r--r--sys/kern/exec_elf64.c36
-rw-r--r--sys/kern/init_main.c39
-rw-r--r--sys/kern/kern_accnt.c128
-rw-r--r--sys/kern/kern_cpu.c61
-rw-r--r--sys/kern/kern_cred.c87
-rw-r--r--sys/kern/kern_descrip.c147
-rw-r--r--sys/kern/kern_disk.c475
-rw-r--r--sys/kern/kern_exec.c4
-rw-r--r--sys/kern/kern_exit.c136
-rw-r--r--sys/kern/kern_fork.c54
-rw-r--r--sys/kern/kern_krq.c61
-rw-r--r--sys/kern/kern_panic.c60
-rw-r--r--sys/kern/kern_proc.c143
-rw-r--r--sys/kern/kern_sched.c285
-rw-r--r--sys/kern/kern_sig.c6
-rw-r--r--sys/kern/kern_socket.c1009
-rw-r--r--sys/kern/kern_spawn.c295
-rw-r--r--sys/kern/kern_stub.c36
-rw-r--r--sys/kern/kern_subr.c21
-rw-r--r--sys/kern/kern_synch.c69
-rw-r--r--sys/kern/kern_syscall.c29
-rw-r--r--sys/kern/kern_sysctl.c81
-rw-r--r--sys/kern/kern_syslog.c137
-rw-r--r--sys/kern/kern_time.c73
-rw-r--r--sys/kern/kern_uio.c272
-rw-r--r--sys/kern/kern_vsr.c344
-rw-r--r--sys/kern/kern_work.c274
-rw-r--r--sys/kern/vfs_init.c4
-rw-r--r--sys/kern/vfs_lookup.c58
-rw-r--r--sys/kern/vfs_subr.c6
-rw-r--r--sys/kern/vfs_syscalls.c48
-rw-r--r--sys/kern/vfs_vcache.c2
-rw-r--r--sys/lib/crc32.c89
-rw-r--r--sys/lib/string/memmove.c50
-rw-r--r--sys/lib/string/strdup.c52
-rw-r--r--sys/lib/string/vsnprintf.c4
-rw-r--r--sys/net/if.c85
-rw-r--r--sys/netinet/if_ether.c122
-rw-r--r--sys/vm/vm_device.c78
-rw-r--r--sys/vm/vm_init.c1
-rw-r--r--sys/vm/vm_map.c124
-rw-r--r--sys/vm/vm_physmem.c111
-rw-r--r--sys/vm/vm_stat.c95
-rw-r--r--sys/vm/vm_vnode.c3
-rwxr-xr-xtools/mktap13
-rw-r--r--tools/omar/Makefile12
-rw-r--r--tools/omar/README9
-rw-r--r--tools/omar/omar.c470
-rw-r--r--usr.bin/Makefile32
-rw-r--r--usr.bin/beep/Makefile6
-rw-r--r--usr.bin/beep/main.c70
-rw-r--r--usr.bin/cat/Makefile6
-rw-r--r--usr.bin/cat/cat.c112
-rw-r--r--usr.bin/date/Makefile6
-rw-r--r--usr.bin/date/date.c132
-rw-r--r--usr.bin/dmidump/Makefile6
-rw-r--r--usr.bin/dmidump/dmidump.c78
-rw-r--r--usr.bin/echo/Makefile6
-rw-r--r--usr.bin/echo/echo.c44
-rw-r--r--usr.bin/elfdump/Makefile6
-rw-r--r--usr.bin/elfdump/elfdump.c171
-rw-r--r--usr.bin/fetch/Makefile6
-rw-r--r--usr.bin/fetch/fetch.c105
-rw-r--r--usr.bin/getconf/Makefile6
-rw-r--r--usr.bin/getconf/getconf.c93
-rw-r--r--usr.bin/kfgwm/Makefile6
-rw-r--r--usr.bin/kfgwm/font.c379
-rw-r--r--usr.bin/kfgwm/include/kfg/font.h40
-rw-r--r--usr.bin/kfgwm/include/kfg/types.h40
-rw-r--r--usr.bin/kfgwm/include/kfg/window.h68
-rw-r--r--usr.bin/kfgwm/kfgwm.c95
-rw-r--r--usr.bin/kfgwm/window.c221
-rw-r--r--usr.bin/kmsg/Makefile6
-rw-r--r--usr.bin/kmsg/kmsg.c58
-rw-r--r--usr.bin/kstat/Makefile6
-rw-r--r--usr.bin/kstat/kstat.c122
-rw-r--r--usr.bin/link.ld5
-rw-r--r--usr.bin/login/Makefile6
-rw-r--r--usr.bin/login/login.c295
-rw-r--r--usr.bin/mex/Makefile6
-rw-r--r--usr.bin/mex/mex.c105
-rw-r--r--usr.bin/mrow/Makefile6
-rw-r--r--usr.bin/mrow/mrow.c287
-rw-r--r--usr.bin/nerve/Makefile6
-rw-r--r--usr.bin/nerve/nerve.c377
-rw-r--r--usr.bin/notes/Makefile6
-rw-r--r--usr.bin/notes/notes.c132
-rw-r--r--usr.bin/oasm/Makefile7
-rw-r--r--usr.bin/oasm/emit.c564
-rw-r--r--usr.bin/oasm/include/oasm/emit.h120
-rw-r--r--usr.bin/oasm/include/oasm/label.h55
-rw-r--r--usr.bin/oasm/include/oasm/lex.h188
-rw-r--r--usr.bin/oasm/include/oasm/log.h48
-rw-r--r--usr.bin/oasm/include/oasm/parse.h37
-rw-r--r--usr.bin/oasm/include/oasm/state.h59
-rw-r--r--usr.bin/oasm/label.c166
-rw-r--r--usr.bin/oasm/lex.c445
-rw-r--r--usr.bin/oasm/log.c81
-rw-r--r--usr.bin/oasm/oasm.c73
-rw-r--r--usr.bin/oasm/parse.c285
-rw-r--r--usr.bin/oemu/Makefile7
-rw-r--r--usr.bin/oemu/cpu.c523
-rw-r--r--usr.bin/oemu/emu.c127
-rw-r--r--usr.bin/oemu/include/oemu/cpu.h83
-rw-r--r--usr.bin/oemu/include/oemu/osmx64.h90
-rw-r--r--usr.bin/oemu/include/oemu/types.h40
-rw-r--r--usr.bin/osh/Makefile4
-rw-r--r--usr.bin/osh/osh.c457
-rw-r--r--usr.bin/readcore/Makefile6
-rw-r--r--usr.bin/readcore/core.c50
-rw-r--r--usr.bin/readcore/crc32.c91
-rw-r--r--usr.bin/readcore/include/core.h44
-rw-r--r--usr.bin/readcore/include/crc32.h37
-rw-r--r--usr.bin/readcore/include/frame.h107
-rw-r--r--usr.bin/readcore/readcore.c78
-rw-r--r--usr.bin/reboot/Makefile6
-rw-r--r--usr.bin/reboot/reboot.c87
-rw-r--r--usr.bin/screensave/Makefile6
-rw-r--r--usr.bin/screensave/screensave.c116
-rw-r--r--usr.bin/sleep/Makefile6
-rw-r--r--usr.bin/sleep/sleep.c55
-rw-r--r--usr.bin/sysctl/Makefile6
-rw-r--r--usr.bin/sysctl/sysctl.c314
-rw-r--r--usr.bin/whoami/Makefile6
-rw-r--r--usr.bin/whoami/whoami.c38
-rw-r--r--usr.sbin/Makefile10
-rw-r--r--usr.sbin/init/Makefile2
-rw-r--r--usr.sbin/init/main.c62
-rw-r--r--usr.sbin/inject/Makefile6
-rw-r--r--usr.sbin/inject/inject.c43
-rw-r--r--usr.sbin/install/Makefile6
-rw-r--r--usr.sbin/install/install.c315
-rw-r--r--usr.sbin/link.ld5
703 files changed, 82412 insertions, 1114 deletions
diff --git a/.github/assets/hyra.png b/.github/assets/hyra.png
new file mode 100644
index 0000000..bcfaa68
--- /dev/null
+++ b/.github/assets/hyra.png
Binary files differ
diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml
index ad6d236..c56641e 100644
--- a/.github/workflows/cicd.yml
+++ b/.github/workflows/cicd.yml
@@ -20,5 +20,5 @@ jobs:
run: sudo apt-get install -y lld clang xorriso
- name: Bootstrap and configure
run: ./bootstrap && ./configure
- - name: Build world with clang
- run: make
+ - name: Build world
+ run: ./hyra-build.sh -i
diff --git a/.gitignore b/.gitignore
index 001bc03..c93d8a9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,5 +12,6 @@
/base
/sys/include/machine
/lib/libc/include/machine/
+/tools/omar/bin/
/share/misc/hwdoc
autom4te.cache
diff --git a/Makefile.in b/Makefile.in
index 35595b1..a7d84c4 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -4,17 +4,18 @@ override PROMPT := printf "%s\t\t%s\n"
###############################
# CFLAGS, QEMU flags + misc
###############################
+KBUILD_ARGS = ""
+override PROJECT_ROOT = @PROJECT_ROOT@
override BOOT_FW = @BOOT_FW@
override ARCH = @ARCH@
override HYRA_VERSION = @PACKAGE_VERSION@
override PROMPT := printf "%s\t\t%s\n"
override KERNEL_CFLAGS = @KERNEL_CFLAGS@ $(KERNEL_DEFINES)
-override KERNEL_LDFLAGS = -nostdlib -znoexecstack -zmax-page-size=0x1000 -static -Tsys/arch/$(ARCH)/conf/link.ld
+override KERNEL_LDFLAGS = -no-pie -nostdlib -znoexecstack -zmax-page-size=0x1000 -static -Tsys/arch/$(ARCH)/conf/link.ld
override QEMU_FLAGS = @QEMU_FLAGS@
-override KERNEL_DEFINES = $ -DHYRA_VERSION="\"$(HYRA_VERSION)\""\
+override KERNEL_DEFINES = $(KBUILD_ARGS) -DHYRA_VERSION="\"$(HYRA_VERSION)\""\
-DHYRA_BUILDDATE="\"@HYRA_BUILDDATE@\""\
- -DHYRA_ARCH="\"@ARCH@\"" $(shell cat sys/arch/$(ARCH)/conf/GENERIC | tools/kconf/kconf)
-
+ -DHYRA_ARCH="\"@ARCH@\"" $(shell cat sys/arch/$(ARCH)/conf/GENERIC sys/conf/GENERIC | tools/kconf/kconf)
######################
# Toolchain
######################
@@ -64,37 +65,28 @@ override SBIN_MAKEDIRS = $(shell find usr.sbin/ -type d -name "*" | awk '!/usr.s
override BIN_MAKEDIRS = $(shell find usr.bin/ -type d -name "*" | awk '!/usr.bin\/$$/')
override USRDIR = $(shell pwd)/base/usr
+
.PHONY: all
-all: base libc sbin bin base/boot/hyra-kernel ramfs iso
+all: stand/boot/ libs sbin bin base/boot/hyra.krq
rm -f sys/include/machine
- rm -rf iso_root
.PHONY: sbin
sbin: $(SBIN_MAKEDIRS)
- $(MAKE) -C $^ -I$(shell pwd)/builddeps \
- LDSCRIPT=$(shell pwd)/usr.sbin/link.ld USRDIR=$(USRDIR)
- find $^ -type f -executable -exec mv {} base/usr/sbin/ \;
+ make -C usr.sbin/ LDSCRIPT=$(shell pwd)/usr.sbin/link.ld USRDIR=$(USRDIR)\
+ ROOT=$(PROJECT_ROOT) OSVER=$(HYRA_VERSION) OSARCH=$(ARCH)\
+ CC="$(CC)"
.PHONY: bin
bin: $(BIN_MAKEDIRS)
- $(MAKE) -C $^ -I$(shell pwd)/builddeps \
- LDSCRIPT=$(shell pwd)/usr.bin/link.ld USRDIR=$(USRDIR)
- find $^ -type f -executable -exec mv {} base/usr/bin/ \;
-
-.PHONY: libc
-libc:
- $(MAKE) -C lib/libc/ -I$(shell pwd)/builddeps \
- USRDIR=$(USRDIR) ARCH=$(ARCH)
- cp lib/libc/build/libc.a base/usr/lib/
-
-.PHONY: base
-base:
- mkdir -p base/usr/lib/
- mkdir -p base/usr/sbin/
- mkdir -p base/usr/bin/
- mkdir -p base/boot/
- mkdir -p base/usr/include/sys/
- cp -f sys/include/sys/*.h base/usr/include/sys/
+ make -C usr.bin/ LDSCRIPT=$(shell pwd)/usr.bin/link.ld USRDIR=$(USRDIR)\
+ ROOT=$(PROJECT_ROOT) OSVER=$(HYRA_VERSION) OSARCH=$(ARCH)\
+ CC="$(CC)"
+
+.PHONY: libs
+libs:
+ $(MAKE) -C lib/ -I$(shell pwd)/builddeps \
+ USRDIR=$(USRDIR) ARCH=$(ARCH) ROOT=$(PROJECT_ROOT) \
+ CC="$(CC)"
.PHONY: cross
cross:
@@ -104,42 +96,23 @@ cross:
run:
$(QEMU) $(QEMU_FLAGS)
-.PHONY: ramfs
-ramfs:
- cd base/; find . -name "*" | cpio --create --format=odc \
- --no-absolute-filenames > ../ramfs.cpio
- $(PROMPT) " RAMFS " $(shell pwd)/ramfs.cpio
-
.PHONY: clean
clean:
rm -f $(KERNEL_ASMOBJECTS) $(KERNEL_OBJECTS) $(KERNEL_HEADER_DEPS)
rm -f sys/include/machine
-.PHONY: iso
-iso:
- mkdir -p iso_root/boot/
- mkdir -p iso_root/EFI/BOOT/
- cp stand/limine/$(BOOT_FW) iso_root/EFI/BOOT/
- mv ramfs.cpio iso_root/boot/
- cp builddeps/limine.conf stand/limine/limine-bios.sys \
- stand/limine/limine-bios-cd.bin stand/limine/limine-uefi-cd.bin iso_root/
- cp base/boot/* iso_root/boot/
- cp builddeps/tree.jpg iso_root/boot/
- xorriso -as mkisofs -b limine-bios-cd.bin -no-emul-boot -boot-load-size 4\
- -boot-info-table --efi-boot limine-uefi-cd.bin -efi-boot-part \
- --efi-boot-image --protective-msdos-label iso_root -o Hyra.iso > /dev/null
- stand/limine/limine bios-install Hyra.iso
- $(PROMPT) " ISO " $(shell pwd)/Hyra.iso
-
-base/boot/hyra-kernel: $(KERNEL_OBJECTS) $(KERNEL_ASMOBJECTS)
- rm -rf iso_root
- $(PROMPT) " LD " $(shell pwd)/base/boot/hyra-kernel
- $(LD) $(KERNEL_LDFLAGS) $(KERNEL_OBJECTS) $(KERNEL_ASMOBJECTS) -o base/boot/hyra-kernel
- tools/ksyms sys/kern/ksyms.c base/boot/hyra-kernel
+stand/boot/:
+ mkdir -p stand/boot/
+ cp stand/limine/$(BOOT_FW) stand/boot/
+
+base/boot/hyra.krq: $(KERNEL_OBJECTS) $(KERNEL_ASMOBJECTS)
+ $(PROMPT) " LD " $(shell pwd)/base/boot/hyra.krq
+ $(LD) $(KERNEL_LDFLAGS) $(KERNEL_OBJECTS) $(KERNEL_ASMOBJECTS) -o base/boot/hyra.krq
+ tools/ksyms sys/kern/ksyms.c base/boot/hyra.krq
# === Generating symbols ===
$(CC) -c $(KERNEL_CFLAGS) $(KERNEL_DEFINES) sys/kern/ksyms.c -o sys/kern/ksyms.o
$(LD) $(KERNEL_LDFLAGS) $(KERNEL_OBJECTS) $(KERNEL_ASMOBJECTS) \
- sys/kern/ksyms.o -o base/boot/hyra-kernel
+ sys/kern/ksyms.o -o base/boot/hyra.krq
sys/include/machine/:
cd sys/include/; ln -sf arch/$(ARCH) machine
diff --git a/README.md b/README.md
index aeb397d..12a63fa 100644
--- a/README.md
+++ b/README.md
@@ -1,11 +1,21 @@
The Hyra Operating System
=========================
-Welcome to the Hyra Operating System project!
+Welcome to the Hyra Operating System project! Hyra is an experimental
+operating system inspired by BSD and Plan 9 while being entirely written from scratch.
+Hyra aims to rethink core fundamentals in modern operating system design in order to
+create new and improved architectural ideas.
+
Project Goal:
--------------
-The goal of this project is to create a modern operating system, taking inspiration from NetBSD and incorporating new ideas and design plans. Our mission is to design a new modernized POSIX-like standard to provide a robust and versatile environment for developers and users.
+The goal of this project is to redefine what modern operating systems are while taking inspiration from BSD. Hyra does
+not use CPIO for its initramfs like other operating systems typically would and instead uses the [OSMORA Archive Format (OMAR)](https://osmora.org/oap/oap-0005).
+
+What Hyra is NOT:
+--------------
+Hyra is *NOT* Linux, nor does extend or share any sources with any existing
+operating systems as it is written entirely from scratch. Hyra is *NOT* intended as a "toy" project as it is aimed to be the used as the main operating system for internal OSMORA operations and infrastructure.
Getting Started:
----------------
@@ -15,20 +25,96 @@ Next, to configure for x86_64 just run configure:
`./configure`
-Now you'll need to build the cross compiler by running:
+After running the configure script, you can now actually build Hyra:
+
+`./hyra-build.sh`
+
+This will generate a new `Hyra.iso` file.
+
+
+Default User:
+----------------
+Upon booting, the `login` program will ask for user credentials. The default username is `root` and the default
+password is also `root`.
+
+Programs:
+----------------
+The Hyra userspace provides the user various programs that they can run. Examples of
+such programs include:
+
+- ``beep`` - Play a tone
+- ``cat`` - Print files to stdout
+- ``date`` - Get the current date or set system time
+- ``echo`` - Print a line of text
+- ``elfdump`` - Get information about an ELF binary
+- ``fetch`` - System fetch! A must have :~)
+- ``getconf`` - Get system configuration values
+- ``mex`` - OSMORA hexdump utility
+- ``sleep`` - Sleep for a number of seconds
+- ``kmsg`` - Read the kernel message buffer
+- ``readcore`` - Read coredump files
+- ``oasm`` - OSMORA [OSMX64](https://github.com/sigsegv7/OSMX64) Assembler
+- ``oemu`` - OSMORA [OSMX64](https://github.com/sigsegv7/OSMX64) Emulator
+- ``kstat`` - Read kernel statistics
+- ``dmidump`` - Dump DMI/SMBios information
+- ``screensave`` - Glitch art screensaver
+- ``whoami`` - Print effective user name
+- ``sysctl`` - Runtime kernel parameters
+- ``notes`` - Music box
-`make cross`
+And more! See ``usr.bin/*``
-This may take awhile so just sit back, relax and do something else like... well I'm not you so
-I don't know what you like.
+Libraries:
+----------------
+The Hyra userspace additionally provides the user various libraries that they can
+link with. Examples of such libraries include:
-After the cross compiler is done building you can build and run the project in a virtual machine:
+- ``libc`` - C library (link flag: ``-lc``)
+- ``libgfx`` - Low-level graphics (link flag: ``-lgfx``)
-`make; make run`
+And more! See ``lib/*``
Documentation:
--------------
-Documentation will be in the form of comments throughout the codebase and can also be found in the share/ directory within the project root.
+Documentation will be in the form of comments throughout the codebase and can also be found in:
+
+- ``share/man/*``: Man pages
+- ``share/contrib``: Information on contributing
+- ``share/docs/kernel``: Kernel documentation
+- ``share/docs/lib``: Library documentation
+
+# Maintainers (by author)
+--------------
+| Maintainer | Component |
+|--------------------|--------------------|
+| <ian@osmora.org> | Hyra AMD64 Kernel |
+| <ian@osmora.org> | User C Library |
+| <ian@osmora.org> | NVMe Driver |
+| <ian@osmora.org> | AHCI Driver |
+| <ian@osmora.org> | xHCI Driver |
+| <ian@osmora.org> | RTL8139 Driver |
+| <ian@osmora.org> | E1000E Driver |
+| <ian@osmora.org> | ET131X Driver |
+| <ian@osmora.org> | PCI Driver |
+| <ian@osmora.org> | PCIe Driver |
+| <quinn@osmora.org> | PCI Driver |
+| <quinn@osmora.org> | User C Library |
+| <quinn@osmora.org> | Killing MS |
+
+--------------
+# To-do
+
+```
+[ ] kern: dev: AHCI DCDR cache (<ian@osmora.org>)
+[ ] kern: Worker threads (<ian@osmora.org>)
+[ ] kern: Multithreaded driver startup (<quinn@osmora.org>)
+[ ] libc: Slab allocator (<quinn@osmora.org>)
+...
+```
+
+Hyra running on bare metal:
+--------------
+![Hyra](./.github/assets/hyra.png)
License:
--------
diff --git a/bootstrap b/bootstrap
index f9370ab..23a7e6f 100755
--- a/bootstrap
+++ b/bootstrap
@@ -3,6 +3,19 @@ set -e
mkdir -p lib/
+SYSTEM_NAME="$(uname -s)"
+MAKE="make"
+GIT="git"
+GCC="gcc"
+CLANG="clang"
+
+
+if [ "$SYSTEM_NAME" = "OpenBSD" ]
+then
+ MAKE="$(which gmake)"
+fi
+
+
# arg0: Output path.
# arg1: Command for downloading
try_fetch() {
@@ -23,20 +36,25 @@ prepare() {
}
fetch() {
- try_fetch "git clone https://github.com/limine-bootloader/limine.git --branch=v9.x-binary --depth=1" "stand/limine"
+ try_fetch "git clone https://github.com/limine-bootloader/limine.git --branch=v9.3.0-binary --depth=1" "stand/limine"
}
build_limine() {
- make -C stand/limine/
+ $MAKE -C stand/limine/
}
build_kconf() {
- make -C tools/kconf/
+ $MAKE -C tools/kconf/
+}
+
+build_omar() {
+ $MAKE -C tools/omar/
}
build() {
build_limine
build_kconf
+ build_omar
}
echo "----------------------------------"
diff --git a/builddeps/limine.conf b/builddeps/limine.conf
index f1907ea..1ad040c 100644
--- a/builddeps/limine.conf
+++ b/builddeps/limine.conf
@@ -1,5 +1,5 @@
timeout=10
-${WALLPAPER_PATH}=boot():/boot/tree.jpg
+${WALLPAPER_PATH}=boot():/boot/wallpaper.jpg
wallpaper: ${WALLPAPER_PATH}
interface_branding_color: 1
term_background: 40000000
@@ -8,6 +8,6 @@ resolution: 1280x720
/Hyra
protocol: limine
- kernel_path: boot():/boot/hyra-kernel
- module_path: boot():/boot/ramfs.cpio
+ kernel_path: boot():/boot/hyra.krq
+ module_path: boot():/boot/ramfs.omar
diff --git a/builddeps/user.mk b/builddeps/user.mk
index d7ad582..d991ef8 100644
--- a/builddeps/user.mk
+++ b/builddeps/user.mk
@@ -4,4 +4,5 @@ USRDIR =
LDSCRIPT =
INTERNAL_CFLAGS = -T$(LDSCRIPT) -znoexecstack -nostdlib -I$(USRDIR)/include/ \
-L$(USRDIR)/lib -lc -pie -no-pie -fno-stack-protector \
- -fno-asynchronous-unwind-tables
+ -fno-asynchronous-unwind-tables -D_OSVER=\"$(OSVER)\" \
+ -D_OSARCH=\"$(OSARCH)\"
diff --git a/builddeps/wallpaper.jpg b/builddeps/wallpaper.jpg
new file mode 100644
index 0000000..5d69f53
--- /dev/null
+++ b/builddeps/wallpaper.jpg
Binary files differ
diff --git a/configure.ac b/configure.ac
index 3729adb..50c56b4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,6 +1,8 @@
-AC_INIT([Hyra], [1.6], [ian@osmora.org])
+AC_INIT([Hyra], [2.6], [ian@osmora.org])
TARGET="amd64"
+QEMU="qemu-system-x86_64"
+PROJECT_ROOT=`pwd`
BOOT_FW="BOOTX64.EFI"
AC_ARG_ENABLE([aarch64],
[AS_HELP_STRING([--enable-aarch64], [Enable AARCH64 Support])],
@@ -13,10 +15,11 @@ KERN_CFLAGS_AMD64="-fexceptions --std=gnu11 -ffreestanding -fno-stack-protector
-Werror=implicit-int -Werror=int-conversion \\
-Werror=missing-prototypes \\
-Werror=incompatible-pointer-types -Werror=int-to-pointer-cast \\
- -Werror=return-type -Wunused -mabi=sysv -mno-80387 -mno-mmx -mno-3dnow \\
- -mno-sse -mno-sse2 -mno-red-zone -mcmodel=kernel -pedantic \\
- -I sys/include/ -I sys/include/lib/ -D_KERNEL -Wno-pointer-sign -MMD \\
- -Wno-gnu-zero-variadic-macro-arguments -Wno-language-extension-token -Wno-tautological-constant-out-of-range-compare -Wno-c23-extensions"
+ -Werror=return-type -Wunused -mno-80387 -mno-mmx -mno-3dnow \\
+ -mno-sse -mno-sse2 -mno-red-zone -mcmodel=kernel -pedantic \\
+ -Isys/include/ -I sys/include/lib/ -D_KERNEL -Wno-pointer-sign -MMD \\
+ -Isys/include/dev/acpi/uacpi/ -nostdinc -Wno-format-pedantic \\
+ -Wno-format-pedantic -Wno-gnu-zero-variadic-macro-arguments -Wno-c2x-extensions -Wno-language-extension-token -Wno-tautological-constant-out-of-range-compare"
KERN_CFLAGS_AARCH64="-fexceptions --std=gnu11 -ffreestanding -fno-stack-protector -fno-pic \\
@@ -26,19 +29,28 @@ KERN_CFLAGS_AARCH64="-fexceptions --std=gnu11 -ffreestanding -fno-stack-protecto
-Werror=incompatible-pointer-types -Werror=int-to-pointer-cast \\
-Werror=return-type -Wunused -pedantic \\
-I sys/include/ -I sys/include/lib/ -D_KERNEL -Wno-pointer-sign -MMD \\
- -Wno-gnu-zero-variadic-macro-arguments -Wno-language-extension-token -Wno-tautological-constant-out-of-range-compare -Wno-c23-extensions"
+ -Wno-format-pedantic -Wno-gnu-zero-variadic-macro-arguments -Wno-c2x-extensions \\
+ -Isys/include/dev/acpi/uacpi/ -nostdinc -Wno-format-pedantic \\
+ -Wno-language-extension-token -Wno-tautological-constant-out-of-range-compare"
QEMU_FLAGS_AMD64="--enable-kvm -monitor stdio \\
-M q35 -m 1G -smp 4 -cpu host \\
-cdrom Hyra.iso"
+QEMU_FLAGS_AARCH64="-monitor stdio \\
+ -M versatilepb -m 256M -smp 1 \\
+ -cdrom Hyra.iso"
+
KERN_CFLAGS="$KERN_CFLAGS_AMD64"
+QEMU_FLAGS=$QEMU_FLAGS_AMD64
if test "x$TARGET" = "xaarch64"
then
KERN_CFLAGS="$KERN_CFLAGS_AARCH64"
BOOT_FW="BOOTAA64.EFI"
+ QEMU_FLAGS=$QEMU_FLAGS_AARCH64
+ QEMU="qemu-system-aarch64"
fi
@@ -49,10 +61,11 @@ AC_SUBST(HYRA_BUILDDATE, [$HYRA_BUILDDATE])
AC_SUBST(HYRA_BUILDBRANCH, [$HYRA_BUILDBRANCH])
AC_SUBST(KERNEL_CFLAGS, [$KERN_CFLAGS])
-AC_SUBST(QEMU_FLAGS, [$QEMU_FLAGS_AMD64])
-AC_SUBST(QEMU, [qemu-system-x86_64])
AC_SUBST(BOOT_FW, [$BOOT_FW])
AC_SUBST(ARCH, [$TARGET])
+AC_SUBST(QEMU_FLAGS, [$QEMU_FLAGS])
+AC_SUBST(QEMU, [$QEMU])
+AC_SUBST(PROJECT_ROOT, [$PROJECT_ROOT])
AC_SUBST(TOOLCHAIN, [clang])
AC_CONFIG_FILES([Makefile])
AC_OUTPUT
diff --git a/etc/hostname b/etc/hostname
new file mode 100644
index 0000000..c90b108
--- /dev/null
+++ b/etc/hostname
@@ -0,0 +1 @@
+osmora
diff --git a/etc/motd b/etc/motd
new file mode 100644
index 0000000..62718f4
--- /dev/null
+++ b/etc/motd
@@ -0,0 +1,4 @@
+:::::::::::::::::::::::::::::::::::::::
+:: OSMORA GATEWAY ~ Every key echos ::
+:: ..... Proceed with purpose ..... ::
+:::::::::::::::::::::::::::::::::::::::
diff --git a/etc/oemu/emutest.rc b/etc/oemu/emutest.rc
new file mode 100644
index 0000000..39607dd
--- /dev/null
+++ b/etc/oemu/emutest.rc
@@ -0,0 +1,5 @@
+@ Test emulator
+echo Generating /tmp/a.out
+oasm /etc/oemu/test-00.s /tmp/a.out
+echo Running emulator...
+oemu /tmp/a.out
diff --git a/etc/oemu/test-00.s b/etc/oemu/test-00.s
new file mode 100644
index 0000000..f6015c1
--- /dev/null
+++ b/etc/oemu/test-00.s
@@ -0,0 +1,20 @@
+mov x0, #1 ! ~ 0x00000000
+mov x1, #2 ! ~ 0x00000004
+mov x2, #3 ! ~ 0x00000008
+ ! X
+some_label: ! X
+ mov x2, #20 ! ~ 0x0000000C
+ br x2 ! ~ 0x00000010 -----+
+ !!!!!!!!!!!!!! X |
+ !!!!!!!!!!!!!! X |
+ ! X |
+ dec x0 ! ~ 0x00000014 <----+
+ inc x1 ! ~ 0x00000018
+ add x2, #3 ! ~ 0x0000001C
+ mrow x4, #0 ! ~ 0x00000020
+ nop ! ~ 0x00000024
+ or x1, #3 ! ~ 0x00000028
+ xor x2, #3 ! ~ 0x0000002C
+ lsr x2, #1 ! ~ 0x00000030
+ lsl x2, #1 ! ~ 0x00000034
+ hlt ! ~ 0x00000038
diff --git a/etc/passwd b/etc/passwd
new file mode 100644
index 0000000..7855b99
--- /dev/null
+++ b/etc/passwd
@@ -0,0 +1 @@
+root:4813494d137e1631bba301d5acab6e7bb7aa74ce1185d456565ef51d737677b2:0:0:Not a ruler:/root:/usr/bin/osh
diff --git a/hyra-build.sh b/hyra-build.sh
new file mode 100755
index 0000000..ab19724
--- /dev/null
+++ b/hyra-build.sh
@@ -0,0 +1,227 @@
+#!/bin/bash
+
+#
+# Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. Neither the name of Hyra nor the names of its contributors may be used
+# to endorse or promote products derived from this software without
+# specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+set -e
+
+RAMFS_TOOL="tools/omar/bin/omar"
+RAMFS_NAME="ramfs.omar"
+install_flag="false"
+NTHREADS="-j$(nproc)"
+
+###############################
+# Generate sysroot skeleton
+###############################
+sysroot_skel() {
+ mkdir -p base/usr/lib/
+ mkdir -p base/usr/sbin/
+ mkdir -p base/usr/bin/
+ mkdir -p base/boot/
+ mkdir -p base/usr/include/sys/
+ mkdir -p base/usr/rc
+
+ cp -r rc/* base/usr/rc
+ cp -f sys/include/sys/*.h base/usr/include/sys
+ cp -r etc base/etc/
+
+ # Populate ESP
+ make stand/boot/
+ cp stand/boot/*.EFI iso_root/EFI/BOOT/
+}
+
+iso_root_skel() {
+ mkdir -p iso_root/boot/
+ mkdir -p iso_root/EFI/BOOT/
+}
+
+###############################
+# Generate ISO root
+###############################
+gen_iso_root() {
+ cp $RAMFS_NAME iso_root/boot/
+ cp builddeps/limine.conf stand/limine/limine-bios.sys \
+ stand/limine/limine-bios-cd.bin stand/limine/limine-uefi-cd.bin iso_root/
+ cp builddeps/wallpaper.jpg iso_root/boot/
+}
+
+##################################
+# Stage 1 - generate isofs
+#
+# ++ ARGS ++
+# $1: ISO output name
+# -- --
+##################################
+gen_isofs() {
+ cp base/boot/* iso_root/boot/
+ xorriso -as mkisofs -b limine-bios-cd.bin -no-emul-boot -boot-load-size 4\
+ -boot-info-table --efi-boot limine-uefi-cd.bin -efi-boot-part \
+ --efi-boot-image --protective-msdos-label iso_root -o $1 > /dev/null
+ stand/limine/limine bios-install $1
+}
+
+build() {
+ echo "-- Building libs --"
+ make libs
+
+ echo "-- Building world --"
+ make $NTHREADS sbin bin
+
+ echo "-- Building kernel --"
+ make $NTHREADS base/boot/hyra.krq
+}
+
+####################################
+# Stage 1 - build production media
+####################################
+stage1() {
+ iso_root_skel
+ sysroot_skel
+
+ echo "[*] stage1: Build kernel"
+ build
+
+ echo "[*] stage1: Generate stage 1 RAMFS via OMAR"
+ $RAMFS_TOOL -i base/ -o $RAMFS_NAME
+
+ echo "[*] stage1: Generate stage 1 ISOFS (production)"
+ gen_iso_root
+ gen_isofs "Hyra.iso"
+
+ # Clean up
+ rm $RAMFS_NAME
+ rm -r iso_root
+}
+
+#################################
+# Stage 2 - build install media
+#################################
+stage2() {
+ make clean
+ rm -f base/boot/hyra.krq
+
+ iso_root_skel
+ sysroot_skel
+
+ echo "[*] stage2: Generate stage 2 RAMFS via OMAR"
+ mv Hyra.iso base/boot/
+ $RAMFS_TOOL -i base/ -o $RAMFS_NAME
+
+ echo "[*] stage2: Build kernel"
+ gen_iso_root
+ export KBUILD_ARGS="-D_INSTALL_MEDIA=1"
+ build
+
+ echo "[*] stage2: Generate stage 2 ISOFS (installer)"
+ gen_isofs "Hyra-install.iso"
+
+ # Clean up
+ rm $RAMFS_NAME
+ rm base/boot/Hyra.iso
+ rm -r iso_root
+}
+
+##################################
+# Clean up completly after build
+##################################
+hard_clean() {
+ make clean
+ rm -rf base/
+}
+
+##################################
+# Build results
+#
+# ++ ARGS ++
+# $1: ISO output name
+# -- --
+##################################
+result() {
+ echo "-------------------------------------------"
+ echo "Build finish"
+
+ if [[ $1 == "Hyra-install.iso" ]]
+ then
+ hard_clean # XXX: For safety
+ echo "Installer is at ./Hyra-install.iso"
+ echo "!!NOTE!!: OSMORA is not responsible for incidental data loss"
+ else
+ echo "Boot image is at ./Hyra.iso"
+ fi
+
+ echo "Finished in $(($SECONDS / 60)) minutes and $(($SECONDS % 60)) seconds"
+ echo "-------------------------------------------"
+}
+
+while getopts "ih" flag
+do
+ case "${flag}" in
+ i) install_flag="true"
+ ;;
+ *)
+ echo "Hyra build script"
+ echo "[-i] Build installer"
+ echo "[-h] Help"
+ exit 1
+ ;;
+ esac
+done
+
+if [[ ! -f ./configure ]]
+then
+ echo "[!] Please bootstrap and configure Hyra!"
+ echo "[!] Error in stage 1, exiting"
+ exit 1
+fi
+
+if [[ ! -f Makefile ]]
+then
+ echo "[!] 'Makefile' not found, did you run './configure'?"
+ echo "[!] Error in stage 1, exiting"
+fi
+
+echo "-- Begin stage 1 --"
+stage1
+
+if [[ $install_flag != "true" ]]
+then
+ echo "[?] Not building installer (-i unset)"
+ echo "-- Skipping stage 2 --"
+ result "Hyra.iso"
+else
+ echo "-- Begin stage 2 --"
+ stage2
+ result "Hyra-install.iso"
+fi
+
+if [[ $install_flag == "true" ]]
+then
+ make clean
+ rm -rf base/
+fi
diff --git a/lib/Makefile b/lib/Makefile
new file mode 100644
index 0000000..fc77815
--- /dev/null
+++ b/lib/Makefile
@@ -0,0 +1,10 @@
+LDSCRIPT =
+USRDIR =
+ROOT =
+ARGS = -I$(ROOT)/builddeps LDSCRIPT=$(LDSCRIPT) USRDIR=$(USRDIR) ROOT=$(ROOT)
+
+.PHONY: all
+all:
+ make -C libc/ $(ARGS)
+ make -C libgfx/ $(ARGS)
+ make -C liboda/ $(ARGS)
diff --git a/lib/libc/Makefile b/lib/libc/Makefile
index 43c823e..be693bd 100644
--- a/lib/libc/Makefile
+++ b/lib/libc/Makefile
@@ -1,6 +1,6 @@
-CFLAGS = -c -fno-stack-protector -nostdlib -static -Iinclude/
+CFLAGS = -c -fno-stack-protector -nostdlib -static -Iinclude/ -D_OLIBC
LIBC_CFILES = $(shell find src/ -name "*.c")
-LIBC_ASMFILES = $(shell find src/ -name "*.S")
+LIBC_ASMFILES = $(shell find src/arch/$(ARCH) -name "*.S")
LIBC_OBJ = $(LIBC_CFILES:.c=.o)
LIBC_ASMOBJ = $(LIBC_ASMFILES:.S=.S.o)
@@ -9,6 +9,9 @@ all: sys/include/machine headers $(LIBC_ASMOBJ) $(LIBC_OBJ) build/libc.a
build/libc.a: build/
ar rcs build/libc.a $(LIBC_OBJ) $(LIBC_ASMOBJ)
+ mv $(LIBC_ASMOBJ) build/ # For initial bootstrapping
+ mv build/crti.S.o build/crti.o
+ mv build/*.o build/*.a $(ROOT)/base/usr/lib/
%.o: %.c
$(CC) $(CFLAGS) -Iinclude/ $< -o $@
@@ -27,6 +30,7 @@ headers: sys/include/machine
cp -f include/*.h $(USRDIR)/include/
cp -f include/stdlib/*.h $(USRDIR)/include/
cp -rf include/ousi $(USRDIR)/include/
+ cp -rf include/crypto $(USRDIR)/include/
.PHONY:
build/:
diff --git a/lib/libc/include/arch/aarch64/syscall.h b/lib/libc/include/arch/aarch64/syscall.h
new file mode 100644
index 0000000..84a51e0
--- /dev/null
+++ b/lib/libc/include/arch/aarch64/syscall.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _MACHINE_SYSCALL_H_
+#define _MACHINE_SYSCALL_H_
+
+#if !defined(__ASSEMBLER__)
+__always_inline static inline long
+syscall0(uint64_t code)
+{
+ return 0;
+}
+
+__always_inline static inline long
+syscall1(uint64_t code, uint64_t arg0)
+{
+ return 0;
+}
+
+__always_inline static long inline
+syscall2(uint64_t code, uint64_t arg0, uint64_t arg1)
+{
+ return 0;
+}
+
+__always_inline static inline long
+syscall3(uint64_t code, uint64_t arg0, uint64_t arg1, uint64_t arg2)
+{
+ return 0;
+}
+
+__always_inline static inline long
+syscall4(uint64_t code, uint64_t arg0, uint64_t arg1, uint64_t arg2, uint64_t arg3)
+{
+ return 0;
+}
+
+__always_inline static inline long
+syscall5(uint64_t code, uint64_t arg0, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4)
+{
+ return 0;
+}
+
+__always_inline static inline long
+syscall6(uint64_t code, uint64_t arg0, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4, uint64_t arg5)
+{
+ return 0;
+}
+
+#define _SYSCALL_N(a0, a1, a2, a3, a4, a5, a6, name, ...) \
+ name
+
+#define syscall(...) \
+_SYSCALL_N(__VA_ARGS__, syscall6, syscall5, \
+ syscall4, syscall3, syscall2, syscall1, \
+ syscall0)(__VA_ARGS__)
+
+#endif /* !__ASSEMBLER__ */
+#endif /* !_MACHINE_SYSCALL_H_ */
diff --git a/lib/libc/include/crypto/sha256.h b/lib/libc/include/crypto/sha256.h
new file mode 100644
index 0000000..6bd1077
--- /dev/null
+++ b/lib/libc/include/crypto/sha256.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _CRYPTO_SHA256_H
+#define _CRYPTO_SHA256_H 1
+
+#include <stddef.h>
+#include <stdint.h>
+
+#define SHA256_HEX_SIZE (64 + 1)
+#define SHA256_BYTES_SIZE 32
+
+/*
+ * Compute the SHA-256 checksum of a memory region given a pointer and
+ * the size of that memory region.
+ * The output is a hexadecimal string of 65 characters.
+ * The last character will be the null-character.
+ */
+void sha256_hex(const void *src, size_t n_bytes, char *dst_hex65);
+
+void sha256_bytes(const void *src, size_t n_bytes, void *dst_bytes32);
+
+typedef struct sha256 {
+ uint32_t state[8];
+ uint8_t buffer[64];
+ uint64_t n_bits;
+ uint8_t buffer_counter;
+} sha256;
+
+/* Functions to compute streaming SHA-256 checksums. */
+void sha256_init(struct sha256 *sha);
+void sha256_append(struct sha256 *sha, const void *data, size_t n_bytes);
+void sha256_finalize_hex(struct sha256 *sha, char *dst_hex65);
+void sha256_finalize_bytes(struct sha256 *sha, void *dst_bytes32);
+
+#endif /* !_CRYPTO_SHA256_H */
diff --git a/lib/libc/include/ctype.h b/lib/libc/include/ctype.h
new file mode 100644
index 0000000..2a827e3
--- /dev/null
+++ b/lib/libc/include/ctype.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _CTYPE_H
+#define _CTYPE_H 1
+
+#include <sys/param.h>
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+__always_inline static inline int
+__isascii(int c)
+{
+ return c >= 0 && c <= 127;
+}
+
+__always_inline static inline int
+__tolower(int c)
+{
+ return c | 0x20;
+}
+
+__always_inline static inline int
+__toupper(int c)
+{
+ return c & ~0x20;
+}
+
+__always_inline static inline int
+__isalpha(int c)
+{
+ c = __tolower(c);
+ return c >= 'a' && c <= 'z';
+}
+
+__always_inline static inline int
+__isdigit(int c)
+{
+ return c >= '0' && c <= '9';
+}
+
+__always_inline static inline int
+__isspace(int c)
+{
+ switch (c) {
+ case ' ':
+ case '\t':
+ case '\n':
+ case '\r':
+ case '\v':
+ return 1;
+
+ return 0;
+ }
+}
+
+__END_DECLS
+
+/* Conver char to lowercase */
+#define tolower(C) __tolower((C))
+
+/* Conver char to uppercase */
+#define toupper(C) __toupper((C))
+
+/* Is alphabetical? */
+#define isalpha(C) __isalpha((C))
+
+/* Is a digit? */
+#define isdigit(C) __isdigit((C))
+
+/* Is a space? */
+#define isspace(C) __isspace((C))
+
+/* Is ascii? */
+#define isascii(C) __isascii((C))
+
+#endif /* _CTYPE_H */
diff --git a/lib/libc/include/fenv.h b/lib/libc/include/fenv.h
new file mode 100644
index 0000000..d0e2fec
--- /dev/null
+++ b/lib/libc/include/fenv.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _FENV_H_
+#define _FENV_H_ 1A
+
+#include <sys/types.h>
+
+typedef struct {
+ __uint32_t __control_word;
+ __uint32_t __status_word;
+ __uint32_t __unused[5];
+ __uint32_t __mxcsr;
+} fenv_t;
+
+typedef __uint16_t fexcept_t;
+
+#define FE_TONEAREST 0
+#define FE_DOWNWARD 0x400
+#define FE_UPWARD 0x800
+#define FE_TOWARDZERO 0xC00
+
+int feclearexcept(int __excepts);
+int fegetenv(fenv_t *__envp);
+int fegetexceptflag(fexcept_t *__envp, int __excepts);
+int fegetround(void);
+int feholdexcept(fenv_t *__envp);
+int feraiseexcept(int __excepts);
+int fesetenv(const fenv_t *__envp);
+int fesetexceptflag(const fexcept_t *__envp, int __excepts);
+int fesetround(int __round);
+int fetestexcept(int __excepts);
+int feupdateenv(const fenv_t *__envp);
+
+#endif /* !_FENV_H_ */
diff --git a/lib/libc/include/math.h b/lib/libc/include/math.h
new file mode 100644
index 0000000..13988cb
--- /dev/null
+++ b/lib/libc/include/math.h
@@ -0,0 +1,274 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _MATH_H_
+#define _MATH_H_ 1
+
+#define M_E 2.7182818284590452354
+#define M_LOG2E 1.4426950408889634074
+#define M_LOG10E 0.43429448190325182765
+#define M_LN2 0.69314718055994530942
+#define M_LN10 2.30258509299404568402
+#define M_PI 3.14159265358979323846
+#define M_PI_2 1.57079632679489661923
+#define M_PI_4 0.78539816339744830962
+#define M_1_PI 0.31830988618379067154
+#define M_2_PI 0.63661977236758134308
+#define M_2_SQRTPI 1.12837916709551257390
+#define M_SQRT2 1.41421356237309504880
+#define M_SQRT1_2 0.70710678118654752440
+#define M_PIl 3.141592653589793238462643383279502884L
+
+#define FP_ILOGBNAN (-1 - (int)(((unsigned)-1) >> 1))
+#define FP_ILOGB0 FP_ILOGBNAN
+#define FP_INFINITE 1
+#define FP_NAN 2
+#define FP_NORMAL 4
+#define FP_SUBNORMAL 8
+#define FP_ZERO 16
+
+#define isfinite(x) (fpclassify(x) & (FP_NORMAL | FP_SUBNORMAL | FP_ZERO))
+#define isnan(x) (fpclassify(x) == FP_NAN)
+#define isinf(x) (fpclassify(x) == FP_INFINITE)
+#define isnormal(x) (fpclassify(x) == FP_NORMAL)
+#define signbit(x) (__builtin_signbit(x))
+
+#define INFINITY (__builtin_inff())
+#define NAN (__builtin_nanf(""))
+
+int __fpclassify(double __x);
+int __fpclassifyf(float __x);
+int __fpclassifyl(long double __x);
+
+#define fpclassify(x) \
+ (sizeof(x) == sizeof(double) ? __fpclassify(x) : \
+ (sizeof(x) == sizeof(float) ? __fpclassifyf(x) : \
+ (sizeof(x) == sizeof(long double) ? __fpclassifyl(x) : \
+ 0)))
+
+typedef double double_t;
+typedef float float_t;
+
+double exp10(double __x);
+float exp10f(float __x);
+long double exp10l(long double __x);
+
+double exp(double __x);
+float expf(float __x);
+long double expl(long double __x);
+
+double exp2(double __x);
+float exp2f(float __x);
+long double exp2l(long double __x);
+
+double expm1(double __x);
+float expm1f(float __x);
+long double expm1l(long double __x);
+
+double frexp(double __x, int *__power);
+float frexpf(float __x, int *__power);
+long double frexpl(long double __x, int *__power);
+
+int ilogb(double __x);
+int ilogbf(float __x);
+int ilogbl(long double __x);
+
+double ldexp(double __x, int __power);
+float ldexpf(float __x, int __power);
+long double ldexpl(long double __x, int __power);
+
+double log(double __x);
+float logf(float __x);
+long double logl(long double __x);
+
+double log10(double __x);
+float log10f(float __x);
+long double log10l(long double __x);
+
+double log1p(double __x);
+float log1pf(float __x);
+long double log1pl(long double __x);
+
+double log2(double __x);
+float log2f(float __x);
+long double log2l(long double __x);
+
+double logb(double __x);
+float logbf(float __x);
+long double logbl(long double __x);
+
+double modf(double __x, double *__integral);
+float modff(float __x, float *__integral);
+long double modfl(long double __x, long double *__integral);
+
+double scalbn(double __x, int __power);
+float scalbnf(float __x, int __power);
+long double scalbnl(long double __x, int __power);
+
+double scalbln(double __x, long __power);
+float scalblnf(float __x, long __power);
+long double scalblnl(long double __x, long __power);
+
+double cbrt(double __x);
+float cbrtf(float __x);
+long double cbrtl(long double __x);
+
+double fabs(double __x);
+float fabsf(float __x);
+long double fabsl(long double __x);
+
+double hypot(double __x, double __y);
+float hypotf(float __x, float __y);
+long double hypotl(long double __x, long double __y);
+
+double pow(double __x, double __y);
+float powf(float __x, float __y);
+long double powl(long double __x, long double __y);
+
+double sqrt(double __x);
+float sqrtf(float __x);
+long double sqrtl(long double __x);
+
+double erf(double __x);
+float erff(float __x);
+long double erfl(long double __x);
+
+double erfc(double __x);
+float erfcf(float __x);
+long double erfcl(long double __x);
+
+double lgamma(double __x);
+float lgammaf(float __x);
+long double lgammal(long double __x);
+
+double tgamma(double __x);
+float tgammaf(float __x);
+long double tgammal(long double __x);
+
+double ceil(double __x);
+float ceilf(float __x);
+long double ceill(long double __x);
+
+double floor(double __x);
+float floorf(float __x);
+long double floorl(long double __x);
+
+double nearbyint(double __x);
+float nearbyintf(float __x);
+long double nearbyintl(long double __x);
+
+double rint(double __x);
+float rintf(float __x);
+long double rintl(long double __x);
+
+long lrint(double __x);
+long lrintf(float __x);
+long lrintl(long double __x);
+
+long long llrint(double __x);
+long long llrintf(float __x);
+long long llrintl(long double __x);
+
+double round(double __x);
+float roundf(float __x);
+long double roundl(long double __x);
+
+long lround(double __x);
+long lroundf(float __x);
+long lroundl(long double __x);
+
+long long llround(double __x);
+long long llroundf(float __x);
+long long llroundl(long double __x);
+
+double trunc(double __x);
+float truncf(float __x);
+long double truncl(long double __x);
+
+double fmod(double __x, double __y);
+float fmodf(float __x, float __y);
+long double fmodl(long double __x, long double __y);
+
+double remainder(double __x, double __y);
+float remainderf(float __x, float __y);
+long double remainderl(long double __x, long double __y);
+
+double remquo(double __x, double __y, int *__quotient);
+float remquof(float __x, float __y, int *__quotient);
+long double remquol(long double __x, long double __y, int *__quotient);
+
+double copysign(double __x, double __sign);
+float copysignf(float __x, float __sign);
+long double copysignl(long double __x, long double __sign);
+
+double nan(const char *__tag);
+float nanf(const char *__tag);
+long double nanl(const char *__tag);
+
+double nextafter(double __x, double __dir);
+float nextafterf(float __x, float __dir);
+long double nextafterl(long double __x, long double __dir);
+
+double nexttoward(double __x, long double __dir);
+float nexttowardf(float __x, long double __dir);
+long double nexttowardl(long double __x, long double __dir);
+
+double fdim(double __x, double __y);
+float fdimf(float __x, float __y);
+long double fdiml(long double __x, long double __y);
+
+double fmax(double __x, double __y);
+float fmaxf(float __x, float __y);
+long double fmaxl(long double __x, long double __y);
+
+double fmin(double __x, double __y);
+float fminf(float __x, float __y);
+long double fminl(long double __x, long double __y);
+
+double atan(double __x);
+float atanf(float __x);
+long double atanl(long double __x);
+
+double atan2(double __x, double __y);
+float atan2f(float __x, float __y);
+long double atan2l(long double __x, long double __y);
+
+double cos(double __x);
+float cosf(float __x);
+long double cosl(long double __x);
+
+double sin(double __x);
+float sinf(float __x);
+long double sinl(long double __x);
+
+double tan(double __x);
+float tanf(float __x);
+long double tanl(long double __x);
+
+#endif /* !_MATH_H_ */
diff --git a/lib/libc/include/stdarg.h b/lib/libc/include/stdarg.h
new file mode 100644
index 0000000..dc75475
--- /dev/null
+++ b/lib/libc/include/stdarg.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _STDARG_H
+#define _STDARG_H 1
+
+#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L
+#define __STDC_VERSION_STDARG_H__ 202311L
+#endif
+
+/* Determine which definitions are needed */
+#if !defined(__need___va_list) && !defined(__need_va_list) && \
+ !defined(__need_va_arg) && \
+ !defined(__need___va_copy) && !defined(__need_va_copy)
+#define __need___va_list
+#define __need_va_list
+#define __need_va_arg
+#define __need___va_copy
+#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || (defined(__cplusplus) && __cplusplus >= 201103L)
+#define __need_va_copy
+#endif
+#endif
+
+/* __gnuc_va_list type */
+#ifdef __need___va_list
+#ifndef __GNUC_VA_LIST
+#define __GNUC_VA_LIST
+typedef __builtin_va_list __gnuc_va_list;
+#endif /* !__GNUC_VA_LIST */
+#undef __need___va_list
+#endif /* __need___va_list */
+
+/* va_list type */
+#ifdef __need_va_list
+#ifndef _VA_LIST
+#define _VA_LIST
+typedef __builtin_va_list va_list;
+#endif /* !_VA_LIST */
+#undef __need_va_list
+#endif /* __need_va_list */
+
+/* va_start(), va_end(), and va_arg() macros */
+#ifdef __need_va_arg
+#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L
+#define va_start(ap, ...) __builtin_va_start(ap, 0)
+#else
+#define va_start(ap, arg) __builtin_va_start(ap, arg)
+#endif
+#define va_end(ap) __builtin_va_end(ap)
+#define va_arg(ap, type) __builtin_va_arg(ap, type)
+#undef __need_va_arg
+#endif /* __need_va_arg */
+
+/* __va_copy() macro */
+#ifdef __need___va_copy
+#define __va_copy(dest, src) __builtin_va_copy(dest, src)
+#undef __need___va_copy
+#endif /* __need___va_copy */
+
+/* va_copy() macro */
+#ifdef __need_va_copy
+#define va_copy(dest, src) __builtin_va_copy(dest, src)
+#undef __need_va_copy
+#endif /* __need_va_copy */
+
+#endif /* !_STDARG_H */
diff --git a/lib/libc/include/stdatomic.h b/lib/libc/include/stdatomic.h
new file mode 100644
index 0000000..3f80270
--- /dev/null
+++ b/lib/libc/include/stdatomic.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _STDATOMIC_H
+#define _STDATOMIC_H 1
+
+#include <stddef.h>
+#include <stdint.h>
+
+#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L
+#define __STDC_VERSION_STDATOMIC_H__ 202311L
+#endif
+
+#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
+
+#define kill_dependency(y) (y)
+
+#if (defined(__STDC_VERSION__) && __STDC_VERSION__ < 202311L) || defined(__cplusplus)
+/* Deprecated in C17, removed in C23 */
+#define ATOMIC_VAR_INIT(value) (value)
+#endif
+
+#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L) || defined(__cplusplus)
+#define ATOMIC_FLAG_INIT { false }
+typedef _Atomic(bool) atomic_bool;
+#else
+#define ATOMIC_FLAG_INIT { 0 }
+typedef _Atomic(_Bool) atomic_bool;
+#endif
+
+typedef _Atomic(signed char) atomic_schar;
+
+typedef _Atomic(char) atomic_char;
+typedef _Atomic(short) atomic_short;
+typedef _Atomic(int) atomic_int;
+typedef _Atomic(long) atomic_long;
+typedef _Atomic(long long) atomic_llong;
+
+typedef _Atomic(unsigned char) atomic_uchar;
+typedef _Atomic(unsigned short) atomic_ushort;
+typedef _Atomic(unsigned int) atomic_uint;
+typedef _Atomic(unsigned long) atomic_ulong;
+typedef _Atomic(unsigned long long) atomic_ullong;
+
+typedef _Atomic(uintptr_t) atomic_uintptr_t;
+typedef _Atomic(size_t) atomic_size_t;
+
+typedef struct atomic_flag {
+ atomic_bool _Value;
+} atomic_flag;
+
+typedef enum memory_order {
+ memory_order_relaxed = __ATOMIC_RELAXED,
+ memory_order_consume = __ATOMIC_CONSUME,
+ memory_order_acquire = __ATOMIC_ACQUIRE,
+ memory_order_release = __ATOMIC_RELEASE,
+ memory_order_acq_rel = __ATOMIC_ACQ_REL,
+ memory_order_seq_cst = __ATOMIC_SEQ_CST
+} memory_order;
+
+#define atomic_is_lock_free(obj) __atomic_is_lock_free(sizeof(*(obj)), 0)
+#define atomic_flag_test_and_set(obj) __atomic_test_and_set((obj), memory_order_seq_cst)
+#define atomic_flag_clear(obj) __atomic_clear((obj), memory_order_seq_cst)
+#define atomic_load(obj) __atomic_load_n((obj), memory_order_seq_cst)
+#define atomic_store(obj, desired) __atomic_store_n((obj), (desired), memory_order_seq_cst)
+#define atomic_exchange(obj, desired) __atomic_exchange_n((obj), (desired), memory_order_seq_cst)
+#define atomic_fetch_add(obj, arg) __atomic_fetch_add((obj), (arg), memory_order_seq_cst)
+#define atomic_fetch_sub(obj, arg) __atomic_fetch_sub((obj), (arg), memory_order_seq_cst)
+#define atomic_fetch_and(obj, arg) __atomic_fetch_and((obj), (arg), memory_order_seq_cst)
+#define atomic_fetch_or(obj, arg) __atomic_fetch_or((obj), (arg), memory_order_seq_cst)
+#define atomic_fetch_xor(obj, arg) __atomic_fetch_xor((obj), (arg), memory_order_seq_cst)
+
+#define atomic_signal_fence __atomic_signal_fence
+#define atomic_thread_fence __atomic_thread_fence
+#define atomic_flag_test_and_set_explicit __atomic_test_and_set
+#define atomic_flag_clear_explicit __atomic_clear
+#define atomic_load_explicit __atomic_load_n
+#define atomic_store_explicit __atomic_store_n
+#define atomic_exchange_explicit __atomic_exchange_n
+#define atomic_fetch_add_explicit __atomic_fetch_add
+#define atomic_fetch_sub_explicit __atomic_fetch_sub
+#define atomic_fetch_and_explicit __atomic_fetch_and
+#define atomic_fetch_or_explicit __atomic_fetch_or
+#define atomic_fetch_xor_explicit __atomic_fetch_xor
+
+#define atomic_compare_exchange_strong(obj, expected, desired) __atomic_compare_exchange_n((obj), (expected), (desired), false, memory_order_seq_cst, memory_order_seq_cst)
+#define atomic_compare_exchange_weak(obj, expected, desired) __atomic_compare_exchange_n((obj), (expected), (desired), true, memory_order_seq_cst, memory_order_seq_cst)
+#define atomic_compare_exchange_strong_explicit __atomic_compare_exchange_n
+#define atomic_compare_exchange_weak_explicit __atomic_compare_exchange_n
+
+#endif
+
+#endif /* !_STDATOMIC_H */
diff --git a/lib/libc/include/stddef.h b/lib/libc/include/stddef.h
index 557f69b..642f773 100644
--- a/lib/libc/include/stddef.h
+++ b/lib/libc/include/stddef.h
@@ -28,18 +28,157 @@
*/
#ifndef _STDDEF_H
-#define _STDDEF_H
+#define _STDDEF_H 1
-#include <sys/types.h>
+#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L
+#define __STDC_VERSION_STDDEF_H__ 202311L
+#endif
+
+/* Determine which definitions are needed */
+#if !defined(__need_NULL) && !defined(__need_nullptr_t) && \
+ !defined(__need_size_t) && !defined(__need_rsize_t) && \
+ !defined(__need_wchar_t) && !defined(__need_wint_t) && \
+ !defined(__need_ptrdiff_t) && !defined(__need_max_align_t) && \
+ !defined(__need_offsetof) && !defined(__need_unreachable)
+#define __need_NULL
+#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L) || defined(__cplusplus)
+#define __need_nullptr_t
+#endif
+#define __need_ptrdiff_t
+#define __need_size_t
+#if defined(__STDC_WANT_LIB_EXT1__) && __STDC_WANT_LIB_EXT1__ >= 1
+#define __need_rsize_t
+#endif
+#define __need_wchar_t
+#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L) || (defined(__cplusplus) && __cplusplus >= 201103L)
+#define __need_max_align_t
+#endif
+#define __need_offsetof
+#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L
+#define __need_unreachable
+#endif
+#endif
+/* NULL pointer constant */
+#ifdef __need_NULL
+#ifdef __cplusplus
#if __cplusplus >= 201103L
#define NULL nullptr
-#elif defined(__cplusplus)
+#else
#define NULL 0L
+#endif /* __cplusplus >= 201103L */
#else
-#define NULL ((void *) 0)
+#define NULL ((void *) 0)
+#endif /* __cplusplus */
+#undef __need_NULL
+#endif /* __need_NULL */
+
+/* nullptr_t type */
+#ifdef __need_nullptr_t
+#ifndef _NULLPTR_T
+#define _NULLPTR_T
+#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L
+typedef typeof(nullptr) nullptr_t;
#endif
+#endif /* !_NULLPTR_T */
+#undef __need_nullptr_t
+#endif /* __need_nullptr_t */
+
+/* size_t type */
+#ifdef __need_size_t
+#ifndef _SIZE_T
+#define _SIZE_T
+#ifdef __SIZE_TYPE__
+typedef __SIZE_TYPE__ size_t;
+#else
+typedef long unsigned int size_t;
+#endif /* __SIZE_TYPE__ */
+#endif /* !_SIZE_T */
+#undef __need_size_t
+#endif /* __need_size_t */
+
+/* rsize_t type */
+#ifdef __need_rsize_t
+#ifndef _RSIZE_T
+#define _RSIZE_T
+#ifdef __SIZE_TYPE__
+typedef __SIZE_TYPE__ rsize_t;
+#else
+typedef long unsigned int rsize_t;
+#endif /* __SIZE_TYPE__ */
+#endif /* !_RSIZE_T */
+#undef __need_rsize_t
+#endif /* __need_rsize_t */
-typedef __size_t size_t;
+/* wchar_t type */
+#ifdef __need_wchar_t
+#ifndef _WCHAR_T
+#define _WCHAR_T
+#ifdef __WCHAR_TYPE__
+typedef __WCHAR_TYPE__ wchar_t;
+#else
+typedef int wchar_t;
+#endif /* __WCHAR_TYPE__ */
+#endif /* !_WCHAR_T */
+#undef __need_wchar_t
+#endif /* __need_wchar_t */
+
+/* wint_t type */
+#ifdef __need_wint_t
+#ifndef _WINT_T
+#define _WINT_T
+#ifdef __WINT_TYPE__
+typedef __WINT_TYPE__ wint_t;
+#else
+typedef unsigned int wint_t;
+#endif /* __WINT_TYPE__ */
+#endif /* !_WINT_T */
+#undef __need_wint_t
+#endif /* __need_wint_t */
+
+/* ptrdiff_t type */
+#ifdef __need_ptrdiff_t
+#ifndef _PTRDIFF_T
+#define _PTRDIFF_T
+#ifdef __PTRDIFF_TYPE__
+typedef __PTRDIFF_TYPE__ ptrdiff_t;
+#else
+typedef long int ptrdiff_t;
+#endif /* __PTRDIFF_TYPE__ */
+#endif /* !_PTRDIFF_T */
+#undef __need_ptrdiff_t
+#endif /* __need_ptrdiff_t */
+
+/* max_align_t type */
+#ifdef __need_max_align_t
+#if defined (_MSC_VER)
+typedef double max_align_t;
+#elif defined(__APPLE__)
+typedef long double max_align_t;
+#else
+typedef struct {
+ long long __longlong __attribute__((__aligned__(__alignof__(long long))));
+ long double __longdouble __attribute__((__aligned__(__alignof__(long double))));
+} max_align_t;
+#endif
+#undef __need_max_align_t
+#endif /* __need_max_align_t */
+
+/* offsetof() macro */
+#ifdef __need_offsetof
+#ifndef offsetof
+#define offsetof(type, member) __builtin_offsetof(type, member)
+#endif
+#undef __need_offsetof
+#endif /* __need_offsetof */
+
+/* unreachable() macro */
+#ifdef __need_unreachable
+/* C++ has std::unreachable() */
+#if !defined(__cplusplus) && !defined(unreachable)
+#define unreachable() __builtin_unreachable()
+#endif
+#undef __need_unreachable
+#endif /* __need_unreachable */
#endif /* !_STDDEF_H */
diff --git a/lib/libc/include/stdio.h b/lib/libc/include/stdio.h
new file mode 100644
index 0000000..37931ce
--- /dev/null
+++ b/lib/libc/include/stdio.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _STDIO_H
+#define _STDIO_H 1
+
+#include <sys/cdefs.h>
+#define __need_NULL
+#define __need_size_t
+#include <stddef.h>
+#include <unistd.h>
+#define __need_va_list
+#include <stdarg.h>
+
+#if __STDC_VERSION__ >= 202311L
+#define __STDC_VERSION_STDIO_H__ 202311L
+#endif
+
+/* Buffering modes */
+#define _IOFBF 0 /* Fully buffered */
+#define _IOLBF 1 /* Line buffered */
+#define _IONBF 2 /* Unbuffered */
+
+/* Default buffer size */
+#define BUFSIZ 256
+
+/* End-Of-File indicator */
+#define EOF (-1)
+
+/* Spec says these should be defined as macros */
+#define stdin stdin
+#define stdout stdout
+#define stderr stderr
+
+/* File structure */
+typedef struct _IO_FILE {
+ int fd;
+ int buf_mode;
+} FILE;
+
+extern FILE *stdin;
+extern FILE *stdout;
+extern FILE *stderr;
+
+#define putc(c, stream) fputc((c), (stream))
+#define getc(stream) fgetc((stream))
+
+__BEGIN_DECLS
+
+size_t fread(void *__restrict ptr, size_t size, size_t n, FILE *__restrict stream);
+size_t fwrite(const void *__restrict ptr, size_t size, size_t n, FILE *__restrict stream);
+
+long ftell(FILE *stream);
+char *fgets(char *__restrict s, int size, FILE *__restrict stream);
+
+FILE *fopen(const char *__restrict path, const char *__restrict mode);
+int fseek(FILE *stream, long offset, int whence);
+int fclose(FILE *stream);
+
+int vsnprintf(char *s, size_t size, const char *fmt, va_list ap);
+int snprintf(char *s, size_t size, const char *fmt, ...);
+
+int printf(const char *__restrict fmt, ...);
+int fileno(FILE *stream);
+int fputc(int c, FILE *stream);
+
+int putchar(int c);
+int fgetc(FILE *stream);
+int getchar(void);
+
+#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+int fputs(const char *__restrict s, FILE *__restrict stream);
+#else
+int fputs(const char *s, FILE *stream);
+#endif
+int puts(const char *s);
+
+__END_DECLS
+
+#endif /* !_STDIO_H */
diff --git a/lib/libc/include/stdlib.h b/lib/libc/include/stdlib.h
new file mode 100644
index 0000000..b1de3f3
--- /dev/null
+++ b/lib/libc/include/stdlib.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _STDLIB_H
+#define _STDLIB_H 1
+
+/* For __dead */
+#include <sys/cdefs.h>
+
+/* Get specific definitions from stddef.h */
+#define __need_NULL
+#define __need_size_t
+#define __need_wchar_t
+#include <stddef.h>
+
+#if __STDC_VERSION__ >= 202311L
+#define __STDC_VERSION_STDLIB_H__ 202311L
+#endif
+
+#define EXIT_SUCCESS 0
+#define EXIT_FAILURE 1
+
+typedef struct {
+ int quot;
+ int rem;
+} div_t;
+
+#ifndef __ldiv_t_defined
+typedef struct {
+ long int quot;
+ long int rem;
+} ldiv_t;
+#define __ldiv_t_defined 1
+#endif /* !__ldiv_t_defined */
+
+#ifndef __lldiv_t_defined
+typedef struct {
+ long long int quot;
+ long long int rem;
+} lldiv_t;
+#define __lldiv_t_defined 1
+#endif /* !__lldiv_t_defined */
+
+__BEGIN_DECLS
+
+#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L) || (defined(__cplusplus) && __cplusplus >= 201103L)
+[[noreturn]] void abort(void);
+[[noreturn]] void exit(int status);
+[[noreturn]] void _Exit(int status);
+#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
+_Noreturn void abort(void);
+_Noreturn void exit(int status);
+_Noreturn void _Exit(int status);
+#else
+__dead void abort(void);
+__dead void exit(int status);
+#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+__dead void _Exit(int status);
+#endif
+#endif
+
+void *malloc(size_t size);
+void *realloc(void *ptr, size_t size);
+void free(void *ptr);
+
+void srand(unsigned int r);
+int rand(void);
+
+__END_DECLS
+
+#endif /* !_STDLIB_H */
diff --git a/lib/libc/include/string.h b/lib/libc/include/string.h
index d1613e4..4ab1e45 100644
--- a/lib/libc/include/string.h
+++ b/lib/libc/include/string.h
@@ -31,9 +31,18 @@
#define _STRING_H_ 1
#include <stddef.h>
+#include <stdint.h>
size_t strlen(const char *s);
+char *strtok(char *s, const char *delim);
+char *strdup(const char *s);
+
+void *memset(void *dst, int c, size_t n);
int memcmp(const void *s1, const void *s2, size_t n);
+
+char *itoa(int64_t value, char *buf, int base);
+void *memcpy(void *dest, const void *src, size_t n);
int strcmp(const char *s1, const char *s2);
+int atoi(const char *s);
#endif /* !_STRING_H_ */
diff --git a/lib/libc/include/time.h b/lib/libc/include/time.h
new file mode 100644
index 0000000..afb8adc
--- /dev/null
+++ b/lib/libc/include/time.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _TIME_H_
+#define _TIME_H_ 1
+
+#include <sys/time.h>
+
+int sleep(struct timespec *__restrict tsp, struct timespec *__restrict remp);
+
+#endif /* !_TIME_H_ */
diff --git a/lib/libc/include/unistd.h b/lib/libc/include/unistd.h
index 0a8c429..40ebee2 100644
--- a/lib/libc/include/unistd.h
+++ b/lib/libc/include/unistd.h
@@ -30,15 +30,56 @@
#ifndef _UNISTD_H
#define _UNISTD_H
+#include <sys/exec.h>
#include <sys/types.h>
#include <sys/cdefs.h>
#include <stddef.h>
+#define F_OK 0
+
+/* lseek whence, follows Hyra ABI */
+#define SEEK_SET 0
+#define SEEK_CUR 1
+#define SEEK_END 2
+
__BEGIN_DECLS
+int sysconf(int name);
+int setuid(uid_t new);
+
+int gethostname(char *name, size_t size);
+int sethostname(const char *name, size_t size);
+
+uid_t getuid(void);
+char *getlogin(void);
+
+char *getcwd(char *buf, size_t size);
+char *getwd(char *pathname);
+
+int symlink(const char *target, const char *linkpath);
+int synlinkat(const char *target, int newdirfd, const char *linkpath);
+
ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);
+
int close(int fd);
+int access(const char *path, int mode);
+
+off_t lseek(int fildes, off_t offset, int whence);
+int unlinkat(int dirfd, const char *pathname, int flags);
+int unlink(const char *path);
+
+int dup(int fd);
+int dup2(int fd, int fd1);
+
+pid_t getpid(void);
+pid_t getppid(void);
+pid_t fork(void);
+
+extern char *optarg;
+extern int optind, opterr, optopt;
+
+int getopt(int argc, char *argv[], const char *optstring);
__END_DECLS
diff --git a/lib/libc/src/arch/aarch64/crti.S b/lib/libc/src/arch/aarch64/crti.S
new file mode 100644
index 0000000..d95220b
--- /dev/null
+++ b/lib/libc/src/arch/aarch64/crti.S
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+ .text
+ .globl _start
+_start:
+ mov fp, xzr
+ mov x0, sp
+1: b 1b
diff --git a/lib/libc/src/arch/amd64/crt0.S b/lib/libc/src/arch/amd64/crti.S
index 1a5b466..582fea4 100644
--- a/lib/libc/src/arch/amd64/crt0.S
+++ b/lib/libc/src/arch/amd64/crti.S
@@ -29,8 +29,8 @@
#include <sys/syscall.h>
-.text
-.globl _start
+ .section .init
+ .globl _start
_start:
// Mark end of callstack
xor %rbp, %rbp
diff --git a/lib/libc/src/crypto/sha256.c b/lib/libc/src/crypto/sha256.c
new file mode 100644
index 0000000..c722026
--- /dev/null
+++ b/lib/libc/src/crypto/sha256.c
@@ -0,0 +1,242 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <crypto/sha256.h>
+#include <stdint.h>
+#include <stddef.h>
+
+static inline uint32_t
+rotr(uint32_t x, int n)
+{
+ return (x >> n) | (x << (32 - n));
+}
+
+static inline uint32_t
+step1(uint32_t e, uint32_t f, uint32_t g)
+{
+ return (rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25)) + ((e & f) ^ ((~ e) & g));
+}
+
+static inline uint32_t
+step2(uint32_t a, uint32_t b, uint32_t c)
+{
+ return (rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22)) + ((a & b) ^ (a & c) ^ (b & c));
+}
+
+static inline void
+update_w(uint32_t *w, int i, const uint8_t *buffer)
+{
+ int j;
+ for (j = 0; j < 16; j++) {
+ if (i < 16){
+ w[j] =
+ ((uint32_t)buffer[0] << 24) |
+ ((uint32_t)buffer[1] << 16) |
+ ((uint32_t)buffer[2] << 8) |
+ ((uint32_t)buffer[3]);
+ buffer += 4;
+ }else {
+ uint32_t a = w[(j + 1) & 15];
+ uint32_t b = w[(j + 14) & 15];
+ uint32_t s0 = (rotr(a, 7) ^ rotr(a, 18) ^ (a >> 3));
+ uint32_t s1 = (rotr(b, 17) ^ rotr(b, 19) ^ (b >> 10));
+ w[j] += w[(j + 9) & 15] + s0 + s1;
+ }
+ }
+}
+
+static void
+sha256_block(struct sha256 *sha)
+{
+ uint32_t *state = sha->state;
+
+ static const uint32_t k[8 * 8] = {
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
+ 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
+ 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
+ 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
+ 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
+ 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
+ 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
+ 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
+ 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+ 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
+ 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
+ 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
+ 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
+ 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
+ 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
+ };
+
+ uint32_t a = state[0];
+ uint32_t b = state[1];
+ uint32_t c = state[2];
+ uint32_t d = state[3];
+ uint32_t e = state[4];
+ uint32_t f = state[5];
+ uint32_t g = state[6];
+ uint32_t h = state[7];
+
+ uint32_t w[16];
+
+ int i, j;
+ for (i = 0; i < 64; i += 16) {
+ update_w(w, i, sha->buffer);
+
+ for (j = 0; j < 16; j += 4) {
+ uint32_t temp;
+ temp = h + step1(e, f, g) + k[i + j + 0] + w[j + 0];
+ h = temp + d;
+ d = temp + step2(a, b, c);
+ temp = g + step1(h, e, f) + k[i + j + 1] + w[j + 1];
+ g = temp + c;
+ c = temp + step2(d, a, b);
+ temp = f + step1(g, h, e) + k[i + j + 2] + w[j + 2];
+ f = temp + b;
+ b = temp + step2(c, d, a);
+ temp = e + step1(f, g, h) + k[i + j + 3] + w[j + 3];
+ e = temp + a;
+ a = temp + step2(b, c, d);
+ }
+ }
+
+ state[0] += a;
+ state[1] += b;
+ state[2] += c;
+ state[3] += d;
+ state[4] += e;
+ state[5] += f;
+ state[6] += g;
+ state[7] += h;
+}
+
+void
+sha256_init(struct sha256 *sha)
+{
+ sha->state[0] = 0x6a09e667;
+ sha->state[1] = 0xbb67ae85;
+ sha->state[2] = 0x3c6ef372;
+ sha->state[3] = 0xa54ff53a;
+ sha->state[4] = 0x510e527f;
+ sha->state[5] = 0x9b05688c;
+ sha->state[6] = 0x1f83d9ab;
+ sha->state[7] = 0x5be0cd19;
+ sha->n_bits = 0;
+ sha->buffer_counter = 0;
+}
+
+void
+sha256_append_byte(struct sha256 *sha, uint8_t byte)
+{
+ sha->buffer[sha->buffer_counter++] = byte;
+ sha->n_bits += 8;
+
+ if (sha->buffer_counter == 64) {
+ sha->buffer_counter = 0;
+ sha256_block(sha);
+ }
+}
+
+void
+sha256_append(struct sha256 *sha, const void *src, size_t n_bytes)
+{
+ const uint8_t *bytes = (const uint8_t*)src;
+ size_t i;
+
+ for (i = 0; i < n_bytes; i++) {
+ sha256_append_byte(sha, bytes[i]);
+ }
+}
+
+void
+sha256_finalize(struct sha256 *sha)
+{
+ int i;
+ uint64_t n_bits = sha->n_bits;
+
+ sha256_append_byte(sha, 0x80);
+
+ while (sha->buffer_counter != 56) {
+ sha256_append_byte(sha, 0);
+ }
+
+ for (i = 7; i >= 0; i--) {
+ uint8_t byte = (n_bits >> 8 * i) & 0xff;
+ sha256_append_byte(sha, byte);
+ }
+}
+
+void
+sha256_finalize_hex(struct sha256 *sha, char *dst_hex65)
+{
+ int i, j;
+ sha256_finalize(sha);
+
+ for (i = 0; i < 8; i++) {
+ for (j = 7; j >= 0; j--) {
+ uint8_t nibble = (sha->state[i] >> j * 4) & 0xf;
+ *dst_hex65++ = "0123456789abcdef"[nibble];
+ }
+ }
+
+ *dst_hex65 = '\0';
+}
+
+void
+sha256_finalize_bytes(struct sha256 *sha, void *dst_bytes32)
+{
+ uint8_t *ptr = (uint8_t*)dst_bytes32;
+ int i, j;
+ sha256_finalize(sha);
+
+ for (i = 0; i < 8; i++) {
+ for (j = 3; j >= 0; j--) {
+ *ptr++ = (sha->state[i] >> j * 8) & 0xff;
+ }
+ }
+}
+
+void
+sha256_hex(const void *src, size_t n_bytes, char *dst_hex65)
+{
+ struct sha256 sha;
+
+ sha256_init(&sha);
+ sha256_append(&sha, src, n_bytes);
+ sha256_finalize_hex(&sha, dst_hex65);
+}
+
+void sha256_bytes(const void *src, size_t n_bytes, void *dst_bytes32){
+ struct sha256 sha;
+
+ sha256_init(&sha);
+ sha256_append(&sha, src, n_bytes);
+ sha256_finalize_bytes(&sha, dst_bytes32);
+}
diff --git a/lib/libc/src/hyra/disk.c b/lib/libc/src/hyra/disk.c
new file mode 100644
index 0000000..c7c7930
--- /dev/null
+++ b/lib/libc/src/hyra/disk.c
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/errno.h>
+#include <sys/syscall.h>
+#include <sys/disk.h>
+
+/*
+ * Disk I/O multiplexer system call which routes
+ * various disk operations via a single call.
+ *
+ * @id: The ID of the disk to be operated on
+ * @op: Operation code
+ * @param: Operation parameters
+ *
+ * Returns the number of bytes operated on upon success,
+ * otherwise a less than zero value is returned.
+ */
+ssize_t
+__disk_io(diskid_t id, diskop_t op, const struct disk_param *param)
+{
+ if (param == NULL) {
+ return -EINVAL;
+ }
+
+ return syscall(
+ SYS_disk,
+ id,
+ op,
+ (uintptr_t)param
+ );
+}
+
+/*
+ * Performs a write operation on a specific disk
+ *
+ * @id: ID of disk to operate on
+ * @blk: Block offset to operate on
+ * @buf: Data to write
+ * @len: Number of bytes to write
+ *
+ * Returns the number of bytes written upon success, otherwise
+ * a less than zero value is returned on error.
+ */
+ssize_t
+disk_write(diskid_t id, blkoff_t blk, const void *buf, size_t len)
+{
+ struct disk_param param;
+
+ if (buf == NULL || len == 0) {
+ return -EINVAL;
+ }
+
+ disk_param_init((void *)buf, blk, len, &param);
+ return __disk_io(id, DISK_IO_WRITE, &param);
+}
+
+/*
+ * Performs a read operation on a specific disk
+ *
+ * @id: ID of disk to operate on
+ * @blk: Block offset to operate on
+ * @buf: Buffer to read data into
+ * @len: Number of bytes to read
+ *
+ * Returns the number of bytes read upon success, otherwise
+ * a less than zero value is returned on error.
+ */
+ssize_t
+disk_read(diskid_t id, blkoff_t blk, void *buf, size_t len)
+{
+ struct disk_param param;
+
+ if (buf == NULL || len == 0) {
+ return -EINVAL;
+ }
+
+ disk_param_init(buf, blk, len, &param);
+ return __disk_io(id, DISK_IO_READ, &param);
+}
+
+/*
+ * Query information from a specific disk
+ *
+ * @id: ID of disk to query from
+ * @res: Resulting information goes here
+ */
+int
+disk_query(diskid_t id, struct disk_info *res)
+{
+ struct disk_param param;
+
+ if (res == NULL) {
+ return -EINVAL;
+ }
+
+ disk_param_init(res, 0, sizeof(*res), &param);
+ return __disk_io(id, DISK_IO_QUERY, &param);
+}
diff --git a/lib/libc/src/hyra/inject.c b/lib/libc/src/hyra/inject.c
new file mode 100644
index 0000000..b1fd7dc
--- /dev/null
+++ b/lib/libc/src/hyra/inject.c
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/syscall.h>
+#include <sys/errno.h>
+#include <sys/krq.h>
+#include <stdlib.h>
+
+/*
+ * Inject a kernel runtime quantum
+ *
+ * @path: NULL for all builtin but deferrable drivers.
+ * They are not initialized more than once but
+ * can be send up through this routine.
+ */
+int
+inject(const char *path)
+{
+ /* TODO: Support this */
+ if (path != NULL) {
+ return -EINVAL;
+ }
+
+ return syscall(SYS_inject, (uintptr_t)path);
+}
diff --git a/lib/libc/src/hyra/mmap.c b/lib/libc/src/hyra/mmap.c
new file mode 100644
index 0000000..6870c75
--- /dev/null
+++ b/lib/libc/src/hyra/mmap.c
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/syscall.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+
+void *
+mmap(void *addr, size_t len, int prot, int flags,
+ int fildes, off_t off)
+
+{
+ return (void *)syscall(SYS_mmap, (uintptr_t)addr, len,
+ prot, flags, fildes, off);
+}
+
+int
+munmap(void *addr, size_t len)
+{
+ return syscall(SYS_munmap, (uintptr_t)addr, len);
+}
diff --git a/lib/libc/src/hyra/reboot.c b/lib/libc/src/hyra/reboot.c
new file mode 100644
index 0000000..1bb8faa
--- /dev/null
+++ b/lib/libc/src/hyra/reboot.c
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/reboot.h>
+#include <sys/syscall.h>
+
+/*
+ * Reboot or poweroff the machine.
+ */
+void
+cpu_reboot(int method)
+{
+ syscall(SYS_reboot, method);
+}
diff --git a/lib/libc/src/hyra/sleep.c b/lib/libc/src/hyra/sleep.c
new file mode 100644
index 0000000..14830f6
--- /dev/null
+++ b/lib/libc/src/hyra/sleep.c
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/syscall.h>
+#include <time.h>
+
+/*
+ * Sleep using a timespec value
+ *
+ * @tsp: Timespec to sleep with
+ * @remp: Remaining time if interrupted during sleep
+ */
+int
+sleep(struct timespec *__restrict tsp, struct timespec *__restrict remp)
+{
+ return syscall(SYS_sleep, (uintptr_t)tsp, (uintptr_t)remp);
+}
diff --git a/lib/libc/src/hyra/socket.c b/lib/libc/src/hyra/socket.c
new file mode 100644
index 0000000..2a62541
--- /dev/null
+++ b/lib/libc/src/hyra/socket.c
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/socket.h>
+#include <sys/syscall.h>
+
+int
+socket(int domain, int type, int protocol)
+{
+ return syscall(SYS_socket, domain, type, protocol);
+}
+
+int
+bind(int sockfd, const struct sockaddr *addr, socklen_t len)
+{
+ return syscall(SYS_bind, sockfd, (uintptr_t)addr, len);
+}
+
+ssize_t
+send(int sockfd, const void *buf, size_t size, int flags)
+{
+ return syscall(SYS_send, sockfd, (uintptr_t)buf, size, flags);
+}
+
+ssize_t
+recv(int sockfd, void *buf, size_t len, int flags)
+{
+ return syscall(SYS_recv, sockfd, (uintptr_t)buf, len, flags);
+}
+
+ssize_t
+sendmsg(int socket, const struct msghdr *msg, int flags)
+{
+ return syscall(SYS_sendmsg, socket, (uintptr_t)msg, flags);
+}
+
+ssize_t
+recvmsg(int socket, struct msghdr *msg, int flags)
+{
+ return syscall(SYS_recvmsg, socket, (uintptr_t)msg, flags);
+}
+
+int
+connect(int socket, const struct sockaddr *address, socklen_t len)
+{
+ return syscall(SYS_connect, socket, (uintptr_t)address, len);
+}
+
+int
+setsockopt(int sockfd, int level, int name, const void *v, socklen_t len)
+{
+ return syscall(
+ SYS_setsockopt,
+ sockfd,
+ level,
+ name,
+ (uintptr_t)v,
+ len
+ );
+}
diff --git a/lib/libc/src/hyra/spawn.c b/lib/libc/src/hyra/spawn.c
new file mode 100644
index 0000000..b4c92ef
--- /dev/null
+++ b/lib/libc/src/hyra/spawn.c
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/spawn.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+
+/*
+ * Spawn a process
+ *
+ * @pathname: Path to executable.
+ * @argv: Argument vector
+ * @envp: Environment vector
+ * @flags: Spawn flags.
+ */
+pid_t
+spawn(const char *pathname, char **argv, char **envp, int flags)
+{
+ return syscall(SYS_spawn, (uintptr_t)pathname, (uintptr_t)argv,
+ (uintptr_t)envp, flags);
+}
diff --git a/lib/libc/src/hyra/stat.c b/lib/libc/src/hyra/stat.c
new file mode 100644
index 0000000..42a353b
--- /dev/null
+++ b/lib/libc/src/hyra/stat.c
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/stat.h>
+#include <sys/syscall.h>
+
+int
+stat(const char *path, struct stat *buf)
+{
+ return syscall(SYS_stat, (uintptr_t)path, (uintptr_t)buf);
+}
diff --git a/lib/libc/src/hyra/sysctl.c b/lib/libc/src/hyra/sysctl.c
new file mode 100644
index 0000000..2903e6f
--- /dev/null
+++ b/lib/libc/src/hyra/sysctl.c
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/syscall.h>
+#include <sys/sysctl.h>
+
+int
+sysctl(struct sysctl_args *args)
+{
+ return syscall(SYS_sysctl, (uintptr_t)args);
+}
diff --git a/lib/libc/src/hyra/wait.c b/lib/libc/src/hyra/wait.c
new file mode 100644
index 0000000..99f9228
--- /dev/null
+++ b/lib/libc/src/hyra/wait.c
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/syscall.h>
+#include <sys/wait.h>
+
+pid_t
+waitpid(pid_t pid, int *wstatus, int options)
+{
+ return syscall(SYS_waitpid, pid, (uintptr_t)wstatus, options);
+}
diff --git a/lib/libc/src/main.c b/lib/libc/src/main.c
index 1154b21..68f9bdb 100644
--- a/lib/libc/src/main.c
+++ b/lib/libc/src/main.c
@@ -27,13 +27,57 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
+#include <sys/exec.h>
#include <stdint.h>
#include <stddef.h>
+#include <unistd.h>
+
+extern int __libc_stdio_init(void);
+extern int __malloc_mem_init(void);
+uint64_t __libc_auxv[_AT_MAX];
int main(int argc, char **argv);
+struct auxv_entry {
+ uint64_t tag;
+ uint64_t val;
+};
+
int
__libc_entry(uint64_t *ctx)
{
- return main(0, NULL);
+ const struct auxv_entry *auxvp;
+ int status;
+ uint64_t argc, envc, tag;
+ char **argv;
+ char **envp;
+
+ optind = 1;
+ argc = *ctx;
+ argv = (char **)(ctx + 1);
+ envp = (char **)(argv + argc + 1);
+
+ envc = 0;
+ while (envp[envc] != NULL) {
+ ++envc;
+ }
+
+ auxvp = (void *)(envp + envc + 1);
+ for (int i = 0; i < _AT_MAX; ++i) {
+ if (auxvp->tag == AT_NULL) {
+ break;
+ }
+ if (auxvp->tag < _AT_MAX) {
+ __libc_auxv[auxvp->tag] = auxvp->val;
+ }
+
+ ++auxvp;
+ }
+
+ if ((status = __libc_stdio_init()) != 0) {
+ return status;
+ }
+
+ __malloc_mem_init();
+ return main(argc, argv);
}
diff --git a/lib/libc/src/musl-math/__cos.c b/lib/libc/src/musl-math/__cos.c
new file mode 100644
index 0000000..46cefb3
--- /dev/null
+++ b/lib/libc/src/musl-math/__cos.c
@@ -0,0 +1,71 @@
+/* origin: FreeBSD /usr/src/lib/msun/src/k_cos.c */
+/*
+ * ====================================================
+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ *
+ * Developed at SunSoft, a Sun Microsystems, Inc. business.
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ */
+/*
+ * __cos( x, y )
+ * kernel cos function on [-pi/4, pi/4], pi/4 ~ 0.785398164
+ * Input x is assumed to be bounded by ~pi/4 in magnitude.
+ * Input y is the tail of x.
+ *
+ * Algorithm
+ * 1. Since cos(-x) = cos(x), we need only to consider positive x.
+ * 2. if x < 2^-27 (hx<0x3e400000 0), return 1 with inexact if x!=0.
+ * 3. cos(x) is approximated by a polynomial of degree 14 on
+ * [0,pi/4]
+ * 4 14
+ * cos(x) ~ 1 - x*x/2 + C1*x + ... + C6*x
+ * where the remez error is
+ *
+ * | 2 4 6 8 10 12 14 | -58
+ * |cos(x)-(1-.5*x +C1*x +C2*x +C3*x +C4*x +C5*x +C6*x )| <= 2
+ * | |
+ *
+ * 4 6 8 10 12 14
+ * 4. let r = C1*x +C2*x +C3*x +C4*x +C5*x +C6*x , then
+ * cos(x) ~ 1 - x*x/2 + r
+ * since cos(x+y) ~ cos(x) - sin(x)*y
+ * ~ cos(x) - x*y,
+ * a correction term is necessary in cos(x) and hence
+ * cos(x+y) = 1 - (x*x/2 - (r - x*y))
+ * For better accuracy, rearrange to
+ * cos(x+y) ~ w + (tmp + (r-x*y))
+ * where w = 1 - x*x/2 and tmp is a tiny correction term
+ * (1 - x*x/2 == w + tmp exactly in infinite precision).
+ * The exactness of w + tmp in infinite precision depends on w
+ * and tmp having the same precision as x. If they have extra
+ * precision due to compiler bugs, then the extra precision is
+ * only good provided it is retained in all terms of the final
+ * expression for cos(). Retention happens in all cases tested
+ * under FreeBSD, so don't pessimize things by forcibly clipping
+ * any extra precision in w.
+ */
+
+#include "libm.h"
+
+static const double
+C1 = 4.16666666666666019037e-02, /* 0x3FA55555, 0x5555554C */
+C2 = -1.38888888888741095749e-03, /* 0xBF56C16C, 0x16C15177 */
+C3 = 2.48015872894767294178e-05, /* 0x3EFA01A0, 0x19CB1590 */
+C4 = -2.75573143513906633035e-07, /* 0xBE927E4F, 0x809C52AD */
+C5 = 2.08757232129817482790e-09, /* 0x3E21EE9E, 0xBDB4B1C4 */
+C6 = -1.13596475577881948265e-11; /* 0xBDA8FAE9, 0xBE8838D4 */
+
+double __cos(double x, double y)
+{
+ double_t hz,z,r,w;
+
+ z = x*x;
+ w = z*z;
+ r = z*(C1+z*(C2+z*C3)) + w*w*(C4+z*(C5+z*C6));
+ hz = 0.5*z;
+ w = 1.0-hz;
+ return w + (((1.0-w)-hz) + (z*r-x*y));
+}
diff --git a/lib/libc/src/musl-math/__cosdf.c b/lib/libc/src/musl-math/__cosdf.c
new file mode 100644
index 0000000..2124989
--- /dev/null
+++ b/lib/libc/src/musl-math/__cosdf.c
@@ -0,0 +1,35 @@
+/* origin: FreeBSD /usr/src/lib/msun/src/k_cosf.c */
+/*
+ * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com.
+ * Debugged and optimized by Bruce D. Evans.
+ */
+/*
+ * ====================================================
+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ *
+ * Developed at SunPro, a Sun Microsystems, Inc. business.
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ */
+
+#include "libm.h"
+
+/* |cos(x) - c(x)| < 2**-34.1 (~[-5.37e-11, 5.295e-11]). */
+static const double
+C0 = -0x1ffffffd0c5e81.0p-54, /* -0.499999997251031003120 */
+C1 = 0x155553e1053a42.0p-57, /* 0.0416666233237390631894 */
+C2 = -0x16c087e80f1e27.0p-62, /* -0.00138867637746099294692 */
+C3 = 0x199342e0ee5069.0p-68; /* 0.0000243904487962774090654 */
+
+float __cosdf(double x)
+{
+ double_t r, w, z;
+
+ /* Try to optimize for parallel evaluation as in __tandf.c. */
+ z = x*x;
+ w = z*z;
+ r = C2+z*C3;
+ return ((1.0+z*C0) + w*C1) + (w*z)*r;
+}
diff --git a/lib/libc/src/musl-math/__cosl.c b/lib/libc/src/musl-math/__cosl.c
new file mode 100644
index 0000000..fa522dd
--- /dev/null
+++ b/lib/libc/src/musl-math/__cosl.c
@@ -0,0 +1,96 @@
+/* origin: FreeBSD /usr/src/lib/msun/ld80/k_cosl.c */
+/* origin: FreeBSD /usr/src/lib/msun/ld128/k_cosl.c */
+/*
+ * ====================================================
+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ * Copyright (c) 2008 Steven G. Kargl, David Schultz, Bruce D. Evans.
+ *
+ * Developed at SunSoft, a Sun Microsystems, Inc. business.
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ */
+
+
+#include "libm.h"
+
+#if (LDBL_MANT_DIG == 64 || LDBL_MANT_DIG == 113) && LDBL_MAX_EXP == 16384
+#if LDBL_MANT_DIG == 64
+/*
+ * ld80 version of __cos.c. See __cos.c for most comments.
+ */
+/*
+ * Domain [-0.7854, 0.7854], range ~[-2.43e-23, 2.425e-23]:
+ * |cos(x) - c(x)| < 2**-75.1
+ *
+ * The coefficients of c(x) were generated by a pari-gp script using
+ * a Remez algorithm that searches for the best higher coefficients
+ * after rounding leading coefficients to a specified precision.
+ *
+ * Simpler methods like Chebyshev or basic Remez barely suffice for
+ * cos() in 64-bit precision, because we want the coefficient of x^2
+ * to be precisely -0.5 so that multiplying by it is exact, and plain
+ * rounding of the coefficients of a good polynomial approximation only
+ * gives this up to about 64-bit precision. Plain rounding also gives
+ * a mediocre approximation for the coefficient of x^4, but a rounding
+ * error of 0.5 ulps for this coefficient would only contribute ~0.01
+ * ulps to the final error, so this is unimportant. Rounding errors in
+ * higher coefficients are even less important.
+ *
+ * In fact, coefficients above the x^4 one only need to have 53-bit
+ * precision, and this is more efficient. We get this optimization
+ * almost for free from the complications needed to search for the best
+ * higher coefficients.
+ */
+static const long double
+C1 = 0.0416666666666666666136L; /* 0xaaaaaaaaaaaaaa9b.0p-68 */
+static const double
+C2 = -0.0013888888888888874, /* -0x16c16c16c16c10.0p-62 */
+C3 = 0.000024801587301571716, /* 0x1a01a01a018e22.0p-68 */
+C4 = -0.00000027557319215507120, /* -0x127e4fb7602f22.0p-74 */
+C5 = 0.0000000020876754400407278, /* 0x11eed8caaeccf1.0p-81 */
+C6 = -1.1470297442401303e-11, /* -0x19393412bd1529.0p-89 */
+C7 = 4.7383039476436467e-14; /* 0x1aac9d9af5c43e.0p-97 */
+#define POLY(z) (z*(C1+z*(C2+z*(C3+z*(C4+z*(C5+z*(C6+z*C7)))))))
+#elif LDBL_MANT_DIG == 113
+/*
+ * ld128 version of __cos.c. See __cos.c for most comments.
+ */
+/*
+ * Domain [-0.7854, 0.7854], range ~[-1.80e-37, 1.79e-37]:
+ * |cos(x) - c(x))| < 2**-122.0
+ *
+ * 113-bit precision requires more care than 64-bit precision, since
+ * simple methods give a minimax polynomial with coefficient for x^2
+ * that is 1 ulp below 0.5, but we want it to be precisely 0.5. See
+ * above for more details.
+ */
+static const long double
+C1 = 0.04166666666666666666666666666666658424671L,
+C2 = -0.001388888888888888888888888888863490893732L,
+C3 = 0.00002480158730158730158730158600795304914210L,
+C4 = -0.2755731922398589065255474947078934284324e-6L,
+C5 = 0.2087675698786809897659225313136400793948e-8L,
+C6 = -0.1147074559772972315817149986812031204775e-10L,
+C7 = 0.4779477332386808976875457937252120293400e-13L;
+static const double
+C8 = -0.1561920696721507929516718307820958119868e-15,
+C9 = 0.4110317413744594971475941557607804508039e-18,
+C10 = -0.8896592467191938803288521958313920156409e-21,
+C11 = 0.1601061435794535138244346256065192782581e-23;
+#define POLY(z) (z*(C1+z*(C2+z*(C3+z*(C4+z*(C5+z*(C6+z*(C7+ \
+ z*(C8+z*(C9+z*(C10+z*C11)))))))))))
+#endif
+
+long double __cosl(long double x, long double y)
+{
+ long double hz,z,r,w;
+
+ z = x*x;
+ r = POLY(z);
+ hz = 0.5*z;
+ w = 1.0-hz;
+ return w + (((1.0-w)-hz) + (z*r-x*y));
+}
+#endif
diff --git a/lib/libc/src/musl-math/__expo2.c b/lib/libc/src/musl-math/__expo2.c
new file mode 100644
index 0000000..740ac68
--- /dev/null
+++ b/lib/libc/src/musl-math/__expo2.c
@@ -0,0 +1,16 @@
+#include "libm.h"
+
+/* k is such that k*ln2 has minimal relative error and x - kln2 > log(DBL_MIN) */
+static const int k = 2043;
+static const double kln2 = 0x1.62066151add8bp+10;
+
+/* exp(x)/2 for x >= log(DBL_MAX), slightly better than 0.5*exp(x/2)*exp(x/2) */
+double __expo2(double x)
+{
+ double scale;
+
+ /* note that k is odd and scale*scale overflows */
+ INSERT_WORDS(scale, (uint32_t)(0x3ff + k/2) << 20, 0);
+ /* exp(x - k ln2) * 2**(k-1) */
+ return exp(x - kln2) * scale * scale;
+}
diff --git a/lib/libc/src/musl-math/__expo2f.c b/lib/libc/src/musl-math/__expo2f.c
new file mode 100644
index 0000000..5163e41
--- /dev/null
+++ b/lib/libc/src/musl-math/__expo2f.c
@@ -0,0 +1,16 @@
+#include "libm.h"
+
+/* k is such that k*ln2 has minimal relative error and x - kln2 > log(FLT_MIN) */
+static const int k = 235;
+static const float kln2 = 0x1.45c778p+7f;
+
+/* expf(x)/2 for x >= log(FLT_MAX), slightly better than 0.5f*expf(x/2)*expf(x/2) */
+float __expo2f(float x)
+{
+ float scale;
+
+ /* note that k is odd and scale*scale overflows */
+ SET_FLOAT_WORD(scale, (uint32_t)(0x7f + k/2) << 23);
+ /* exp(x - k ln2) * 2**(k-1) */
+ return expf(x - kln2) * scale * scale;
+}
diff --git a/lib/libc/src/musl-math/__fpclassify.c b/lib/libc/src/musl-math/__fpclassify.c
new file mode 100644
index 0000000..f7c0e2d
--- /dev/null
+++ b/lib/libc/src/musl-math/__fpclassify.c
@@ -0,0 +1,11 @@
+#include <math.h>
+#include <stdint.h>
+
+int __fpclassify(double x)
+{
+ union {double f; uint64_t i;} u = {x};
+ int e = u.i>>52 & 0x7ff;
+ if (!e) return u.i<<1 ? FP_SUBNORMAL : FP_ZERO;
+ if (e==0x7ff) return u.i<<12 ? FP_NAN : FP_INFINITE;
+ return FP_NORMAL;
+}
diff --git a/lib/libc/src/musl-math/__fpclassifyf.c b/lib/libc/src/musl-math/__fpclassifyf.c
new file mode 100644
index 0000000..fd00eb1
--- /dev/null
+++ b/lib/libc/src/musl-math/__fpclassifyf.c
@@ -0,0 +1,11 @@
+#include <math.h>
+#include <stdint.h>
+
+int __fpclassifyf(float x)
+{
+ union {float f; uint32_t i;} u = {x};
+ int e = u.i>>23 & 0xff;
+ if (!e) return u.i<<1 ? FP_SUBNORMAL : FP_ZERO;
+ if (e==0xff) return u.i<<9 ? FP_NAN : FP_INFINITE;
+ return FP_NORMAL;
+}
diff --git a/lib/libc/src/musl-math/__fpclassifyl.c b/lib/libc/src/musl-math/__fpclassifyl.c
new file mode 100644
index 0000000..fb62dd9
--- /dev/null
+++ b/lib/libc/src/musl-math/__fpclassifyl.c
@@ -0,0 +1,42 @@
+#include "libm.h"
+
+#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
+int __fpclassifyl(long double x)
+{
+ return __fpclassify(x);
+}
+#elif LDBL_MANT_DIG == 64 && LDBL_MAX_EXP == 16384
+int __fpclassifyl(long double x)
+{
+ union ldshape u = {x};
+ int e = u.i.se & 0x7fff;
+ int msb = u.i.m>>63;
+ if (!e && !msb)
+ return u.i.m ? FP_SUBNORMAL : FP_ZERO;
+ if (e == 0x7fff) {
+ /* The x86 variant of 80-bit extended precision only admits
+ * one representation of each infinity, with the mantissa msb
+ * necessarily set. The version with it clear is invalid/nan.
+ * The m68k variant, however, allows either, and tooling uses
+ * the version with it clear. */
+ if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ && !msb)
+ return FP_NAN;
+ return u.i.m << 1 ? FP_NAN : FP_INFINITE;
+ }
+ if (!msb)
+ return FP_NAN;
+ return FP_NORMAL;
+}
+#elif LDBL_MANT_DIG == 113 && LDBL_MAX_EXP == 16384
+int __fpclassifyl(long double x)
+{
+ union ldshape u = {x};
+ int e = u.i.se & 0x7fff;
+ u.i.se = 0;
+ if (!e)
+ return u.i2.lo | u.i2.hi ? FP_SUBNORMAL : FP_ZERO;
+ if (e == 0x7fff)
+ return u.i2.lo | u.i2.hi ? FP_NAN : FP_INFINITE;
+ return FP_NORMAL;
+}
+#endif
diff --git a/lib/libc/src/musl-math/__invtrigl.c b/lib/libc/src/musl-math/__invtrigl.c
new file mode 100644
index 0000000..48f83aa
--- /dev/null
+++ b/lib/libc/src/musl-math/__invtrigl.c
@@ -0,0 +1,63 @@
+#include <float.h>
+#include "__invtrigl.h"
+
+#if LDBL_MANT_DIG == 64 && LDBL_MAX_EXP == 16384
+static const long double
+pS0 = 1.66666666666666666631e-01L,
+pS1 = -4.16313987993683104320e-01L,
+pS2 = 3.69068046323246813704e-01L,
+pS3 = -1.36213932016738603108e-01L,
+pS4 = 1.78324189708471965733e-02L,
+pS5 = -2.19216428382605211588e-04L,
+pS6 = -7.10526623669075243183e-06L,
+qS1 = -2.94788392796209867269e+00L,
+qS2 = 3.27309890266528636716e+00L,
+qS3 = -1.68285799854822427013e+00L,
+qS4 = 3.90699412641738801874e-01L,
+qS5 = -3.14365703596053263322e-02L;
+
+const long double pio2_hi = 1.57079632679489661926L;
+const long double pio2_lo = -2.50827880633416601173e-20L;
+
+/* used in asinl() and acosl() */
+/* R(x^2) is a rational approximation of (asin(x)-x)/x^3 with Remez algorithm */
+long double __invtrigl_R(long double z)
+{
+ long double p, q;
+ p = z*(pS0+z*(pS1+z*(pS2+z*(pS3+z*(pS4+z*(pS5+z*pS6))))));
+ q = 1.0+z*(qS1+z*(qS2+z*(qS3+z*(qS4+z*qS5))));
+ return p/q;
+}
+#elif LDBL_MANT_DIG == 113 && LDBL_MAX_EXP == 16384
+static const long double
+pS0 = 1.66666666666666666666666666666700314e-01L,
+pS1 = -7.32816946414566252574527475428622708e-01L,
+pS2 = 1.34215708714992334609030036562143589e+00L,
+pS3 = -1.32483151677116409805070261790752040e+00L,
+pS4 = 7.61206183613632558824485341162121989e-01L,
+pS5 = -2.56165783329023486777386833928147375e-01L,
+pS6 = 4.80718586374448793411019434585413855e-02L,
+pS7 = -4.42523267167024279410230886239774718e-03L,
+pS8 = 1.44551535183911458253205638280410064e-04L,
+pS9 = -2.10558957916600254061591040482706179e-07L,
+qS1 = -4.84690167848739751544716485245697428e+00L,
+qS2 = 9.96619113536172610135016921140206980e+00L,
+qS3 = -1.13177895428973036660836798461641458e+01L,
+qS4 = 7.74004374389488266169304117714658761e+00L,
+qS5 = -3.25871986053534084709023539900339905e+00L,
+qS6 = 8.27830318881232209752469022352928864e-01L,
+qS7 = -1.18768052702942805423330715206348004e-01L,
+qS8 = 8.32600764660522313269101537926539470e-03L,
+qS9 = -1.99407384882605586705979504567947007e-04L;
+
+const long double pio2_hi = 1.57079632679489661923132169163975140L;
+const long double pio2_lo = 4.33590506506189051239852201302167613e-35L;
+
+long double __invtrigl_R(long double z)
+{
+ long double p, q;
+ p = z*(pS0+z*(pS1+z*(pS2+z*(pS3+z*(pS4+z*(pS5+z*(pS6+z*(pS7+z*(pS8+z*pS9)))))))));
+ q = 1.0+z*(qS1+z*(qS2+z*(qS3+z*(qS4+z*(qS5+z*(qS6+z*(qS7+z*(qS8+z*qS9))))))));
+ return p/q;
+}
+#endif
diff --git a/lib/libc/src/musl-math/__invtrigl.h b/lib/libc/src/musl-math/__invtrigl.h
new file mode 100644
index 0000000..6dedac3
--- /dev/null
+++ b/lib/libc/src/musl-math/__invtrigl.h
@@ -0,0 +1,11 @@
+/* shared by acosl, asinl and atan2l */
+#define pio2_hi __pio2_hi
+#define pio2_lo __pio2_lo
+
+#ifndef __MLIBC_ABI_ONLY
+
+extern const long double pio2_hi, pio2_lo;
+
+long double __invtrigl_R(long double z);
+
+#endif /* !__MLIBC_ABI_ONLY */
diff --git a/lib/libc/src/musl-math/__polevll.c b/lib/libc/src/musl-math/__polevll.c
new file mode 100644
index 0000000..ce1a840
--- /dev/null
+++ b/lib/libc/src/musl-math/__polevll.c
@@ -0,0 +1,93 @@
+/* origin: OpenBSD /usr/src/lib/libm/src/polevll.c */
+/*
+ * Copyright (c) 2008 Stephen L. Moshier <steve@moshier.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+/*
+ * Evaluate polynomial
+ *
+ *
+ * SYNOPSIS:
+ *
+ * int N;
+ * long double x, y, coef[N+1], polevl[];
+ *
+ * y = polevll( x, coef, N );
+ *
+ *
+ * DESCRIPTION:
+ *
+ * Evaluates polynomial of degree N:
+ *
+ * 2 N
+ * y = C + C x + C x +...+ C x
+ * 0 1 2 N
+ *
+ * Coefficients are stored in reverse order:
+ *
+ * coef[0] = C , ..., coef[N] = C .
+ * N 0
+ *
+ * The function p1evll() assumes that coef[N] = 1.0 and is
+ * omitted from the array. Its calling arguments are
+ * otherwise the same as polevll().
+ *
+ *
+ * SPEED:
+ *
+ * In the interest of speed, there are no checks for out
+ * of bounds arithmetic. This routine is used by most of
+ * the functions in the library. Depending on available
+ * equipment features, the user may wish to rewrite the
+ * program in microcode or assembly language.
+ *
+ */
+
+#include "libm.h"
+
+#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
+#else
+/*
+ * Polynomial evaluator:
+ * P[0] x^n + P[1] x^(n-1) + ... + P[n]
+ */
+long double __polevll(long double x, const long double *P, int n)
+{
+ long double y;
+
+ y = *P++;
+ do {
+ y = y * x + *P++;
+ } while (--n);
+
+ return y;
+}
+
+/*
+ * Polynomial evaluator:
+ * x^n + P[0] x^(n-1) + P[1] x^(n-2) + ... + P[n]
+ */
+long double __p1evll(long double x, const long double *P, int n)
+{
+ long double y;
+
+ n -= 1;
+ y = x + *P++;
+ do {
+ y = y * x + *P++;
+ } while (--n);
+
+ return y;
+}
+#endif
diff --git a/lib/libc/src/musl-math/__rem_pio2.c b/lib/libc/src/musl-math/__rem_pio2.c
new file mode 100644
index 0000000..d403f81
--- /dev/null
+++ b/lib/libc/src/musl-math/__rem_pio2.c
@@ -0,0 +1,177 @@
+/* origin: FreeBSD /usr/src/lib/msun/src/e_rem_pio2.c */
+/*
+ * ====================================================
+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ *
+ * Developed at SunSoft, a Sun Microsystems, Inc. business.
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ *
+ * Optimized by Bruce D. Evans.
+ */
+/* __rem_pio2(x,y)
+ *
+ * return the remainder of x rem pi/2 in y[0]+y[1]
+ * use __rem_pio2_large() for large x
+ */
+
+#include "libm.h"
+
+#if FLT_EVAL_METHOD==0 || FLT_EVAL_METHOD==1
+#define EPS DBL_EPSILON
+#elif FLT_EVAL_METHOD==2
+#define EPS LDBL_EPSILON
+#endif
+
+/*
+ * invpio2: 53 bits of 2/pi
+ * pio2_1: first 33 bit of pi/2
+ * pio2_1t: pi/2 - pio2_1
+ * pio2_2: second 33 bit of pi/2
+ * pio2_2t: pi/2 - (pio2_1+pio2_2)
+ * pio2_3: third 33 bit of pi/2
+ * pio2_3t: pi/2 - (pio2_1+pio2_2+pio2_3)
+ */
+static const double
+toint = 1.5/EPS,
+invpio2 = 6.36619772367581382433e-01, /* 0x3FE45F30, 0x6DC9C883 */
+pio2_1 = 1.57079632673412561417e+00, /* 0x3FF921FB, 0x54400000 */
+pio2_1t = 6.07710050650619224932e-11, /* 0x3DD0B461, 0x1A626331 */
+pio2_2 = 6.07710050630396597660e-11, /* 0x3DD0B461, 0x1A600000 */
+pio2_2t = 2.02226624879595063154e-21, /* 0x3BA3198A, 0x2E037073 */
+pio2_3 = 2.02226624871116645580e-21, /* 0x3BA3198A, 0x2E000000 */
+pio2_3t = 8.47842766036889956997e-32; /* 0x397B839A, 0x252049C1 */
+
+/* caller must handle the case when reduction is not needed: |x| ~<= pi/4 */
+int __rem_pio2(double x, double *y)
+{
+ union {double f; uint64_t i;} u = {x};
+ double_t z,w,t,r,fn;
+ double tx[3],ty[2];
+ uint32_t ix;
+ int sign, n, ex, ey, i;
+
+ sign = u.i>>63;
+ ix = u.i>>32 & 0x7fffffff;
+ if (ix <= 0x400f6a7a) { /* |x| ~<= 5pi/4 */
+ if ((ix & 0xfffff) == 0x921fb) /* |x| ~= pi/2 or 2pi/2 */
+ goto medium; /* cancellation -- use medium case */
+ if (ix <= 0x4002d97c) { /* |x| ~<= 3pi/4 */
+ if (!sign) {
+ z = x - pio2_1; /* one round good to 85 bits */
+ y[0] = z - pio2_1t;
+ y[1] = (z-y[0]) - pio2_1t;
+ return 1;
+ } else {
+ z = x + pio2_1;
+ y[0] = z + pio2_1t;
+ y[1] = (z-y[0]) + pio2_1t;
+ return -1;
+ }
+ } else {
+ if (!sign) {
+ z = x - 2*pio2_1;
+ y[0] = z - 2*pio2_1t;
+ y[1] = (z-y[0]) - 2*pio2_1t;
+ return 2;
+ } else {
+ z = x + 2*pio2_1;
+ y[0] = z + 2*pio2_1t;
+ y[1] = (z-y[0]) + 2*pio2_1t;
+ return -2;
+ }
+ }
+ }
+ if (ix <= 0x401c463b) { /* |x| ~<= 9pi/4 */
+ if (ix <= 0x4015fdbc) { /* |x| ~<= 7pi/4 */
+ if (ix == 0x4012d97c) /* |x| ~= 3pi/2 */
+ goto medium;
+ if (!sign) {
+ z = x - 3*pio2_1;
+ y[0] = z - 3*pio2_1t;
+ y[1] = (z-y[0]) - 3*pio2_1t;
+ return 3;
+ } else {
+ z = x + 3*pio2_1;
+ y[0] = z + 3*pio2_1t;
+ y[1] = (z-y[0]) + 3*pio2_1t;
+ return -3;
+ }
+ } else {
+ if (ix == 0x401921fb) /* |x| ~= 4pi/2 */
+ goto medium;
+ if (!sign) {
+ z = x - 4*pio2_1;
+ y[0] = z - 4*pio2_1t;
+ y[1] = (z-y[0]) - 4*pio2_1t;
+ return 4;
+ } else {
+ z = x + 4*pio2_1;
+ y[0] = z + 4*pio2_1t;
+ y[1] = (z-y[0]) + 4*pio2_1t;
+ return -4;
+ }
+ }
+ }
+ if (ix < 0x413921fb) { /* |x| ~< 2^20*(pi/2), medium size */
+medium:
+ /* rint(x/(pi/2)), Assume round-to-nearest. */
+ fn = (double_t)x*invpio2 + toint - toint;
+ n = (int32_t)fn;
+ r = x - fn*pio2_1;
+ w = fn*pio2_1t; /* 1st round, good to 85 bits */
+ y[0] = r - w;
+ u.f = y[0];
+ ey = u.i>>52 & 0x7ff;
+ ex = ix>>20;
+ if (ex - ey > 16) { /* 2nd round, good to 118 bits */
+ t = r;
+ w = fn*pio2_2;
+ r = t - w;
+ w = fn*pio2_2t - ((t-r)-w);
+ y[0] = r - w;
+ u.f = y[0];
+ ey = u.i>>52 & 0x7ff;
+ if (ex - ey > 49) { /* 3rd round, good to 151 bits, covers all cases */
+ t = r;
+ w = fn*pio2_3;
+ r = t - w;
+ w = fn*pio2_3t - ((t-r)-w);
+ y[0] = r - w;
+ }
+ }
+ y[1] = (r - y[0]) - w;
+ return n;
+ }
+ /*
+ * all other (large) arguments
+ */
+ if (ix >= 0x7ff00000) { /* x is inf or NaN */
+ y[0] = y[1] = x - x;
+ return 0;
+ }
+ /* set z = scalbn(|x|,-ilogb(x)+23) */
+ u.f = x;
+ u.i &= (uint64_t)-1>>12;
+ u.i |= (uint64_t)(0x3ff + 23)<<52;
+ z = u.f;
+ for (i=0; i < 2; i++) {
+ tx[i] = (double)(int32_t)z;
+ z = (z-tx[i])*0x1p24;
+ }
+ tx[i] = z;
+ /* skip zero terms, first term is non-zero */
+ while (tx[i] == 0.0)
+ i--;
+ n = __rem_pio2_large(tx,ty,(int)(ix>>20)-(0x3ff+23),i+1,1);
+ if (sign) {
+ y[0] = -ty[0];
+ y[1] = -ty[1];
+ return -n;
+ }
+ y[0] = ty[0];
+ y[1] = ty[1];
+ return n;
+}
diff --git a/lib/libc/src/musl-math/__rem_pio2_large.c b/lib/libc/src/musl-math/__rem_pio2_large.c
new file mode 100644
index 0000000..958f28c
--- /dev/null
+++ b/lib/libc/src/musl-math/__rem_pio2_large.c
@@ -0,0 +1,442 @@
+/* origin: FreeBSD /usr/src/lib/msun/src/k_rem_pio2.c */
+/*
+ * ====================================================
+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ *
+ * Developed at SunSoft, a Sun Microsystems, Inc. business.
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ */
+/*
+ * __rem_pio2_large(x,y,e0,nx,prec)
+ * double x[],y[]; int e0,nx,prec;
+ *
+ * __rem_pio2_large return the last three digits of N with
+ * y = x - N*pi/2
+ * so that |y| < pi/2.
+ *
+ * The method is to compute the integer (mod 8) and fraction parts of
+ * (2/pi)*x without doing the full multiplication. In general we
+ * skip the part of the product that are known to be a huge integer (
+ * more accurately, = 0 mod 8 ). Thus the number of operations are
+ * independent of the exponent of the input.
+ *
+ * (2/pi) is represented by an array of 24-bit integers in ipio2[].
+ *
+ * Input parameters:
+ * x[] The input value (must be positive) is broken into nx
+ * pieces of 24-bit integers in double precision format.
+ * x[i] will be the i-th 24 bit of x. The scaled exponent
+ * of x[0] is given in input parameter e0 (i.e., x[0]*2^e0
+ * match x's up to 24 bits.
+ *
+ * Example of breaking a double positive z into x[0]+x[1]+x[2]:
+ * e0 = ilogb(z)-23
+ * z = scalbn(z,-e0)
+ * for i = 0,1,2
+ * x[i] = floor(z)
+ * z = (z-x[i])*2**24
+ *
+ *
+ * y[] ouput result in an array of double precision numbers.
+ * The dimension of y[] is:
+ * 24-bit precision 1
+ * 53-bit precision 2
+ * 64-bit precision 2
+ * 113-bit precision 3
+ * The actual value is the sum of them. Thus for 113-bit
+ * precison, one may have to do something like:
+ *
+ * long double t,w,r_head, r_tail;
+ * t = (long double)y[2] + (long double)y[1];
+ * w = (long double)y[0];
+ * r_head = t+w;
+ * r_tail = w - (r_head - t);
+ *
+ * e0 The exponent of x[0]. Must be <= 16360 or you need to
+ * expand the ipio2 table.
+ *
+ * nx dimension of x[]
+ *
+ * prec an integer indicating the precision:
+ * 0 24 bits (single)
+ * 1 53 bits (double)
+ * 2 64 bits (extended)
+ * 3 113 bits (quad)
+ *
+ * External function:
+ * double scalbn(), floor();
+ *
+ *
+ * Here is the description of some local variables:
+ *
+ * jk jk+1 is the initial number of terms of ipio2[] needed
+ * in the computation. The minimum and recommended value
+ * for jk is 3,4,4,6 for single, double, extended, and quad.
+ * jk+1 must be 2 larger than you might expect so that our
+ * recomputation test works. (Up to 24 bits in the integer
+ * part (the 24 bits of it that we compute) and 23 bits in
+ * the fraction part may be lost to cancelation before we
+ * recompute.)
+ *
+ * jz local integer variable indicating the number of
+ * terms of ipio2[] used.
+ *
+ * jx nx - 1
+ *
+ * jv index for pointing to the suitable ipio2[] for the
+ * computation. In general, we want
+ * ( 2^e0*x[0] * ipio2[jv-1]*2^(-24jv) )/8
+ * is an integer. Thus
+ * e0-3-24*jv >= 0 or (e0-3)/24 >= jv
+ * Hence jv = max(0,(e0-3)/24).
+ *
+ * jp jp+1 is the number of terms in PIo2[] needed, jp = jk.
+ *
+ * q[] double array with integral value, representing the
+ * 24-bits chunk of the product of x and 2/pi.
+ *
+ * q0 the corresponding exponent of q[0]. Note that the
+ * exponent for q[i] would be q0-24*i.
+ *
+ * PIo2[] double precision array, obtained by cutting pi/2
+ * into 24 bits chunks.
+ *
+ * f[] ipio2[] in floating point
+ *
+ * iq[] integer array by breaking up q[] in 24-bits chunk.
+ *
+ * fq[] final product of x*(2/pi) in fq[0],..,fq[jk]
+ *
+ * ih integer. If >0 it indicates q[] is >= 0.5, hence
+ * it also indicates the *sign* of the result.
+ *
+ */
+/*
+ * Constants:
+ * The hexadecimal values are the intended ones for the following
+ * constants. The decimal values may be used, provided that the
+ * compiler will convert from decimal to binary accurately enough
+ * to produce the hexadecimal values shown.
+ */
+
+#include "libm.h"
+
+static const int init_jk[] = {3,4,4,6}; /* initial value for jk */
+
+/*
+ * Table of constants for 2/pi, 396 Hex digits (476 decimal) of 2/pi
+ *
+ * integer array, contains the (24*i)-th to (24*i+23)-th
+ * bit of 2/pi after binary point. The corresponding
+ * floating value is
+ *
+ * ipio2[i] * 2^(-24(i+1)).
+ *
+ * NB: This table must have at least (e0-3)/24 + jk terms.
+ * For quad precision (e0 <= 16360, jk = 6), this is 686.
+ */
+static const int32_t ipio2[] = {
+0xA2F983, 0x6E4E44, 0x1529FC, 0x2757D1, 0xF534DD, 0xC0DB62,
+0x95993C, 0x439041, 0xFE5163, 0xABDEBB, 0xC561B7, 0x246E3A,
+0x424DD2, 0xE00649, 0x2EEA09, 0xD1921C, 0xFE1DEB, 0x1CB129,
+0xA73EE8, 0x8235F5, 0x2EBB44, 0x84E99C, 0x7026B4, 0x5F7E41,
+0x3991D6, 0x398353, 0x39F49C, 0x845F8B, 0xBDF928, 0x3B1FF8,
+0x97FFDE, 0x05980F, 0xEF2F11, 0x8B5A0A, 0x6D1F6D, 0x367ECF,
+0x27CB09, 0xB74F46, 0x3F669E, 0x5FEA2D, 0x7527BA, 0xC7EBE5,
+0xF17B3D, 0x0739F7, 0x8A5292, 0xEA6BFB, 0x5FB11F, 0x8D5D08,
+0x560330, 0x46FC7B, 0x6BABF0, 0xCFBC20, 0x9AF436, 0x1DA9E3,
+0x91615E, 0xE61B08, 0x659985, 0x5F14A0, 0x68408D, 0xFFD880,
+0x4D7327, 0x310606, 0x1556CA, 0x73A8C9, 0x60E27B, 0xC08C6B,
+
+#if LDBL_MAX_EXP > 1024
+0x47C419, 0xC367CD, 0xDCE809, 0x2A8359, 0xC4768B, 0x961CA6,
+0xDDAF44, 0xD15719, 0x053EA5, 0xFF0705, 0x3F7E33, 0xE832C2,
+0xDE4F98, 0x327DBB, 0xC33D26, 0xEF6B1E, 0x5EF89F, 0x3A1F35,
+0xCAF27F, 0x1D87F1, 0x21907C, 0x7C246A, 0xFA6ED5, 0x772D30,
+0x433B15, 0xC614B5, 0x9D19C3, 0xC2C4AD, 0x414D2C, 0x5D000C,
+0x467D86, 0x2D71E3, 0x9AC69B, 0x006233, 0x7CD2B4, 0x97A7B4,
+0xD55537, 0xF63ED7, 0x1810A3, 0xFC764D, 0x2A9D64, 0xABD770,
+0xF87C63, 0x57B07A, 0xE71517, 0x5649C0, 0xD9D63B, 0x3884A7,
+0xCB2324, 0x778AD6, 0x23545A, 0xB91F00, 0x1B0AF1, 0xDFCE19,
+0xFF319F, 0x6A1E66, 0x615799, 0x47FBAC, 0xD87F7E, 0xB76522,
+0x89E832, 0x60BFE6, 0xCDC4EF, 0x09366C, 0xD43F5D, 0xD7DE16,
+0xDE3B58, 0x929BDE, 0x2822D2, 0xE88628, 0x4D58E2, 0x32CAC6,
+0x16E308, 0xCB7DE0, 0x50C017, 0xA71DF3, 0x5BE018, 0x34132E,
+0x621283, 0x014883, 0x5B8EF5, 0x7FB0AD, 0xF2E91E, 0x434A48,
+0xD36710, 0xD8DDAA, 0x425FAE, 0xCE616A, 0xA4280A, 0xB499D3,
+0xF2A606, 0x7F775C, 0x83C2A3, 0x883C61, 0x78738A, 0x5A8CAF,
+0xBDD76F, 0x63A62D, 0xCBBFF4, 0xEF818D, 0x67C126, 0x45CA55,
+0x36D9CA, 0xD2A828, 0x8D61C2, 0x77C912, 0x142604, 0x9B4612,
+0xC459C4, 0x44C5C8, 0x91B24D, 0xF31700, 0xAD43D4, 0xE54929,
+0x10D5FD, 0xFCBE00, 0xCC941E, 0xEECE70, 0xF53E13, 0x80F1EC,
+0xC3E7B3, 0x28F8C7, 0x940593, 0x3E71C1, 0xB3092E, 0xF3450B,
+0x9C1288, 0x7B20AB, 0x9FB52E, 0xC29247, 0x2F327B, 0x6D550C,
+0x90A772, 0x1FE76B, 0x96CB31, 0x4A1679, 0xE27941, 0x89DFF4,
+0x9794E8, 0x84E6E2, 0x973199, 0x6BED88, 0x365F5F, 0x0EFDBB,
+0xB49A48, 0x6CA467, 0x427271, 0x325D8D, 0xB8159F, 0x09E5BC,
+0x25318D, 0x3974F7, 0x1C0530, 0x010C0D, 0x68084B, 0x58EE2C,
+0x90AA47, 0x02E774, 0x24D6BD, 0xA67DF7, 0x72486E, 0xEF169F,
+0xA6948E, 0xF691B4, 0x5153D1, 0xF20ACF, 0x339820, 0x7E4BF5,
+0x6863B2, 0x5F3EDD, 0x035D40, 0x7F8985, 0x295255, 0xC06437,
+0x10D86D, 0x324832, 0x754C5B, 0xD4714E, 0x6E5445, 0xC1090B,
+0x69F52A, 0xD56614, 0x9D0727, 0x50045D, 0xDB3BB4, 0xC576EA,
+0x17F987, 0x7D6B49, 0xBA271D, 0x296996, 0xACCCC6, 0x5414AD,
+0x6AE290, 0x89D988, 0x50722C, 0xBEA404, 0x940777, 0x7030F3,
+0x27FC00, 0xA871EA, 0x49C266, 0x3DE064, 0x83DD97, 0x973FA3,
+0xFD9443, 0x8C860D, 0xDE4131, 0x9D3992, 0x8C70DD, 0xE7B717,
+0x3BDF08, 0x2B3715, 0xA0805C, 0x93805A, 0x921110, 0xD8E80F,
+0xAF806C, 0x4BFFDB, 0x0F9038, 0x761859, 0x15A562, 0xBBCB61,
+0xB989C7, 0xBD4010, 0x04F2D2, 0x277549, 0xF6B6EB, 0xBB22DB,
+0xAA140A, 0x2F2689, 0x768364, 0x333B09, 0x1A940E, 0xAA3A51,
+0xC2A31D, 0xAEEDAF, 0x12265C, 0x4DC26D, 0x9C7A2D, 0x9756C0,
+0x833F03, 0xF6F009, 0x8C402B, 0x99316D, 0x07B439, 0x15200C,
+0x5BC3D8, 0xC492F5, 0x4BADC6, 0xA5CA4E, 0xCD37A7, 0x36A9E6,
+0x9492AB, 0x6842DD, 0xDE6319, 0xEF8C76, 0x528B68, 0x37DBFC,
+0xABA1AE, 0x3115DF, 0xA1AE00, 0xDAFB0C, 0x664D64, 0xB705ED,
+0x306529, 0xBF5657, 0x3AFF47, 0xB9F96A, 0xF3BE75, 0xDF9328,
+0x3080AB, 0xF68C66, 0x15CB04, 0x0622FA, 0x1DE4D9, 0xA4B33D,
+0x8F1B57, 0x09CD36, 0xE9424E, 0xA4BE13, 0xB52333, 0x1AAAF0,
+0xA8654F, 0xA5C1D2, 0x0F3F0B, 0xCD785B, 0x76F923, 0x048B7B,
+0x721789, 0x53A6C6, 0xE26E6F, 0x00EBEF, 0x584A9B, 0xB7DAC4,
+0xBA66AA, 0xCFCF76, 0x1D02D1, 0x2DF1B1, 0xC1998C, 0x77ADC3,
+0xDA4886, 0xA05DF7, 0xF480C6, 0x2FF0AC, 0x9AECDD, 0xBC5C3F,
+0x6DDED0, 0x1FC790, 0xB6DB2A, 0x3A25A3, 0x9AAF00, 0x9353AD,
+0x0457B6, 0xB42D29, 0x7E804B, 0xA707DA, 0x0EAA76, 0xA1597B,
+0x2A1216, 0x2DB7DC, 0xFDE5FA, 0xFEDB89, 0xFDBE89, 0x6C76E4,
+0xFCA906, 0x70803E, 0x156E85, 0xFF87FD, 0x073E28, 0x336761,
+0x86182A, 0xEABD4D, 0xAFE7B3, 0x6E6D8F, 0x396795, 0x5BBF31,
+0x48D784, 0x16DF30, 0x432DC7, 0x356125, 0xCE70C9, 0xB8CB30,
+0xFD6CBF, 0xA200A4, 0xE46C05, 0xA0DD5A, 0x476F21, 0xD21262,
+0x845CB9, 0x496170, 0xE0566B, 0x015299, 0x375550, 0xB7D51E,
+0xC4F133, 0x5F6E13, 0xE4305D, 0xA92E85, 0xC3B21D, 0x3632A1,
+0xA4B708, 0xD4B1EA, 0x21F716, 0xE4698F, 0x77FF27, 0x80030C,
+0x2D408D, 0xA0CD4F, 0x99A520, 0xD3A2B3, 0x0A5D2F, 0x42F9B4,
+0xCBDA11, 0xD0BE7D, 0xC1DB9B, 0xBD17AB, 0x81A2CA, 0x5C6A08,
+0x17552E, 0x550027, 0xF0147F, 0x8607E1, 0x640B14, 0x8D4196,
+0xDEBE87, 0x2AFDDA, 0xB6256B, 0x34897B, 0xFEF305, 0x9EBFB9,
+0x4F6A68, 0xA82A4A, 0x5AC44F, 0xBCF82D, 0x985AD7, 0x95C7F4,
+0x8D4D0D, 0xA63A20, 0x5F57A4, 0xB13F14, 0x953880, 0x0120CC,
+0x86DD71, 0xB6DEC9, 0xF560BF, 0x11654D, 0x6B0701, 0xACB08C,
+0xD0C0B2, 0x485551, 0x0EFB1E, 0xC37295, 0x3B06A3, 0x3540C0,
+0x7BDC06, 0xCC45E0, 0xFA294E, 0xC8CAD6, 0x41F3E8, 0xDE647C,
+0xD8649B, 0x31BED9, 0xC397A4, 0xD45877, 0xC5E369, 0x13DAF0,
+0x3C3ABA, 0x461846, 0x5F7555, 0xF5BDD2, 0xC6926E, 0x5D2EAC,
+0xED440E, 0x423E1C, 0x87C461, 0xE9FD29, 0xF3D6E7, 0xCA7C22,
+0x35916F, 0xC5E008, 0x8DD7FF, 0xE26A6E, 0xC6FDB0, 0xC10893,
+0x745D7C, 0xB2AD6B, 0x9D6ECD, 0x7B723E, 0x6A11C6, 0xA9CFF7,
+0xDF7329, 0xBAC9B5, 0x5100B7, 0x0DB2E2, 0x24BA74, 0x607DE5,
+0x8AD874, 0x2C150D, 0x0C1881, 0x94667E, 0x162901, 0x767A9F,
+0xBEFDFD, 0xEF4556, 0x367ED9, 0x13D9EC, 0xB9BA8B, 0xFC97C4,
+0x27A831, 0xC36EF1, 0x36C594, 0x56A8D8, 0xB5A8B4, 0x0ECCCF,
+0x2D8912, 0x34576F, 0x89562C, 0xE3CE99, 0xB920D6, 0xAA5E6B,
+0x9C2A3E, 0xCC5F11, 0x4A0BFD, 0xFBF4E1, 0x6D3B8E, 0x2C86E2,
+0x84D4E9, 0xA9B4FC, 0xD1EEEF, 0xC9352E, 0x61392F, 0x442138,
+0xC8D91B, 0x0AFC81, 0x6A4AFB, 0xD81C2F, 0x84B453, 0x8C994E,
+0xCC2254, 0xDC552A, 0xD6C6C0, 0x96190B, 0xB8701A, 0x649569,
+0x605A26, 0xEE523F, 0x0F117F, 0x11B5F4, 0xF5CBFC, 0x2DBC34,
+0xEEBC34, 0xCC5DE8, 0x605EDD, 0x9B8E67, 0xEF3392, 0xB817C9,
+0x9B5861, 0xBC57E1, 0xC68351, 0x103ED8, 0x4871DD, 0xDD1C2D,
+0xA118AF, 0x462C21, 0xD7F359, 0x987AD9, 0xC0549E, 0xFA864F,
+0xFC0656, 0xAE79E5, 0x362289, 0x22AD38, 0xDC9367, 0xAAE855,
+0x382682, 0x9BE7CA, 0xA40D51, 0xB13399, 0x0ED7A9, 0x480569,
+0xF0B265, 0xA7887F, 0x974C88, 0x36D1F9, 0xB39221, 0x4A827B,
+0x21CF98, 0xDC9F40, 0x5547DC, 0x3A74E1, 0x42EB67, 0xDF9DFE,
+0x5FD45E, 0xA4677B, 0x7AACBA, 0xA2F655, 0x23882B, 0x55BA41,
+0x086E59, 0x862A21, 0x834739, 0xE6E389, 0xD49EE5, 0x40FB49,
+0xE956FF, 0xCA0F1C, 0x8A59C5, 0x2BFA94, 0xC5C1D3, 0xCFC50F,
+0xAE5ADB, 0x86C547, 0x624385, 0x3B8621, 0x94792C, 0x876110,
+0x7B4C2A, 0x1A2C80, 0x12BF43, 0x902688, 0x893C78, 0xE4C4A8,
+0x7BDBE5, 0xC23AC4, 0xEAF426, 0x8A67F7, 0xBF920D, 0x2BA365,
+0xB1933D, 0x0B7CBD, 0xDC51A4, 0x63DD27, 0xDDE169, 0x19949A,
+0x9529A8, 0x28CE68, 0xB4ED09, 0x209F44, 0xCA984E, 0x638270,
+0x237C7E, 0x32B90F, 0x8EF5A7, 0xE75614, 0x08F121, 0x2A9DB5,
+0x4D7E6F, 0x5119A5, 0xABF9B5, 0xD6DF82, 0x61DD96, 0x023616,
+0x9F3AC4, 0xA1A283, 0x6DED72, 0x7A8D39, 0xA9B882, 0x5C326B,
+0x5B2746, 0xED3400, 0x7700D2, 0x55F4FC, 0x4D5901, 0x8071E0,
+#endif
+};
+
+static const double PIo2[] = {
+ 1.57079625129699707031e+00, /* 0x3FF921FB, 0x40000000 */
+ 7.54978941586159635335e-08, /* 0x3E74442D, 0x00000000 */
+ 5.39030252995776476554e-15, /* 0x3CF84698, 0x80000000 */
+ 3.28200341580791294123e-22, /* 0x3B78CC51, 0x60000000 */
+ 1.27065575308067607349e-29, /* 0x39F01B83, 0x80000000 */
+ 1.22933308981111328932e-36, /* 0x387A2520, 0x40000000 */
+ 2.73370053816464559624e-44, /* 0x36E38222, 0x80000000 */
+ 2.16741683877804819444e-51, /* 0x3569F31D, 0x00000000 */
+};
+
+int __rem_pio2_large(double *x, double *y, int e0, int nx, int prec)
+{
+ int32_t jz,jx,jv,jp,jk,carry,n,iq[20],i,j,k,m,q0,ih;
+ double z,fw,f[20],fq[20],q[20];
+
+ /* initialize jk*/
+ jk = init_jk[prec];
+ jp = jk;
+
+ /* determine jx,jv,q0, note that 3>q0 */
+ jx = nx-1;
+ jv = (e0-3)/24; if(jv<0) jv=0;
+ q0 = e0-24*(jv+1);
+
+ /* set up f[0] to f[jx+jk] where f[jx+jk] = ipio2[jv+jk] */
+ j = jv-jx; m = jx+jk;
+ for (i=0; i<=m; i++,j++)
+ f[i] = j<0 ? 0.0 : (double)ipio2[j];
+
+ /* compute q[0],q[1],...q[jk] */
+ for (i=0; i<=jk; i++) {
+ for (j=0,fw=0.0; j<=jx; j++)
+ fw += x[j]*f[jx+i-j];
+ q[i] = fw;
+ }
+
+ jz = jk;
+recompute:
+ /* distill q[] into iq[] reversingly */
+ for (i=0,j=jz,z=q[jz]; j>0; i++,j--) {
+ fw = (double)(int32_t)(0x1p-24*z);
+ iq[i] = (int32_t)(z - 0x1p24*fw);
+ z = q[j-1]+fw;
+ }
+
+ /* compute n */
+ z = scalbn(z,q0); /* actual value of z */
+ z -= 8.0*floor(z*0.125); /* trim off integer >= 8 */
+ n = (int32_t)z;
+ z -= (double)n;
+ ih = 0;
+ if (q0 > 0) { /* need iq[jz-1] to determine n */
+ i = iq[jz-1]>>(24-q0); n += i;
+ iq[jz-1] -= i<<(24-q0);
+ ih = iq[jz-1]>>(23-q0);
+ }
+ else if (q0 == 0) ih = iq[jz-1]>>23;
+ else if (z >= 0.5) ih = 2;
+
+ if (ih > 0) { /* q > 0.5 */
+ n += 1; carry = 0;
+ for (i=0; i<jz; i++) { /* compute 1-q */
+ j = iq[i];
+ if (carry == 0) {
+ if (j != 0) {
+ carry = 1;
+ iq[i] = 0x1000000 - j;
+ }
+ } else
+ iq[i] = 0xffffff - j;
+ }
+ if (q0 > 0) { /* rare case: chance is 1 in 12 */
+ switch(q0) {
+ case 1:
+ iq[jz-1] &= 0x7fffff; break;
+ case 2:
+ iq[jz-1] &= 0x3fffff; break;
+ }
+ }
+ if (ih == 2) {
+ z = 1.0 - z;
+ if (carry != 0)
+ z -= scalbn(1.0,q0);
+ }
+ }
+
+ /* check if recomputation is needed */
+ if (z == 0.0) {
+ j = 0;
+ for (i=jz-1; i>=jk; i--) j |= iq[i];
+ if (j == 0) { /* need recomputation */
+ for (k=1; iq[jk-k]==0; k++); /* k = no. of terms needed */
+
+ for (i=jz+1; i<=jz+k; i++) { /* add q[jz+1] to q[jz+k] */
+ f[jx+i] = (double)ipio2[jv+i];
+ for (j=0,fw=0.0; j<=jx; j++)
+ fw += x[j]*f[jx+i-j];
+ q[i] = fw;
+ }
+ jz += k;
+ goto recompute;
+ }
+ }
+
+ /* chop off zero terms */
+ if (z == 0.0) {
+ jz -= 1;
+ q0 -= 24;
+ while (iq[jz] == 0) {
+ jz--;
+ q0 -= 24;
+ }
+ } else { /* break z into 24-bit if necessary */
+ z = scalbn(z,-q0);
+ if (z >= 0x1p24) {
+ fw = (double)(int32_t)(0x1p-24*z);
+ iq[jz] = (int32_t)(z - 0x1p24*fw);
+ jz += 1;
+ q0 += 24;
+ iq[jz] = (int32_t)fw;
+ } else
+ iq[jz] = (int32_t)z;
+ }
+
+ /* convert integer "bit" chunk to floating-point value */
+ fw = scalbn(1.0,q0);
+ for (i=jz; i>=0; i--) {
+ q[i] = fw*(double)iq[i];
+ fw *= 0x1p-24;
+ }
+
+ /* compute PIo2[0,...,jp]*q[jz,...,0] */
+ for(i=jz; i>=0; i--) {
+ for (fw=0.0,k=0; k<=jp && k<=jz-i; k++)
+ fw += PIo2[k]*q[i+k];
+ fq[jz-i] = fw;
+ }
+
+ /* compress fq[] into y[] */
+ switch(prec) {
+ case 0:
+ fw = 0.0;
+ for (i=jz; i>=0; i--)
+ fw += fq[i];
+ y[0] = ih==0 ? fw : -fw;
+ break;
+ case 1:
+ case 2:
+ fw = 0.0;
+ for (i=jz; i>=0; i--)
+ fw += fq[i];
+ // TODO: drop excess precision here once double_t is used
+ fw = (double)fw;
+ y[0] = ih==0 ? fw : -fw;
+ fw = fq[0]-fw;
+ for (i=1; i<=jz; i++)
+ fw += fq[i];
+ y[1] = ih==0 ? fw : -fw;
+ break;
+ case 3: /* painful */
+ for (i=jz; i>0; i--) {
+ fw = fq[i-1]+fq[i];
+ fq[i] += fq[i-1]-fw;
+ fq[i-1] = fw;
+ }
+ for (i=jz; i>1; i--) {
+ fw = fq[i-1]+fq[i];
+ fq[i] += fq[i-1]-fw;
+ fq[i-1] = fw;
+ }
+ for (fw=0.0,i=jz; i>=2; i--)
+ fw += fq[i];
+ if (ih==0) {
+ y[0] = fq[0]; y[1] = fq[1]; y[2] = fw;
+ } else {
+ y[0] = -fq[0]; y[1] = -fq[1]; y[2] = -fw;
+ }
+ }
+ return n&7;
+}
diff --git a/lib/libc/src/musl-math/__rem_pio2f.c b/lib/libc/src/musl-math/__rem_pio2f.c
new file mode 100644
index 0000000..4473c1c
--- /dev/null
+++ b/lib/libc/src/musl-math/__rem_pio2f.c
@@ -0,0 +1,75 @@
+/* origin: FreeBSD /usr/src/lib/msun/src/e_rem_pio2f.c */
+/*
+ * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com.
+ * Debugged and optimized by Bruce D. Evans.
+ */
+/*
+ * ====================================================
+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ *
+ * Developed at SunPro, a Sun Microsystems, Inc. business.
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ */
+/* __rem_pio2f(x,y)
+ *
+ * return the remainder of x rem pi/2 in *y
+ * use double precision for everything except passing x
+ * use __rem_pio2_large() for large x
+ */
+
+#include "libm.h"
+
+#if FLT_EVAL_METHOD==0 || FLT_EVAL_METHOD==1
+#define EPS DBL_EPSILON
+#elif FLT_EVAL_METHOD==2
+#define EPS LDBL_EPSILON
+#endif
+
+/*
+ * invpio2: 53 bits of 2/pi
+ * pio2_1: first 25 bits of pi/2
+ * pio2_1t: pi/2 - pio2_1
+ */
+static const double
+toint = 1.5/EPS,
+invpio2 = 6.36619772367581382433e-01, /* 0x3FE45F30, 0x6DC9C883 */
+pio2_1 = 1.57079631090164184570e+00, /* 0x3FF921FB, 0x50000000 */
+pio2_1t = 1.58932547735281966916e-08; /* 0x3E5110b4, 0x611A6263 */
+
+int __rem_pio2f(float x, double *y)
+{
+ union {float f; uint32_t i;} u = {x};
+ double tx[1],ty[1];
+ double_t fn;
+ uint32_t ix;
+ int n, sign, e0;
+
+ ix = u.i & 0x7fffffff;
+ /* 25+53 bit pi is good enough for medium size */
+ if (ix < 0x4dc90fdb) { /* |x| ~< 2^28*(pi/2), medium size */
+ /* Use a specialized rint() to get fn. Assume round-to-nearest. */
+ fn = (double_t)x*invpio2 + toint - toint;
+ n = (int32_t)fn;
+ *y = x - fn*pio2_1 - fn*pio2_1t;
+ return n;
+ }
+ if(ix>=0x7f800000) { /* x is inf or NaN */
+ *y = x-x;
+ return 0;
+ }
+ /* scale x into [2^23, 2^24-1] */
+ sign = u.i>>31;
+ e0 = (ix>>23) - (0x7f+23); /* e0 = ilogb(|x|)-23, positive */
+ u.i = ix - (e0<<23);
+ tx[0] = u.f;
+ n = __rem_pio2_large(tx,ty,e0,1,0);
+ if (sign) {
+ *y = -ty[0];
+ return -n;
+ }
+ *y = ty[0];
+ return n;
+}
diff --git a/lib/libc/src/musl-math/__rem_pio2l.c b/lib/libc/src/musl-math/__rem_pio2l.c
new file mode 100644
index 0000000..77255bd
--- /dev/null
+++ b/lib/libc/src/musl-math/__rem_pio2l.c
@@ -0,0 +1,141 @@
+/* origin: FreeBSD /usr/src/lib/msun/ld80/e_rem_pio2.c */
+/*
+ * ====================================================
+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ * Copyright (c) 2008 Steven G. Kargl, David Schultz, Bruce D. Evans.
+ *
+ * Developed at SunSoft, a Sun Microsystems, Inc. business.
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ *
+ * Optimized by Bruce D. Evans.
+ */
+#include "libm.h"
+#if (LDBL_MANT_DIG == 64 || LDBL_MANT_DIG == 113) && LDBL_MAX_EXP == 16384
+/* ld80 and ld128 version of __rem_pio2(x,y)
+ *
+ * return the remainder of x rem pi/2 in y[0]+y[1]
+ * use __rem_pio2_large() for large x
+ */
+
+static const long double toint = 1.5/LDBL_EPSILON;
+
+#if LDBL_MANT_DIG == 64
+/* u ~< 0x1p25*pi/2 */
+#define SMALL(u) (((u.i.se & 0x7fffU)<<16 | u.i.m>>48) < ((0x3fff + 25)<<16 | 0x921f>>1 | 0x8000))
+#define QUOBITS(x) ((uint32_t)(int32_t)x & 0x7fffffff)
+#define ROUND1 22
+#define ROUND2 61
+#define NX 3
+#define NY 2
+/*
+ * invpio2: 64 bits of 2/pi
+ * pio2_1: first 39 bits of pi/2
+ * pio2_1t: pi/2 - pio2_1
+ * pio2_2: second 39 bits of pi/2
+ * pio2_2t: pi/2 - (pio2_1+pio2_2)
+ * pio2_3: third 39 bits of pi/2
+ * pio2_3t: pi/2 - (pio2_1+pio2_2+pio2_3)
+ */
+static const double
+pio2_1 = 1.57079632679597125389e+00, /* 0x3FF921FB, 0x54444000 */
+pio2_2 = -1.07463465549783099519e-12, /* -0x12e7b967674000.0p-92 */
+pio2_3 = 6.36831716351370313614e-25; /* 0x18a2e037074000.0p-133 */
+static const long double
+invpio2 = 6.36619772367581343076e-01L, /* 0xa2f9836e4e44152a.0p-64 */
+pio2_1t = -1.07463465549719416346e-12L, /* -0x973dcb3b399d747f.0p-103 */
+pio2_2t = 6.36831716351095013979e-25L, /* 0xc51701b839a25205.0p-144 */
+pio2_3t = -2.75299651904407171810e-37L; /* -0xbb5bf6c7ddd660ce.0p-185 */
+#elif LDBL_MANT_DIG == 113
+/* u ~< 0x1p45*pi/2 */
+#define SMALL(u) (((u.i.se & 0x7fffU)<<16 | u.i.top) < ((0x3fff + 45)<<16 | 0x921f))
+#define QUOBITS(x) ((uint32_t)(int64_t)x & 0x7fffffff)
+#define ROUND1 51
+#define ROUND2 119
+#define NX 5
+#define NY 3
+static const long double
+invpio2 = 6.3661977236758134307553505349005747e-01L, /* 0x145f306dc9c882a53f84eafa3ea6a.0p-113 */
+pio2_1 = 1.5707963267948966192292994253909555e+00L, /* 0x1921fb54442d18469800000000000.0p-112 */
+pio2_1t = 2.0222662487959507323996846200947577e-21L, /* 0x13198a2e03707344a4093822299f3.0p-181 */
+pio2_2 = 2.0222662487959507323994779168837751e-21L, /* 0x13198a2e03707344a400000000000.0p-181 */
+pio2_2t = 2.0670321098263988236496903051604844e-43L, /* 0x127044533e63a0105df531d89cd91.0p-254 */
+pio2_3 = 2.0670321098263988236499468110329591e-43L, /* 0x127044533e63a0105e00000000000.0p-254 */
+pio2_3t = -2.5650587247459238361625433492959285e-65L; /* -0x159c4ec64ddaeb5f78671cbfb2210.0p-327 */
+#endif
+
+int __rem_pio2l(long double x, long double *y)
+{
+ union ldshape u,uz;
+ long double z,w,t,r,fn;
+ double tx[NX],ty[NY];
+ int ex,ey,n,i;
+
+ u.f = x;
+ ex = u.i.se & 0x7fff;
+ if (SMALL(u)) {
+ /* rint(x/(pi/2)), Assume round-to-nearest. */
+ fn = x*invpio2 + toint - toint;
+ n = QUOBITS(fn);
+ r = x-fn*pio2_1;
+ w = fn*pio2_1t; /* 1st round good to 102/180 bits (ld80/ld128) */
+ y[0] = r-w;
+ u.f = y[0];
+ ey = u.i.se & 0x7fff;
+ if (ex - ey > ROUND1) { /* 2nd iteration needed, good to 141/248 (ld80/ld128) */
+ t = r;
+ w = fn*pio2_2;
+ r = t-w;
+ w = fn*pio2_2t-((t-r)-w);
+ y[0] = r-w;
+ u.f = y[0];
+ ey = u.i.se & 0x7fff;
+ if (ex - ey > ROUND2) { /* 3rd iteration, good to 180/316 bits */
+ t = r; /* will cover all possible cases (not verified for ld128) */
+ w = fn*pio2_3;
+ r = t-w;
+ w = fn*pio2_3t-((t-r)-w);
+ y[0] = r-w;
+ }
+ }
+ y[1] = (r - y[0]) - w;
+ return n;
+ }
+ /*
+ * all other (large) arguments
+ */
+ if (ex == 0x7fff) { /* x is inf or NaN */
+ y[0] = y[1] = x - x;
+ return 0;
+ }
+ /* set z = scalbn(|x|,-ilogb(x)+23) */
+ uz.f = x;
+ uz.i.se = 0x3fff + 23;
+ z = uz.f;
+ for (i=0; i < NX - 1; i++) {
+ tx[i] = (double)(int32_t)z;
+ z = (z-tx[i])*0x1p24;
+ }
+ tx[i] = z;
+ while (tx[i] == 0)
+ i--;
+ n = __rem_pio2_large(tx, ty, ex-0x3fff-23, i+1, NY);
+ w = ty[1];
+ if (NY == 3)
+ w += ty[2];
+ r = ty[0] + w;
+ /* TODO: for ld128 this does not follow the recommendation of the
+ comments of __rem_pio2_large which seem wrong if |ty[0]| > |ty[1]+ty[2]| */
+ w -= r - ty[0];
+ if (u.i.se >> 15) {
+ y[0] = -r;
+ y[1] = -w;
+ return -n;
+ }
+ y[0] = r;
+ y[1] = w;
+ return n;
+}
+#endif
diff --git a/lib/libc/src/musl-math/__signbit.c b/lib/libc/src/musl-math/__signbit.c
new file mode 100644
index 0000000..e700b6b
--- /dev/null
+++ b/lib/libc/src/musl-math/__signbit.c
@@ -0,0 +1,13 @@
+#include "libm.h"
+
+// FIXME: macro in math.h
+int __signbit(double x)
+{
+ union {
+ double d;
+ uint64_t i;
+ } y = { x };
+ return y.i>>63;
+}
+
+
diff --git a/lib/libc/src/musl-math/__signbitf.c b/lib/libc/src/musl-math/__signbitf.c
new file mode 100644
index 0000000..40ad3cf
--- /dev/null
+++ b/lib/libc/src/musl-math/__signbitf.c
@@ -0,0 +1,11 @@
+#include "libm.h"
+
+// FIXME: macro in math.h
+int __signbitf(float x)
+{
+ union {
+ float f;
+ uint32_t i;
+ } y = { x };
+ return y.i>>31;
+}
diff --git a/lib/libc/src/musl-math/__signbitl.c b/lib/libc/src/musl-math/__signbitl.c
new file mode 100644
index 0000000..63b3dc5
--- /dev/null
+++ b/lib/libc/src/musl-math/__signbitl.c
@@ -0,0 +1,14 @@
+#include "libm.h"
+
+#if (LDBL_MANT_DIG == 64 || LDBL_MANT_DIG == 113) && LDBL_MAX_EXP == 16384
+int __signbitl(long double x)
+{
+ union ldshape u = {x};
+ return u.i.se >> 15;
+}
+#elif LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
+int __signbitl(long double x)
+{
+ return __signbit(x);
+}
+#endif
diff --git a/lib/libc/src/musl-math/__sin.c b/lib/libc/src/musl-math/__sin.c
new file mode 100644
index 0000000..4030949
--- /dev/null
+++ b/lib/libc/src/musl-math/__sin.c
@@ -0,0 +1,64 @@
+/* origin: FreeBSD /usr/src/lib/msun/src/k_sin.c */
+/*
+ * ====================================================
+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ *
+ * Developed at SunSoft, a Sun Microsystems, Inc. business.
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ */
+/* __sin( x, y, iy)
+ * kernel sin function on ~[-pi/4, pi/4] (except on -0), pi/4 ~ 0.7854
+ * Input x is assumed to be bounded by ~pi/4 in magnitude.
+ * Input y is the tail of x.
+ * Input iy indicates whether y is 0. (if iy=0, y assume to be 0).
+ *
+ * Algorithm
+ * 1. Since sin(-x) = -sin(x), we need only to consider positive x.
+ * 2. Callers must return sin(-0) = -0 without calling here since our
+ * odd polynomial is not evaluated in a way that preserves -0.
+ * Callers may do the optimization sin(x) ~ x for tiny x.
+ * 3. sin(x) is approximated by a polynomial of degree 13 on
+ * [0,pi/4]
+ * 3 13
+ * sin(x) ~ x + S1*x + ... + S6*x
+ * where
+ *
+ * |sin(x) 2 4 6 8 10 12 | -58
+ * |----- - (1+S1*x +S2*x +S3*x +S4*x +S5*x +S6*x )| <= 2
+ * | x |
+ *
+ * 4. sin(x+y) = sin(x) + sin'(x')*y
+ * ~ sin(x) + (1-x*x/2)*y
+ * For better accuracy, let
+ * 3 2 2 2 2
+ * r = x *(S2+x *(S3+x *(S4+x *(S5+x *S6))))
+ * then 3 2
+ * sin(x) = x + (S1*x + (x *(r-y/2)+y))
+ */
+
+#include "libm.h"
+
+static const double
+S1 = -1.66666666666666324348e-01, /* 0xBFC55555, 0x55555549 */
+S2 = 8.33333333332248946124e-03, /* 0x3F811111, 0x1110F8A6 */
+S3 = -1.98412698298579493134e-04, /* 0xBF2A01A0, 0x19C161D5 */
+S4 = 2.75573137070700676789e-06, /* 0x3EC71DE3, 0x57B1FE7D */
+S5 = -2.50507602534068634195e-08, /* 0xBE5AE5E6, 0x8A2B9CEB */
+S6 = 1.58969099521155010221e-10; /* 0x3DE5D93A, 0x5ACFD57C */
+
+double __sin(double x, double y, int iy)
+{
+ double_t z,r,v,w;
+
+ z = x*x;
+ w = z*z;
+ r = S2 + z*(S3 + z*S4) + z*w*(S5 + z*S6);
+ v = z*x;
+ if (iy == 0)
+ return x + v*(S1 + z*r);
+ else
+ return x - ((z*(0.5*y - v*r) - y) - v*S1);
+}
diff --git a/lib/libc/src/musl-math/__sindf.c b/lib/libc/src/musl-math/__sindf.c
new file mode 100644
index 0000000..8fec2a3
--- /dev/null
+++ b/lib/libc/src/musl-math/__sindf.c
@@ -0,0 +1,36 @@
+/* origin: FreeBSD /usr/src/lib/msun/src/k_sinf.c */
+/*
+ * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com.
+ * Optimized by Bruce D. Evans.
+ */
+/*
+ * ====================================================
+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ *
+ * Developed at SunPro, a Sun Microsystems, Inc. business.
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ */
+
+#include "libm.h"
+
+/* |sin(x)/x - s(x)| < 2**-37.5 (~[-4.89e-12, 4.824e-12]). */
+static const double
+S1 = -0x15555554cbac77.0p-55, /* -0.166666666416265235595 */
+S2 = 0x111110896efbb2.0p-59, /* 0.0083333293858894631756 */
+S3 = -0x1a00f9e2cae774.0p-65, /* -0.000198393348360966317347 */
+S4 = 0x16cd878c3b46a7.0p-71; /* 0.0000027183114939898219064 */
+
+float __sindf(double x)
+{
+ double_t r, s, w, z;
+
+ /* Try to optimize for parallel evaluation as in __tandf.c. */
+ z = x*x;
+ w = z*z;
+ r = S3 + z*S4;
+ s = z*x;
+ return (x + s*(S1 + z*S2)) + s*w*r;
+}
diff --git a/lib/libc/src/musl-math/__sinl.c b/lib/libc/src/musl-math/__sinl.c
new file mode 100644
index 0000000..2525bbe
--- /dev/null
+++ b/lib/libc/src/musl-math/__sinl.c
@@ -0,0 +1,78 @@
+/* origin: FreeBSD /usr/src/lib/msun/ld80/k_sinl.c */
+/* origin: FreeBSD /usr/src/lib/msun/ld128/k_sinl.c */
+/*
+ * ====================================================
+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ * Copyright (c) 2008 Steven G. Kargl, David Schultz, Bruce D. Evans.
+ *
+ * Developed at SunSoft, a Sun Microsystems, Inc. business.
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ */
+
+#include "libm.h"
+
+#if (LDBL_MANT_DIG == 64 || LDBL_MANT_DIG == 113) && LDBL_MAX_EXP == 16384
+#if LDBL_MANT_DIG == 64
+/*
+ * ld80 version of __sin.c. See __sin.c for most comments.
+ */
+/*
+ * Domain [-0.7854, 0.7854], range ~[-1.89e-22, 1.915e-22]
+ * |sin(x)/x - s(x)| < 2**-72.1
+ *
+ * See __cosl.c for more details about the polynomial.
+ */
+static const long double
+S1 = -0.166666666666666666671L; /* -0xaaaaaaaaaaaaaaab.0p-66 */
+static const double
+S2 = 0.0083333333333333332, /* 0x11111111111111.0p-59 */
+S3 = -0.00019841269841269427, /* -0x1a01a01a019f81.0p-65 */
+S4 = 0.0000027557319223597490, /* 0x171de3a55560f7.0p-71 */
+S5 = -0.000000025052108218074604, /* -0x1ae64564f16cad.0p-78 */
+S6 = 1.6059006598854211e-10, /* 0x161242b90243b5.0p-85 */
+S7 = -7.6429779983024564e-13, /* -0x1ae42ebd1b2e00.0p-93 */
+S8 = 2.6174587166648325e-15; /* 0x179372ea0b3f64.0p-101 */
+#define POLY(z) (S2+z*(S3+z*(S4+z*(S5+z*(S6+z*(S7+z*S8))))))
+#elif LDBL_MANT_DIG == 113
+/*
+ * ld128 version of __sin.c. See __sin.c for most comments.
+ */
+/*
+ * Domain [-0.7854, 0.7854], range ~[-1.53e-37, 1.659e-37]
+ * |sin(x)/x - s(x)| < 2**-122.1
+ *
+ * See __cosl.c for more details about the polynomial.
+ */
+static const long double
+S1 = -0.16666666666666666666666666666666666606732416116558L,
+S2 = 0.0083333333333333333333333333333331135404851288270047L,
+S3 = -0.00019841269841269841269841269839935785325638310428717L,
+S4 = 0.27557319223985890652557316053039946268333231205686e-5L,
+S5 = -0.25052108385441718775048214826384312253862930064745e-7L,
+S6 = 0.16059043836821614596571832194524392581082444805729e-9L,
+S7 = -0.76471637318198151807063387954939213287488216303768e-12L,
+S8 = 0.28114572543451292625024967174638477283187397621303e-14L;
+static const double
+S9 = -0.82206352458348947812512122163446202498005154296863e-17,
+S10 = 0.19572940011906109418080609928334380560135358385256e-19,
+S11 = -0.38680813379701966970673724299207480965452616911420e-22,
+S12 = 0.64038150078671872796678569586315881020659912139412e-25;
+#define POLY(z) (S2+z*(S3+z*(S4+z*(S5+z*(S6+z*(S7+z*(S8+ \
+ z*(S9+z*(S10+z*(S11+z*S12))))))))))
+#endif
+
+long double __sinl(long double x, long double y, int iy)
+{
+ long double z,r,v;
+
+ z = x*x;
+ v = z*x;
+ r = POLY(z);
+ if (iy == 0)
+ return x+v*(S1+z*r);
+ return x-((z*(0.5*y-v*r)-y)-v*S1);
+}
+#endif
diff --git a/lib/libc/src/musl-math/__tan.c b/lib/libc/src/musl-math/__tan.c
new file mode 100644
index 0000000..8019844
--- /dev/null
+++ b/lib/libc/src/musl-math/__tan.c
@@ -0,0 +1,110 @@
+/* origin: FreeBSD /usr/src/lib/msun/src/k_tan.c */
+/*
+ * ====================================================
+ * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ */
+/* __tan( x, y, k )
+ * kernel tan function on ~[-pi/4, pi/4] (except on -0), pi/4 ~ 0.7854
+ * Input x is assumed to be bounded by ~pi/4 in magnitude.
+ * Input y is the tail of x.
+ * Input odd indicates whether tan (if odd = 0) or -1/tan (if odd = 1) is returned.
+ *
+ * Algorithm
+ * 1. Since tan(-x) = -tan(x), we need only to consider positive x.
+ * 2. Callers must return tan(-0) = -0 without calling here since our
+ * odd polynomial is not evaluated in a way that preserves -0.
+ * Callers may do the optimization tan(x) ~ x for tiny x.
+ * 3. tan(x) is approximated by a odd polynomial of degree 27 on
+ * [0,0.67434]
+ * 3 27
+ * tan(x) ~ x + T1*x + ... + T13*x
+ * where
+ *
+ * |tan(x) 2 4 26 | -59.2
+ * |----- - (1+T1*x +T2*x +.... +T13*x )| <= 2
+ * | x |
+ *
+ * Note: tan(x+y) = tan(x) + tan'(x)*y
+ * ~ tan(x) + (1+x*x)*y
+ * Therefore, for better accuracy in computing tan(x+y), let
+ * 3 2 2 2 2
+ * r = x *(T2+x *(T3+x *(...+x *(T12+x *T13))))
+ * then
+ * 3 2
+ * tan(x+y) = x + (T1*x + (x *(r+y)+y))
+ *
+ * 4. For x in [0.67434,pi/4], let y = pi/4 - x, then
+ * tan(x) = tan(pi/4-y) = (1-tan(y))/(1+tan(y))
+ * = 1 - 2*(tan(y) - (tan(y)^2)/(1+tan(y)))
+ */
+
+#include "libm.h"
+
+static const double T[] = {
+ 3.33333333333334091986e-01, /* 3FD55555, 55555563 */
+ 1.33333333333201242699e-01, /* 3FC11111, 1110FE7A */
+ 5.39682539762260521377e-02, /* 3FABA1BA, 1BB341FE */
+ 2.18694882948595424599e-02, /* 3F9664F4, 8406D637 */
+ 8.86323982359930005737e-03, /* 3F8226E3, E96E8493 */
+ 3.59207910759131235356e-03, /* 3F6D6D22, C9560328 */
+ 1.45620945432529025516e-03, /* 3F57DBC8, FEE08315 */
+ 5.88041240820264096874e-04, /* 3F4344D8, F2F26501 */
+ 2.46463134818469906812e-04, /* 3F3026F7, 1A8D1068 */
+ 7.81794442939557092300e-05, /* 3F147E88, A03792A6 */
+ 7.14072491382608190305e-05, /* 3F12B80F, 32F0A7E9 */
+ -1.85586374855275456654e-05, /* BEF375CB, DB605373 */
+ 2.59073051863633712884e-05, /* 3EFB2A70, 74BF7AD4 */
+},
+pio4 = 7.85398163397448278999e-01, /* 3FE921FB, 54442D18 */
+pio4lo = 3.06161699786838301793e-17; /* 3C81A626, 33145C07 */
+
+double __tan(double x, double y, int odd)
+{
+ double_t z, r, v, w, s, a;
+ double w0, a0;
+ uint32_t hx;
+ int big, sign;
+
+ GET_HIGH_WORD(hx,x);
+ big = (hx&0x7fffffff) >= 0x3FE59428; /* |x| >= 0.6744 */
+ if (big) {
+ sign = hx>>31;
+ if (sign) {
+ x = -x;
+ y = -y;
+ }
+ x = (pio4 - x) + (pio4lo - y);
+ y = 0.0;
+ }
+ z = x * x;
+ w = z * z;
+ /*
+ * Break x^5*(T[1]+x^2*T[2]+...) into
+ * x^5(T[1]+x^4*T[3]+...+x^20*T[11]) +
+ * x^5(x^2*(T[2]+x^4*T[4]+...+x^22*[T12]))
+ */
+ r = T[1] + w*(T[3] + w*(T[5] + w*(T[7] + w*(T[9] + w*T[11]))));
+ v = z*(T[2] + w*(T[4] + w*(T[6] + w*(T[8] + w*(T[10] + w*T[12])))));
+ s = z * x;
+ r = y + z*(s*(r + v) + y) + s*T[0];
+ w = x + r;
+ if (big) {
+ s = 1 - 2*odd;
+ v = s - 2.0 * (x + (r - w*w/(w + s)));
+ return sign ? -v : v;
+ }
+ if (!odd)
+ return w;
+ /* -1.0/(x+r) has up to 2ulp error, so compute it accurately */
+ w0 = w;
+ SET_LOW_WORD(w0, 0);
+ v = r - (w0 - x); /* w0+v = r+x */
+ a0 = a = -1.0 / w;
+ SET_LOW_WORD(a0, 0);
+ return a0 + a*(1.0 + a0*w0 + a0*v);
+}
diff --git a/lib/libc/src/musl-math/__tandf.c b/lib/libc/src/musl-math/__tandf.c
new file mode 100644
index 0000000..25047ee
--- /dev/null
+++ b/lib/libc/src/musl-math/__tandf.c
@@ -0,0 +1,54 @@
+/* origin: FreeBSD /usr/src/lib/msun/src/k_tanf.c */
+/*
+ * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com.
+ * Optimized by Bruce D. Evans.
+ */
+/*
+ * ====================================================
+ * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ */
+
+#include "libm.h"
+
+/* |tan(x)/x - t(x)| < 2**-25.5 (~[-2e-08, 2e-08]). */
+static const double T[] = {
+ 0x15554d3418c99f.0p-54, /* 0.333331395030791399758 */
+ 0x1112fd38999f72.0p-55, /* 0.133392002712976742718 */
+ 0x1b54c91d865afe.0p-57, /* 0.0533812378445670393523 */
+ 0x191df3908c33ce.0p-58, /* 0.0245283181166547278873 */
+ 0x185dadfcecf44e.0p-61, /* 0.00297435743359967304927 */
+ 0x1362b9bf971bcd.0p-59, /* 0.00946564784943673166728 */
+};
+
+float __tandf(double x, int odd)
+{
+ double_t z,r,w,s,t,u;
+
+ z = x*x;
+ /*
+ * Split up the polynomial into small independent terms to give
+ * opportunities for parallel evaluation. The chosen splitting is
+ * micro-optimized for Athlons (XP, X64). It costs 2 multiplications
+ * relative to Horner's method on sequential machines.
+ *
+ * We add the small terms from lowest degree up for efficiency on
+ * non-sequential machines (the lowest degree terms tend to be ready
+ * earlier). Apart from this, we don't care about order of
+ * operations, and don't need to to care since we have precision to
+ * spare. However, the chosen splitting is good for accuracy too,
+ * and would give results as accurate as Horner's method if the
+ * small terms were added from highest degree down.
+ */
+ r = T[4] + z*T[5];
+ t = T[2] + z*T[3];
+ w = z*z;
+ s = z*x;
+ u = T[0] + z*T[1];
+ r = (x + s*u) + (s*w)*(t + w*r);
+ return odd ? -1.0/r : r;
+}
diff --git a/lib/libc/src/musl-math/__tanl.c b/lib/libc/src/musl-math/__tanl.c
new file mode 100644
index 0000000..54abc3d
--- /dev/null
+++ b/lib/libc/src/musl-math/__tanl.c
@@ -0,0 +1,143 @@
+/* origin: FreeBSD /usr/src/lib/msun/ld80/k_tanl.c */
+/* origin: FreeBSD /usr/src/lib/msun/ld128/k_tanl.c */
+/*
+ * ====================================================
+ * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright (c) 2008 Steven G. Kargl, David Schultz, Bruce D. Evans.
+ *
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ */
+
+#include "libm.h"
+
+#if (LDBL_MANT_DIG == 64 || LDBL_MANT_DIG == 113) && LDBL_MAX_EXP == 16384
+#if LDBL_MANT_DIG == 64
+/*
+ * ld80 version of __tan.c. See __tan.c for most comments.
+ */
+/*
+ * Domain [-0.67434, 0.67434], range ~[-2.25e-22, 1.921e-22]
+ * |tan(x)/x - t(x)| < 2**-71.9
+ *
+ * See __cosl.c for more details about the polynomial.
+ */
+static const long double
+T3 = 0.333333333333333333180L, /* 0xaaaaaaaaaaaaaaa5.0p-65 */
+T5 = 0.133333333333333372290L, /* 0x88888888888893c3.0p-66 */
+T7 = 0.0539682539682504975744L, /* 0xdd0dd0dd0dc13ba2.0p-68 */
+pio4 = 0.785398163397448309628L, /* 0xc90fdaa22168c235.0p-64 */
+pio4lo = -1.25413940316708300586e-20L; /* -0xece675d1fc8f8cbb.0p-130 */
+static const double
+T9 = 0.021869488536312216, /* 0x1664f4882cc1c2.0p-58 */
+T11 = 0.0088632355256619590, /* 0x1226e355c17612.0p-59 */
+T13 = 0.0035921281113786528, /* 0x1d6d3d185d7ff8.0p-61 */
+T15 = 0.0014558334756312418, /* 0x17da354aa3f96b.0p-62 */
+T17 = 0.00059003538700862256, /* 0x13559358685b83.0p-63 */
+T19 = 0.00023907843576635544, /* 0x1f56242026b5be.0p-65 */
+T21 = 0.000097154625656538905, /* 0x1977efc26806f4.0p-66 */
+T23 = 0.000038440165747303162, /* 0x14275a09b3ceac.0p-67 */
+T25 = 0.000018082171885432524, /* 0x12f5e563e5487e.0p-68 */
+T27 = 0.0000024196006108814377, /* 0x144c0d80cc6896.0p-71 */
+T29 = 0.0000078293456938132840, /* 0x106b59141a6cb3.0p-69 */
+T31 = -0.0000032609076735050182, /* -0x1b5abef3ba4b59.0p-71 */
+T33 = 0.0000023261313142559411; /* 0x13835436c0c87f.0p-71 */
+#define RPOLY(w) (T5 + w * (T9 + w * (T13 + w * (T17 + w * (T21 + \
+ w * (T25 + w * (T29 + w * T33)))))))
+#define VPOLY(w) (T7 + w * (T11 + w * (T15 + w * (T19 + w * (T23 + \
+ w * (T27 + w * T31))))))
+#elif LDBL_MANT_DIG == 113
+/*
+ * ld128 version of __tan.c. See __tan.c for most comments.
+ */
+/*
+ * Domain [-0.67434, 0.67434], range ~[-3.37e-36, 1.982e-37]
+ * |tan(x)/x - t(x)| < 2**-117.8 (XXX should be ~1e-37)
+ *
+ * See __cosl.c for more details about the polynomial.
+ */
+static const long double
+T3 = 0x1.5555555555555555555555555553p-2L,
+T5 = 0x1.1111111111111111111111111eb5p-3L,
+T7 = 0x1.ba1ba1ba1ba1ba1ba1ba1b694cd6p-5L,
+T9 = 0x1.664f4882c10f9f32d6bbe09d8bcdp-6L,
+T11 = 0x1.226e355e6c23c8f5b4f5762322eep-7L,
+T13 = 0x1.d6d3d0e157ddfb5fed8e84e27b37p-9L,
+T15 = 0x1.7da36452b75e2b5fce9ee7c2c92ep-10L,
+T17 = 0x1.355824803674477dfcf726649efep-11L,
+T19 = 0x1.f57d7734d1656e0aceb716f614c2p-13L,
+T21 = 0x1.967e18afcb180ed942dfdc518d6cp-14L,
+T23 = 0x1.497d8eea21e95bc7e2aa79b9f2cdp-15L,
+T25 = 0x1.0b132d39f055c81be49eff7afd50p-16L,
+T27 = 0x1.b0f72d33eff7bfa2fbc1059d90b6p-18L,
+T29 = 0x1.5ef2daf21d1113df38d0fbc00267p-19L,
+T31 = 0x1.1c77d6eac0234988cdaa04c96626p-20L,
+T33 = 0x1.cd2a5a292b180e0bdd701057dfe3p-22L,
+T35 = 0x1.75c7357d0298c01a31d0a6f7d518p-23L,
+T37 = 0x1.2f3190f4718a9a520f98f50081fcp-24L,
+pio4 = 0x1.921fb54442d18469898cc51701b8p-1L,
+pio4lo = 0x1.cd129024e088a67cc74020bbea60p-116L;
+static const double
+T39 = 0.000000028443389121318352, /* 0x1e8a7592977938.0p-78 */
+T41 = 0.000000011981013102001973, /* 0x19baa1b1223219.0p-79 */
+T43 = 0.0000000038303578044958070, /* 0x107385dfb24529.0p-80 */
+T45 = 0.0000000034664378216909893, /* 0x1dc6c702a05262.0p-81 */
+T47 = -0.0000000015090641701997785, /* -0x19ecef3569ebb6.0p-82 */
+T49 = 0.0000000029449552300483952, /* 0x194c0668da786a.0p-81 */
+T51 = -0.0000000022006995706097711, /* -0x12e763b8845268.0p-81 */
+T53 = 0.0000000015468200913196612, /* 0x1a92fc98c29554.0p-82 */
+T55 = -0.00000000061311613386849674, /* -0x151106cbc779a9.0p-83 */
+T57 = 1.4912469681508012e-10; /* 0x147edbdba6f43a.0p-85 */
+#define RPOLY(w) (T5 + w * (T9 + w * (T13 + w * (T17 + w * (T21 + \
+ w * (T25 + w * (T29 + w * (T33 + w * (T37 + w * (T41 + \
+ w * (T45 + w * (T49 + w * (T53 + w * T57)))))))))))))
+#define VPOLY(w) (T7 + w * (T11 + w * (T15 + w * (T19 + w * (T23 + \
+ w * (T27 + w * (T31 + w * (T35 + w * (T39 + w * (T43 + \
+ w * (T47 + w * (T51 + w * T55))))))))))))
+#endif
+
+long double __tanl(long double x, long double y, int odd) {
+ long double z, r, v, w, s, a, t;
+ int big, sign;
+
+ big = fabsl(x) >= 0.67434;
+ if (big) {
+ sign = 0;
+ if (x < 0) {
+ sign = 1;
+ x = -x;
+ y = -y;
+ }
+ x = (pio4 - x) + (pio4lo - y);
+ y = 0.0;
+ }
+ z = x * x;
+ w = z * z;
+ r = RPOLY(w);
+ v = z * VPOLY(w);
+ s = z * x;
+ r = y + z * (s * (r + v) + y) + T3 * s;
+ w = x + r;
+ if (big) {
+ s = 1 - 2*odd;
+ v = s - 2.0 * (x + (r - w * w / (w + s)));
+ return sign ? -v : v;
+ }
+ if (!odd)
+ return w;
+ /*
+ * if allow error up to 2 ulp, simply return
+ * -1.0 / (x+r) here
+ */
+ /* compute -1.0 / (x+r) accurately */
+ z = w;
+ z = z + 0x1p32 - 0x1p32;
+ v = r - (z - x); /* z+v = r+x */
+ t = a = -1.0 / w; /* a = -1.0/w */
+ t = t + 0x1p32 - 0x1p32;
+ s = 1.0 + t * z;
+ return t + a * (s + t * v);
+}
+#endif
diff --git a/lib/libc/src/musl-math/acos.c b/lib/libc/src/musl-math/acos.c
new file mode 100644
index 0000000..ea9c87b
--- /dev/null
+++ b/lib/libc/src/musl-math/acos.c
@@ -0,0 +1,101 @@
+/* origin: FreeBSD /usr/src/lib/msun/src/e_acos.c */
+/*
+ * ====================================================
+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ *
+ * Developed at SunSoft, a Sun Microsystems, Inc. business.
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ */
+/* acos(x)
+ * Method :
+ * acos(x) = pi/2 - asin(x)
+ * acos(-x) = pi/2 + asin(x)
+ * For |x|<=0.5
+ * acos(x) = pi/2 - (x + x*x^2*R(x^2)) (see asin.c)
+ * For x>0.5
+ * acos(x) = pi/2 - (pi/2 - 2asin(sqrt((1-x)/2)))
+ * = 2asin(sqrt((1-x)/2))
+ * = 2s + 2s*z*R(z) ...z=(1-x)/2, s=sqrt(z)
+ * = 2f + (2c + 2s*z*R(z))
+ * where f=hi part of s, and c = (z-f*f)/(s+f) is the correction term
+ * for f so that f+c ~ sqrt(z).
+ * For x<-0.5
+ * acos(x) = pi - 2asin(sqrt((1-|x|)/2))
+ * = pi - 0.5*(s+s*z*R(z)), where z=(1-|x|)/2,s=sqrt(z)
+ *
+ * Special cases:
+ * if x is NaN, return x itself;
+ * if |x|>1, return NaN with invalid signal.
+ *
+ * Function needed: sqrt
+ */
+
+#include "libm.h"
+
+static const double
+pio2_hi = 1.57079632679489655800e+00, /* 0x3FF921FB, 0x54442D18 */
+pio2_lo = 6.12323399573676603587e-17, /* 0x3C91A626, 0x33145C07 */
+pS0 = 1.66666666666666657415e-01, /* 0x3FC55555, 0x55555555 */
+pS1 = -3.25565818622400915405e-01, /* 0xBFD4D612, 0x03EB6F7D */
+pS2 = 2.01212532134862925881e-01, /* 0x3FC9C155, 0x0E884455 */
+pS3 = -4.00555345006794114027e-02, /* 0xBFA48228, 0xB5688F3B */
+pS4 = 7.91534994289814532176e-04, /* 0x3F49EFE0, 0x7501B288 */
+pS5 = 3.47933107596021167570e-05, /* 0x3F023DE1, 0x0DFDF709 */
+qS1 = -2.40339491173441421878e+00, /* 0xC0033A27, 0x1C8A2D4B */
+qS2 = 2.02094576023350569471e+00, /* 0x40002AE5, 0x9C598AC8 */
+qS3 = -6.88283971605453293030e-01, /* 0xBFE6066C, 0x1B8D0159 */
+qS4 = 7.70381505559019352791e-02; /* 0x3FB3B8C5, 0xB12E9282 */
+
+static double R(double z)
+{
+ double_t p, q;
+ p = z*(pS0+z*(pS1+z*(pS2+z*(pS3+z*(pS4+z*pS5)))));
+ q = 1.0+z*(qS1+z*(qS2+z*(qS3+z*qS4)));
+ return p/q;
+}
+
+double acos(double x)
+{
+ double z,w,s,c,df;
+ uint32_t hx,ix;
+
+ GET_HIGH_WORD(hx, x);
+ ix = hx & 0x7fffffff;
+ /* |x| >= 1 or nan */
+ if (ix >= 0x3ff00000) {
+ uint32_t lx;
+
+ GET_LOW_WORD(lx,x);
+ if ((ix-0x3ff00000 | lx) == 0) {
+ /* acos(1)=0, acos(-1)=pi */
+ if (hx >> 31)
+ return 2*pio2_hi + 0x1p-120f;
+ return 0;
+ }
+ return 0/(x-x);
+ }
+ /* |x| < 0.5 */
+ if (ix < 0x3fe00000) {
+ if (ix <= 0x3c600000) /* |x| < 2**-57 */
+ return pio2_hi + 0x1p-120f;
+ return pio2_hi - (x - (pio2_lo-x*R(x*x)));
+ }
+ /* x < -0.5 */
+ if (hx >> 31) {
+ z = (1.0+x)*0.5;
+ s = sqrt(z);
+ w = R(z)*s-pio2_lo;
+ return 2*(pio2_hi - (s+w));
+ }
+ /* x > 0.5 */
+ z = (1.0-x)*0.5;
+ s = sqrt(z);
+ df = s;
+ SET_LOW_WORD(df,0);
+ c = (z-df*df)/(s+df);
+ w = R(z)*s+c;
+ return 2*(df+w);
+}
diff --git a/lib/libc/src/musl-math/acosf.c b/lib/libc/src/musl-math/acosf.c
new file mode 100644
index 0000000..8ee1a71
--- /dev/null
+++ b/lib/libc/src/musl-math/acosf.c
@@ -0,0 +1,71 @@
+/* origin: FreeBSD /usr/src/lib/msun/src/e_acosf.c */
+/*
+ * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com.
+ */
+/*
+ * ====================================================
+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ *
+ * Developed at SunPro, a Sun Microsystems, Inc. business.
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ */
+
+#include "libm.h"
+
+static const float
+pio2_hi = 1.5707962513e+00, /* 0x3fc90fda */
+pio2_lo = 7.5497894159e-08, /* 0x33a22168 */
+pS0 = 1.6666586697e-01,
+pS1 = -4.2743422091e-02,
+pS2 = -8.6563630030e-03,
+qS1 = -7.0662963390e-01;
+
+static float R(float z)
+{
+ float_t p, q;
+ p = z*(pS0+z*(pS1+z*pS2));
+ q = 1.0f+z*qS1;
+ return p/q;
+}
+
+float acosf(float x)
+{
+ float z,w,s,c,df;
+ uint32_t hx,ix;
+
+ GET_FLOAT_WORD(hx, x);
+ ix = hx & 0x7fffffff;
+ /* |x| >= 1 or nan */
+ if (ix >= 0x3f800000) {
+ if (ix == 0x3f800000) {
+ if (hx >> 31)
+ return 2*pio2_hi + 0x1p-120f;
+ return 0;
+ }
+ return 0/(x-x);
+ }
+ /* |x| < 0.5 */
+ if (ix < 0x3f000000) {
+ if (ix <= 0x32800000) /* |x| < 2**-26 */
+ return pio2_hi + 0x1p-120f;
+ return pio2_hi - (x - (pio2_lo-x*R(x*x)));
+ }
+ /* x < -0.5 */
+ if (hx >> 31) {
+ z = (1+x)*0.5f;
+ s = sqrtf(z);
+ w = R(z)*s-pio2_lo;
+ return 2*(pio2_hi - (s+w));
+ }
+ /* x > 0.5 */
+ z = (1-x)*0.5f;
+ s = sqrtf(z);
+ GET_FLOAT_WORD(hx,s);
+ SET_FLOAT_WORD(df,hx&0xfffff000);
+ c = (z-df*df)/(s+df);
+ w = R(z)*s+c;
+ return 2*(df+w);
+}
diff --git a/lib/libc/src/musl-math/acosh.c b/lib/libc/src/musl-math/acosh.c
new file mode 100644
index 0000000..badbf90
--- /dev/null
+++ b/lib/libc/src/musl-math/acosh.c
@@ -0,0 +1,24 @@
+#include "libm.h"
+
+#if FLT_EVAL_METHOD==2
+#undef sqrt
+#define sqrt sqrtl
+#endif
+
+/* acosh(x) = log(x + sqrt(x*x-1)) */
+double acosh(double x)
+{
+ union {double f; uint64_t i;} u = {.f = x};
+ unsigned e = u.i >> 52 & 0x7ff;
+
+ /* x < 1 domain error is handled in the called functions */
+
+ if (e < 0x3ff + 1)
+ /* |x| < 2, up to 2ulp error in [1,1.125] */
+ return log1p(x-1 + sqrt((x-1)*(x-1)+2*(x-1)));
+ if (e < 0x3ff + 26)
+ /* |x| < 0x1p26 */
+ return log(2*x - 1/(x+sqrt(x*x-1)));
+ /* |x| >= 0x1p26 or nan */
+ return log(x) + 0.693147180559945309417232121458176568;
+}
diff --git a/lib/libc/src/musl-math/acoshf.c b/lib/libc/src/musl-math/acoshf.c
new file mode 100644
index 0000000..8a4ec4d
--- /dev/null
+++ b/lib/libc/src/musl-math/acoshf.c
@@ -0,0 +1,26 @@
+#include "libm.h"
+
+#if FLT_EVAL_METHOD==2
+#undef sqrtf
+#define sqrtf sqrtl
+#elif FLT_EVAL_METHOD==1
+#undef sqrtf
+#define sqrtf sqrt
+#endif
+
+/* acosh(x) = log(x + sqrt(x*x-1)) */
+float acoshf(float x)
+{
+ union {float f; uint32_t i;} u = {x};
+ uint32_t a = u.i & 0x7fffffff;
+
+ if (a < 0x3f800000+(1<<23))
+ /* |x| < 2, invalid if x < 1 or nan */
+ /* up to 2ulp error in [1,1.125] */
+ return log1pf(x-1 + sqrtf((x-1)*(x-1)+2*(x-1)));
+ if (a < 0x3f800000+(12<<23))
+ /* |x| < 0x1p12 */
+ return logf(2*x - 1/(x+sqrtf(x*x-1)));
+ /* x >= 0x1p12 */
+ return logf(x) + 0.693147180559945309417232121458176568f;
+}
diff --git a/lib/libc/src/musl-math/acoshl.c b/lib/libc/src/musl-math/acoshl.c
new file mode 100644
index 0000000..8d4b43f
--- /dev/null
+++ b/lib/libc/src/musl-math/acoshl.c
@@ -0,0 +1,29 @@
+#include "libm.h"
+
+#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
+long double acoshl(long double x)
+{
+ return acosh(x);
+}
+#elif LDBL_MANT_DIG == 64 && LDBL_MAX_EXP == 16384
+/* acosh(x) = log(x + sqrt(x*x-1)) */
+long double acoshl(long double x)
+{
+ union ldshape u = {x};
+ int e = u.i.se & 0x7fff;
+
+ if (e < 0x3fff + 1)
+ /* |x| < 2, invalid if x < 1 or nan */
+ return log1pl(x-1 + sqrtl((x-1)*(x-1)+2*(x-1)));
+ if (e < 0x3fff + 32)
+ /* |x| < 0x1p32 */
+ return logl(2*x - 1/(x+sqrtl(x*x-1)));
+ return logl(x) + 0.693147180559945309417232121458176568L;
+}
+#elif LDBL_MANT_DIG == 113 && LDBL_MAX_EXP == 16384
+// TODO: broken implementation to make things compile
+long double acoshl(long double x)
+{
+ return acosh(x);
+}
+#endif
diff --git a/lib/libc/src/musl-math/acosl.c b/lib/libc/src/musl-math/acosl.c
new file mode 100644
index 0000000..c03bdf0
--- /dev/null
+++ b/lib/libc/src/musl-math/acosl.c
@@ -0,0 +1,67 @@
+/* origin: FreeBSD /usr/src/lib/msun/src/e_acosl.c */
+/*
+ * ====================================================
+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ *
+ * Developed at SunSoft, a Sun Microsystems, Inc. business.
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ */
+/*
+ * See comments in acos.c.
+ * Converted to long double by David Schultz <das@FreeBSD.ORG>.
+ */
+
+#include "libm.h"
+
+#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
+long double acosl(long double x)
+{
+ return acos(x);
+}
+#elif (LDBL_MANT_DIG == 64 || LDBL_MANT_DIG == 113) && LDBL_MAX_EXP == 16384
+#include "__invtrigl.h"
+#if LDBL_MANT_DIG == 64
+#define CLEARBOTTOM(u) (u.i.m &= -1ULL << 32)
+#elif LDBL_MANT_DIG == 113
+#define CLEARBOTTOM(u) (u.i.lo = 0)
+#endif
+
+long double acosl(long double x)
+{
+ union ldshape u = {x};
+ long double z, s, c, f;
+ uint16_t e = u.i.se & 0x7fff;
+
+ /* |x| >= 1 or nan */
+ if (e >= 0x3fff) {
+ if (x == 1)
+ return 0;
+ if (x == -1)
+ return 2*pio2_hi + 0x1p-120f;
+ return 0/(x-x);
+ }
+ /* |x| < 0.5 */
+ if (e < 0x3fff - 1) {
+ if (e < 0x3fff - LDBL_MANT_DIG - 1)
+ return pio2_hi + 0x1p-120f;
+ return pio2_hi - (__invtrigl_R(x*x)*x - pio2_lo + x);
+ }
+ /* x < -0.5 */
+ if (u.i.se >> 15) {
+ z = (1 + x)*0.5;
+ s = sqrtl(z);
+ return 2*(pio2_hi - (__invtrigl_R(z)*s - pio2_lo + s));
+ }
+ /* x > 0.5 */
+ z = (1 - x)*0.5;
+ s = sqrtl(z);
+ u.f = s;
+ CLEARBOTTOM(u);
+ f = u.f;
+ c = (z - f*f)/(s + f);
+ return 2*(__invtrigl_R(z)*s + c + f);
+}
+#endif
diff --git a/lib/libc/src/musl-math/asin.c b/lib/libc/src/musl-math/asin.c
new file mode 100644
index 0000000..c926b18
--- /dev/null
+++ b/lib/libc/src/musl-math/asin.c
@@ -0,0 +1,107 @@
+/* origin: FreeBSD /usr/src/lib/msun/src/e_asin.c */
+/*
+ * ====================================================
+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ *
+ * Developed at SunSoft, a Sun Microsystems, Inc. business.
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ */
+/* asin(x)
+ * Method :
+ * Since asin(x) = x + x^3/6 + x^5*3/40 + x^7*15/336 + ...
+ * we approximate asin(x) on [0,0.5] by
+ * asin(x) = x + x*x^2*R(x^2)
+ * where
+ * R(x^2) is a rational approximation of (asin(x)-x)/x^3
+ * and its remez error is bounded by
+ * |(asin(x)-x)/x^3 - R(x^2)| < 2^(-58.75)
+ *
+ * For x in [0.5,1]
+ * asin(x) = pi/2-2*asin(sqrt((1-x)/2))
+ * Let y = (1-x), z = y/2, s := sqrt(z), and pio2_hi+pio2_lo=pi/2;
+ * then for x>0.98
+ * asin(x) = pi/2 - 2*(s+s*z*R(z))
+ * = pio2_hi - (2*(s+s*z*R(z)) - pio2_lo)
+ * For x<=0.98, let pio4_hi = pio2_hi/2, then
+ * f = hi part of s;
+ * c = sqrt(z) - f = (z-f*f)/(s+f) ...f+c=sqrt(z)
+ * and
+ * asin(x) = pi/2 - 2*(s+s*z*R(z))
+ * = pio4_hi+(pio4-2s)-(2s*z*R(z)-pio2_lo)
+ * = pio4_hi+(pio4-2f)-(2s*z*R(z)-(pio2_lo+2c))
+ *
+ * Special cases:
+ * if x is NaN, return x itself;
+ * if |x|>1, return NaN with invalid signal.
+ *
+ */
+
+#include "libm.h"
+
+static const double
+pio2_hi = 1.57079632679489655800e+00, /* 0x3FF921FB, 0x54442D18 */
+pio2_lo = 6.12323399573676603587e-17, /* 0x3C91A626, 0x33145C07 */
+/* coefficients for R(x^2) */
+pS0 = 1.66666666666666657415e-01, /* 0x3FC55555, 0x55555555 */
+pS1 = -3.25565818622400915405e-01, /* 0xBFD4D612, 0x03EB6F7D */
+pS2 = 2.01212532134862925881e-01, /* 0x3FC9C155, 0x0E884455 */
+pS3 = -4.00555345006794114027e-02, /* 0xBFA48228, 0xB5688F3B */
+pS4 = 7.91534994289814532176e-04, /* 0x3F49EFE0, 0x7501B288 */
+pS5 = 3.47933107596021167570e-05, /* 0x3F023DE1, 0x0DFDF709 */
+qS1 = -2.40339491173441421878e+00, /* 0xC0033A27, 0x1C8A2D4B */
+qS2 = 2.02094576023350569471e+00, /* 0x40002AE5, 0x9C598AC8 */
+qS3 = -6.88283971605453293030e-01, /* 0xBFE6066C, 0x1B8D0159 */
+qS4 = 7.70381505559019352791e-02; /* 0x3FB3B8C5, 0xB12E9282 */
+
+static double R(double z)
+{
+ double_t p, q;
+ p = z*(pS0+z*(pS1+z*(pS2+z*(pS3+z*(pS4+z*pS5)))));
+ q = 1.0+z*(qS1+z*(qS2+z*(qS3+z*qS4)));
+ return p/q;
+}
+
+double asin(double x)
+{
+ double z,r,s;
+ uint32_t hx,ix;
+
+ GET_HIGH_WORD(hx, x);
+ ix = hx & 0x7fffffff;
+ /* |x| >= 1 or nan */
+ if (ix >= 0x3ff00000) {
+ uint32_t lx;
+ GET_LOW_WORD(lx, x);
+ if ((ix-0x3ff00000 | lx) == 0)
+ /* asin(1) = +-pi/2 with inexact */
+ return x*pio2_hi + 0x1p-120f;
+ return 0/(x-x);
+ }
+ /* |x| < 0.5 */
+ if (ix < 0x3fe00000) {
+ /* if 0x1p-1022 <= |x| < 0x1p-26, avoid raising underflow */
+ if (ix < 0x3e500000 && ix >= 0x00100000)
+ return x;
+ return x + x*R(x*x);
+ }
+ /* 1 > |x| >= 0.5 */
+ z = (1 - fabs(x))*0.5;
+ s = sqrt(z);
+ r = R(z);
+ if (ix >= 0x3fef3333) { /* if |x| > 0.975 */
+ x = pio2_hi-(2*(s+s*r)-pio2_lo);
+ } else {
+ double f,c;
+ /* f+c = sqrt(z) */
+ f = s;
+ SET_LOW_WORD(f,0);
+ c = (z-f*f)/(s+f);
+ x = 0.5*pio2_hi - (2*s*r - (pio2_lo-2*c) - (0.5*pio2_hi-2*f));
+ }
+ if (hx >> 31)
+ return -x;
+ return x;
+}
diff --git a/lib/libc/src/musl-math/asinf.c b/lib/libc/src/musl-math/asinf.c
new file mode 100644
index 0000000..bcd304a
--- /dev/null
+++ b/lib/libc/src/musl-math/asinf.c
@@ -0,0 +1,61 @@
+/* origin: FreeBSD /usr/src/lib/msun/src/e_asinf.c */
+/*
+ * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com.
+ */
+/*
+ * ====================================================
+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ *
+ * Developed at SunPro, a Sun Microsystems, Inc. business.
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ */
+#include "libm.h"
+
+static const double
+pio2 = 1.570796326794896558e+00;
+
+static const float
+/* coefficients for R(x^2) */
+pS0 = 1.6666586697e-01,
+pS1 = -4.2743422091e-02,
+pS2 = -8.6563630030e-03,
+qS1 = -7.0662963390e-01;
+
+static float R(float z)
+{
+ float_t p, q;
+ p = z*(pS0+z*(pS1+z*pS2));
+ q = 1.0f+z*qS1;
+ return p/q;
+}
+
+float asinf(float x)
+{
+ double s;
+ float z;
+ uint32_t hx,ix;
+
+ GET_FLOAT_WORD(hx, x);
+ ix = hx & 0x7fffffff;
+ if (ix >= 0x3f800000) { /* |x| >= 1 */
+ if (ix == 0x3f800000) /* |x| == 1 */
+ return x*pio2 + 0x1p-120f; /* asin(+-1) = +-pi/2 with inexact */
+ return 0/(x-x); /* asin(|x|>1) is NaN */
+ }
+ if (ix < 0x3f000000) { /* |x| < 0.5 */
+ /* if 0x1p-126 <= |x| < 0x1p-12, avoid raising underflow */
+ if (ix < 0x39800000 && ix >= 0x00800000)
+ return x;
+ return x + x*R(x*x);
+ }
+ /* 1 > |x| >= 0.5 */
+ z = (1 - fabsf(x))*0.5f;
+ s = sqrt(z);
+ x = pio2 - 2*(s+s*R(z));
+ if (hx >> 31)
+ return -x;
+ return x;
+}
diff --git a/lib/libc/src/musl-math/asinh.c b/lib/libc/src/musl-math/asinh.c
new file mode 100644
index 0000000..0829f22
--- /dev/null
+++ b/lib/libc/src/musl-math/asinh.c
@@ -0,0 +1,28 @@
+#include "libm.h"
+
+/* asinh(x) = sign(x)*log(|x|+sqrt(x*x+1)) ~= x - x^3/6 + o(x^5) */
+double asinh(double x)
+{
+ union {double f; uint64_t i;} u = {.f = x};
+ unsigned e = u.i >> 52 & 0x7ff;
+ unsigned s = u.i >> 63;
+
+ /* |x| */
+ u.i &= (uint64_t)-1/2;
+ x = u.f;
+
+ if (e >= 0x3ff + 26) {
+ /* |x| >= 0x1p26 or inf or nan */
+ x = log(x) + 0.693147180559945309417232121458176568;
+ } else if (e >= 0x3ff + 1) {
+ /* |x| >= 2 */
+ x = log(2*x + 1/(sqrt(x*x+1)+x));
+ } else if (e >= 0x3ff - 26) {
+ /* |x| >= 0x1p-26, up to 1.6ulp error in [0.125,0.5] */
+ x = log1p(x + x*x/(sqrt(x*x+1)+1));
+ } else {
+ /* |x| < 0x1p-26, raise inexact if x != 0 */
+ FORCE_EVAL(x + 0x1p120f);
+ }
+ return s ? -x : x;
+}
diff --git a/lib/libc/src/musl-math/asinhf.c b/lib/libc/src/musl-math/asinhf.c
new file mode 100644
index 0000000..fc9f091
--- /dev/null
+++ b/lib/libc/src/musl-math/asinhf.c
@@ -0,0 +1,28 @@
+#include "libm.h"
+
+/* asinh(x) = sign(x)*log(|x|+sqrt(x*x+1)) ~= x - x^3/6 + o(x^5) */
+float asinhf(float x)
+{
+ union {float f; uint32_t i;} u = {.f = x};
+ uint32_t i = u.i & 0x7fffffff;
+ unsigned s = u.i >> 31;
+
+ /* |x| */
+ u.i = i;
+ x = u.f;
+
+ if (i >= 0x3f800000 + (12<<23)) {
+ /* |x| >= 0x1p12 or inf or nan */
+ x = logf(x) + 0.693147180559945309417232121458176568f;
+ } else if (i >= 0x3f800000 + (1<<23)) {
+ /* |x| >= 2 */
+ x = logf(2*x + 1/(sqrtf(x*x+1)+x));
+ } else if (i >= 0x3f800000 - (12<<23)) {
+ /* |x| >= 0x1p-12, up to 1.6ulp error in [0.125,0.5] */
+ x = log1pf(x + x*x/(sqrtf(x*x+1)+1));
+ } else {
+ /* |x| < 0x1p-12, raise inexact if x!=0 */
+ FORCE_EVAL(x + 0x1p120f);
+ }
+ return s ? -x : x;
+}
diff --git a/lib/libc/src/musl-math/asinhl.c b/lib/libc/src/musl-math/asinhl.c
new file mode 100644
index 0000000..8635f52
--- /dev/null
+++ b/lib/libc/src/musl-math/asinhl.c
@@ -0,0 +1,41 @@
+#include "libm.h"
+
+#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
+long double asinhl(long double x)
+{
+ return asinh(x);
+}
+#elif LDBL_MANT_DIG == 64 && LDBL_MAX_EXP == 16384
+/* asinh(x) = sign(x)*log(|x|+sqrt(x*x+1)) ~= x - x^3/6 + o(x^5) */
+long double asinhl(long double x)
+{
+ union ldshape u = {x};
+ unsigned e = u.i.se & 0x7fff;
+ unsigned s = u.i.se >> 15;
+
+ /* |x| */
+ u.i.se = e;
+ x = u.f;
+
+ if (e >= 0x3fff + 32) {
+ /* |x| >= 0x1p32 or inf or nan */
+ x = logl(x) + 0.693147180559945309417232121458176568L;
+ } else if (e >= 0x3fff + 1) {
+ /* |x| >= 2 */
+ x = logl(2*x + 1/(sqrtl(x*x+1)+x));
+ } else if (e >= 0x3fff - 32) {
+ /* |x| >= 0x1p-32 */
+ x = log1pl(x + x*x/(sqrtl(x*x+1)+1));
+ } else {
+ /* |x| < 0x1p-32, raise inexact if x!=0 */
+ FORCE_EVAL(x + 0x1p120f);
+ }
+ return s ? -x : x;
+}
+#elif LDBL_MANT_DIG == 113 && LDBL_MAX_EXP == 16384
+// TODO: broken implementation to make things compile
+long double asinhl(long double x)
+{
+ return asinh(x);
+}
+#endif
diff --git a/lib/libc/src/musl-math/asinl.c b/lib/libc/src/musl-math/asinl.c
new file mode 100644
index 0000000..347c535
--- /dev/null
+++ b/lib/libc/src/musl-math/asinl.c
@@ -0,0 +1,71 @@
+/* origin: FreeBSD /usr/src/lib/msun/src/e_asinl.c */
+/*
+ * ====================================================
+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ *
+ * Developed at SunSoft, a Sun Microsystems, Inc. business.
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ */
+/*
+ * See comments in asin.c.
+ * Converted to long double by David Schultz <das@FreeBSD.ORG>.
+ */
+
+#include "libm.h"
+
+#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
+long double asinl(long double x)
+{
+ return asin(x);
+}
+#elif (LDBL_MANT_DIG == 64 || LDBL_MANT_DIG == 113) && LDBL_MAX_EXP == 16384
+#include "__invtrigl.h"
+#if LDBL_MANT_DIG == 64
+#define CLOSETO1(u) (u.i.m>>56 >= 0xf7)
+#define CLEARBOTTOM(u) (u.i.m &= -1ULL << 32)
+#elif LDBL_MANT_DIG == 113
+#define CLOSETO1(u) (u.i.top >= 0xee00)
+#define CLEARBOTTOM(u) (u.i.lo = 0)
+#endif
+
+long double asinl(long double x)
+{
+ union ldshape u = {x};
+ long double z, r, s;
+ uint16_t e = u.i.se & 0x7fff;
+ int sign = u.i.se >> 15;
+
+ if (e >= 0x3fff) { /* |x| >= 1 or nan */
+ /* asin(+-1)=+-pi/2 with inexact */
+ if (x == 1 || x == -1)
+ return x*pio2_hi + 0x1p-120f;
+ return 0/(x-x);
+ }
+ if (e < 0x3fff - 1) { /* |x| < 0.5 */
+ if (e < 0x3fff - (LDBL_MANT_DIG+1)/2) {
+ /* return x with inexact if x!=0 */
+ FORCE_EVAL(x + 0x1p120f);
+ return x;
+ }
+ return x + x*__invtrigl_R(x*x);
+ }
+ /* 1 > |x| >= 0.5 */
+ z = (1.0 - fabsl(x))*0.5;
+ s = sqrtl(z);
+ r = __invtrigl_R(z);
+ if (CLOSETO1(u)) {
+ x = pio2_hi - (2*(s+s*r)-pio2_lo);
+ } else {
+ long double f, c;
+ u.f = s;
+ CLEARBOTTOM(u);
+ f = u.f;
+ c = (z - f*f)/(s + f);
+ x = 0.5*pio2_hi-(2*s*r - (pio2_lo-2*c) - (0.5*pio2_hi-2*f));
+ }
+ return sign ? -x : x;
+}
+#endif
diff --git a/lib/libc/src/musl-math/atan.c b/lib/libc/src/musl-math/atan.c
new file mode 100644
index 0000000..63b0ab2
--- /dev/null
+++ b/lib/libc/src/musl-math/atan.c
@@ -0,0 +1,116 @@
+/* origin: FreeBSD /usr/src/lib/msun/src/s_atan.c */
+/*
+ * ====================================================
+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ *
+ * Developed at SunPro, a Sun Microsystems, Inc. business.
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ */
+/* atan(x)
+ * Method
+ * 1. Reduce x to positive by atan(x) = -atan(-x).
+ * 2. According to the integer k=4t+0.25 chopped, t=x, the argument
+ * is further reduced to one of the following intervals and the
+ * arctangent of t is evaluated by the corresponding formula:
+ *
+ * [0,7/16] atan(x) = t-t^3*(a1+t^2*(a2+...(a10+t^2*a11)...)
+ * [7/16,11/16] atan(x) = atan(1/2) + atan( (t-0.5)/(1+t/2) )
+ * [11/16.19/16] atan(x) = atan( 1 ) + atan( (t-1)/(1+t) )
+ * [19/16,39/16] atan(x) = atan(3/2) + atan( (t-1.5)/(1+1.5t) )
+ * [39/16,INF] atan(x) = atan(INF) + atan( -1/t )
+ *
+ * Constants:
+ * The hexadecimal values are the intended ones for the following
+ * constants. The decimal values may be used, provided that the
+ * compiler will convert from decimal to binary accurately enough
+ * to produce the hexadecimal values shown.
+ */
+
+
+#include "libm.h"
+
+static const double atanhi[] = {
+ 4.63647609000806093515e-01, /* atan(0.5)hi 0x3FDDAC67, 0x0561BB4F */
+ 7.85398163397448278999e-01, /* atan(1.0)hi 0x3FE921FB, 0x54442D18 */
+ 9.82793723247329054082e-01, /* atan(1.5)hi 0x3FEF730B, 0xD281F69B */
+ 1.57079632679489655800e+00, /* atan(inf)hi 0x3FF921FB, 0x54442D18 */
+};
+
+static const double atanlo[] = {
+ 2.26987774529616870924e-17, /* atan(0.5)lo 0x3C7A2B7F, 0x222F65E2 */
+ 3.06161699786838301793e-17, /* atan(1.0)lo 0x3C81A626, 0x33145C07 */
+ 1.39033110312309984516e-17, /* atan(1.5)lo 0x3C700788, 0x7AF0CBBD */
+ 6.12323399573676603587e-17, /* atan(inf)lo 0x3C91A626, 0x33145C07 */
+};
+
+static const double aT[] = {
+ 3.33333333333329318027e-01, /* 0x3FD55555, 0x5555550D */
+ -1.99999999998764832476e-01, /* 0xBFC99999, 0x9998EBC4 */
+ 1.42857142725034663711e-01, /* 0x3FC24924, 0x920083FF */
+ -1.11111104054623557880e-01, /* 0xBFBC71C6, 0xFE231671 */
+ 9.09088713343650656196e-02, /* 0x3FB745CD, 0xC54C206E */
+ -7.69187620504482999495e-02, /* 0xBFB3B0F2, 0xAF749A6D */
+ 6.66107313738753120669e-02, /* 0x3FB10D66, 0xA0D03D51 */
+ -5.83357013379057348645e-02, /* 0xBFADDE2D, 0x52DEFD9A */
+ 4.97687799461593236017e-02, /* 0x3FA97B4B, 0x24760DEB */
+ -3.65315727442169155270e-02, /* 0xBFA2B444, 0x2C6A6C2F */
+ 1.62858201153657823623e-02, /* 0x3F90AD3A, 0xE322DA11 */
+};
+
+double atan(double x)
+{
+ double_t w,s1,s2,z;
+ uint32_t ix,sign;
+ int id;
+
+ GET_HIGH_WORD(ix, x);
+ sign = ix >> 31;
+ ix &= 0x7fffffff;
+ if (ix >= 0x44100000) { /* if |x| >= 2^66 */
+ if (isnan(x))
+ return x;
+ z = atanhi[3] + 0x1p-120f;
+ return sign ? -z : z;
+ }
+ if (ix < 0x3fdc0000) { /* |x| < 0.4375 */
+ if (ix < 0x3e400000) { /* |x| < 2^-27 */
+ if (ix < 0x00100000)
+ /* raise underflow for subnormal x */
+ FORCE_EVAL((float)x);
+ return x;
+ }
+ id = -1;
+ } else {
+ x = fabs(x);
+ if (ix < 0x3ff30000) { /* |x| < 1.1875 */
+ if (ix < 0x3fe60000) { /* 7/16 <= |x| < 11/16 */
+ id = 0;
+ x = (2.0*x-1.0)/(2.0+x);
+ } else { /* 11/16 <= |x| < 19/16 */
+ id = 1;
+ x = (x-1.0)/(x+1.0);
+ }
+ } else {
+ if (ix < 0x40038000) { /* |x| < 2.4375 */
+ id = 2;
+ x = (x-1.5)/(1.0+1.5*x);
+ } else { /* 2.4375 <= |x| < 2^66 */
+ id = 3;
+ x = -1.0/x;
+ }
+ }
+ }
+ /* end of argument reduction */
+ z = x*x;
+ w = z*z;
+ /* break sum from i=0 to 10 aT[i]z**(i+1) into odd and even poly */
+ s1 = z*(aT[0]+w*(aT[2]+w*(aT[4]+w*(aT[6]+w*(aT[8]+w*aT[10])))));
+ s2 = w*(aT[1]+w*(aT[3]+w*(aT[5]+w*(aT[7]+w*aT[9]))));
+ if (id < 0)
+ return x - x*(s1+s2);
+ z = atanhi[id] - (x*(s1+s2) - atanlo[id] - x);
+ return sign ? -z : z;
+}
diff --git a/lib/libc/src/musl-math/atan2.c b/lib/libc/src/musl-math/atan2.c
new file mode 100644
index 0000000..5a1903c
--- /dev/null
+++ b/lib/libc/src/musl-math/atan2.c
@@ -0,0 +1,107 @@
+/* origin: FreeBSD /usr/src/lib/msun/src/e_atan2.c */
+/*
+ * ====================================================
+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ *
+ * Developed at SunSoft, a Sun Microsystems, Inc. business.
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ *
+ */
+/* atan2(y,x)
+ * Method :
+ * 1. Reduce y to positive by atan2(y,x)=-atan2(-y,x).
+ * 2. Reduce x to positive by (if x and y are unexceptional):
+ * ARG (x+iy) = arctan(y/x) ... if x > 0,
+ * ARG (x+iy) = pi - arctan[y/(-x)] ... if x < 0,
+ *
+ * Special cases:
+ *
+ * ATAN2((anything), NaN ) is NaN;
+ * ATAN2(NAN , (anything) ) is NaN;
+ * ATAN2(+-0, +(anything but NaN)) is +-0 ;
+ * ATAN2(+-0, -(anything but NaN)) is +-pi ;
+ * ATAN2(+-(anything but 0 and NaN), 0) is +-pi/2;
+ * ATAN2(+-(anything but INF and NaN), +INF) is +-0 ;
+ * ATAN2(+-(anything but INF and NaN), -INF) is +-pi;
+ * ATAN2(+-INF,+INF ) is +-pi/4 ;
+ * ATAN2(+-INF,-INF ) is +-3pi/4;
+ * ATAN2(+-INF, (anything but,0,NaN, and INF)) is +-pi/2;
+ *
+ * Constants:
+ * The hexadecimal values are the intended ones for the following
+ * constants. The decimal values may be used, provided that the
+ * compiler will convert from decimal to binary accurately enough
+ * to produce the hexadecimal values shown.
+ */
+
+#include "libm.h"
+
+static const double
+pi = 3.1415926535897931160E+00, /* 0x400921FB, 0x54442D18 */
+pi_lo = 1.2246467991473531772E-16; /* 0x3CA1A626, 0x33145C07 */
+
+double atan2(double y, double x)
+{
+ double z;
+ uint32_t m,lx,ly,ix,iy;
+
+ if (isnan(x) || isnan(y))
+ return x+y;
+ EXTRACT_WORDS(ix, lx, x);
+ EXTRACT_WORDS(iy, ly, y);
+ if ((ix-0x3ff00000 | lx) == 0) /* x = 1.0 */
+ return atan(y);
+ m = ((iy>>31)&1) | ((ix>>30)&2); /* 2*sign(x)+sign(y) */
+ ix = ix & 0x7fffffff;
+ iy = iy & 0x7fffffff;
+
+ /* when y = 0 */
+ if ((iy|ly) == 0) {
+ switch(m) {
+ case 0:
+ case 1: return y; /* atan(+-0,+anything)=+-0 */
+ case 2: return pi; /* atan(+0,-anything) = pi */
+ case 3: return -pi; /* atan(-0,-anything) =-pi */
+ }
+ }
+ /* when x = 0 */
+ if ((ix|lx) == 0)
+ return m&1 ? -pi/2 : pi/2;
+ /* when x is INF */
+ if (ix == 0x7ff00000) {
+ if (iy == 0x7ff00000) {
+ switch(m) {
+ case 0: return pi/4; /* atan(+INF,+INF) */
+ case 1: return -pi/4; /* atan(-INF,+INF) */
+ case 2: return 3*pi/4; /* atan(+INF,-INF) */
+ case 3: return -3*pi/4; /* atan(-INF,-INF) */
+ }
+ } else {
+ switch(m) {
+ case 0: return 0.0; /* atan(+...,+INF) */
+ case 1: return -0.0; /* atan(-...,+INF) */
+ case 2: return pi; /* atan(+...,-INF) */
+ case 3: return -pi; /* atan(-...,-INF) */
+ }
+ }
+ }
+ /* |y/x| > 0x1p64 */
+ if (ix+(64<<20) < iy || iy == 0x7ff00000)
+ return m&1 ? -pi/2 : pi/2;
+
+ /* z = atan(|y/x|) without spurious underflow */
+ if ((m&2) && iy+(64<<20) < ix) /* |y/x| < 0x1p-64, x<0 */
+ z = 0;
+ else
+ z = atan(fabs(y/x));
+ switch (m) {
+ case 0: return z; /* atan(+,+) */
+ case 1: return -z; /* atan(-,+) */
+ case 2: return pi - (z-pi_lo); /* atan(+,-) */
+ default: /* case 3 */
+ return (z-pi_lo) - pi; /* atan(-,-) */
+ }
+}
diff --git a/lib/libc/src/musl-math/atan2f.c b/lib/libc/src/musl-math/atan2f.c
new file mode 100644
index 0000000..c634d00
--- /dev/null
+++ b/lib/libc/src/musl-math/atan2f.c
@@ -0,0 +1,83 @@
+/* origin: FreeBSD /usr/src/lib/msun/src/e_atan2f.c */
+/*
+ * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com.
+ */
+/*
+ * ====================================================
+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ *
+ * Developed at SunPro, a Sun Microsystems, Inc. business.
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ */
+
+#include "libm.h"
+
+static const float
+pi = 3.1415927410e+00, /* 0x40490fdb */
+pi_lo = -8.7422776573e-08; /* 0xb3bbbd2e */
+
+float atan2f(float y, float x)
+{
+ float z;
+ uint32_t m,ix,iy;
+
+ if (isnan(x) || isnan(y))
+ return x+y;
+ GET_FLOAT_WORD(ix, x);
+ GET_FLOAT_WORD(iy, y);
+ if (ix == 0x3f800000) /* x=1.0 */
+ return atanf(y);
+ m = ((iy>>31)&1) | ((ix>>30)&2); /* 2*sign(x)+sign(y) */
+ ix &= 0x7fffffff;
+ iy &= 0x7fffffff;
+
+ /* when y = 0 */
+ if (iy == 0) {
+ switch (m) {
+ case 0:
+ case 1: return y; /* atan(+-0,+anything)=+-0 */
+ case 2: return pi; /* atan(+0,-anything) = pi */
+ case 3: return -pi; /* atan(-0,-anything) =-pi */
+ }
+ }
+ /* when x = 0 */
+ if (ix == 0)
+ return m&1 ? -pi/2 : pi/2;
+ /* when x is INF */
+ if (ix == 0x7f800000) {
+ if (iy == 0x7f800000) {
+ switch (m) {
+ case 0: return pi/4; /* atan(+INF,+INF) */
+ case 1: return -pi/4; /* atan(-INF,+INF) */
+ case 2: return 3*pi/4; /*atan(+INF,-INF)*/
+ case 3: return -3*pi/4; /*atan(-INF,-INF)*/
+ }
+ } else {
+ switch (m) {
+ case 0: return 0.0f; /* atan(+...,+INF) */
+ case 1: return -0.0f; /* atan(-...,+INF) */
+ case 2: return pi; /* atan(+...,-INF) */
+ case 3: return -pi; /* atan(-...,-INF) */
+ }
+ }
+ }
+ /* |y/x| > 0x1p26 */
+ if (ix+(26<<23) < iy || iy == 0x7f800000)
+ return m&1 ? -pi/2 : pi/2;
+
+ /* z = atan(|y/x|) with correct underflow */
+ if ((m&2) && iy+(26<<23) < ix) /*|y/x| < 0x1p-26, x < 0 */
+ z = 0.0;
+ else
+ z = atanf(fabsf(y/x));
+ switch (m) {
+ case 0: return z; /* atan(+,+) */
+ case 1: return -z; /* atan(-,+) */
+ case 2: return pi - (z-pi_lo); /* atan(+,-) */
+ default: /* case 3 */
+ return (z-pi_lo) - pi; /* atan(-,-) */
+ }
+}
diff --git a/lib/libc/src/musl-math/atan2l.c b/lib/libc/src/musl-math/atan2l.c
new file mode 100644
index 0000000..f0937a9
--- /dev/null
+++ b/lib/libc/src/musl-math/atan2l.c
@@ -0,0 +1,85 @@
+/* origin: FreeBSD /usr/src/lib/msun/src/e_atan2l.c */
+/*
+ * ====================================================
+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ *
+ * Developed at SunSoft, a Sun Microsystems, Inc. business.
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ *
+ */
+/*
+ * See comments in atan2.c.
+ * Converted to long double by David Schultz <das@FreeBSD.ORG>.
+ */
+
+#include "libm.h"
+
+#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
+long double atan2l(long double y, long double x)
+{
+ return atan2(y, x);
+}
+#elif (LDBL_MANT_DIG == 64 || LDBL_MANT_DIG == 113) && LDBL_MAX_EXP == 16384
+#include "__invtrigl.h"
+
+long double atan2l(long double y, long double x)
+{
+ union ldshape ux, uy;
+ long double z;
+ int m, ex, ey;
+
+ if (isnan(x) || isnan(y))
+ return x+y;
+ if (x == 1)
+ return atanl(y);
+ ux.f = x;
+ uy.f = y;
+ ex = ux.i.se & 0x7fff;
+ ey = uy.i.se & 0x7fff;
+ m = 2*(ux.i.se>>15) | uy.i.se>>15;
+ if (y == 0) {
+ switch(m) {
+ case 0:
+ case 1: return y; /* atan(+-0,+anything)=+-0 */
+ case 2: return 2*pio2_hi; /* atan(+0,-anything) = pi */
+ case 3: return -2*pio2_hi; /* atan(-0,-anything) =-pi */
+ }
+ }
+ if (x == 0)
+ return m&1 ? -pio2_hi : pio2_hi;
+ if (ex == 0x7fff) {
+ if (ey == 0x7fff) {
+ switch(m) {
+ case 0: return pio2_hi/2; /* atan(+INF,+INF) */
+ case 1: return -pio2_hi/2; /* atan(-INF,+INF) */
+ case 2: return 1.5*pio2_hi; /* atan(+INF,-INF) */
+ case 3: return -1.5*pio2_hi; /* atan(-INF,-INF) */
+ }
+ } else {
+ switch(m) {
+ case 0: return 0.0; /* atan(+...,+INF) */
+ case 1: return -0.0; /* atan(-...,+INF) */
+ case 2: return 2*pio2_hi; /* atan(+...,-INF) */
+ case 3: return -2*pio2_hi; /* atan(-...,-INF) */
+ }
+ }
+ }
+ if (ex+120 < ey || ey == 0x7fff)
+ return m&1 ? -pio2_hi : pio2_hi;
+ /* z = atan(|y/x|) without spurious underflow */
+ if ((m&2) && ey+120 < ex) /* |y/x| < 0x1p-120, x<0 */
+ z = 0.0;
+ else
+ z = atanl(fabsl(y/x));
+ switch (m) {
+ case 0: return z; /* atan(+,+) */
+ case 1: return -z; /* atan(-,+) */
+ case 2: return 2*pio2_hi-(z-2*pio2_lo); /* atan(+,-) */
+ default: /* case 3 */
+ return (z-2*pio2_lo)-2*pio2_hi; /* atan(-,-) */
+ }
+}
+#endif
diff --git a/lib/libc/src/musl-math/atanf.c b/lib/libc/src/musl-math/atanf.c
new file mode 100644
index 0000000..178341b
--- /dev/null
+++ b/lib/libc/src/musl-math/atanf.c
@@ -0,0 +1,94 @@
+/* origin: FreeBSD /usr/src/lib/msun/src/s_atanf.c */
+/*
+ * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com.
+ */
+/*
+ * ====================================================
+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ *
+ * Developed at SunPro, a Sun Microsystems, Inc. business.
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ */
+
+
+#include "libm.h"
+
+static const float atanhi[] = {
+ 4.6364760399e-01, /* atan(0.5)hi 0x3eed6338 */
+ 7.8539812565e-01, /* atan(1.0)hi 0x3f490fda */
+ 9.8279368877e-01, /* atan(1.5)hi 0x3f7b985e */
+ 1.5707962513e+00, /* atan(inf)hi 0x3fc90fda */
+};
+
+static const float atanlo[] = {
+ 5.0121582440e-09, /* atan(0.5)lo 0x31ac3769 */
+ 3.7748947079e-08, /* atan(1.0)lo 0x33222168 */
+ 3.4473217170e-08, /* atan(1.5)lo 0x33140fb4 */
+ 7.5497894159e-08, /* atan(inf)lo 0x33a22168 */
+};
+
+static const float aT[] = {
+ 3.3333328366e-01,
+ -1.9999158382e-01,
+ 1.4253635705e-01,
+ -1.0648017377e-01,
+ 6.1687607318e-02,
+};
+
+float atanf(float x)
+{
+ float_t w,s1,s2,z;
+ uint32_t ix,sign;
+ int id;
+
+ GET_FLOAT_WORD(ix, x);
+ sign = ix>>31;
+ ix &= 0x7fffffff;
+ if (ix >= 0x4c800000) { /* if |x| >= 2**26 */
+ if (isnan(x))
+ return x;
+ z = atanhi[3] + 0x1p-120f;
+ return sign ? -z : z;
+ }
+ if (ix < 0x3ee00000) { /* |x| < 0.4375 */
+ if (ix < 0x39800000) { /* |x| < 2**-12 */
+ if (ix < 0x00800000)
+ /* raise underflow for subnormal x */
+ FORCE_EVAL(x*x);
+ return x;
+ }
+ id = -1;
+ } else {
+ x = fabsf(x);
+ if (ix < 0x3f980000) { /* |x| < 1.1875 */
+ if (ix < 0x3f300000) { /* 7/16 <= |x| < 11/16 */
+ id = 0;
+ x = (2.0f*x - 1.0f)/(2.0f + x);
+ } else { /* 11/16 <= |x| < 19/16 */
+ id = 1;
+ x = (x - 1.0f)/(x + 1.0f);
+ }
+ } else {
+ if (ix < 0x401c0000) { /* |x| < 2.4375 */
+ id = 2;
+ x = (x - 1.5f)/(1.0f + 1.5f*x);
+ } else { /* 2.4375 <= |x| < 2**26 */
+ id = 3;
+ x = -1.0f/x;
+ }
+ }
+ }
+ /* end of argument reduction */
+ z = x*x;
+ w = z*z;
+ /* break sum from i=0 to 10 aT[i]z**(i+1) into odd and even poly */
+ s1 = z*(aT[0]+w*(aT[2]+w*aT[4]));
+ s2 = w*(aT[1]+w*aT[3]);
+ if (id < 0)
+ return x - x*(s1+s2);
+ z = atanhi[id] - ((x*(s1+s2) - atanlo[id]) - x);
+ return sign ? -z : z;
+}
diff --git a/lib/libc/src/musl-math/atanh.c b/lib/libc/src/musl-math/atanh.c
new file mode 100644
index 0000000..63a035d
--- /dev/null
+++ b/lib/libc/src/musl-math/atanh.c
@@ -0,0 +1,29 @@
+#include "libm.h"
+
+/* atanh(x) = log((1+x)/(1-x))/2 = log1p(2x/(1-x))/2 ~= x + x^3/3 + o(x^5) */
+double atanh(double x)
+{
+ union {double f; uint64_t i;} u = {.f = x};
+ unsigned e = u.i >> 52 & 0x7ff;
+ unsigned s = u.i >> 63;
+ double_t y;
+
+ /* |x| */
+ u.i &= (uint64_t)-1/2;
+ y = u.f;
+
+ if (e < 0x3ff - 1) {
+ if (e < 0x3ff - 32) {
+ /* handle underflow */
+ if (e == 0)
+ FORCE_EVAL((float)y);
+ } else {
+ /* |x| < 0.5, up to 1.7ulp error */
+ y = 0.5*log1p(2*y + 2*y*y/(1-y));
+ }
+ } else {
+ /* avoid overflow */
+ y = 0.5*log1p(2*(y/(1-y)));
+ }
+ return s ? -y : y;
+}
diff --git a/lib/libc/src/musl-math/atanhf.c b/lib/libc/src/musl-math/atanhf.c
new file mode 100644
index 0000000..65f07c0
--- /dev/null
+++ b/lib/libc/src/musl-math/atanhf.c
@@ -0,0 +1,28 @@
+#include "libm.h"
+
+/* atanh(x) = log((1+x)/(1-x))/2 = log1p(2x/(1-x))/2 ~= x + x^3/3 + o(x^5) */
+float atanhf(float x)
+{
+ union {float f; uint32_t i;} u = {.f = x};
+ unsigned s = u.i >> 31;
+ float_t y;
+
+ /* |x| */
+ u.i &= 0x7fffffff;
+ y = u.f;
+
+ if (u.i < 0x3f800000 - (1<<23)) {
+ if (u.i < 0x3f800000 - (32<<23)) {
+ /* handle underflow */
+ if (u.i < (1<<23))
+ FORCE_EVAL((float)(y*y));
+ } else {
+ /* |x| < 0.5, up to 1.7ulp error */
+ y = 0.5f*log1pf(2*y + 2*y*y/(1-y));
+ }
+ } else {
+ /* avoid overflow */
+ y = 0.5f*log1pf(2*(y/(1-y)));
+ }
+ return s ? -y : y;
+}
diff --git a/lib/libc/src/musl-math/atanhl.c b/lib/libc/src/musl-math/atanhl.c
new file mode 100644
index 0000000..87cd1cd
--- /dev/null
+++ b/lib/libc/src/musl-math/atanhl.c
@@ -0,0 +1,35 @@
+#include "libm.h"
+
+#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
+long double atanhl(long double x)
+{
+ return atanh(x);
+}
+#elif (LDBL_MANT_DIG == 64 || LDBL_MANT_DIG == 113) && LDBL_MAX_EXP == 16384
+/* atanh(x) = log((1+x)/(1-x))/2 = log1p(2x/(1-x))/2 ~= x + x^3/3 + o(x^5) */
+long double atanhl(long double x)
+{
+ union ldshape u = {x};
+ unsigned e = u.i.se & 0x7fff;
+ unsigned s = u.i.se >> 15;
+
+ /* |x| */
+ u.i.se = e;
+ x = u.f;
+
+ if (e < 0x3ff - 1) {
+ if (e < 0x3ff - LDBL_MANT_DIG/2) {
+ /* handle underflow */
+ if (e == 0)
+ FORCE_EVAL((float)x);
+ } else {
+ /* |x| < 0.5, up to 1.7ulp error */
+ x = 0.5*log1pl(2*x + 2*x*x/(1-x));
+ }
+ } else {
+ /* avoid overflow */
+ x = 0.5*log1pl(2*(x/(1-x)));
+ }
+ return s ? -x : x;
+}
+#endif
diff --git a/lib/libc/src/musl-math/atanl.c b/lib/libc/src/musl-math/atanl.c
new file mode 100644
index 0000000..79a3edb
--- /dev/null
+++ b/lib/libc/src/musl-math/atanl.c
@@ -0,0 +1,184 @@
+/* origin: FreeBSD /usr/src/lib/msun/src/s_atanl.c */
+/*
+ * ====================================================
+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ *
+ * Developed at SunPro, a Sun Microsystems, Inc. business.
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ */
+/*
+ * See comments in atan.c.
+ * Converted to long double by David Schultz <das@FreeBSD.ORG>.
+ */
+
+#include "libm.h"
+
+#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
+long double atanl(long double x)
+{
+ return atan(x);
+}
+#elif (LDBL_MANT_DIG == 64 || LDBL_MANT_DIG == 113) && LDBL_MAX_EXP == 16384
+
+#if LDBL_MANT_DIG == 64
+#define EXPMAN(u) ((u.i.se & 0x7fff)<<8 | (u.i.m>>55 & 0xff))
+
+static const long double atanhi[] = {
+ 4.63647609000806116202e-01L,
+ 7.85398163397448309628e-01L,
+ 9.82793723247329067960e-01L,
+ 1.57079632679489661926e+00L,
+};
+
+static const long double atanlo[] = {
+ 1.18469937025062860669e-20L,
+ -1.25413940316708300586e-20L,
+ 2.55232234165405176172e-20L,
+ -2.50827880633416601173e-20L,
+};
+
+static const long double aT[] = {
+ 3.33333333333333333017e-01L,
+ -1.99999999999999632011e-01L,
+ 1.42857142857046531280e-01L,
+ -1.11111111100562372733e-01L,
+ 9.09090902935647302252e-02L,
+ -7.69230552476207730353e-02L,
+ 6.66661718042406260546e-02L,
+ -5.88158892835030888692e-02L,
+ 5.25499891539726639379e-02L,
+ -4.70119845393155721494e-02L,
+ 4.03539201366454414072e-02L,
+ -2.91303858419364158725e-02L,
+ 1.24822046299269234080e-02L,
+};
+
+static long double T_even(long double x)
+{
+ return aT[0] + x * (aT[2] + x * (aT[4] + x * (aT[6] +
+ x * (aT[8] + x * (aT[10] + x * aT[12])))));
+}
+
+static long double T_odd(long double x)
+{
+ return aT[1] + x * (aT[3] + x * (aT[5] + x * (aT[7] +
+ x * (aT[9] + x * aT[11]))));
+}
+#elif LDBL_MANT_DIG == 113
+#define EXPMAN(u) ((u.i.se & 0x7fff)<<8 | u.i.top>>8)
+
+const long double atanhi[] = {
+ 4.63647609000806116214256231461214397e-01L,
+ 7.85398163397448309615660845819875699e-01L,
+ 9.82793723247329067985710611014666038e-01L,
+ 1.57079632679489661923132169163975140e+00L,
+};
+
+const long double atanlo[] = {
+ 4.89509642257333492668618435220297706e-36L,
+ 2.16795253253094525619926100651083806e-35L,
+ -2.31288434538183565909319952098066272e-35L,
+ 4.33590506506189051239852201302167613e-35L,
+};
+
+const long double aT[] = {
+ 3.33333333333333333333333333333333125e-01L,
+ -1.99999999999999999999999999999180430e-01L,
+ 1.42857142857142857142857142125269827e-01L,
+ -1.11111111111111111111110834490810169e-01L,
+ 9.09090909090909090908522355708623681e-02L,
+ -7.69230769230769230696553844935357021e-02L,
+ 6.66666666666666660390096773046256096e-02L,
+ -5.88235294117646671706582985209643694e-02L,
+ 5.26315789473666478515847092020327506e-02L,
+ -4.76190476189855517021024424991436144e-02L,
+ 4.34782608678695085948531993458097026e-02L,
+ -3.99999999632663469330634215991142368e-02L,
+ 3.70370363987423702891250829918659723e-02L,
+ -3.44827496515048090726669907612335954e-02L,
+ 3.22579620681420149871973710852268528e-02L,
+ -3.03020767654269261041647570626778067e-02L,
+ 2.85641979882534783223403715930946138e-02L,
+ -2.69824879726738568189929461383741323e-02L,
+ 2.54194698498808542954187110873675769e-02L,
+ -2.35083879708189059926183138130183215e-02L,
+ 2.04832358998165364349957325067131428e-02L,
+ -1.54489555488544397858507248612362957e-02L,
+ 8.64492360989278761493037861575248038e-03L,
+ -2.58521121597609872727919154569765469e-03L,
+};
+
+static long double T_even(long double x)
+{
+ return (aT[0] + x * (aT[2] + x * (aT[4] + x * (aT[6] + x * (aT[8] +
+ x * (aT[10] + x * (aT[12] + x * (aT[14] + x * (aT[16] +
+ x * (aT[18] + x * (aT[20] + x * aT[22])))))))))));
+}
+
+static long double T_odd(long double x)
+{
+ return (aT[1] + x * (aT[3] + x * (aT[5] + x * (aT[7] + x * (aT[9] +
+ x * (aT[11] + x * (aT[13] + x * (aT[15] + x * (aT[17] +
+ x * (aT[19] + x * (aT[21] + x * aT[23])))))))))));
+}
+#endif
+
+long double atanl(long double x)
+{
+ union ldshape u = {x};
+ long double w, s1, s2, z;
+ int id;
+ unsigned e = u.i.se & 0x7fff;
+ unsigned sign = u.i.se >> 15;
+ unsigned expman;
+
+ if (e >= 0x3fff + LDBL_MANT_DIG + 1) { /* if |x| is large, atan(x)~=pi/2 */
+ if (isnan(x))
+ return x;
+ return sign ? -atanhi[3] : atanhi[3];
+ }
+ /* Extract the exponent and the first few bits of the mantissa. */
+ expman = EXPMAN(u);
+ if (expman < ((0x3fff - 2) << 8) + 0xc0) { /* |x| < 0.4375 */
+ if (e < 0x3fff - (LDBL_MANT_DIG+1)/2) { /* if |x| is small, atanl(x)~=x */
+ /* raise underflow if subnormal */
+ if (e == 0)
+ FORCE_EVAL((float)x);
+ return x;
+ }
+ id = -1;
+ } else {
+ x = fabsl(x);
+ if (expman < (0x3fff << 8) + 0x30) { /* |x| < 1.1875 */
+ if (expman < ((0x3fff - 1) << 8) + 0x60) { /* 7/16 <= |x| < 11/16 */
+ id = 0;
+ x = (2.0*x-1.0)/(2.0+x);
+ } else { /* 11/16 <= |x| < 19/16 */
+ id = 1;
+ x = (x-1.0)/(x+1.0);
+ }
+ } else {
+ if (expman < ((0x3fff + 1) << 8) + 0x38) { /* |x| < 2.4375 */
+ id = 2;
+ x = (x-1.5)/(1.0+1.5*x);
+ } else { /* 2.4375 <= |x| */
+ id = 3;
+ x = -1.0/x;
+ }
+ }
+ }
+ /* end of argument reduction */
+ z = x*x;
+ w = z*z;
+ /* break sum aT[i]z**(i+1) into odd and even poly */
+ s1 = z*T_even(w);
+ s2 = w*T_odd(w);
+ if (id < 0)
+ return x - x*(s1+s2);
+ z = atanhi[id] - ((x*(s1+s2) - atanlo[id]) - x);
+ return sign ? -z : z;
+}
+#endif
diff --git a/lib/libc/src/musl-math/cbrt.c b/lib/libc/src/musl-math/cbrt.c
new file mode 100644
index 0000000..7599d3e
--- /dev/null
+++ b/lib/libc/src/musl-math/cbrt.c
@@ -0,0 +1,103 @@
+/* origin: FreeBSD /usr/src/lib/msun/src/s_cbrt.c */
+/*
+ * ====================================================
+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ *
+ * Developed at SunPro, a Sun Microsystems, Inc. business.
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ *
+ * Optimized by Bruce D. Evans.
+ */
+/* cbrt(x)
+ * Return cube root of x
+ */
+
+#include <math.h>
+#include <stdint.h>
+
+static const uint32_t
+B1 = 715094163, /* B1 = (1023-1023/3-0.03306235651)*2**20 */
+B2 = 696219795; /* B2 = (1023-1023/3-54/3-0.03306235651)*2**20 */
+
+/* |1/cbrt(x) - p(x)| < 2**-23.5 (~[-7.93e-8, 7.929e-8]). */
+static const double
+P0 = 1.87595182427177009643, /* 0x3ffe03e6, 0x0f61e692 */
+P1 = -1.88497979543377169875, /* 0xbffe28e0, 0x92f02420 */
+P2 = 1.621429720105354466140, /* 0x3ff9f160, 0x4a49d6c2 */
+P3 = -0.758397934778766047437, /* 0xbfe844cb, 0xbee751d9 */
+P4 = 0.145996192886612446982; /* 0x3fc2b000, 0xd4e4edd7 */
+
+double cbrt(double x)
+{
+ union {double f; uint64_t i;} u = {x};
+ double_t r,s,t,w;
+ uint32_t hx = u.i>>32 & 0x7fffffff;
+
+ if (hx >= 0x7ff00000) /* cbrt(NaN,INF) is itself */
+ return x+x;
+
+ /*
+ * Rough cbrt to 5 bits:
+ * cbrt(2**e*(1+m) ~= 2**(e/3)*(1+(e%3+m)/3)
+ * where e is integral and >= 0, m is real and in [0, 1), and "/" and
+ * "%" are integer division and modulus with rounding towards minus
+ * infinity. The RHS is always >= the LHS and has a maximum relative
+ * error of about 1 in 16. Adding a bias of -0.03306235651 to the
+ * (e%3+m)/3 term reduces the error to about 1 in 32. With the IEEE
+ * floating point representation, for finite positive normal values,
+ * ordinary integer divison of the value in bits magically gives
+ * almost exactly the RHS of the above provided we first subtract the
+ * exponent bias (1023 for doubles) and later add it back. We do the
+ * subtraction virtually to keep e >= 0 so that ordinary integer
+ * division rounds towards minus infinity; this is also efficient.
+ */
+ if (hx < 0x00100000) { /* zero or subnormal? */
+ u.f = x*0x1p54;
+ hx = u.i>>32 & 0x7fffffff;
+ if (hx == 0)
+ return x; /* cbrt(0) is itself */
+ hx = hx/3 + B2;
+ } else
+ hx = hx/3 + B1;
+ u.i &= 1ULL<<63;
+ u.i |= (uint64_t)hx << 32;
+ t = u.f;
+
+ /*
+ * New cbrt to 23 bits:
+ * cbrt(x) = t*cbrt(x/t**3) ~= t*P(t**3/x)
+ * where P(r) is a polynomial of degree 4 that approximates 1/cbrt(r)
+ * to within 2**-23.5 when |r - 1| < 1/10. The rough approximation
+ * has produced t such than |t/cbrt(x) - 1| ~< 1/32, and cubing this
+ * gives us bounds for r = t**3/x.
+ *
+ * Try to optimize for parallel evaluation as in __tanf.c.
+ */
+ r = (t*t)*(t/x);
+ t = t*((P0+r*(P1+r*P2))+((r*r)*r)*(P3+r*P4));
+
+ /*
+ * Round t away from zero to 23 bits (sloppily except for ensuring that
+ * the result is larger in magnitude than cbrt(x) but not much more than
+ * 2 23-bit ulps larger). With rounding towards zero, the error bound
+ * would be ~5/6 instead of ~4/6. With a maximum error of 2 23-bit ulps
+ * in the rounded t, the infinite-precision error in the Newton
+ * approximation barely affects third digit in the final error
+ * 0.667; the error in the rounded t can be up to about 3 23-bit ulps
+ * before the final error is larger than 0.667 ulps.
+ */
+ u.f = t;
+ u.i = (u.i + 0x80000000) & 0xffffffffc0000000ULL;
+ t = u.f;
+
+ /* one step Newton iteration to 53 bits with error < 0.667 ulps */
+ s = t*t; /* t*t is exact */
+ r = x/s; /* error <= 0.5 ulps; |r| < |t| */
+ w = t+t; /* t+t is exact */
+ r = (r-t)/(w+r); /* r-t is exact; w+r ~= 3*t */
+ t = t+t*r; /* error <= 0.5 + 0.5/3 + epsilon */
+ return t;
+}
diff --git a/lib/libc/src/musl-math/cbrtf.c b/lib/libc/src/musl-math/cbrtf.c
new file mode 100644
index 0000000..89c2c86
--- /dev/null
+++ b/lib/libc/src/musl-math/cbrtf.c
@@ -0,0 +1,66 @@
+/* origin: FreeBSD /usr/src/lib/msun/src/s_cbrtf.c */
+/*
+ * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com.
+ * Debugged and optimized by Bruce D. Evans.
+ */
+/*
+ * ====================================================
+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ *
+ * Developed at SunPro, a Sun Microsystems, Inc. business.
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ */
+/* cbrtf(x)
+ * Return cube root of x
+ */
+
+#include <math.h>
+#include <stdint.h>
+
+static const unsigned
+B1 = 709958130, /* B1 = (127-127.0/3-0.03306235651)*2**23 */
+B2 = 642849266; /* B2 = (127-127.0/3-24/3-0.03306235651)*2**23 */
+
+float cbrtf(float x)
+{
+ double_t r,T;
+ union {float f; uint32_t i;} u = {x};
+ uint32_t hx = u.i & 0x7fffffff;
+
+ if (hx >= 0x7f800000) /* cbrt(NaN,INF) is itself */
+ return x + x;
+
+ /* rough cbrt to 5 bits */
+ if (hx < 0x00800000) { /* zero or subnormal? */
+ if (hx == 0)
+ return x; /* cbrt(+-0) is itself */
+ u.f = x*0x1p24f;
+ hx = u.i & 0x7fffffff;
+ hx = hx/3 + B2;
+ } else
+ hx = hx/3 + B1;
+ u.i &= 0x80000000;
+ u.i |= hx;
+
+ /*
+ * First step Newton iteration (solving t*t-x/t == 0) to 16 bits. In
+ * double precision so that its terms can be arranged for efficiency
+ * without causing overflow or underflow.
+ */
+ T = u.f;
+ r = T*T*T;
+ T = T*((double_t)x+x+r)/(x+r+r);
+
+ /*
+ * Second step Newton iteration to 47 bits. In double precision for
+ * efficiency and accuracy.
+ */
+ r = T*T*T;
+ T = T*((double_t)x+x+r)/(x+r+r);
+
+ /* rounding to 24 bits is perfect in round-to-nearest mode */
+ return T;
+}
diff --git a/lib/libc/src/musl-math/cbrtl.c b/lib/libc/src/musl-math/cbrtl.c
new file mode 100644
index 0000000..ceff913
--- /dev/null
+++ b/lib/libc/src/musl-math/cbrtl.c
@@ -0,0 +1,124 @@
+/* origin: FreeBSD /usr/src/lib/msun/src/s_cbrtl.c */
+/*-
+ * ====================================================
+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ * Copyright (c) 2009-2011, Bruce D. Evans, Steven G. Kargl, David Schultz.
+ *
+ * Developed at SunPro, a Sun Microsystems, Inc. business.
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ *
+ * The argument reduction and testing for exceptional cases was
+ * written by Steven G. Kargl with input from Bruce D. Evans
+ * and David A. Schultz.
+ */
+
+#include "libm.h"
+
+#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
+long double cbrtl(long double x)
+{
+ return cbrt(x);
+}
+#elif (LDBL_MANT_DIG == 64 || LDBL_MANT_DIG == 113) && LDBL_MAX_EXP == 16384
+static const unsigned B1 = 709958130; /* B1 = (127-127.0/3-0.03306235651)*2**23 */
+
+long double cbrtl(long double x)
+{
+ union ldshape u = {x}, v;
+ union {float f; uint32_t i;} uft;
+ long double r, s, t, w;
+ double_t dr, dt, dx;
+ float_t ft;
+ int e = u.i.se & 0x7fff;
+ int sign = u.i.se & 0x8000;
+
+ /*
+ * If x = +-Inf, then cbrt(x) = +-Inf.
+ * If x = NaN, then cbrt(x) = NaN.
+ */
+ if (e == 0x7fff)
+ return x + x;
+ if (e == 0) {
+ /* Adjust subnormal numbers. */
+ u.f *= 0x1p120;
+ e = u.i.se & 0x7fff;
+ /* If x = +-0, then cbrt(x) = +-0. */
+ if (e == 0)
+ return x;
+ e -= 120;
+ }
+ e -= 0x3fff;
+ u.i.se = 0x3fff;
+ x = u.f;
+ switch (e % 3) {
+ case 1:
+ case -2:
+ x *= 2;
+ e--;
+ break;
+ case 2:
+ case -1:
+ x *= 4;
+ e -= 2;
+ break;
+ }
+ v.f = 1.0;
+ v.i.se = sign | (0x3fff + e/3);
+
+ /*
+ * The following is the guts of s_cbrtf, with the handling of
+ * special values removed and extra care for accuracy not taken,
+ * but with most of the extra accuracy not discarded.
+ */
+
+ /* ~5-bit estimate: */
+ uft.f = x;
+ uft.i = (uft.i & 0x7fffffff)/3 + B1;
+ ft = uft.f;
+
+ /* ~16-bit estimate: */
+ dx = x;
+ dt = ft;
+ dr = dt * dt * dt;
+ dt = dt * (dx + dx + dr) / (dx + dr + dr);
+
+ /* ~47-bit estimate: */
+ dr = dt * dt * dt;
+ dt = dt * (dx + dx + dr) / (dx + dr + dr);
+
+#if LDBL_MANT_DIG == 64
+ /*
+ * dt is cbrtl(x) to ~47 bits (after x has been reduced to 1 <= x < 8).
+ * Round it away from zero to 32 bits (32 so that t*t is exact, and
+ * away from zero for technical reasons).
+ */
+ t = dt + (0x1.0p32L + 0x1.0p-31L) - 0x1.0p32;
+#elif LDBL_MANT_DIG == 113
+ /*
+ * Round dt away from zero to 47 bits. Since we don't trust the 47,
+ * add 2 47-bit ulps instead of 1 to round up. Rounding is slow and
+ * might be avoidable in this case, since on most machines dt will
+ * have been evaluated in 53-bit precision and the technical reasons
+ * for rounding up might not apply to either case in cbrtl() since
+ * dt is much more accurate than needed.
+ */
+ t = dt + 0x2.0p-46 + 0x1.0p60L - 0x1.0p60;
+#endif
+
+ /*
+ * Final step Newton iteration to 64 or 113 bits with
+ * error < 0.667 ulps
+ */
+ s = t*t; /* t*t is exact */
+ r = x/s; /* error <= 0.5 ulps; |r| < |t| */
+ w = t+t; /* t+t is exact */
+ r = (r-t)/(w+r); /* r-t is exact; w+r ~= 3*t */
+ t = t+t*r; /* error <= 0.5 + 0.5/3 + epsilon */
+
+ t *= v.f;
+ return t;
+}
+#endif
diff --git a/lib/libc/src/musl-math/ceil.c b/lib/libc/src/musl-math/ceil.c
new file mode 100644
index 0000000..b13e6f2
--- /dev/null
+++ b/lib/libc/src/musl-math/ceil.c
@@ -0,0 +1,31 @@
+#include "libm.h"
+
+#if FLT_EVAL_METHOD==0 || FLT_EVAL_METHOD==1
+#define EPS DBL_EPSILON
+#elif FLT_EVAL_METHOD==2
+#define EPS LDBL_EPSILON
+#endif
+static const double_t toint = 1/EPS;
+
+double ceil(double x)
+{
+ union {double f; uint64_t i;} u = {x};
+ int e = u.i >> 52 & 0x7ff;
+ double_t y;
+
+ if (e >= 0x3ff+52 || x == 0)
+ return x;
+ /* y = int(x) - x, where int(x) is an integer neighbor of x */
+ if (u.i >> 63)
+ y = x - toint + toint - x;
+ else
+ y = x + toint - toint - x;
+ /* special case because of non-nearest rounding modes */
+ if (e <= 0x3ff-1) {
+ FORCE_EVAL(y);
+ return u.i >> 63 ? -0.0 : 1;
+ }
+ if (y < 0)
+ return x + y + 1;
+ return x + y;
+}
diff --git a/lib/libc/src/musl-math/ceilf.c b/lib/libc/src/musl-math/ceilf.c
new file mode 100644
index 0000000..869835f
--- /dev/null
+++ b/lib/libc/src/musl-math/ceilf.c
@@ -0,0 +1,27 @@
+#include "libm.h"
+
+float ceilf(float x)
+{
+ union {float f; uint32_t i;} u = {x};
+ int e = (int)(u.i >> 23 & 0xff) - 0x7f;
+ uint32_t m;
+
+ if (e >= 23)
+ return x;
+ if (e >= 0) {
+ m = 0x007fffff >> e;
+ if ((u.i & m) == 0)
+ return x;
+ FORCE_EVAL(x + 0x1p120f);
+ if (u.i >> 31 == 0)
+ u.i += m;
+ u.i &= ~m;
+ } else {
+ FORCE_EVAL(x + 0x1p120f);
+ if (u.i >> 31)
+ u.f = -0.0;
+ else if (u.i << 1)
+ u.f = 1.0;
+ }
+ return u.f;
+}
diff --git a/lib/libc/src/musl-math/ceill.c b/lib/libc/src/musl-math/ceill.c
new file mode 100644
index 0000000..60a8302
--- /dev/null
+++ b/lib/libc/src/musl-math/ceill.c
@@ -0,0 +1,34 @@
+#include "libm.h"
+
+#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
+long double ceill(long double x)
+{
+ return ceil(x);
+}
+#elif (LDBL_MANT_DIG == 64 || LDBL_MANT_DIG == 113) && LDBL_MAX_EXP == 16384
+
+static const long double toint = 1/LDBL_EPSILON;
+
+long double ceill(long double x)
+{
+ union ldshape u = {x};
+ int e = u.i.se & 0x7fff;
+ long double y;
+
+ if (e >= 0x3fff+LDBL_MANT_DIG-1 || x == 0)
+ return x;
+ /* y = int(x) - x, where int(x) is an integer neighbor of x */
+ if (u.i.se >> 15)
+ y = x - toint + toint - x;
+ else
+ y = x + toint - toint - x;
+ /* special case because of non-nearest rounding modes */
+ if (e <= 0x3fff-1) {
+ FORCE_EVAL(y);
+ return u.i.se >> 15 ? -0.0 : 1;
+ }
+ if (y < 0)
+ return x + y + 1;
+ return x + y;
+}
+#endif
diff --git a/lib/libc/src/musl-math/copysign.c b/lib/libc/src/musl-math/copysign.c
new file mode 100644
index 0000000..b09331b
--- /dev/null
+++ b/lib/libc/src/musl-math/copysign.c
@@ -0,0 +1,8 @@
+#include "libm.h"
+
+double copysign(double x, double y) {
+ union {double f; uint64_t i;} ux={x}, uy={y};
+ ux.i &= -1ULL/2;
+ ux.i |= uy.i & 1ULL<<63;
+ return ux.f;
+}
diff --git a/lib/libc/src/musl-math/copysignf.c b/lib/libc/src/musl-math/copysignf.c
new file mode 100644
index 0000000..0af6ae9
--- /dev/null
+++ b/lib/libc/src/musl-math/copysignf.c
@@ -0,0 +1,10 @@
+#include <math.h>
+#include <stdint.h>
+
+float copysignf(float x, float y)
+{
+ union {float f; uint32_t i;} ux={x}, uy={y};
+ ux.i &= 0x7fffffff;
+ ux.i |= uy.i & 0x80000000;
+ return ux.f;
+}
diff --git a/lib/libc/src/musl-math/copysignl.c b/lib/libc/src/musl-math/copysignl.c
new file mode 100644
index 0000000..9dd933c
--- /dev/null
+++ b/lib/libc/src/musl-math/copysignl.c
@@ -0,0 +1,16 @@
+#include "libm.h"
+
+#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
+long double copysignl(long double x, long double y)
+{
+ return copysign(x, y);
+}
+#elif (LDBL_MANT_DIG == 64 || LDBL_MANT_DIG == 113) && LDBL_MAX_EXP == 16384
+long double copysignl(long double x, long double y)
+{
+ union ldshape ux = {x}, uy = {y};
+ ux.i.se &= 0x7fff;
+ ux.i.se |= uy.i.se & 0x8000;
+ return ux.f;
+}
+#endif
diff --git a/lib/libc/src/musl-math/cos.c b/lib/libc/src/musl-math/cos.c
new file mode 100644
index 0000000..ee97f68
--- /dev/null
+++ b/lib/libc/src/musl-math/cos.c
@@ -0,0 +1,77 @@
+/* origin: FreeBSD /usr/src/lib/msun/src/s_cos.c */
+/*
+ * ====================================================
+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ *
+ * Developed at SunPro, a Sun Microsystems, Inc. business.
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ */
+/* cos(x)
+ * Return cosine function of x.
+ *
+ * kernel function:
+ * __sin ... sine function on [-pi/4,pi/4]
+ * __cos ... cosine function on [-pi/4,pi/4]
+ * __rem_pio2 ... argument reduction routine
+ *
+ * Method.
+ * Let S,C and T denote the sin, cos and tan respectively on
+ * [-PI/4, +PI/4]. Reduce the argument x to y1+y2 = x-k*pi/2
+ * in [-pi/4 , +pi/4], and let n = k mod 4.
+ * We have
+ *
+ * n sin(x) cos(x) tan(x)
+ * ----------------------------------------------------------
+ * 0 S C T
+ * 1 C -S -1/T
+ * 2 -S -C T
+ * 3 -C S -1/T
+ * ----------------------------------------------------------
+ *
+ * Special cases:
+ * Let trig be any of sin, cos, or tan.
+ * trig(+-INF) is NaN, with signals;
+ * trig(NaN) is that NaN;
+ *
+ * Accuracy:
+ * TRIG(x) returns trig(x) nearly rounded
+ */
+
+#include "libm.h"
+
+double cos(double x)
+{
+ double y[2];
+ uint32_t ix;
+ unsigned n;
+
+ GET_HIGH_WORD(ix, x);
+ ix &= 0x7fffffff;
+
+ /* |x| ~< pi/4 */
+ if (ix <= 0x3fe921fb) {
+ if (ix < 0x3e46a09e) { /* |x| < 2**-27 * sqrt(2) */
+ /* raise inexact if x!=0 */
+ FORCE_EVAL(x + 0x1p120f);
+ return 1.0;
+ }
+ return __cos(x, 0);
+ }
+
+ /* cos(Inf or NaN) is NaN */
+ if (ix >= 0x7ff00000)
+ return x-x;
+
+ /* argument reduction */
+ n = __rem_pio2(x, y);
+ switch (n&3) {
+ case 0: return __cos(y[0], y[1]);
+ case 1: return -__sin(y[0], y[1], 1);
+ case 2: return -__cos(y[0], y[1]);
+ default:
+ return __sin(y[0], y[1], 1);
+ }
+}
diff --git a/lib/libc/src/musl-math/cosf.c b/lib/libc/src/musl-math/cosf.c
new file mode 100644
index 0000000..23f3e5b
--- /dev/null
+++ b/lib/libc/src/musl-math/cosf.c
@@ -0,0 +1,78 @@
+/* origin: FreeBSD /usr/src/lib/msun/src/s_cosf.c */
+/*
+ * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com.
+ * Optimized by Bruce D. Evans.
+ */
+/*
+ * ====================================================
+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ *
+ * Developed at SunPro, a Sun Microsystems, Inc. business.
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ */
+
+#include "libm.h"
+
+/* Small multiples of pi/2 rounded to double precision. */
+static const double
+c1pio2 = 1*M_PI_2, /* 0x3FF921FB, 0x54442D18 */
+c2pio2 = 2*M_PI_2, /* 0x400921FB, 0x54442D18 */
+c3pio2 = 3*M_PI_2, /* 0x4012D97C, 0x7F3321D2 */
+c4pio2 = 4*M_PI_2; /* 0x401921FB, 0x54442D18 */
+
+float cosf(float x)
+{
+ double y;
+ uint32_t ix;
+ unsigned n, sign;
+
+ GET_FLOAT_WORD(ix, x);
+ sign = ix >> 31;
+ ix &= 0x7fffffff;
+
+ if (ix <= 0x3f490fda) { /* |x| ~<= pi/4 */
+ if (ix < 0x39800000) { /* |x| < 2**-12 */
+ /* raise inexact if x != 0 */
+ FORCE_EVAL(x + 0x1p120f);
+ return 1.0f;
+ }
+ return __cosdf(x);
+ }
+ if (ix <= 0x407b53d1) { /* |x| ~<= 5*pi/4 */
+ if (ix > 0x4016cbe3) /* |x| ~> 3*pi/4 */
+ return -__cosdf(sign ? x+c2pio2 : x-c2pio2);
+ else {
+ if (sign)
+ return __sindf(x + c1pio2);
+ else
+ return __sindf(c1pio2 - x);
+ }
+ }
+ if (ix <= 0x40e231d5) { /* |x| ~<= 9*pi/4 */
+ if (ix > 0x40afeddf) /* |x| ~> 7*pi/4 */
+ return __cosdf(sign ? x+c4pio2 : x-c4pio2);
+ else {
+ if (sign)
+ return __sindf(-x - c3pio2);
+ else
+ return __sindf(x - c3pio2);
+ }
+ }
+
+ /* cos(Inf or NaN) is NaN */
+ if (ix >= 0x7f800000)
+ return x-x;
+
+ /* general argument reduction needed */
+ n = __rem_pio2f(x,&y);
+ switch (n&3) {
+ case 0: return __cosdf(y);
+ case 1: return __sindf(-y);
+ case 2: return -__cosdf(y);
+ default:
+ return __sindf(y);
+ }
+}
diff --git a/lib/libc/src/musl-math/cosh.c b/lib/libc/src/musl-math/cosh.c
new file mode 100644
index 0000000..100f823
--- /dev/null
+++ b/lib/libc/src/musl-math/cosh.c
@@ -0,0 +1,40 @@
+#include "libm.h"
+
+/* cosh(x) = (exp(x) + 1/exp(x))/2
+ * = 1 + 0.5*(exp(x)-1)*(exp(x)-1)/exp(x)
+ * = 1 + x*x/2 + o(x^4)
+ */
+double cosh(double x)
+{
+ union {double f; uint64_t i;} u = {.f = x};
+ uint32_t w;
+ double t;
+
+ /* |x| */
+ u.i &= (uint64_t)-1/2;
+ x = u.f;
+ w = u.i >> 32;
+
+ /* |x| < log(2) */
+ if (w < 0x3fe62e42) {
+ if (w < 0x3ff00000 - (26<<20)) {
+ /* raise inexact if x!=0 */
+ FORCE_EVAL(x + 0x1p120f);
+ return 1;
+ }
+ t = expm1(x);
+ return 1 + t*t/(2*(1+t));
+ }
+
+ /* |x| < log(DBL_MAX) */
+ if (w < 0x40862e42) {
+ t = exp(x);
+ /* note: if x>log(0x1p26) then the 1/t is not needed */
+ return 0.5*(t + 1/t);
+ }
+
+ /* |x| > log(DBL_MAX) or nan */
+ /* note: the result is stored to handle overflow */
+ t = __expo2(x);
+ return t;
+}
diff --git a/lib/libc/src/musl-math/coshf.c b/lib/libc/src/musl-math/coshf.c
new file mode 100644
index 0000000..b09f2ee
--- /dev/null
+++ b/lib/libc/src/musl-math/coshf.c
@@ -0,0 +1,33 @@
+#include "libm.h"
+
+float coshf(float x)
+{
+ union {float f; uint32_t i;} u = {.f = x};
+ uint32_t w;
+ float t;
+
+ /* |x| */
+ u.i &= 0x7fffffff;
+ x = u.f;
+ w = u.i;
+
+ /* |x| < log(2) */
+ if (w < 0x3f317217) {
+ if (w < 0x3f800000 - (12<<23)) {
+ FORCE_EVAL(x + 0x1p120f);
+ return 1;
+ }
+ t = expm1f(x);
+ return 1 + t*t/(2*(1+t));
+ }
+
+ /* |x| < log(FLT_MAX) */
+ if (w < 0x42b17217) {
+ t = expf(x);
+ return 0.5f*(t + 1/t);
+ }
+
+ /* |x| > log(FLT_MAX) or nan */
+ t = __expo2f(x);
+ return t;
+}
diff --git a/lib/libc/src/musl-math/coshl.c b/lib/libc/src/musl-math/coshl.c
new file mode 100644
index 0000000..06a56fe
--- /dev/null
+++ b/lib/libc/src/musl-math/coshl.c
@@ -0,0 +1,47 @@
+#include "libm.h"
+
+#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
+long double coshl(long double x)
+{
+ return cosh(x);
+}
+#elif LDBL_MANT_DIG == 64 && LDBL_MAX_EXP == 16384
+long double coshl(long double x)
+{
+ union ldshape u = {x};
+ unsigned ex = u.i.se & 0x7fff;
+ uint32_t w;
+ long double t;
+
+ /* |x| */
+ u.i.se = ex;
+ x = u.f;
+ w = u.i.m >> 32;
+
+ /* |x| < log(2) */
+ if (ex < 0x3fff-1 || (ex == 0x3fff-1 && w < 0xb17217f7)) {
+ if (ex < 0x3fff-32) {
+ FORCE_EVAL(x + 0x1p120f);
+ return 1;
+ }
+ t = expm1l(x);
+ return 1 + t*t/(2*(1+t));
+ }
+
+ /* |x| < log(LDBL_MAX) */
+ if (ex < 0x3fff+13 || (ex == 0x3fff+13 && w < 0xb17217f7)) {
+ t = expl(x);
+ return 0.5*(t + 1/t);
+ }
+
+ /* |x| > log(LDBL_MAX) or nan */
+ t = expl(0.5*x);
+ return 0.5*t*t;
+}
+#elif LDBL_MANT_DIG == 113 && LDBL_MAX_EXP == 16384
+// TODO: broken implementation to make things compile
+long double coshl(long double x)
+{
+ return cosh(x);
+}
+#endif
diff --git a/lib/libc/src/musl-math/cosl.c b/lib/libc/src/musl-math/cosl.c
new file mode 100644
index 0000000..79c41c7
--- /dev/null
+++ b/lib/libc/src/musl-math/cosl.c
@@ -0,0 +1,39 @@
+#include "libm.h"
+
+#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
+long double cosl(long double x) {
+ return cos(x);
+}
+#elif (LDBL_MANT_DIG == 64 || LDBL_MANT_DIG == 113) && LDBL_MAX_EXP == 16384
+long double cosl(long double x)
+{
+ union ldshape u = {x};
+ unsigned n;
+ long double y[2], hi, lo;
+
+ u.i.se &= 0x7fff;
+ if (u.i.se == 0x7fff)
+ return x - x;
+ x = u.f;
+ if (x < M_PI_4) {
+ if (u.i.se < 0x3fff - LDBL_MANT_DIG)
+ /* raise inexact if x!=0 */
+ return 1.0 + x;
+ return __cosl(x, 0);
+ }
+ n = __rem_pio2l(x, y);
+ hi = y[0];
+ lo = y[1];
+ switch (n & 3) {
+ case 0:
+ return __cosl(hi, lo);
+ case 1:
+ return -__sinl(hi, lo, 1);
+ case 2:
+ return -__cosl(hi, lo);
+ case 3:
+ default:
+ return __sinl(hi, lo, 1);
+ }
+}
+#endif
diff --git a/lib/libc/src/musl-math/erf.c b/lib/libc/src/musl-math/erf.c
new file mode 100644
index 0000000..2f30a29
--- /dev/null
+++ b/lib/libc/src/musl-math/erf.c
@@ -0,0 +1,273 @@
+/* origin: FreeBSD /usr/src/lib/msun/src/s_erf.c */
+/*
+ * ====================================================
+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ *
+ * Developed at SunPro, a Sun Microsystems, Inc. business.
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ */
+/* double erf(double x)
+ * double erfc(double x)
+ * x
+ * 2 |\
+ * erf(x) = --------- | exp(-t*t)dt
+ * sqrt(pi) \|
+ * 0
+ *
+ * erfc(x) = 1-erf(x)
+ * Note that
+ * erf(-x) = -erf(x)
+ * erfc(-x) = 2 - erfc(x)
+ *
+ * Method:
+ * 1. For |x| in [0, 0.84375]
+ * erf(x) = x + x*R(x^2)
+ * erfc(x) = 1 - erf(x) if x in [-.84375,0.25]
+ * = 0.5 + ((0.5-x)-x*R) if x in [0.25,0.84375]
+ * where R = P/Q where P is an odd poly of degree 8 and
+ * Q is an odd poly of degree 10.
+ * -57.90
+ * | R - (erf(x)-x)/x | <= 2
+ *
+ *
+ * Remark. The formula is derived by noting
+ * erf(x) = (2/sqrt(pi))*(x - x^3/3 + x^5/10 - x^7/42 + ....)
+ * and that
+ * 2/sqrt(pi) = 1.128379167095512573896158903121545171688
+ * is close to one. The interval is chosen because the fix
+ * point of erf(x) is near 0.6174 (i.e., erf(x)=x when x is
+ * near 0.6174), and by some experiment, 0.84375 is chosen to
+ * guarantee the error is less than one ulp for erf.
+ *
+ * 2. For |x| in [0.84375,1.25], let s = |x| - 1, and
+ * c = 0.84506291151 rounded to single (24 bits)
+ * erf(x) = sign(x) * (c + P1(s)/Q1(s))
+ * erfc(x) = (1-c) - P1(s)/Q1(s) if x > 0
+ * 1+(c+P1(s)/Q1(s)) if x < 0
+ * |P1/Q1 - (erf(|x|)-c)| <= 2**-59.06
+ * Remark: here we use the taylor series expansion at x=1.
+ * erf(1+s) = erf(1) + s*Poly(s)
+ * = 0.845.. + P1(s)/Q1(s)
+ * That is, we use rational approximation to approximate
+ * erf(1+s) - (c = (single)0.84506291151)
+ * Note that |P1/Q1|< 0.078 for x in [0.84375,1.25]
+ * where
+ * P1(s) = degree 6 poly in s
+ * Q1(s) = degree 6 poly in s
+ *
+ * 3. For x in [1.25,1/0.35(~2.857143)],
+ * erfc(x) = (1/x)*exp(-x*x-0.5625+R1/S1)
+ * erf(x) = 1 - erfc(x)
+ * where
+ * R1(z) = degree 7 poly in z, (z=1/x^2)
+ * S1(z) = degree 8 poly in z
+ *
+ * 4. For x in [1/0.35,28]
+ * erfc(x) = (1/x)*exp(-x*x-0.5625+R2/S2) if x > 0
+ * = 2.0 - (1/x)*exp(-x*x-0.5625+R2/S2) if -6<x<0
+ * = 2.0 - tiny (if x <= -6)
+ * erf(x) = sign(x)*(1.0 - erfc(x)) if x < 6, else
+ * erf(x) = sign(x)*(1.0 - tiny)
+ * where
+ * R2(z) = degree 6 poly in z, (z=1/x^2)
+ * S2(z) = degree 7 poly in z
+ *
+ * Note1:
+ * To compute exp(-x*x-0.5625+R/S), let s be a single
+ * precision number and s := x; then
+ * -x*x = -s*s + (s-x)*(s+x)
+ * exp(-x*x-0.5626+R/S) =
+ * exp(-s*s-0.5625)*exp((s-x)*(s+x)+R/S);
+ * Note2:
+ * Here 4 and 5 make use of the asymptotic series
+ * exp(-x*x)
+ * erfc(x) ~ ---------- * ( 1 + Poly(1/x^2) )
+ * x*sqrt(pi)
+ * We use rational approximation to approximate
+ * g(s)=f(1/x^2) = log(erfc(x)*x) - x*x + 0.5625
+ * Here is the error bound for R1/S1 and R2/S2
+ * |R1/S1 - f(x)| < 2**(-62.57)
+ * |R2/S2 - f(x)| < 2**(-61.52)
+ *
+ * 5. For inf > x >= 28
+ * erf(x) = sign(x) *(1 - tiny) (raise inexact)
+ * erfc(x) = tiny*tiny (raise underflow) if x > 0
+ * = 2 - tiny if x<0
+ *
+ * 7. Special case:
+ * erf(0) = 0, erf(inf) = 1, erf(-inf) = -1,
+ * erfc(0) = 1, erfc(inf) = 0, erfc(-inf) = 2,
+ * erfc/erf(NaN) is NaN
+ */
+
+#include "libm.h"
+
+static const double
+erx = 8.45062911510467529297e-01, /* 0x3FEB0AC1, 0x60000000 */
+/*
+ * Coefficients for approximation to erf on [0,0.84375]
+ */
+efx8 = 1.02703333676410069053e+00, /* 0x3FF06EBA, 0x8214DB69 */
+pp0 = 1.28379167095512558561e-01, /* 0x3FC06EBA, 0x8214DB68 */
+pp1 = -3.25042107247001499370e-01, /* 0xBFD4CD7D, 0x691CB913 */
+pp2 = -2.84817495755985104766e-02, /* 0xBF9D2A51, 0xDBD7194F */
+pp3 = -5.77027029648944159157e-03, /* 0xBF77A291, 0x236668E4 */
+pp4 = -2.37630166566501626084e-05, /* 0xBEF8EAD6, 0x120016AC */
+qq1 = 3.97917223959155352819e-01, /* 0x3FD97779, 0xCDDADC09 */
+qq2 = 6.50222499887672944485e-02, /* 0x3FB0A54C, 0x5536CEBA */
+qq3 = 5.08130628187576562776e-03, /* 0x3F74D022, 0xC4D36B0F */
+qq4 = 1.32494738004321644526e-04, /* 0x3F215DC9, 0x221C1A10 */
+qq5 = -3.96022827877536812320e-06, /* 0xBED09C43, 0x42A26120 */
+/*
+ * Coefficients for approximation to erf in [0.84375,1.25]
+ */
+pa0 = -2.36211856075265944077e-03, /* 0xBF6359B8, 0xBEF77538 */
+pa1 = 4.14856118683748331666e-01, /* 0x3FDA8D00, 0xAD92B34D */
+pa2 = -3.72207876035701323847e-01, /* 0xBFD7D240, 0xFBB8C3F1 */
+pa3 = 3.18346619901161753674e-01, /* 0x3FD45FCA, 0x805120E4 */
+pa4 = -1.10894694282396677476e-01, /* 0xBFBC6398, 0x3D3E28EC */
+pa5 = 3.54783043256182359371e-02, /* 0x3FA22A36, 0x599795EB */
+pa6 = -2.16637559486879084300e-03, /* 0xBF61BF38, 0x0A96073F */
+qa1 = 1.06420880400844228286e-01, /* 0x3FBB3E66, 0x18EEE323 */
+qa2 = 5.40397917702171048937e-01, /* 0x3FE14AF0, 0x92EB6F33 */
+qa3 = 7.18286544141962662868e-02, /* 0x3FB2635C, 0xD99FE9A7 */
+qa4 = 1.26171219808761642112e-01, /* 0x3FC02660, 0xE763351F */
+qa5 = 1.36370839120290507362e-02, /* 0x3F8BEDC2, 0x6B51DD1C */
+qa6 = 1.19844998467991074170e-02, /* 0x3F888B54, 0x5735151D */
+/*
+ * Coefficients for approximation to erfc in [1.25,1/0.35]
+ */
+ra0 = -9.86494403484714822705e-03, /* 0xBF843412, 0x600D6435 */
+ra1 = -6.93858572707181764372e-01, /* 0xBFE63416, 0xE4BA7360 */
+ra2 = -1.05586262253232909814e+01, /* 0xC0251E04, 0x41B0E726 */
+ra3 = -6.23753324503260060396e+01, /* 0xC04F300A, 0xE4CBA38D */
+ra4 = -1.62396669462573470355e+02, /* 0xC0644CB1, 0x84282266 */
+ra5 = -1.84605092906711035994e+02, /* 0xC067135C, 0xEBCCABB2 */
+ra6 = -8.12874355063065934246e+01, /* 0xC0545265, 0x57E4D2F2 */
+ra7 = -9.81432934416914548592e+00, /* 0xC023A0EF, 0xC69AC25C */
+sa1 = 1.96512716674392571292e+01, /* 0x4033A6B9, 0xBD707687 */
+sa2 = 1.37657754143519042600e+02, /* 0x4061350C, 0x526AE721 */
+sa3 = 4.34565877475229228821e+02, /* 0x407B290D, 0xD58A1A71 */
+sa4 = 6.45387271733267880336e+02, /* 0x40842B19, 0x21EC2868 */
+sa5 = 4.29008140027567833386e+02, /* 0x407AD021, 0x57700314 */
+sa6 = 1.08635005541779435134e+02, /* 0x405B28A3, 0xEE48AE2C */
+sa7 = 6.57024977031928170135e+00, /* 0x401A47EF, 0x8E484A93 */
+sa8 = -6.04244152148580987438e-02, /* 0xBFAEEFF2, 0xEE749A62 */
+/*
+ * Coefficients for approximation to erfc in [1/.35,28]
+ */
+rb0 = -9.86494292470009928597e-03, /* 0xBF843412, 0x39E86F4A */
+rb1 = -7.99283237680523006574e-01, /* 0xBFE993BA, 0x70C285DE */
+rb2 = -1.77579549177547519889e+01, /* 0xC031C209, 0x555F995A */
+rb3 = -1.60636384855821916062e+02, /* 0xC064145D, 0x43C5ED98 */
+rb4 = -6.37566443368389627722e+02, /* 0xC083EC88, 0x1375F228 */
+rb5 = -1.02509513161107724954e+03, /* 0xC0900461, 0x6A2E5992 */
+rb6 = -4.83519191608651397019e+02, /* 0xC07E384E, 0x9BDC383F */
+sb1 = 3.03380607434824582924e+01, /* 0x403E568B, 0x261D5190 */
+sb2 = 3.25792512996573918826e+02, /* 0x40745CAE, 0x221B9F0A */
+sb3 = 1.53672958608443695994e+03, /* 0x409802EB, 0x189D5118 */
+sb4 = 3.19985821950859553908e+03, /* 0x40A8FFB7, 0x688C246A */
+sb5 = 2.55305040643316442583e+03, /* 0x40A3F219, 0xCEDF3BE6 */
+sb6 = 4.74528541206955367215e+02, /* 0x407DA874, 0xE79FE763 */
+sb7 = -2.24409524465858183362e+01; /* 0xC03670E2, 0x42712D62 */
+
+static double erfc1(double x)
+{
+ double_t s,P,Q;
+
+ s = fabs(x) - 1;
+ P = pa0+s*(pa1+s*(pa2+s*(pa3+s*(pa4+s*(pa5+s*pa6)))));
+ Q = 1+s*(qa1+s*(qa2+s*(qa3+s*(qa4+s*(qa5+s*qa6)))));
+ return 1 - erx - P/Q;
+}
+
+static double erfc2(uint32_t ix, double x)
+{
+ double_t s,R,S;
+ double z;
+
+ if (ix < 0x3ff40000) /* |x| < 1.25 */
+ return erfc1(x);
+
+ x = fabs(x);
+ s = 1/(x*x);
+ if (ix < 0x4006db6d) { /* |x| < 1/.35 ~ 2.85714 */
+ R = ra0+s*(ra1+s*(ra2+s*(ra3+s*(ra4+s*(
+ ra5+s*(ra6+s*ra7))))));
+ S = 1.0+s*(sa1+s*(sa2+s*(sa3+s*(sa4+s*(
+ sa5+s*(sa6+s*(sa7+s*sa8)))))));
+ } else { /* |x| > 1/.35 */
+ R = rb0+s*(rb1+s*(rb2+s*(rb3+s*(rb4+s*(
+ rb5+s*rb6)))));
+ S = 1.0+s*(sb1+s*(sb2+s*(sb3+s*(sb4+s*(
+ sb5+s*(sb6+s*sb7))))));
+ }
+ z = x;
+ SET_LOW_WORD(z,0);
+ return exp(-z*z-0.5625)*exp((z-x)*(z+x)+R/S)/x;
+}
+
+double erf(double x)
+{
+ double r,s,z,y;
+ uint32_t ix;
+ int sign;
+
+ GET_HIGH_WORD(ix, x);
+ sign = ix>>31;
+ ix &= 0x7fffffff;
+ if (ix >= 0x7ff00000) {
+ /* erf(nan)=nan, erf(+-inf)=+-1 */
+ return 1-2*sign + 1/x;
+ }
+ if (ix < 0x3feb0000) { /* |x| < 0.84375 */
+ if (ix < 0x3e300000) { /* |x| < 2**-28 */
+ /* avoid underflow */
+ return 0.125*(8*x + efx8*x);
+ }
+ z = x*x;
+ r = pp0+z*(pp1+z*(pp2+z*(pp3+z*pp4)));
+ s = 1.0+z*(qq1+z*(qq2+z*(qq3+z*(qq4+z*qq5))));
+ y = r/s;
+ return x + x*y;
+ }
+ if (ix < 0x40180000) /* 0.84375 <= |x| < 6 */
+ y = 1 - erfc2(ix,x);
+ else
+ y = 1 - 0x1p-1022;
+ return sign ? -y : y;
+}
+
+double erfc(double x)
+{
+ double r,s,z,y;
+ uint32_t ix;
+ int sign;
+
+ GET_HIGH_WORD(ix, x);
+ sign = ix>>31;
+ ix &= 0x7fffffff;
+ if (ix >= 0x7ff00000) {
+ /* erfc(nan)=nan, erfc(+-inf)=0,2 */
+ return 2*sign + 1/x;
+ }
+ if (ix < 0x3feb0000) { /* |x| < 0.84375 */
+ if (ix < 0x3c700000) /* |x| < 2**-56 */
+ return 1.0 - x;
+ z = x*x;
+ r = pp0+z*(pp1+z*(pp2+z*(pp3+z*pp4)));
+ s = 1.0+z*(qq1+z*(qq2+z*(qq3+z*(qq4+z*qq5))));
+ y = r/s;
+ if (sign || ix < 0x3fd00000) { /* x < 1/4 */
+ return 1.0 - (x+x*y);
+ }
+ return 0.5 - (x - 0.5 + x*y);
+ }
+ if (ix < 0x403c0000) { /* 0.84375 <= |x| < 28 */
+ return sign ? 2 - erfc2(ix,x) : erfc2(ix,x);
+ }
+ return sign ? 2 - 0x1p-1022 : 0x1p-1022*0x1p-1022;
+}
diff --git a/lib/libc/src/musl-math/erff.c b/lib/libc/src/musl-math/erff.c
new file mode 100644
index 0000000..ed5f397
--- /dev/null
+++ b/lib/libc/src/musl-math/erff.c
@@ -0,0 +1,183 @@
+/* origin: FreeBSD /usr/src/lib/msun/src/s_erff.c */
+/*
+ * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com.
+ */
+/*
+ * ====================================================
+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ *
+ * Developed at SunPro, a Sun Microsystems, Inc. business.
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ */
+
+#include "libm.h"
+
+static const float
+erx = 8.4506291151e-01, /* 0x3f58560b */
+/*
+ * Coefficients for approximation to erf on [0,0.84375]
+ */
+efx8 = 1.0270333290e+00, /* 0x3f8375d4 */
+pp0 = 1.2837916613e-01, /* 0x3e0375d4 */
+pp1 = -3.2504209876e-01, /* 0xbea66beb */
+pp2 = -2.8481749818e-02, /* 0xbce9528f */
+pp3 = -5.7702702470e-03, /* 0xbbbd1489 */
+pp4 = -2.3763017452e-05, /* 0xb7c756b1 */
+qq1 = 3.9791721106e-01, /* 0x3ecbbbce */
+qq2 = 6.5022252500e-02, /* 0x3d852a63 */
+qq3 = 5.0813062117e-03, /* 0x3ba68116 */
+qq4 = 1.3249473704e-04, /* 0x390aee49 */
+qq5 = -3.9602282413e-06, /* 0xb684e21a */
+/*
+ * Coefficients for approximation to erf in [0.84375,1.25]
+ */
+pa0 = -2.3621185683e-03, /* 0xbb1acdc6 */
+pa1 = 4.1485610604e-01, /* 0x3ed46805 */
+pa2 = -3.7220788002e-01, /* 0xbebe9208 */
+pa3 = 3.1834661961e-01, /* 0x3ea2fe54 */
+pa4 = -1.1089469492e-01, /* 0xbde31cc2 */
+pa5 = 3.5478305072e-02, /* 0x3d1151b3 */
+pa6 = -2.1663755178e-03, /* 0xbb0df9c0 */
+qa1 = 1.0642088205e-01, /* 0x3dd9f331 */
+qa2 = 5.4039794207e-01, /* 0x3f0a5785 */
+qa3 = 7.1828655899e-02, /* 0x3d931ae7 */
+qa4 = 1.2617121637e-01, /* 0x3e013307 */
+qa5 = 1.3637083583e-02, /* 0x3c5f6e13 */
+qa6 = 1.1984500103e-02, /* 0x3c445aa3 */
+/*
+ * Coefficients for approximation to erfc in [1.25,1/0.35]
+ */
+ra0 = -9.8649440333e-03, /* 0xbc21a093 */
+ra1 = -6.9385856390e-01, /* 0xbf31a0b7 */
+ra2 = -1.0558626175e+01, /* 0xc128f022 */
+ra3 = -6.2375331879e+01, /* 0xc2798057 */
+ra4 = -1.6239666748e+02, /* 0xc322658c */
+ra5 = -1.8460508728e+02, /* 0xc3389ae7 */
+ra6 = -8.1287437439e+01, /* 0xc2a2932b */
+ra7 = -9.8143291473e+00, /* 0xc11d077e */
+sa1 = 1.9651271820e+01, /* 0x419d35ce */
+sa2 = 1.3765776062e+02, /* 0x4309a863 */
+sa3 = 4.3456588745e+02, /* 0x43d9486f */
+sa4 = 6.4538726807e+02, /* 0x442158c9 */
+sa5 = 4.2900814819e+02, /* 0x43d6810b */
+sa6 = 1.0863500214e+02, /* 0x42d9451f */
+sa7 = 6.5702495575e+00, /* 0x40d23f7c */
+sa8 = -6.0424413532e-02, /* 0xbd777f97 */
+/*
+ * Coefficients for approximation to erfc in [1/.35,28]
+ */
+rb0 = -9.8649431020e-03, /* 0xbc21a092 */
+rb1 = -7.9928326607e-01, /* 0xbf4c9dd4 */
+rb2 = -1.7757955551e+01, /* 0xc18e104b */
+rb3 = -1.6063638306e+02, /* 0xc320a2ea */
+rb4 = -6.3756646729e+02, /* 0xc41f6441 */
+rb5 = -1.0250950928e+03, /* 0xc480230b */
+rb6 = -4.8351919556e+02, /* 0xc3f1c275 */
+sb1 = 3.0338060379e+01, /* 0x41f2b459 */
+sb2 = 3.2579251099e+02, /* 0x43a2e571 */
+sb3 = 1.5367296143e+03, /* 0x44c01759 */
+sb4 = 3.1998581543e+03, /* 0x4547fdbb */
+sb5 = 2.5530502930e+03, /* 0x451f90ce */
+sb6 = 4.7452853394e+02, /* 0x43ed43a7 */
+sb7 = -2.2440952301e+01; /* 0xc1b38712 */
+
+static float erfc1(float x)
+{
+ float_t s,P,Q;
+
+ s = fabsf(x) - 1;
+ P = pa0+s*(pa1+s*(pa2+s*(pa3+s*(pa4+s*(pa5+s*pa6)))));
+ Q = 1+s*(qa1+s*(qa2+s*(qa3+s*(qa4+s*(qa5+s*qa6)))));
+ return 1 - erx - P/Q;
+}
+
+static float erfc2(uint32_t ix, float x)
+{
+ float_t s,R,S;
+ float z;
+
+ if (ix < 0x3fa00000) /* |x| < 1.25 */
+ return erfc1(x);
+
+ x = fabsf(x);
+ s = 1/(x*x);
+ if (ix < 0x4036db6d) { /* |x| < 1/0.35 */
+ R = ra0+s*(ra1+s*(ra2+s*(ra3+s*(ra4+s*(
+ ra5+s*(ra6+s*ra7))))));
+ S = 1.0f+s*(sa1+s*(sa2+s*(sa3+s*(sa4+s*(
+ sa5+s*(sa6+s*(sa7+s*sa8)))))));
+ } else { /* |x| >= 1/0.35 */
+ R = rb0+s*(rb1+s*(rb2+s*(rb3+s*(rb4+s*(
+ rb5+s*rb6)))));
+ S = 1.0f+s*(sb1+s*(sb2+s*(sb3+s*(sb4+s*(
+ sb5+s*(sb6+s*sb7))))));
+ }
+ GET_FLOAT_WORD(ix, x);
+ SET_FLOAT_WORD(z, ix&0xffffe000);
+ return expf(-z*z - 0.5625f) * expf((z-x)*(z+x) + R/S)/x;
+}
+
+float erff(float x)
+{
+ float r,s,z,y;
+ uint32_t ix;
+ int sign;
+
+ GET_FLOAT_WORD(ix, x);
+ sign = ix>>31;
+ ix &= 0x7fffffff;
+ if (ix >= 0x7f800000) {
+ /* erf(nan)=nan, erf(+-inf)=+-1 */
+ return 1-2*sign + 1/x;
+ }
+ if (ix < 0x3f580000) { /* |x| < 0.84375 */
+ if (ix < 0x31800000) { /* |x| < 2**-28 */
+ /*avoid underflow */
+ return 0.125f*(8*x + efx8*x);
+ }
+ z = x*x;
+ r = pp0+z*(pp1+z*(pp2+z*(pp3+z*pp4)));
+ s = 1+z*(qq1+z*(qq2+z*(qq3+z*(qq4+z*qq5))));
+ y = r/s;
+ return x + x*y;
+ }
+ if (ix < 0x40c00000) /* |x| < 6 */
+ y = 1 - erfc2(ix,x);
+ else
+ y = 1 - 0x1p-120f;
+ return sign ? -y : y;
+}
+
+float erfcf(float x)
+{
+ float r,s,z,y;
+ uint32_t ix;
+ int sign;
+
+ GET_FLOAT_WORD(ix, x);
+ sign = ix>>31;
+ ix &= 0x7fffffff;
+ if (ix >= 0x7f800000) {
+ /* erfc(nan)=nan, erfc(+-inf)=0,2 */
+ return 2*sign + 1/x;
+ }
+
+ if (ix < 0x3f580000) { /* |x| < 0.84375 */
+ if (ix < 0x23800000) /* |x| < 2**-56 */
+ return 1.0f - x;
+ z = x*x;
+ r = pp0+z*(pp1+z*(pp2+z*(pp3+z*pp4)));
+ s = 1.0f+z*(qq1+z*(qq2+z*(qq3+z*(qq4+z*qq5))));
+ y = r/s;
+ if (sign || ix < 0x3e800000) /* x < 1/4 */
+ return 1.0f - (x+x*y);
+ return 0.5f - (x - 0.5f + x*y);
+ }
+ if (ix < 0x41e00000) { /* |x| < 28 */
+ return sign ? 2 - erfc2(ix,x) : erfc2(ix,x);
+ }
+ return sign ? 2 - 0x1p-120f : 0x1p-120f*0x1p-120f;
+}
diff --git a/lib/libc/src/musl-math/erfl.c b/lib/libc/src/musl-math/erfl.c
new file mode 100644
index 0000000..e267c23
--- /dev/null
+++ b/lib/libc/src/musl-math/erfl.c
@@ -0,0 +1,353 @@
+/* origin: OpenBSD /usr/src/lib/libm/src/ld80/e_erfl.c */
+/*
+ * ====================================================
+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ *
+ * Developed at SunPro, a Sun Microsystems, Inc. business.
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ */
+/*
+ * Copyright (c) 2008 Stephen L. Moshier <steve@moshier.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+/* double erf(double x)
+ * double erfc(double x)
+ * x
+ * 2 |\
+ * erf(x) = --------- | exp(-t*t)dt
+ * sqrt(pi) \|
+ * 0
+ *
+ * erfc(x) = 1-erf(x)
+ * Note that
+ * erf(-x) = -erf(x)
+ * erfc(-x) = 2 - erfc(x)
+ *
+ * Method:
+ * 1. For |x| in [0, 0.84375]
+ * erf(x) = x + x*R(x^2)
+ * erfc(x) = 1 - erf(x) if x in [-.84375,0.25]
+ * = 0.5 + ((0.5-x)-x*R) if x in [0.25,0.84375]
+ * Remark. The formula is derived by noting
+ * erf(x) = (2/sqrt(pi))*(x - x^3/3 + x^5/10 - x^7/42 + ....)
+ * and that
+ * 2/sqrt(pi) = 1.128379167095512573896158903121545171688
+ * is close to one. The interval is chosen because the fix
+ * point of erf(x) is near 0.6174 (i.e., erf(x)=x when x is
+ * near 0.6174), and by some experiment, 0.84375 is chosen to
+ * guarantee the error is less than one ulp for erf.
+ *
+ * 2. For |x| in [0.84375,1.25], let s = |x| - 1, and
+ * c = 0.84506291151 rounded to single (24 bits)
+ * erf(x) = sign(x) * (c + P1(s)/Q1(s))
+ * erfc(x) = (1-c) - P1(s)/Q1(s) if x > 0
+ * 1+(c+P1(s)/Q1(s)) if x < 0
+ * Remark: here we use the taylor series expansion at x=1.
+ * erf(1+s) = erf(1) + s*Poly(s)
+ * = 0.845.. + P1(s)/Q1(s)
+ * Note that |P1/Q1|< 0.078 for x in [0.84375,1.25]
+ *
+ * 3. For x in [1.25,1/0.35(~2.857143)],
+ * erfc(x) = (1/x)*exp(-x*x-0.5625+R1(z)/S1(z))
+ * z=1/x^2
+ * erf(x) = 1 - erfc(x)
+ *
+ * 4. For x in [1/0.35,107]
+ * erfc(x) = (1/x)*exp(-x*x-0.5625+R2/S2) if x > 0
+ * = 2.0 - (1/x)*exp(-x*x-0.5625+R2(z)/S2(z))
+ * if -6.666<x<0
+ * = 2.0 - tiny (if x <= -6.666)
+ * z=1/x^2
+ * erf(x) = sign(x)*(1.0 - erfc(x)) if x < 6.666, else
+ * erf(x) = sign(x)*(1.0 - tiny)
+ * Note1:
+ * To compute exp(-x*x-0.5625+R/S), let s be a single
+ * precision number and s := x; then
+ * -x*x = -s*s + (s-x)*(s+x)
+ * exp(-x*x-0.5626+R/S) =
+ * exp(-s*s-0.5625)*exp((s-x)*(s+x)+R/S);
+ * Note2:
+ * Here 4 and 5 make use of the asymptotic series
+ * exp(-x*x)
+ * erfc(x) ~ ---------- * ( 1 + Poly(1/x^2) )
+ * x*sqrt(pi)
+ *
+ * 5. For inf > x >= 107
+ * erf(x) = sign(x) *(1 - tiny) (raise inexact)
+ * erfc(x) = tiny*tiny (raise underflow) if x > 0
+ * = 2 - tiny if x<0
+ *
+ * 7. Special case:
+ * erf(0) = 0, erf(inf) = 1, erf(-inf) = -1,
+ * erfc(0) = 1, erfc(inf) = 0, erfc(-inf) = 2,
+ * erfc/erf(NaN) is NaN
+ */
+
+
+#include "libm.h"
+
+#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
+long double erfl(long double x)
+{
+ return erf(x);
+}
+long double erfcl(long double x)
+{
+ return erfc(x);
+}
+#elif LDBL_MANT_DIG == 64 && LDBL_MAX_EXP == 16384
+static const long double
+erx = 0.845062911510467529296875L,
+
+/*
+ * Coefficients for approximation to erf on [0,0.84375]
+ */
+/* 8 * (2/sqrt(pi) - 1) */
+efx8 = 1.0270333367641005911692712249723613735048E0L,
+pp[6] = {
+ 1.122751350964552113068262337278335028553E6L,
+ -2.808533301997696164408397079650699163276E6L,
+ -3.314325479115357458197119660818768924100E5L,
+ -6.848684465326256109712135497895525446398E4L,
+ -2.657817695110739185591505062971929859314E3L,
+ -1.655310302737837556654146291646499062882E2L,
+},
+qq[6] = {
+ 8.745588372054466262548908189000448124232E6L,
+ 3.746038264792471129367533128637019611485E6L,
+ 7.066358783162407559861156173539693900031E5L,
+ 7.448928604824620999413120955705448117056E4L,
+ 4.511583986730994111992253980546131408924E3L,
+ 1.368902937933296323345610240009071254014E2L,
+ /* 1.000000000000000000000000000000000000000E0 */
+},
+
+/*
+ * Coefficients for approximation to erf in [0.84375,1.25]
+ */
+/* erf(x+1) = 0.845062911510467529296875 + pa(x)/qa(x)
+ -0.15625 <= x <= +.25
+ Peak relative error 8.5e-22 */
+pa[8] = {
+ -1.076952146179812072156734957705102256059E0L,
+ 1.884814957770385593365179835059971587220E2L,
+ -5.339153975012804282890066622962070115606E1L,
+ 4.435910679869176625928504532109635632618E1L,
+ 1.683219516032328828278557309642929135179E1L,
+ -2.360236618396952560064259585299045804293E0L,
+ 1.852230047861891953244413872297940938041E0L,
+ 9.394994446747752308256773044667843200719E-2L,
+},
+qa[7] = {
+ 4.559263722294508998149925774781887811255E2L,
+ 3.289248982200800575749795055149780689738E2L,
+ 2.846070965875643009598627918383314457912E2L,
+ 1.398715859064535039433275722017479994465E2L,
+ 6.060190733759793706299079050985358190726E1L,
+ 2.078695677795422351040502569964299664233E1L,
+ 4.641271134150895940966798357442234498546E0L,
+ /* 1.000000000000000000000000000000000000000E0 */
+},
+
+/*
+ * Coefficients for approximation to erfc in [1.25,1/0.35]
+ */
+/* erfc(1/x) = x exp (-1/x^2 - 0.5625 + ra(x^2)/sa(x^2))
+ 1/2.85711669921875 < 1/x < 1/1.25
+ Peak relative error 3.1e-21 */
+ra[] = {
+ 1.363566591833846324191000679620738857234E-1L,
+ 1.018203167219873573808450274314658434507E1L,
+ 1.862359362334248675526472871224778045594E2L,
+ 1.411622588180721285284945138667933330348E3L,
+ 5.088538459741511988784440103218342840478E3L,
+ 8.928251553922176506858267311750789273656E3L,
+ 7.264436000148052545243018622742770549982E3L,
+ 2.387492459664548651671894725748959751119E3L,
+ 2.220916652813908085449221282808458466556E2L,
+},
+sa[] = {
+ -1.382234625202480685182526402169222331847E1L,
+ -3.315638835627950255832519203687435946482E2L,
+ -2.949124863912936259747237164260785326692E3L,
+ -1.246622099070875940506391433635999693661E4L,
+ -2.673079795851665428695842853070996219632E4L,
+ -2.880269786660559337358397106518918220991E4L,
+ -1.450600228493968044773354186390390823713E4L,
+ -2.874539731125893533960680525192064277816E3L,
+ -1.402241261419067750237395034116942296027E2L,
+ /* 1.000000000000000000000000000000000000000E0 */
+},
+
+/*
+ * Coefficients for approximation to erfc in [1/.35,107]
+ */
+/* erfc(1/x) = x exp (-1/x^2 - 0.5625 + rb(x^2)/sb(x^2))
+ 1/6.6666259765625 < 1/x < 1/2.85711669921875
+ Peak relative error 4.2e-22 */
+rb[] = {
+ -4.869587348270494309550558460786501252369E-5L,
+ -4.030199390527997378549161722412466959403E-3L,
+ -9.434425866377037610206443566288917589122E-2L,
+ -9.319032754357658601200655161585539404155E-1L,
+ -4.273788174307459947350256581445442062291E0L,
+ -8.842289940696150508373541814064198259278E0L,
+ -7.069215249419887403187988144752613025255E0L,
+ -1.401228723639514787920274427443330704764E0L,
+},
+sb[] = {
+ 4.936254964107175160157544545879293019085E-3L,
+ 1.583457624037795744377163924895349412015E-1L,
+ 1.850647991850328356622940552450636420484E0L,
+ 9.927611557279019463768050710008450625415E0L,
+ 2.531667257649436709617165336779212114570E1L,
+ 2.869752886406743386458304052862814690045E1L,
+ 1.182059497870819562441683560749192539345E1L,
+ /* 1.000000000000000000000000000000000000000E0 */
+},
+/* erfc(1/x) = x exp (-1/x^2 - 0.5625 + rc(x^2)/sc(x^2))
+ 1/107 <= 1/x <= 1/6.6666259765625
+ Peak relative error 1.1e-21 */
+rc[] = {
+ -8.299617545269701963973537248996670806850E-5L,
+ -6.243845685115818513578933902532056244108E-3L,
+ -1.141667210620380223113693474478394397230E-1L,
+ -7.521343797212024245375240432734425789409E-1L,
+ -1.765321928311155824664963633786967602934E0L,
+ -1.029403473103215800456761180695263439188E0L,
+},
+sc[] = {
+ 8.413244363014929493035952542677768808601E-3L,
+ 2.065114333816877479753334599639158060979E-1L,
+ 1.639064941530797583766364412782135680148E0L,
+ 4.936788463787115555582319302981666347450E0L,
+ 5.005177727208955487404729933261347679090E0L,
+ /* 1.000000000000000000000000000000000000000E0 */
+};
+
+static long double erfc1(long double x)
+{
+ long double s,P,Q;
+
+ s = fabsl(x) - 1;
+ P = pa[0] + s * (pa[1] + s * (pa[2] +
+ s * (pa[3] + s * (pa[4] + s * (pa[5] + s * (pa[6] + s * pa[7]))))));
+ Q = qa[0] + s * (qa[1] + s * (qa[2] +
+ s * (qa[3] + s * (qa[4] + s * (qa[5] + s * (qa[6] + s))))));
+ return 1 - erx - P / Q;
+}
+
+static long double erfc2(uint32_t ix, long double x)
+{
+ union ldshape u;
+ long double s,z,R,S;
+
+ if (ix < 0x3fffa000) /* 0.84375 <= |x| < 1.25 */
+ return erfc1(x);
+
+ x = fabsl(x);
+ s = 1 / (x * x);
+ if (ix < 0x4000b6db) { /* 1.25 <= |x| < 2.857 ~ 1/.35 */
+ R = ra[0] + s * (ra[1] + s * (ra[2] + s * (ra[3] + s * (ra[4] +
+ s * (ra[5] + s * (ra[6] + s * (ra[7] + s * ra[8])))))));
+ S = sa[0] + s * (sa[1] + s * (sa[2] + s * (sa[3] + s * (sa[4] +
+ s * (sa[5] + s * (sa[6] + s * (sa[7] + s * (sa[8] + s))))))));
+ } else if (ix < 0x4001d555) { /* 2.857 <= |x| < 6.6666259765625 */
+ R = rb[0] + s * (rb[1] + s * (rb[2] + s * (rb[3] + s * (rb[4] +
+ s * (rb[5] + s * (rb[6] + s * rb[7]))))));
+ S = sb[0] + s * (sb[1] + s * (sb[2] + s * (sb[3] + s * (sb[4] +
+ s * (sb[5] + s * (sb[6] + s))))));
+ } else { /* 6.666 <= |x| < 107 (erfc only) */
+ R = rc[0] + s * (rc[1] + s * (rc[2] + s * (rc[3] +
+ s * (rc[4] + s * rc[5]))));
+ S = sc[0] + s * (sc[1] + s * (sc[2] + s * (sc[3] +
+ s * (sc[4] + s))));
+ }
+ u.f = x;
+ u.i.m &= -1ULL << 40;
+ z = u.f;
+ return expl(-z*z - 0.5625) * expl((z - x) * (z + x) + R / S) / x;
+}
+
+long double erfl(long double x)
+{
+ long double r, s, z, y;
+ union ldshape u = {x};
+ uint32_t ix = (u.i.se & 0x7fffU)<<16 | u.i.m>>48;
+ int sign = u.i.se >> 15;
+
+ if (ix >= 0x7fff0000)
+ /* erf(nan)=nan, erf(+-inf)=+-1 */
+ return 1 - 2*sign + 1/x;
+ if (ix < 0x3ffed800) { /* |x| < 0.84375 */
+ if (ix < 0x3fde8000) { /* |x| < 2**-33 */
+ return 0.125 * (8 * x + efx8 * x); /* avoid underflow */
+ }
+ z = x * x;
+ r = pp[0] + z * (pp[1] +
+ z * (pp[2] + z * (pp[3] + z * (pp[4] + z * pp[5]))));
+ s = qq[0] + z * (qq[1] +
+ z * (qq[2] + z * (qq[3] + z * (qq[4] + z * (qq[5] + z)))));
+ y = r / s;
+ return x + x * y;
+ }
+ if (ix < 0x4001d555) /* |x| < 6.6666259765625 */
+ y = 1 - erfc2(ix,x);
+ else
+ y = 1 - 0x1p-16382L;
+ return sign ? -y : y;
+}
+
+long double erfcl(long double x)
+{
+ long double r, s, z, y;
+ union ldshape u = {x};
+ uint32_t ix = (u.i.se & 0x7fffU)<<16 | u.i.m>>48;
+ int sign = u.i.se >> 15;
+
+ if (ix >= 0x7fff0000)
+ /* erfc(nan) = nan, erfc(+-inf) = 0,2 */
+ return 2*sign + 1/x;
+ if (ix < 0x3ffed800) { /* |x| < 0.84375 */
+ if (ix < 0x3fbe0000) /* |x| < 2**-65 */
+ return 1.0 - x;
+ z = x * x;
+ r = pp[0] + z * (pp[1] +
+ z * (pp[2] + z * (pp[3] + z * (pp[4] + z * pp[5]))));
+ s = qq[0] + z * (qq[1] +
+ z * (qq[2] + z * (qq[3] + z * (qq[4] + z * (qq[5] + z)))));
+ y = r / s;
+ if (ix < 0x3ffd8000) /* x < 1/4 */
+ return 1.0 - (x + x * y);
+ return 0.5 - (x - 0.5 + x * y);
+ }
+ if (ix < 0x4005d600) /* |x| < 107 */
+ return sign ? 2 - erfc2(ix,x) : erfc2(ix,x);
+ y = 0x1p-16382L;
+ return sign ? 2 - y : y*y;
+}
+#elif LDBL_MANT_DIG == 113 && LDBL_MAX_EXP == 16384
+// TODO: broken implementation to make things compile
+long double erfl(long double x)
+{
+ return erf(x);
+}
+long double erfcl(long double x)
+{
+ return erfc(x);
+}
+#endif
diff --git a/lib/libc/src/musl-math/exp.c b/lib/libc/src/musl-math/exp.c
new file mode 100644
index 0000000..9ea672f
--- /dev/null
+++ b/lib/libc/src/musl-math/exp.c
@@ -0,0 +1,134 @@
+/* origin: FreeBSD /usr/src/lib/msun/src/e_exp.c */
+/*
+ * ====================================================
+ * Copyright (C) 2004 by Sun Microsystems, Inc. All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ */
+/* exp(x)
+ * Returns the exponential of x.
+ *
+ * Method
+ * 1. Argument reduction:
+ * Reduce x to an r so that |r| <= 0.5*ln2 ~ 0.34658.
+ * Given x, find r and integer k such that
+ *
+ * x = k*ln2 + r, |r| <= 0.5*ln2.
+ *
+ * Here r will be represented as r = hi-lo for better
+ * accuracy.
+ *
+ * 2. Approximation of exp(r) by a special rational function on
+ * the interval [0,0.34658]:
+ * Write
+ * R(r**2) = r*(exp(r)+1)/(exp(r)-1) = 2 + r*r/6 - r**4/360 + ...
+ * We use a special Remez algorithm on [0,0.34658] to generate
+ * a polynomial of degree 5 to approximate R. The maximum error
+ * of this polynomial approximation is bounded by 2**-59. In
+ * other words,
+ * R(z) ~ 2.0 + P1*z + P2*z**2 + P3*z**3 + P4*z**4 + P5*z**5
+ * (where z=r*r, and the values of P1 to P5 are listed below)
+ * and
+ * | 5 | -59
+ * | 2.0+P1*z+...+P5*z - R(z) | <= 2
+ * | |
+ * The computation of exp(r) thus becomes
+ * 2*r
+ * exp(r) = 1 + ----------
+ * R(r) - r
+ * r*c(r)
+ * = 1 + r + ----------- (for better accuracy)
+ * 2 - c(r)
+ * where
+ * 2 4 10
+ * c(r) = r - (P1*r + P2*r + ... + P5*r ).
+ *
+ * 3. Scale back to obtain exp(x):
+ * From step 1, we have
+ * exp(x) = 2^k * exp(r)
+ *
+ * Special cases:
+ * exp(INF) is INF, exp(NaN) is NaN;
+ * exp(-INF) is 0, and
+ * for finite argument, only exp(0)=1 is exact.
+ *
+ * Accuracy:
+ * according to an error analysis, the error is always less than
+ * 1 ulp (unit in the last place).
+ *
+ * Misc. info.
+ * For IEEE double
+ * if x > 709.782712893383973096 then exp(x) overflows
+ * if x < -745.133219101941108420 then exp(x) underflows
+ */
+
+#include "libm.h"
+
+static const double
+half[2] = {0.5,-0.5},
+ln2hi = 6.93147180369123816490e-01, /* 0x3fe62e42, 0xfee00000 */
+ln2lo = 1.90821492927058770002e-10, /* 0x3dea39ef, 0x35793c76 */
+invln2 = 1.44269504088896338700e+00, /* 0x3ff71547, 0x652b82fe */
+P1 = 1.66666666666666019037e-01, /* 0x3FC55555, 0x5555553E */
+P2 = -2.77777777770155933842e-03, /* 0xBF66C16C, 0x16BEBD93 */
+P3 = 6.61375632143793436117e-05, /* 0x3F11566A, 0xAF25DE2C */
+P4 = -1.65339022054652515390e-06, /* 0xBEBBBD41, 0xC5D26BF1 */
+P5 = 4.13813679705723846039e-08; /* 0x3E663769, 0x72BEA4D0 */
+
+double exp(double x)
+{
+ double_t hi, lo, c, xx, y;
+ int k, sign;
+ uint32_t hx;
+
+ GET_HIGH_WORD(hx, x);
+ sign = hx>>31;
+ hx &= 0x7fffffff; /* high word of |x| */
+
+ /* special cases */
+ if (hx >= 0x4086232b) { /* if |x| >= 708.39... */
+ if (isnan(x))
+ return x;
+ if (x > 709.782712893383973096) {
+ /* overflow if x!=inf */
+ x *= 0x1p1023;
+ return x;
+ }
+ if (x < -708.39641853226410622) {
+ /* underflow if x!=-inf */
+ FORCE_EVAL((float)(-0x1p-149/x));
+ if (x < -745.13321910194110842)
+ return 0;
+ }
+ }
+
+ /* argument reduction */
+ if (hx > 0x3fd62e42) { /* if |x| > 0.5 ln2 */
+ if (hx >= 0x3ff0a2b2) /* if |x| >= 1.5 ln2 */
+ k = (int)(invln2*x + half[sign]);
+ else
+ k = 1 - sign - sign;
+ hi = x - k*ln2hi; /* k*ln2hi is exact here */
+ lo = k*ln2lo;
+ x = hi - lo;
+ } else if (hx > 0x3e300000) { /* if |x| > 2**-28 */
+ k = 0;
+ hi = x;
+ lo = 0;
+ } else {
+ /* inexact if x!=0 */
+ FORCE_EVAL(0x1p1023 + x);
+ return 1 + x;
+ }
+
+ /* x is now in primary range */
+ xx = x*x;
+ c = x - xx*(P1+xx*(P2+xx*(P3+xx*(P4+xx*P5))));
+ y = 1 + (x*c/(2-c) - lo + hi);
+ if (k == 0)
+ return y;
+ return scalbn(y, k);
+}
diff --git a/lib/libc/src/musl-math/exp10.c b/lib/libc/src/musl-math/exp10.c
new file mode 100644
index 0000000..47b4dc7
--- /dev/null
+++ b/lib/libc/src/musl-math/exp10.c
@@ -0,0 +1,26 @@
+#define _GNU_SOURCE
+#include <math.h>
+#include <stdint.h>
+#include "weak_alias.h"
+//#include "libc.h"
+
+double exp10(double x)
+{
+ static const double p10[] = {
+ 1e-15, 1e-14, 1e-13, 1e-12, 1e-11, 1e-10,
+ 1e-9, 1e-8, 1e-7, 1e-6, 1e-5, 1e-4, 1e-3, 1e-2, 1e-1,
+ 1, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9,
+ 1e10, 1e11, 1e12, 1e13, 1e14, 1e15
+ };
+ double n, y = modf(x, &n);
+ union {double f; uint64_t i;} u = {n};
+ /* fabs(n) < 16 without raising invalid on nan */
+ if ((u.i>>52 & 0x7ff) < 0x3ff+4) {
+ if (!y) return p10[(int)n+15];
+ y = exp2(3.32192809488736234787031942948939 * y);
+ return y * p10[(int)n+15];
+ }
+ return pow(10.0, x);
+}
+
+weak_alias(exp10, pow10);
diff --git a/lib/libc/src/musl-math/exp10f.c b/lib/libc/src/musl-math/exp10f.c
new file mode 100644
index 0000000..74f8909
--- /dev/null
+++ b/lib/libc/src/musl-math/exp10f.c
@@ -0,0 +1,24 @@
+#define _GNU_SOURCE
+#include <math.h>
+#include <stdint.h>
+#include "weak_alias.h"
+//#include "libc.h"
+
+float exp10f(float x)
+{
+ static const float p10[] = {
+ 1e-7f, 1e-6f, 1e-5f, 1e-4f, 1e-3f, 1e-2f, 1e-1f,
+ 1, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7
+ };
+ float n, y = modff(x, &n);
+ union {float f; uint32_t i;} u = {n};
+ /* fabsf(n) < 8 without raising invalid on nan */
+ if ((u.i>>23 & 0xff) < 0x7f+3) {
+ if (!y) return p10[(int)n+7];
+ y = exp2f(3.32192809488736234787031942948939f * y);
+ return y * p10[(int)n+7];
+ }
+ return exp2(3.32192809488736234787031942948939 * x);
+}
+
+weak_alias(exp10f, pow10f);
diff --git a/lib/libc/src/musl-math/exp10l.c b/lib/libc/src/musl-math/exp10l.c
new file mode 100644
index 0000000..f18e554
--- /dev/null
+++ b/lib/libc/src/musl-math/exp10l.c
@@ -0,0 +1,34 @@
+#define _GNU_SOURCE
+#include <float.h>
+#include <math.h>
+//#include "libc.h"
+#include "libm.h"
+#include "weak_alias.h"
+
+#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
+long double exp10l(long double x)
+{
+ return exp10(x);
+}
+#elif (LDBL_MANT_DIG == 64 || LDBL_MANT_DIG == 113) && LDBL_MAX_EXP == 16384
+long double exp10l(long double x)
+{
+ static const long double p10[] = {
+ 1e-15L, 1e-14L, 1e-13L, 1e-12L, 1e-11L, 1e-10L,
+ 1e-9L, 1e-8L, 1e-7L, 1e-6L, 1e-5L, 1e-4L, 1e-3L, 1e-2L, 1e-1L,
+ 1, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9,
+ 1e10, 1e11, 1e12, 1e13, 1e14, 1e15
+ };
+ long double n, y = modfl(x, &n);
+ union ldshape u = {n};
+ /* fabsl(n) < 16 without raising invalid on nan */
+ if ((u.i.se & 0x7fff) < 0x3fff+4) {
+ if (!y) return p10[(int)n+15];
+ y = exp2l(3.32192809488736234787031942948939L * y);
+ return y * p10[(int)n+15];
+ }
+ return powl(10.0, x);
+}
+#endif
+
+weak_alias(exp10l, pow10l);
diff --git a/lib/libc/src/musl-math/exp2.c b/lib/libc/src/musl-math/exp2.c
new file mode 100644
index 0000000..e14adba
--- /dev/null
+++ b/lib/libc/src/musl-math/exp2.c
@@ -0,0 +1,375 @@
+/* origin: FreeBSD /usr/src/lib/msun/src/s_exp2.c */
+/*-
+ * Copyright (c) 2005 David Schultz <das@FreeBSD.ORG>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "libm.h"
+
+#define TBLSIZE 256
+
+static const double
+redux = 0x1.8p52 / TBLSIZE,
+P1 = 0x1.62e42fefa39efp-1,
+P2 = 0x1.ebfbdff82c575p-3,
+P3 = 0x1.c6b08d704a0a6p-5,
+P4 = 0x1.3b2ab88f70400p-7,
+P5 = 0x1.5d88003875c74p-10;
+
+static const double tbl[TBLSIZE * 2] = {
+/* exp2(z + eps) eps */
+ 0x1.6a09e667f3d5dp-1, 0x1.9880p-44,
+ 0x1.6b052fa751744p-1, 0x1.8000p-50,
+ 0x1.6c012750bd9fep-1, -0x1.8780p-45,
+ 0x1.6cfdcddd476bfp-1, 0x1.ec00p-46,
+ 0x1.6dfb23c651a29p-1, -0x1.8000p-50,
+ 0x1.6ef9298593ae3p-1, -0x1.c000p-52,
+ 0x1.6ff7df9519386p-1, -0x1.fd80p-45,
+ 0x1.70f7466f42da3p-1, -0x1.c880p-45,
+ 0x1.71f75e8ec5fc3p-1, 0x1.3c00p-46,
+ 0x1.72f8286eacf05p-1, -0x1.8300p-44,
+ 0x1.73f9a48a58152p-1, -0x1.0c00p-47,
+ 0x1.74fbd35d7ccfcp-1, 0x1.f880p-45,
+ 0x1.75feb564267f1p-1, 0x1.3e00p-47,
+ 0x1.77024b1ab6d48p-1, -0x1.7d00p-45,
+ 0x1.780694fde5d38p-1, -0x1.d000p-50,
+ 0x1.790b938ac1d00p-1, 0x1.3000p-49,
+ 0x1.7a11473eb0178p-1, -0x1.d000p-49,
+ 0x1.7b17b0976d060p-1, 0x1.0400p-45,
+ 0x1.7c1ed0130c133p-1, 0x1.0000p-53,
+ 0x1.7d26a62ff8636p-1, -0x1.6900p-45,
+ 0x1.7e2f336cf4e3bp-1, -0x1.2e00p-47,
+ 0x1.7f3878491c3e8p-1, -0x1.4580p-45,
+ 0x1.80427543e1b4ep-1, 0x1.3000p-44,
+ 0x1.814d2add1071ap-1, 0x1.f000p-47,
+ 0x1.82589994ccd7ep-1, -0x1.1c00p-45,
+ 0x1.8364c1eb942d0p-1, 0x1.9d00p-45,
+ 0x1.8471a4623cab5p-1, 0x1.7100p-43,
+ 0x1.857f4179f5bbcp-1, 0x1.2600p-45,
+ 0x1.868d99b4491afp-1, -0x1.2c40p-44,
+ 0x1.879cad931a395p-1, -0x1.3000p-45,
+ 0x1.88ac7d98a65b8p-1, -0x1.a800p-45,
+ 0x1.89bd0a4785800p-1, -0x1.d000p-49,
+ 0x1.8ace5422aa223p-1, 0x1.3280p-44,
+ 0x1.8be05bad619fap-1, 0x1.2b40p-43,
+ 0x1.8cf3216b54383p-1, -0x1.ed00p-45,
+ 0x1.8e06a5e08664cp-1, -0x1.0500p-45,
+ 0x1.8f1ae99157807p-1, 0x1.8280p-45,
+ 0x1.902fed0282c0ep-1, -0x1.cb00p-46,
+ 0x1.9145b0b91ff96p-1, -0x1.5e00p-47,
+ 0x1.925c353aa2ff9p-1, 0x1.5400p-48,
+ 0x1.93737b0cdc64ap-1, 0x1.7200p-46,
+ 0x1.948b82b5f98aep-1, -0x1.9000p-47,
+ 0x1.95a44cbc852cbp-1, 0x1.5680p-45,
+ 0x1.96bdd9a766f21p-1, -0x1.6d00p-44,
+ 0x1.97d829fde4e2ap-1, -0x1.1000p-47,
+ 0x1.98f33e47a23a3p-1, 0x1.d000p-45,
+ 0x1.9a0f170ca0604p-1, -0x1.8a40p-44,
+ 0x1.9b2bb4d53ff89p-1, 0x1.55c0p-44,
+ 0x1.9c49182a3f15bp-1, 0x1.6b80p-45,
+ 0x1.9d674194bb8c5p-1, -0x1.c000p-49,
+ 0x1.9e86319e3238ep-1, 0x1.7d00p-46,
+ 0x1.9fa5e8d07f302p-1, 0x1.6400p-46,
+ 0x1.a0c667b5de54dp-1, -0x1.5000p-48,
+ 0x1.a1e7aed8eb8f6p-1, 0x1.9e00p-47,
+ 0x1.a309bec4a2e27p-1, 0x1.ad80p-45,
+ 0x1.a42c980460a5dp-1, -0x1.af00p-46,
+ 0x1.a5503b23e259bp-1, 0x1.b600p-47,
+ 0x1.a674a8af46213p-1, 0x1.8880p-44,
+ 0x1.a799e1330b3a7p-1, 0x1.1200p-46,
+ 0x1.a8bfe53c12e8dp-1, 0x1.6c00p-47,
+ 0x1.a9e6b5579fcd2p-1, -0x1.9b80p-45,
+ 0x1.ab0e521356fb8p-1, 0x1.b700p-45,
+ 0x1.ac36bbfd3f381p-1, 0x1.9000p-50,
+ 0x1.ad5ff3a3c2780p-1, 0x1.4000p-49,
+ 0x1.ae89f995ad2a3p-1, -0x1.c900p-45,
+ 0x1.afb4ce622f367p-1, 0x1.6500p-46,
+ 0x1.b0e07298db790p-1, 0x1.fd40p-45,
+ 0x1.b20ce6c9a89a9p-1, 0x1.2700p-46,
+ 0x1.b33a2b84f1a4bp-1, 0x1.d470p-43,
+ 0x1.b468415b747e7p-1, -0x1.8380p-44,
+ 0x1.b59728de5593ap-1, 0x1.8000p-54,
+ 0x1.b6c6e29f1c56ap-1, 0x1.ad00p-47,
+ 0x1.b7f76f2fb5e50p-1, 0x1.e800p-50,
+ 0x1.b928cf22749b2p-1, -0x1.4c00p-47,
+ 0x1.ba5b030a10603p-1, -0x1.d700p-47,
+ 0x1.bb8e0b79a6f66p-1, 0x1.d900p-47,
+ 0x1.bcc1e904bc1ffp-1, 0x1.2a00p-47,
+ 0x1.bdf69c3f3a16fp-1, -0x1.f780p-46,
+ 0x1.bf2c25bd71db8p-1, -0x1.0a00p-46,
+ 0x1.c06286141b2e9p-1, -0x1.1400p-46,
+ 0x1.c199bdd8552e0p-1, 0x1.be00p-47,
+ 0x1.c2d1cd9fa64eep-1, -0x1.9400p-47,
+ 0x1.c40ab5fffd02fp-1, -0x1.ed00p-47,
+ 0x1.c544778fafd15p-1, 0x1.9660p-44,
+ 0x1.c67f12e57d0cbp-1, -0x1.a100p-46,
+ 0x1.c7ba88988c1b6p-1, -0x1.8458p-42,
+ 0x1.c8f6d9406e733p-1, -0x1.a480p-46,
+ 0x1.ca3405751c4dfp-1, 0x1.b000p-51,
+ 0x1.cb720dcef9094p-1, 0x1.1400p-47,
+ 0x1.ccb0f2e6d1689p-1, 0x1.0200p-48,
+ 0x1.cdf0b555dc412p-1, 0x1.3600p-48,
+ 0x1.cf3155b5bab3bp-1, -0x1.6900p-47,
+ 0x1.d072d4a0789bcp-1, 0x1.9a00p-47,
+ 0x1.d1b532b08c8fap-1, -0x1.5e00p-46,
+ 0x1.d2f87080d8a85p-1, 0x1.d280p-46,
+ 0x1.d43c8eacaa203p-1, 0x1.1a00p-47,
+ 0x1.d5818dcfba491p-1, 0x1.f000p-50,
+ 0x1.d6c76e862e6a1p-1, -0x1.3a00p-47,
+ 0x1.d80e316c9834ep-1, -0x1.cd80p-47,
+ 0x1.d955d71ff6090p-1, 0x1.4c00p-48,
+ 0x1.da9e603db32aep-1, 0x1.f900p-48,
+ 0x1.dbe7cd63a8325p-1, 0x1.9800p-49,
+ 0x1.dd321f301b445p-1, -0x1.5200p-48,
+ 0x1.de7d5641c05bfp-1, -0x1.d700p-46,
+ 0x1.dfc97337b9aecp-1, -0x1.6140p-46,
+ 0x1.e11676b197d5ep-1, 0x1.b480p-47,
+ 0x1.e264614f5a3e7p-1, 0x1.0ce0p-43,
+ 0x1.e3b333b16ee5cp-1, 0x1.c680p-47,
+ 0x1.e502ee78b3fb4p-1, -0x1.9300p-47,
+ 0x1.e653924676d68p-1, -0x1.5000p-49,
+ 0x1.e7a51fbc74c44p-1, -0x1.7f80p-47,
+ 0x1.e8f7977cdb726p-1, -0x1.3700p-48,
+ 0x1.ea4afa2a490e8p-1, 0x1.5d00p-49,
+ 0x1.eb9f4867ccae4p-1, 0x1.61a0p-46,
+ 0x1.ecf482d8e680dp-1, 0x1.5500p-48,
+ 0x1.ee4aaa2188514p-1, 0x1.6400p-51,
+ 0x1.efa1bee615a13p-1, -0x1.e800p-49,
+ 0x1.f0f9c1cb64106p-1, -0x1.a880p-48,
+ 0x1.f252b376bb963p-1, -0x1.c900p-45,
+ 0x1.f3ac948dd7275p-1, 0x1.a000p-53,
+ 0x1.f50765b6e4524p-1, -0x1.4f00p-48,
+ 0x1.f6632798844fdp-1, 0x1.a800p-51,
+ 0x1.f7bfdad9cbe38p-1, 0x1.abc0p-48,
+ 0x1.f91d802243c82p-1, -0x1.4600p-50,
+ 0x1.fa7c1819e908ep-1, -0x1.b0c0p-47,
+ 0x1.fbdba3692d511p-1, -0x1.0e00p-51,
+ 0x1.fd3c22b8f7194p-1, -0x1.0de8p-46,
+ 0x1.fe9d96b2a23eep-1, 0x1.e430p-49,
+ 0x1.0000000000000p+0, 0x0.0000p+0,
+ 0x1.00b1afa5abcbep+0, -0x1.3400p-52,
+ 0x1.0163da9fb3303p+0, -0x1.2170p-46,
+ 0x1.02168143b0282p+0, 0x1.a400p-52,
+ 0x1.02c9a3e77806cp+0, 0x1.f980p-49,
+ 0x1.037d42e11bbcap+0, -0x1.7400p-51,
+ 0x1.04315e86e7f89p+0, 0x1.8300p-50,
+ 0x1.04e5f72f65467p+0, -0x1.a3f0p-46,
+ 0x1.059b0d315855ap+0, -0x1.2840p-47,
+ 0x1.0650a0e3c1f95p+0, 0x1.1600p-48,
+ 0x1.0706b29ddf71ap+0, 0x1.5240p-46,
+ 0x1.07bd42b72a82dp+0, -0x1.9a00p-49,
+ 0x1.0874518759bd0p+0, 0x1.6400p-49,
+ 0x1.092bdf66607c8p+0, -0x1.0780p-47,
+ 0x1.09e3ecac6f383p+0, -0x1.8000p-54,
+ 0x1.0a9c79b1f3930p+0, 0x1.fa00p-48,
+ 0x1.0b5586cf988fcp+0, -0x1.ac80p-48,
+ 0x1.0c0f145e46c8ap+0, 0x1.9c00p-50,
+ 0x1.0cc922b724816p+0, 0x1.5200p-47,
+ 0x1.0d83b23395dd8p+0, -0x1.ad00p-48,
+ 0x1.0e3ec32d3d1f3p+0, 0x1.bac0p-46,
+ 0x1.0efa55fdfa9a6p+0, -0x1.4e80p-47,
+ 0x1.0fb66affed2f0p+0, -0x1.d300p-47,
+ 0x1.1073028d7234bp+0, 0x1.1500p-48,
+ 0x1.11301d0125b5bp+0, 0x1.c000p-49,
+ 0x1.11edbab5e2af9p+0, 0x1.6bc0p-46,
+ 0x1.12abdc06c31d5p+0, 0x1.8400p-49,
+ 0x1.136a814f2047dp+0, -0x1.ed00p-47,
+ 0x1.1429aaea92de9p+0, 0x1.8e00p-49,
+ 0x1.14e95934f3138p+0, 0x1.b400p-49,
+ 0x1.15a98c8a58e71p+0, 0x1.5300p-47,
+ 0x1.166a45471c3dfp+0, 0x1.3380p-47,
+ 0x1.172b83c7d5211p+0, 0x1.8d40p-45,
+ 0x1.17ed48695bb9fp+0, -0x1.5d00p-47,
+ 0x1.18af9388c8d93p+0, -0x1.c880p-46,
+ 0x1.1972658375d66p+0, 0x1.1f00p-46,
+ 0x1.1a35beb6fcba7p+0, 0x1.0480p-46,
+ 0x1.1af99f81387e3p+0, -0x1.7390p-43,
+ 0x1.1bbe084045d54p+0, 0x1.4e40p-45,
+ 0x1.1c82f95281c43p+0, -0x1.a200p-47,
+ 0x1.1d4873168b9b2p+0, 0x1.3800p-49,
+ 0x1.1e0e75eb44031p+0, 0x1.ac00p-49,
+ 0x1.1ed5022fcd938p+0, 0x1.1900p-47,
+ 0x1.1f9c18438cdf7p+0, -0x1.b780p-46,
+ 0x1.2063b88628d8fp+0, 0x1.d940p-45,
+ 0x1.212be3578a81ep+0, 0x1.8000p-50,
+ 0x1.21f49917ddd41p+0, 0x1.b340p-45,
+ 0x1.22bdda2791323p+0, 0x1.9f80p-46,
+ 0x1.2387a6e7561e7p+0, -0x1.9c80p-46,
+ 0x1.2451ffb821427p+0, 0x1.2300p-47,
+ 0x1.251ce4fb2a602p+0, -0x1.3480p-46,
+ 0x1.25e85711eceb0p+0, 0x1.2700p-46,
+ 0x1.26b4565e27d16p+0, 0x1.1d00p-46,
+ 0x1.2780e341de00fp+0, 0x1.1ee0p-44,
+ 0x1.284dfe1f5633ep+0, -0x1.4c00p-46,
+ 0x1.291ba7591bb30p+0, -0x1.3d80p-46,
+ 0x1.29e9df51fdf09p+0, 0x1.8b00p-47,
+ 0x1.2ab8a66d10e9bp+0, -0x1.27c0p-45,
+ 0x1.2b87fd0dada3ap+0, 0x1.a340p-45,
+ 0x1.2c57e39771af9p+0, -0x1.0800p-46,
+ 0x1.2d285a6e402d9p+0, -0x1.ed00p-47,
+ 0x1.2df961f641579p+0, -0x1.4200p-48,
+ 0x1.2ecafa93e2ecfp+0, -0x1.4980p-45,
+ 0x1.2f9d24abd8822p+0, -0x1.6300p-46,
+ 0x1.306fe0a31b625p+0, -0x1.2360p-44,
+ 0x1.31432edeea50bp+0, -0x1.0df8p-40,
+ 0x1.32170fc4cd7b8p+0, -0x1.2480p-45,
+ 0x1.32eb83ba8e9a2p+0, -0x1.5980p-45,
+ 0x1.33c08b2641766p+0, 0x1.ed00p-46,
+ 0x1.3496266e3fa27p+0, -0x1.c000p-50,
+ 0x1.356c55f929f0fp+0, -0x1.0d80p-44,
+ 0x1.36431a2de88b9p+0, 0x1.2c80p-45,
+ 0x1.371a7373aaa39p+0, 0x1.0600p-45,
+ 0x1.37f26231e74fep+0, -0x1.6600p-46,
+ 0x1.38cae6d05d838p+0, -0x1.ae00p-47,
+ 0x1.39a401b713ec3p+0, -0x1.4720p-43,
+ 0x1.3a7db34e5a020p+0, 0x1.8200p-47,
+ 0x1.3b57fbfec6e95p+0, 0x1.e800p-44,
+ 0x1.3c32dc313a8f2p+0, 0x1.f800p-49,
+ 0x1.3d0e544ede122p+0, -0x1.7a00p-46,
+ 0x1.3dea64c1234bbp+0, 0x1.6300p-45,
+ 0x1.3ec70df1c4eccp+0, -0x1.8a60p-43,
+ 0x1.3fa4504ac7e8cp+0, -0x1.cdc0p-44,
+ 0x1.40822c367a0bbp+0, 0x1.5b80p-45,
+ 0x1.4160a21f72e95p+0, 0x1.ec00p-46,
+ 0x1.423fb27094646p+0, -0x1.3600p-46,
+ 0x1.431f5d950a920p+0, 0x1.3980p-45,
+ 0x1.43ffa3f84b9ebp+0, 0x1.a000p-48,
+ 0x1.44e0860618919p+0, -0x1.6c00p-48,
+ 0x1.45c2042a7d201p+0, -0x1.bc00p-47,
+ 0x1.46a41ed1d0016p+0, -0x1.2800p-46,
+ 0x1.4786d668b3326p+0, 0x1.0e00p-44,
+ 0x1.486a2b5c13c00p+0, -0x1.d400p-45,
+ 0x1.494e1e192af04p+0, 0x1.c200p-47,
+ 0x1.4a32af0d7d372p+0, -0x1.e500p-46,
+ 0x1.4b17dea6db801p+0, 0x1.7800p-47,
+ 0x1.4bfdad53629e1p+0, -0x1.3800p-46,
+ 0x1.4ce41b817c132p+0, 0x1.0800p-47,
+ 0x1.4dcb299fddddbp+0, 0x1.c700p-45,
+ 0x1.4eb2d81d8ab96p+0, -0x1.ce00p-46,
+ 0x1.4f9b2769d2d02p+0, 0x1.9200p-46,
+ 0x1.508417f4531c1p+0, -0x1.8c00p-47,
+ 0x1.516daa2cf662ap+0, -0x1.a000p-48,
+ 0x1.5257de83f51eap+0, 0x1.a080p-43,
+ 0x1.5342b569d4edap+0, -0x1.6d80p-45,
+ 0x1.542e2f4f6ac1ap+0, -0x1.2440p-44,
+ 0x1.551a4ca5d94dbp+0, 0x1.83c0p-43,
+ 0x1.56070dde9116bp+0, 0x1.4b00p-45,
+ 0x1.56f4736b529dep+0, 0x1.15a0p-43,
+ 0x1.57e27dbe2c40ep+0, -0x1.9e00p-45,
+ 0x1.58d12d497c76fp+0, -0x1.3080p-45,
+ 0x1.59c0827ff0b4cp+0, 0x1.dec0p-43,
+ 0x1.5ab07dd485427p+0, -0x1.4000p-51,
+ 0x1.5ba11fba87af4p+0, 0x1.0080p-44,
+ 0x1.5c9268a59460bp+0, -0x1.6c80p-45,
+ 0x1.5d84590998e3fp+0, 0x1.69a0p-43,
+ 0x1.5e76f15ad20e1p+0, -0x1.b400p-46,
+ 0x1.5f6a320dcebcap+0, 0x1.7700p-46,
+ 0x1.605e1b976dcb8p+0, 0x1.6f80p-45,
+ 0x1.6152ae6cdf715p+0, 0x1.1000p-47,
+ 0x1.6247eb03a5531p+0, -0x1.5d00p-46,
+ 0x1.633dd1d1929b5p+0, -0x1.2d00p-46,
+ 0x1.6434634ccc313p+0, -0x1.a800p-49,
+ 0x1.652b9febc8efap+0, -0x1.8600p-45,
+ 0x1.6623882553397p+0, 0x1.1fe0p-40,
+ 0x1.671c1c708328ep+0, -0x1.7200p-44,
+ 0x1.68155d44ca97ep+0, 0x1.6800p-49,
+ 0x1.690f4b19e9471p+0, -0x1.9780p-45,
+};
+
+/*
+ * exp2(x): compute the base 2 exponential of x
+ *
+ * Accuracy: Peak error < 0.503 ulp for normalized results.
+ *
+ * Method: (accurate tables)
+ *
+ * Reduce x:
+ * x = k + y, for integer k and |y| <= 1/2.
+ * Thus we have exp2(x) = 2**k * exp2(y).
+ *
+ * Reduce y:
+ * y = i/TBLSIZE + z - eps[i] for integer i near y * TBLSIZE.
+ * Thus we have exp2(y) = exp2(i/TBLSIZE) * exp2(z - eps[i]),
+ * with |z - eps[i]| <= 2**-9 + 2**-39 for the table used.
+ *
+ * We compute exp2(i/TBLSIZE) via table lookup and exp2(z - eps[i]) via
+ * a degree-5 minimax polynomial with maximum error under 1.3 * 2**-61.
+ * The values in exp2t[] and eps[] are chosen such that
+ * exp2t[i] = exp2(i/TBLSIZE + eps[i]), and eps[i] is a small offset such
+ * that exp2t[i] is accurate to 2**-64.
+ *
+ * Note that the range of i is +-TBLSIZE/2, so we actually index the tables
+ * by i0 = i + TBLSIZE/2. For cache efficiency, exp2t[] and eps[] are
+ * virtual tables, interleaved in the real table tbl[].
+ *
+ * This method is due to Gal, with many details due to Gal and Bachelis:
+ *
+ * Gal, S. and Bachelis, B. An Accurate Elementary Mathematical Library
+ * for the IEEE Floating Point Standard. TOMS 17(1), 26-46 (1991).
+ */
+double exp2(double x)
+{
+ double_t r, t, z;
+ uint32_t ix, i0;
+ union {double f; uint64_t i;} u = {x};
+ union {uint32_t u; int32_t i;} k;
+
+ /* Filter out exceptional cases. */
+ ix = u.i>>32 & 0x7fffffff;
+ if (ix >= 0x408ff000) { /* |x| >= 1022 or nan */
+ if (ix >= 0x40900000 && u.i>>63 == 0) { /* x >= 1024 or nan */
+ /* overflow */
+ x *= 0x1p1023;
+ return x;
+ }
+ if (ix >= 0x7ff00000) /* -inf or -nan */
+ return -1/x;
+ if (u.i>>63) { /* x <= -1022 */
+ /* underflow */
+ if (x <= -1075 || x - 0x1p52 + 0x1p52 != x)
+ FORCE_EVAL((float)(-0x1p-149/x));
+ if (x <= -1075)
+ return 0;
+ }
+ } else if (ix < 0x3c900000) { /* |x| < 0x1p-54 */
+ return 1.0 + x;
+ }
+
+ /* Reduce x, computing z, i0, and k. */
+ u.f = x + redux;
+ i0 = u.i;
+ i0 += TBLSIZE / 2;
+ k.u = i0 / TBLSIZE * TBLSIZE;
+ k.i /= TBLSIZE;
+ i0 %= TBLSIZE;
+ u.f -= redux;
+ z = x - u.f;
+
+ /* Compute r = exp2(y) = exp2t[i0] * p(z - eps[i]). */
+ t = tbl[2*i0]; /* exp2t[i0] */
+ z -= tbl[2*i0 + 1]; /* eps[i0] */
+ r = t + t * z * (P1 + z * (P2 + z * (P3 + z * (P4 + z * P5))));
+
+ return scalbn(r, k.i);
+}
diff --git a/lib/libc/src/musl-math/exp2f.c b/lib/libc/src/musl-math/exp2f.c
new file mode 100644
index 0000000..296b634
--- /dev/null
+++ b/lib/libc/src/musl-math/exp2f.c
@@ -0,0 +1,126 @@
+/* origin: FreeBSD /usr/src/lib/msun/src/s_exp2f.c */
+/*-
+ * Copyright (c) 2005 David Schultz <das@FreeBSD.ORG>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "libm.h"
+
+#define TBLSIZE 16
+
+static const float
+redux = 0x1.8p23f / TBLSIZE,
+P1 = 0x1.62e430p-1f,
+P2 = 0x1.ebfbe0p-3f,
+P3 = 0x1.c6b348p-5f,
+P4 = 0x1.3b2c9cp-7f;
+
+static const double exp2ft[TBLSIZE] = {
+ 0x1.6a09e667f3bcdp-1,
+ 0x1.7a11473eb0187p-1,
+ 0x1.8ace5422aa0dbp-1,
+ 0x1.9c49182a3f090p-1,
+ 0x1.ae89f995ad3adp-1,
+ 0x1.c199bdd85529cp-1,
+ 0x1.d5818dcfba487p-1,
+ 0x1.ea4afa2a490dap-1,
+ 0x1.0000000000000p+0,
+ 0x1.0b5586cf9890fp+0,
+ 0x1.172b83c7d517bp+0,
+ 0x1.2387a6e756238p+0,
+ 0x1.306fe0a31b715p+0,
+ 0x1.3dea64c123422p+0,
+ 0x1.4bfdad5362a27p+0,
+ 0x1.5ab07dd485429p+0,
+};
+
+/*
+ * exp2f(x): compute the base 2 exponential of x
+ *
+ * Accuracy: Peak error < 0.501 ulp; location of peak: -0.030110927.
+ *
+ * Method: (equally-spaced tables)
+ *
+ * Reduce x:
+ * x = k + y, for integer k and |y| <= 1/2.
+ * Thus we have exp2f(x) = 2**k * exp2(y).
+ *
+ * Reduce y:
+ * y = i/TBLSIZE + z for integer i near y * TBLSIZE.
+ * Thus we have exp2(y) = exp2(i/TBLSIZE) * exp2(z),
+ * with |z| <= 2**-(TBLSIZE+1).
+ *
+ * We compute exp2(i/TBLSIZE) via table lookup and exp2(z) via a
+ * degree-4 minimax polynomial with maximum error under 1.4 * 2**-33.
+ * Using double precision for everything except the reduction makes
+ * roundoff error insignificant and simplifies the scaling step.
+ *
+ * This method is due to Tang, but I do not use his suggested parameters:
+ *
+ * Tang, P. Table-driven Implementation of the Exponential Function
+ * in IEEE Floating-Point Arithmetic. TOMS 15(2), 144-157 (1989).
+ */
+float exp2f(float x)
+{
+ double_t t, r, z;
+ union {float f; uint32_t i;} u = {x};
+ union {double f; uint64_t i;} uk;
+ uint32_t ix, i0, k;
+
+ /* Filter out exceptional cases. */
+ ix = u.i & 0x7fffffff;
+ if (ix > 0x42fc0000) { /* |x| > 126 */
+ if (ix > 0x7f800000) /* NaN */
+ return x;
+ if (u.i >= 0x43000000 && u.i < 0x80000000) { /* x >= 128 */
+ x *= 0x1p127f;
+ return x;
+ }
+ if (u.i >= 0x80000000) { /* x < -126 */
+ if (u.i >= 0xc3160000 || (u.i & 0x0000ffff))
+ FORCE_EVAL(-0x1p-149f/x);
+ if (u.i >= 0xc3160000) /* x <= -150 */
+ return 0;
+ }
+ } else if (ix <= 0x33000000) { /* |x| <= 0x1p-25 */
+ return 1.0f + x;
+ }
+
+ /* Reduce x, computing z, i0, and k. */
+ u.f = x + redux;
+ i0 = u.i;
+ i0 += TBLSIZE / 2;
+ k = i0 / TBLSIZE;
+ uk.i = (uint64_t)(0x3ff + k)<<52;
+ i0 &= TBLSIZE - 1;
+ u.f -= redux;
+ z = x - u.f;
+ /* Compute r = exp2(y) = exp2ft[i0] * p(z). */
+ r = exp2ft[i0];
+ t = r * z;
+ r = r + t * (P1 + z * P2) + t * (z * z) * (P3 + z * P4);
+
+ /* Scale by 2**k */
+ return r * uk.f;
+}
diff --git a/lib/libc/src/musl-math/exp2l.c b/lib/libc/src/musl-math/exp2l.c
new file mode 100644
index 0000000..3565c1e
--- /dev/null
+++ b/lib/libc/src/musl-math/exp2l.c
@@ -0,0 +1,619 @@
+/* origin: FreeBSD /usr/src/lib/msun/ld80/s_exp2l.c and /usr/src/lib/msun/ld128/s_exp2l.c */
+/*-
+ * Copyright (c) 2005-2008 David Schultz <das@FreeBSD.ORG>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "libm.h"
+
+#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
+long double exp2l(long double x)
+{
+ return exp2(x);
+}
+#elif LDBL_MANT_DIG == 64 && LDBL_MAX_EXP == 16384
+#define TBLBITS 7
+#define TBLSIZE (1 << TBLBITS)
+
+static const double
+redux = 0x1.8p63 / TBLSIZE,
+P1 = 0x1.62e42fefa39efp-1,
+P2 = 0x1.ebfbdff82c58fp-3,
+P3 = 0x1.c6b08d7049fap-5,
+P4 = 0x1.3b2ab6fba4da5p-7,
+P5 = 0x1.5d8804780a736p-10,
+P6 = 0x1.430918835e33dp-13;
+
+static const double tbl[TBLSIZE * 2] = {
+ 0x1.6a09e667f3bcdp-1, -0x1.bdd3413b2648p-55,
+ 0x1.6c012750bdabfp-1, -0x1.2895667ff0cp-57,
+ 0x1.6dfb23c651a2fp-1, -0x1.bbe3a683c88p-58,
+ 0x1.6ff7df9519484p-1, -0x1.83c0f25860fp-56,
+ 0x1.71f75e8ec5f74p-1, -0x1.16e4786887bp-56,
+ 0x1.73f9a48a58174p-1, -0x1.0a8d96c65d5p-55,
+ 0x1.75feb564267c9p-1, -0x1.0245957316ep-55,
+ 0x1.780694fde5d3fp-1, 0x1.866b80a0216p-55,
+ 0x1.7a11473eb0187p-1, -0x1.41577ee0499p-56,
+ 0x1.7c1ed0130c132p-1, 0x1.f124cd1164ep-55,
+ 0x1.7e2f336cf4e62p-1, 0x1.05d02ba157ap-57,
+ 0x1.80427543e1a12p-1, -0x1.27c86626d97p-55,
+ 0x1.82589994cce13p-1, -0x1.d4c1dd41533p-55,
+ 0x1.8471a4623c7adp-1, -0x1.8d684a341cep-56,
+ 0x1.868d99b4492edp-1, -0x1.fc6f89bd4f68p-55,
+ 0x1.88ac7d98a6699p-1, 0x1.994c2f37cb5p-55,
+ 0x1.8ace5422aa0dbp-1, 0x1.6e9f156864bp-55,
+ 0x1.8cf3216b5448cp-1, -0x1.0d55e32e9e4p-57,
+ 0x1.8f1ae99157736p-1, 0x1.5cc13a2e397p-56,
+ 0x1.9145b0b91ffc6p-1, -0x1.dd6792e5825p-55,
+ 0x1.93737b0cdc5e5p-1, -0x1.75fc781b58p-58,
+ 0x1.95a44cbc8520fp-1, -0x1.64b7c96a5fp-57,
+ 0x1.97d829fde4e5p-1, -0x1.d185b7c1b86p-55,
+ 0x1.9a0f170ca07bap-1, -0x1.173bd91cee6p-55,
+ 0x1.9c49182a3f09p-1, 0x1.c7c46b071f2p-57,
+ 0x1.9e86319e32323p-1, 0x1.824ca78e64cp-57,
+ 0x1.a0c667b5de565p-1, -0x1.359495d1cd5p-55,
+ 0x1.a309bec4a2d33p-1, 0x1.6305c7ddc368p-55,
+ 0x1.a5503b23e255dp-1, -0x1.d2f6edb8d42p-55,
+ 0x1.a799e1330b358p-1, 0x1.bcb7ecac564p-55,
+ 0x1.a9e6b5579fdbfp-1, 0x1.0fac90ef7fdp-55,
+ 0x1.ac36bbfd3f37ap-1, -0x1.f9234cae76dp-56,
+ 0x1.ae89f995ad3adp-1, 0x1.7a1cd345dcc8p-55,
+ 0x1.b0e07298db666p-1, -0x1.bdef54c80e4p-55,
+ 0x1.b33a2b84f15fbp-1, -0x1.2805e3084d8p-58,
+ 0x1.b59728de5593ap-1, -0x1.c71dfbbba6ep-55,
+ 0x1.b7f76f2fb5e47p-1, -0x1.5584f7e54acp-57,
+ 0x1.ba5b030a1064ap-1, -0x1.efcd30e5429p-55,
+ 0x1.bcc1e904bc1d2p-1, 0x1.23dd07a2d9fp-56,
+ 0x1.bf2c25bd71e09p-1, -0x1.efdca3f6b9c8p-55,
+ 0x1.c199bdd85529cp-1, 0x1.11065895049p-56,
+ 0x1.c40ab5fffd07ap-1, 0x1.b4537e083c6p-55,
+ 0x1.c67f12e57d14bp-1, 0x1.2884dff483c8p-55,
+ 0x1.c8f6d9406e7b5p-1, 0x1.1acbc48805cp-57,
+ 0x1.cb720dcef9069p-1, 0x1.503cbd1e94ap-57,
+ 0x1.cdf0b555dc3fap-1, -0x1.dd83b53829dp-56,
+ 0x1.d072d4a07897cp-1, -0x1.cbc3743797a8p-55,
+ 0x1.d2f87080d89f2p-1, -0x1.d487b719d858p-55,
+ 0x1.d5818dcfba487p-1, 0x1.2ed02d75b37p-56,
+ 0x1.d80e316c98398p-1, -0x1.11ec18bedep-55,
+ 0x1.da9e603db3285p-1, 0x1.c2300696db5p-55,
+ 0x1.dd321f301b46p-1, 0x1.2da5778f019p-55,
+ 0x1.dfc97337b9b5fp-1, -0x1.1a5cd4f184b8p-55,
+ 0x1.e264614f5a129p-1, -0x1.7b627817a148p-55,
+ 0x1.e502ee78b3ff6p-1, 0x1.39e8980a9cdp-56,
+ 0x1.e7a51fbc74c83p-1, 0x1.2d522ca0c8ep-55,
+ 0x1.ea4afa2a490dap-1, -0x1.e9c23179c288p-55,
+ 0x1.ecf482d8e67f1p-1, -0x1.c93f3b411ad8p-55,
+ 0x1.efa1bee615a27p-1, 0x1.dc7f486a4b68p-55,
+ 0x1.f252b376bba97p-1, 0x1.3a1a5bf0d8e8p-55,
+ 0x1.f50765b6e454p-1, 0x1.9d3e12dd8a18p-55,
+ 0x1.f7bfdad9cbe14p-1, -0x1.dbb12d00635p-55,
+ 0x1.fa7c1819e90d8p-1, 0x1.74853f3a593p-56,
+ 0x1.fd3c22b8f71f1p-1, 0x1.2eb74966578p-58,
+ 0x1p+0, 0x0p+0,
+ 0x1.0163da9fb3335p+0, 0x1.b61299ab8cd8p-54,
+ 0x1.02c9a3e778061p+0, -0x1.19083535b08p-56,
+ 0x1.04315e86e7f85p+0, -0x1.0a31c1977c98p-54,
+ 0x1.059b0d3158574p+0, 0x1.d73e2a475b4p-55,
+ 0x1.0706b29ddf6dep+0, -0x1.c91dfe2b13cp-55,
+ 0x1.0874518759bc8p+0, 0x1.186be4bb284p-57,
+ 0x1.09e3ecac6f383p+0, 0x1.14878183161p-54,
+ 0x1.0b5586cf9890fp+0, 0x1.8a62e4adc61p-54,
+ 0x1.0cc922b7247f7p+0, 0x1.01edc16e24f8p-54,
+ 0x1.0e3ec32d3d1a2p+0, 0x1.03a1727c58p-59,
+ 0x1.0fb66affed31bp+0, -0x1.b9bedc44ebcp-57,
+ 0x1.11301d0125b51p+0, -0x1.6c51039449bp-54,
+ 0x1.12abdc06c31ccp+0, -0x1.1b514b36ca8p-58,
+ 0x1.1429aaea92dep+0, -0x1.32fbf9af1368p-54,
+ 0x1.15a98c8a58e51p+0, 0x1.2406ab9eeabp-55,
+ 0x1.172b83c7d517bp+0, -0x1.19041b9d78ap-55,
+ 0x1.18af9388c8deap+0, -0x1.11023d1970f8p-54,
+ 0x1.1a35beb6fcb75p+0, 0x1.e5b4c7b4969p-55,
+ 0x1.1bbe084045cd4p+0, -0x1.95386352ef6p-54,
+ 0x1.1d4873168b9aap+0, 0x1.e016e00a264p-54,
+ 0x1.1ed5022fcd91dp+0, -0x1.1df98027bb78p-54,
+ 0x1.2063b88628cd6p+0, 0x1.dc775814a85p-55,
+ 0x1.21f49917ddc96p+0, 0x1.2a97e9494a6p-55,
+ 0x1.2387a6e756238p+0, 0x1.9b07eb6c7058p-54,
+ 0x1.251ce4fb2a63fp+0, 0x1.ac155bef4f5p-55,
+ 0x1.26b4565e27cddp+0, 0x1.2bd339940eap-55,
+ 0x1.284dfe1f56381p+0, -0x1.a4c3a8c3f0d8p-54,
+ 0x1.29e9df51fdee1p+0, 0x1.612e8afad12p-55,
+ 0x1.2b87fd0dad99p+0, -0x1.10adcd6382p-59,
+ 0x1.2d285a6e4030bp+0, 0x1.0024754db42p-54,
+ 0x1.2ecafa93e2f56p+0, 0x1.1ca0f45d524p-56,
+ 0x1.306fe0a31b715p+0, 0x1.6f46ad23183p-55,
+ 0x1.32170fc4cd831p+0, 0x1.a9ce78e1804p-55,
+ 0x1.33c08b26416ffp+0, 0x1.327218436598p-54,
+ 0x1.356c55f929ff1p+0, -0x1.b5cee5c4e46p-55,
+ 0x1.371a7373aa9cbp+0, -0x1.63aeabf42ebp-54,
+ 0x1.38cae6d05d866p+0, -0x1.e958d3c99048p-54,
+ 0x1.3a7db34e59ff7p+0, -0x1.5e436d661f6p-56,
+ 0x1.3c32dc313a8e5p+0, -0x1.efff8375d2ap-54,
+ 0x1.3dea64c123422p+0, 0x1.ada0911f09fp-55,
+ 0x1.3fa4504ac801cp+0, -0x1.7d023f956fap-54,
+ 0x1.4160a21f72e2ap+0, -0x1.ef3691c309p-58,
+ 0x1.431f5d950a897p+0, -0x1.1c7dde35f7ap-55,
+ 0x1.44e086061892dp+0, 0x1.89b7a04ef8p-59,
+ 0x1.46a41ed1d0057p+0, 0x1.c944bd1648a8p-54,
+ 0x1.486a2b5c13cdp+0, 0x1.3c1a3b69062p-56,
+ 0x1.4a32af0d7d3dep+0, 0x1.9cb62f3d1be8p-54,
+ 0x1.4bfdad5362a27p+0, 0x1.d4397afec42p-56,
+ 0x1.4dcb299fddd0dp+0, 0x1.8ecdbbc6a78p-54,
+ 0x1.4f9b2769d2ca7p+0, -0x1.4b309d25958p-54,
+ 0x1.516daa2cf6642p+0, -0x1.f768569bd94p-55,
+ 0x1.5342b569d4f82p+0, -0x1.07abe1db13dp-55,
+ 0x1.551a4ca5d920fp+0, -0x1.d689cefede6p-55,
+ 0x1.56f4736b527dap+0, 0x1.9bb2c011d938p-54,
+ 0x1.58d12d497c7fdp+0, 0x1.295e15b9a1ep-55,
+ 0x1.5ab07dd485429p+0, 0x1.6324c0546478p-54,
+ 0x1.5c9268a5946b7p+0, 0x1.c4b1b81698p-60,
+ 0x1.5e76f15ad2148p+0, 0x1.ba6f93080e68p-54,
+ 0x1.605e1b976dc09p+0, -0x1.3e2429b56de8p-54,
+ 0x1.6247eb03a5585p+0, -0x1.383c17e40b48p-54,
+ 0x1.6434634ccc32p+0, -0x1.c483c759d89p-55,
+ 0x1.6623882552225p+0, -0x1.bb60987591cp-54,
+ 0x1.68155d44ca973p+0, 0x1.038ae44f74p-57,
+};
+
+/*
+ * exp2l(x): compute the base 2 exponential of x
+ *
+ * Accuracy: Peak error < 0.511 ulp.
+ *
+ * Method: (equally-spaced tables)
+ *
+ * Reduce x:
+ * x = 2**k + y, for integer k and |y| <= 1/2.
+ * Thus we have exp2l(x) = 2**k * exp2(y).
+ *
+ * Reduce y:
+ * y = i/TBLSIZE + z for integer i near y * TBLSIZE.
+ * Thus we have exp2(y) = exp2(i/TBLSIZE) * exp2(z),
+ * with |z| <= 2**-(TBLBITS+1).
+ *
+ * We compute exp2(i/TBLSIZE) via table lookup and exp2(z) via a
+ * degree-6 minimax polynomial with maximum error under 2**-69.
+ * The table entries each have 104 bits of accuracy, encoded as
+ * a pair of double precision values.
+ */
+long double exp2l(long double x)
+{
+ union ldshape u = {x};
+ int e = u.i.se & 0x7fff;
+ long double r, z;
+ uint32_t i0;
+ union {uint32_t u; int32_t i;} k;
+
+ /* Filter out exceptional cases. */
+ if (e >= 0x3fff + 13) { /* |x| >= 8192 or x is NaN */
+ if (u.i.se >= 0x3fff + 14 && u.i.se >> 15 == 0)
+ /* overflow */
+ return x * 0x1p16383L;
+ if (e == 0x7fff) /* -inf or -nan */
+ return -1/x;
+ if (x < -16382) {
+ if (x <= -16446 || x - 0x1p63 + 0x1p63 != x)
+ /* underflow */
+ FORCE_EVAL((float)(-0x1p-149/x));
+ if (x <= -16446)
+ return 0;
+ }
+ } else if (e < 0x3fff - 64) {
+ return 1 + x;
+ }
+
+ /*
+ * Reduce x, computing z, i0, and k. The low bits of x + redux
+ * contain the 16-bit integer part of the exponent (k) followed by
+ * TBLBITS fractional bits (i0). We use bit tricks to extract these
+ * as integers, then set z to the remainder.
+ *
+ * Example: Suppose x is 0xabc.123456p0 and TBLBITS is 8.
+ * Then the low-order word of x + redux is 0x000abc12,
+ * We split this into k = 0xabc and i0 = 0x12 (adjusted to
+ * index into the table), then we compute z = 0x0.003456p0.
+ */
+ u.f = x + redux;
+ i0 = u.i.m + TBLSIZE / 2;
+ k.u = i0 / TBLSIZE * TBLSIZE;
+ k.i /= TBLSIZE;
+ i0 %= TBLSIZE;
+ u.f -= redux;
+ z = x - u.f;
+
+ /* Compute r = exp2l(y) = exp2lt[i0] * p(z). */
+ long double t_hi = tbl[2*i0];
+ long double t_lo = tbl[2*i0 + 1];
+ /* XXX This gives > 1 ulp errors outside of FE_TONEAREST mode */
+ r = t_lo + (t_hi + t_lo) * z * (P1 + z * (P2 + z * (P3 + z * (P4
+ + z * (P5 + z * P6))))) + t_hi;
+
+ return scalbnl(r, k.i);
+}
+#elif LDBL_MANT_DIG == 113 && LDBL_MAX_EXP == 16384
+#define TBLBITS 7
+#define TBLSIZE (1 << TBLBITS)
+
+static const long double
+ P1 = 0x1.62e42fefa39ef35793c7673007e6p-1L,
+ P2 = 0x1.ebfbdff82c58ea86f16b06ec9736p-3L,
+ P3 = 0x1.c6b08d704a0bf8b33a762bad3459p-5L,
+ P4 = 0x1.3b2ab6fba4e7729ccbbe0b4f3fc2p-7L,
+ P5 = 0x1.5d87fe78a67311071dee13fd11d9p-10L,
+ P6 = 0x1.430912f86c7876f4b663b23c5fe5p-13L;
+
+static const double
+ P7 = 0x1.ffcbfc588b041p-17,
+ P8 = 0x1.62c0223a5c7c7p-20,
+ P9 = 0x1.b52541ff59713p-24,
+ P10 = 0x1.e4cf56a391e22p-28,
+ redux = 0x1.8p112 / TBLSIZE;
+
+static const long double tbl[TBLSIZE] = {
+ 0x1.6a09e667f3bcc908b2fb1366dfeap-1L,
+ 0x1.6c012750bdabeed76a99800f4edep-1L,
+ 0x1.6dfb23c651a2ef220e2cbe1bc0d4p-1L,
+ 0x1.6ff7df9519483cf87e1b4f3e1e98p-1L,
+ 0x1.71f75e8ec5f73dd2370f2ef0b148p-1L,
+ 0x1.73f9a48a58173bd5c9a4e68ab074p-1L,
+ 0x1.75feb564267c8bf6e9aa33a489a8p-1L,
+ 0x1.780694fde5d3f619ae02808592a4p-1L,
+ 0x1.7a11473eb0186d7d51023f6ccb1ap-1L,
+ 0x1.7c1ed0130c1327c49334459378dep-1L,
+ 0x1.7e2f336cf4e62105d02ba1579756p-1L,
+ 0x1.80427543e1a11b60de67649a3842p-1L,
+ 0x1.82589994cce128acf88afab34928p-1L,
+ 0x1.8471a4623c7acce52f6b97c6444cp-1L,
+ 0x1.868d99b4492ec80e41d90ac2556ap-1L,
+ 0x1.88ac7d98a669966530bcdf2d4cc0p-1L,
+ 0x1.8ace5422aa0db5ba7c55a192c648p-1L,
+ 0x1.8cf3216b5448bef2aa1cd161c57ap-1L,
+ 0x1.8f1ae991577362b982745c72eddap-1L,
+ 0x1.9145b0b91ffc588a61b469f6b6a0p-1L,
+ 0x1.93737b0cdc5e4f4501c3f2540ae8p-1L,
+ 0x1.95a44cbc8520ee9b483695a0e7fep-1L,
+ 0x1.97d829fde4e4f8b9e920f91e8eb6p-1L,
+ 0x1.9a0f170ca07b9ba3109b8c467844p-1L,
+ 0x1.9c49182a3f0901c7c46b071f28dep-1L,
+ 0x1.9e86319e323231824ca78e64c462p-1L,
+ 0x1.a0c667b5de564b29ada8b8cabbacp-1L,
+ 0x1.a309bec4a2d3358c171f770db1f4p-1L,
+ 0x1.a5503b23e255c8b424491caf88ccp-1L,
+ 0x1.a799e1330b3586f2dfb2b158f31ep-1L,
+ 0x1.a9e6b5579fdbf43eb243bdff53a2p-1L,
+ 0x1.ac36bbfd3f379c0db966a3126988p-1L,
+ 0x1.ae89f995ad3ad5e8734d17731c80p-1L,
+ 0x1.b0e07298db66590842acdfc6fb4ep-1L,
+ 0x1.b33a2b84f15faf6bfd0e7bd941b0p-1L,
+ 0x1.b59728de559398e3881111648738p-1L,
+ 0x1.b7f76f2fb5e46eaa7b081ab53ff6p-1L,
+ 0x1.ba5b030a10649840cb3c6af5b74cp-1L,
+ 0x1.bcc1e904bc1d2247ba0f45b3d06cp-1L,
+ 0x1.bf2c25bd71e088408d7025190cd0p-1L,
+ 0x1.c199bdd85529c2220cb12a0916bap-1L,
+ 0x1.c40ab5fffd07a6d14df820f17deap-1L,
+ 0x1.c67f12e57d14b4a2137fd20f2a26p-1L,
+ 0x1.c8f6d9406e7b511acbc48805c3f6p-1L,
+ 0x1.cb720dcef90691503cbd1e949d0ap-1L,
+ 0x1.cdf0b555dc3f9c44f8958fac4f12p-1L,
+ 0x1.d072d4a07897b8d0f22f21a13792p-1L,
+ 0x1.d2f87080d89f18ade123989ea50ep-1L,
+ 0x1.d5818dcfba48725da05aeb66dff8p-1L,
+ 0x1.d80e316c98397bb84f9d048807a0p-1L,
+ 0x1.da9e603db3285708c01a5b6d480cp-1L,
+ 0x1.dd321f301b4604b695de3c0630c0p-1L,
+ 0x1.dfc97337b9b5eb968cac39ed284cp-1L,
+ 0x1.e264614f5a128a12761fa17adc74p-1L,
+ 0x1.e502ee78b3ff6273d130153992d0p-1L,
+ 0x1.e7a51fbc74c834b548b2832378a4p-1L,
+ 0x1.ea4afa2a490d9858f73a18f5dab4p-1L,
+ 0x1.ecf482d8e67f08db0312fb949d50p-1L,
+ 0x1.efa1bee615a27771fd21a92dabb6p-1L,
+ 0x1.f252b376bba974e8696fc3638f24p-1L,
+ 0x1.f50765b6e4540674f84b762861a6p-1L,
+ 0x1.f7bfdad9cbe138913b4bfe72bd78p-1L,
+ 0x1.fa7c1819e90d82e90a7e74b26360p-1L,
+ 0x1.fd3c22b8f71f10975ba4b32bd006p-1L,
+ 0x1.0000000000000000000000000000p+0L,
+ 0x1.0163da9fb33356d84a66ae336e98p+0L,
+ 0x1.02c9a3e778060ee6f7caca4f7a18p+0L,
+ 0x1.04315e86e7f84bd738f9a20da442p+0L,
+ 0x1.059b0d31585743ae7c548eb68c6ap+0L,
+ 0x1.0706b29ddf6ddc6dc403a9d87b1ep+0L,
+ 0x1.0874518759bc808c35f25d942856p+0L,
+ 0x1.09e3ecac6f3834521e060c584d5cp+0L,
+ 0x1.0b5586cf9890f6298b92b7184200p+0L,
+ 0x1.0cc922b7247f7407b705b893dbdep+0L,
+ 0x1.0e3ec32d3d1a2020742e4f8af794p+0L,
+ 0x1.0fb66affed31af232091dd8a169ep+0L,
+ 0x1.11301d0125b50a4ebbf1aed9321cp+0L,
+ 0x1.12abdc06c31cbfb92bad324d6f84p+0L,
+ 0x1.1429aaea92ddfb34101943b2588ep+0L,
+ 0x1.15a98c8a58e512480d573dd562aep+0L,
+ 0x1.172b83c7d517adcdf7c8c50eb162p+0L,
+ 0x1.18af9388c8de9bbbf70b9a3c269cp+0L,
+ 0x1.1a35beb6fcb753cb698f692d2038p+0L,
+ 0x1.1bbe084045cd39ab1e72b442810ep+0L,
+ 0x1.1d4873168b9aa7805b8028990be8p+0L,
+ 0x1.1ed5022fcd91cb8819ff61121fbep+0L,
+ 0x1.2063b88628cd63b8eeb0295093f6p+0L,
+ 0x1.21f49917ddc962552fd29294bc20p+0L,
+ 0x1.2387a6e75623866c1fadb1c159c0p+0L,
+ 0x1.251ce4fb2a63f3582ab7de9e9562p+0L,
+ 0x1.26b4565e27cdd257a673281d3068p+0L,
+ 0x1.284dfe1f5638096cf15cf03c9fa0p+0L,
+ 0x1.29e9df51fdee12c25d15f5a25022p+0L,
+ 0x1.2b87fd0dad98ffddea46538fca24p+0L,
+ 0x1.2d285a6e4030b40091d536d0733ep+0L,
+ 0x1.2ecafa93e2f5611ca0f45d5239a4p+0L,
+ 0x1.306fe0a31b7152de8d5a463063bep+0L,
+ 0x1.32170fc4cd8313539cf1c3009330p+0L,
+ 0x1.33c08b26416ff4c9c8610d96680ep+0L,
+ 0x1.356c55f929ff0c94623476373be4p+0L,
+ 0x1.371a7373aa9caa7145502f45452ap+0L,
+ 0x1.38cae6d05d86585a9cb0d9bed530p+0L,
+ 0x1.3a7db34e59ff6ea1bc9299e0a1fep+0L,
+ 0x1.3c32dc313a8e484001f228b58cf0p+0L,
+ 0x1.3dea64c12342235b41223e13d7eep+0L,
+ 0x1.3fa4504ac801ba0bf701aa417b9cp+0L,
+ 0x1.4160a21f72e29f84325b8f3dbacap+0L,
+ 0x1.431f5d950a896dc704439410b628p+0L,
+ 0x1.44e086061892d03136f409df0724p+0L,
+ 0x1.46a41ed1d005772512f459229f0ap+0L,
+ 0x1.486a2b5c13cd013c1a3b69062f26p+0L,
+ 0x1.4a32af0d7d3de672d8bcf46f99b4p+0L,
+ 0x1.4bfdad5362a271d4397afec42e36p+0L,
+ 0x1.4dcb299fddd0d63b36ef1a9e19dep+0L,
+ 0x1.4f9b2769d2ca6ad33d8b69aa0b8cp+0L,
+ 0x1.516daa2cf6641c112f52c84d6066p+0L,
+ 0x1.5342b569d4f81df0a83c49d86bf4p+0L,
+ 0x1.551a4ca5d920ec52ec620243540cp+0L,
+ 0x1.56f4736b527da66ecb004764e61ep+0L,
+ 0x1.58d12d497c7fd252bc2b7343d554p+0L,
+ 0x1.5ab07dd48542958c93015191e9a8p+0L,
+ 0x1.5c9268a5946b701c4b1b81697ed4p+0L,
+ 0x1.5e76f15ad21486e9be4c20399d12p+0L,
+ 0x1.605e1b976dc08b076f592a487066p+0L,
+ 0x1.6247eb03a5584b1f0fa06fd2d9eap+0L,
+ 0x1.6434634ccc31fc76f8714c4ee122p+0L,
+ 0x1.66238825522249127d9e29b92ea2p+0L,
+ 0x1.68155d44ca973081c57227b9f69ep+0L,
+};
+
+static const float eps[TBLSIZE] = {
+ -0x1.5c50p-101,
+ -0x1.5d00p-106,
+ 0x1.8e90p-102,
+ -0x1.5340p-103,
+ 0x1.1bd0p-102,
+ -0x1.4600p-105,
+ -0x1.7a40p-104,
+ 0x1.d590p-102,
+ -0x1.d590p-101,
+ 0x1.b100p-103,
+ -0x1.0d80p-105,
+ 0x1.6b00p-103,
+ -0x1.9f00p-105,
+ 0x1.c400p-103,
+ 0x1.e120p-103,
+ -0x1.c100p-104,
+ -0x1.9d20p-103,
+ 0x1.a800p-108,
+ 0x1.4c00p-106,
+ -0x1.9500p-106,
+ 0x1.6900p-105,
+ -0x1.29d0p-100,
+ 0x1.4c60p-103,
+ 0x1.13a0p-102,
+ -0x1.5b60p-103,
+ -0x1.1c40p-103,
+ 0x1.db80p-102,
+ 0x1.91a0p-102,
+ 0x1.dc00p-105,
+ 0x1.44c0p-104,
+ 0x1.9710p-102,
+ 0x1.8760p-103,
+ -0x1.a720p-103,
+ 0x1.ed20p-103,
+ -0x1.49c0p-102,
+ -0x1.e000p-111,
+ 0x1.86a0p-103,
+ 0x1.2b40p-103,
+ -0x1.b400p-108,
+ 0x1.1280p-99,
+ -0x1.02d8p-102,
+ -0x1.e3d0p-103,
+ -0x1.b080p-105,
+ -0x1.f100p-107,
+ -0x1.16c0p-105,
+ -0x1.1190p-103,
+ -0x1.a7d2p-100,
+ 0x1.3450p-103,
+ -0x1.67c0p-105,
+ 0x1.4b80p-104,
+ -0x1.c4e0p-103,
+ 0x1.6000p-108,
+ -0x1.3f60p-105,
+ 0x1.93f0p-104,
+ 0x1.5fe0p-105,
+ 0x1.6f80p-107,
+ -0x1.7600p-106,
+ 0x1.21e0p-106,
+ -0x1.3a40p-106,
+ -0x1.40c0p-104,
+ -0x1.9860p-105,
+ -0x1.5d40p-108,
+ -0x1.1d70p-106,
+ 0x1.2760p-105,
+ 0x0.0000p+0,
+ 0x1.21e2p-104,
+ -0x1.9520p-108,
+ -0x1.5720p-106,
+ -0x1.4810p-106,
+ -0x1.be00p-109,
+ 0x1.0080p-105,
+ -0x1.5780p-108,
+ -0x1.d460p-105,
+ -0x1.6140p-105,
+ 0x1.4630p-104,
+ 0x1.ad50p-103,
+ 0x1.82e0p-105,
+ 0x1.1d3cp-101,
+ 0x1.6100p-107,
+ 0x1.ec30p-104,
+ 0x1.f200p-108,
+ 0x1.0b40p-103,
+ 0x1.3660p-102,
+ 0x1.d9d0p-103,
+ -0x1.02d0p-102,
+ 0x1.b070p-103,
+ 0x1.b9c0p-104,
+ -0x1.01c0p-103,
+ -0x1.dfe0p-103,
+ 0x1.1b60p-104,
+ -0x1.ae94p-101,
+ -0x1.3340p-104,
+ 0x1.b3d8p-102,
+ -0x1.6e40p-105,
+ -0x1.3670p-103,
+ 0x1.c140p-104,
+ 0x1.1840p-101,
+ 0x1.1ab0p-102,
+ -0x1.a400p-104,
+ 0x1.1f00p-104,
+ -0x1.7180p-103,
+ 0x1.4ce0p-102,
+ 0x1.9200p-107,
+ -0x1.54c0p-103,
+ 0x1.1b80p-105,
+ -0x1.1828p-101,
+ 0x1.5720p-102,
+ -0x1.a060p-100,
+ 0x1.9160p-102,
+ 0x1.a280p-104,
+ 0x1.3400p-107,
+ 0x1.2b20p-102,
+ 0x1.7800p-108,
+ 0x1.cfd0p-101,
+ 0x1.2ef0p-102,
+ -0x1.2760p-99,
+ 0x1.b380p-104,
+ 0x1.0048p-101,
+ -0x1.60b0p-102,
+ 0x1.a1ccp-100,
+ -0x1.a640p-104,
+ -0x1.08a0p-101,
+ 0x1.7e60p-102,
+ 0x1.22c0p-103,
+ -0x1.7200p-106,
+ 0x1.f0f0p-102,
+ 0x1.eb4ep-99,
+ 0x1.c6e0p-103,
+};
+
+/*
+ * exp2l(x): compute the base 2 exponential of x
+ *
+ * Accuracy: Peak error < 0.502 ulp.
+ *
+ * Method: (accurate tables)
+ *
+ * Reduce x:
+ * x = 2**k + y, for integer k and |y| <= 1/2.
+ * Thus we have exp2(x) = 2**k * exp2(y).
+ *
+ * Reduce y:
+ * y = i/TBLSIZE + z - eps[i] for integer i near y * TBLSIZE.
+ * Thus we have exp2(y) = exp2(i/TBLSIZE) * exp2(z - eps[i]),
+ * with |z - eps[i]| <= 2**-8 + 2**-98 for the table used.
+ *
+ * We compute exp2(i/TBLSIZE) via table lookup and exp2(z - eps[i]) via
+ * a degree-10 minimax polynomial with maximum error under 2**-120.
+ * The values in exp2t[] and eps[] are chosen such that
+ * exp2t[i] = exp2(i/TBLSIZE + eps[i]), and eps[i] is a small offset such
+ * that exp2t[i] is accurate to 2**-122.
+ *
+ * Note that the range of i is +-TBLSIZE/2, so we actually index the tables
+ * by i0 = i + TBLSIZE/2.
+ *
+ * This method is due to Gal, with many details due to Gal and Bachelis:
+ *
+ * Gal, S. and Bachelis, B. An Accurate Elementary Mathematical Library
+ * for the IEEE Floating Point Standard. TOMS 17(1), 26-46 (1991).
+ */
+long double
+exp2l(long double x)
+{
+ union ldshape u = {x};
+ int e = u.i.se & 0x7fff;
+ long double r, z, t;
+ uint32_t i0;
+ union {uint32_t u; int32_t i;} k;
+
+ /* Filter out exceptional cases. */
+ if (e >= 0x3fff + 14) { /* |x| >= 16384 or x is NaN */
+ if (u.i.se >= 0x3fff + 15 && u.i.se >> 15 == 0)
+ /* overflow */
+ return x * 0x1p16383L;
+ if (e == 0x7fff) /* -inf or -nan */
+ return -1/x;
+ if (x < -16382) {
+ if (x <= -16495 || x - 0x1p112 + 0x1p112 != x)
+ /* underflow */
+ FORCE_EVAL((float)(-0x1p-149/x));
+ if (x <= -16446)
+ return 0;
+ }
+ } else if (e < 0x3fff - 114) {
+ return 1 + x;
+ }
+
+ /*
+ * Reduce x, computing z, i0, and k. The low bits of x + redux
+ * contain the 16-bit integer part of the exponent (k) followed by
+ * TBLBITS fractional bits (i0). We use bit tricks to extract these
+ * as integers, then set z to the remainder.
+ *
+ * Example: Suppose x is 0xabc.123456p0 and TBLBITS is 8.
+ * Then the low-order word of x + redux is 0x000abc12,
+ * We split this into k = 0xabc and i0 = 0x12 (adjusted to
+ * index into the table), then we compute z = 0x0.003456p0.
+ */
+ u.f = x + redux;
+ i0 = u.i2.lo + TBLSIZE / 2;
+ k.u = i0 / TBLSIZE * TBLSIZE;
+ k.i /= TBLSIZE;
+ i0 %= TBLSIZE;
+ u.f -= redux;
+ z = x - u.f;
+
+ /* Compute r = exp2(y) = exp2t[i0] * p(z - eps[i]). */
+ t = tbl[i0];
+ z -= eps[i0];
+ r = t + t * z * (P1 + z * (P2 + z * (P3 + z * (P4 + z * (P5 + z * (P6
+ + z * (P7 + z * (P8 + z * (P9 + z * P10)))))))));
+
+ return scalbnl(r, k.i);
+}
+#endif
diff --git a/lib/libc/src/musl-math/expf.c b/lib/libc/src/musl-math/expf.c
new file mode 100644
index 0000000..feee2b0
--- /dev/null
+++ b/lib/libc/src/musl-math/expf.c
@@ -0,0 +1,83 @@
+/* origin: FreeBSD /usr/src/lib/msun/src/e_expf.c */
+/*
+ * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com.
+ */
+/*
+ * ====================================================
+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ *
+ * Developed at SunPro, a Sun Microsystems, Inc. business.
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ */
+
+#include "libm.h"
+
+static const float
+half[2] = {0.5,-0.5},
+ln2hi = 6.9314575195e-1f, /* 0x3f317200 */
+ln2lo = 1.4286067653e-6f, /* 0x35bfbe8e */
+invln2 = 1.4426950216e+0f, /* 0x3fb8aa3b */
+/*
+ * Domain [-0.34568, 0.34568], range ~[-4.278e-9, 4.447e-9]:
+ * |x*(exp(x)+1)/(exp(x)-1) - p(x)| < 2**-27.74
+ */
+P1 = 1.6666625440e-1f, /* 0xaaaa8f.0p-26 */
+P2 = -2.7667332906e-3f; /* -0xb55215.0p-32 */
+
+float expf(float x)
+{
+ float_t hi, lo, c, xx, y;
+ int k, sign;
+ uint32_t hx;
+
+ GET_FLOAT_WORD(hx, x);
+ sign = hx >> 31; /* sign bit of x */
+ hx &= 0x7fffffff; /* high word of |x| */
+
+ /* special cases */
+ if (hx >= 0x42aeac50) { /* if |x| >= -87.33655f or NaN */
+ if (hx > 0x7f800000) /* NaN */
+ return x;
+ if (hx >= 0x42b17218 && !sign) { /* x >= 88.722839f */
+ /* overflow */
+ x *= 0x1p127f;
+ return x;
+ }
+ if (sign) {
+ /* underflow */
+ FORCE_EVAL(-0x1p-149f/x);
+ if (hx >= 0x42cff1b5) /* x <= -103.972084f */
+ return 0;
+ }
+ }
+
+ /* argument reduction */
+ if (hx > 0x3eb17218) { /* if |x| > 0.5 ln2 */
+ if (hx > 0x3f851592) /* if |x| > 1.5 ln2 */
+ k = invln2*x + half[sign];
+ else
+ k = 1 - sign - sign;
+ hi = x - k*ln2hi; /* k*ln2hi is exact here */
+ lo = k*ln2lo;
+ x = hi - lo;
+ } else if (hx > 0x39000000) { /* |x| > 2**-14 */
+ k = 0;
+ hi = x;
+ lo = 0;
+ } else {
+ /* raise inexact */
+ FORCE_EVAL(0x1p127f + x);
+ return 1 + x;
+ }
+
+ /* x is now in primary range */
+ xx = x*x;
+ c = x - xx*(P1+xx*P2);
+ y = 1 + (x*c/(2-c) - lo + hi);
+ if (k == 0)
+ return y;
+ return scalbnf(y, k);
+}
diff --git a/lib/libc/src/musl-math/expl.c b/lib/libc/src/musl-math/expl.c
new file mode 100644
index 0000000..0a7f44f
--- /dev/null
+++ b/lib/libc/src/musl-math/expl.c
@@ -0,0 +1,128 @@
+/* origin: OpenBSD /usr/src/lib/libm/src/ld80/e_expl.c */
+/*
+ * Copyright (c) 2008 Stephen L. Moshier <steve@moshier.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+/*
+ * Exponential function, long double precision
+ *
+ *
+ * SYNOPSIS:
+ *
+ * long double x, y, expl();
+ *
+ * y = expl( x );
+ *
+ *
+ * DESCRIPTION:
+ *
+ * Returns e (2.71828...) raised to the x power.
+ *
+ * Range reduction is accomplished by separating the argument
+ * into an integer k and fraction f such that
+ *
+ * x k f
+ * e = 2 e.
+ *
+ * A Pade' form of degree 5/6 is used to approximate exp(f) - 1
+ * in the basic range [-0.5 ln 2, 0.5 ln 2].
+ *
+ *
+ * ACCURACY:
+ *
+ * Relative error:
+ * arithmetic domain # trials peak rms
+ * IEEE +-10000 50000 1.12e-19 2.81e-20
+ *
+ *
+ * Error amplification in the exponential function can be
+ * a serious matter. The error propagation involves
+ * exp( X(1+delta) ) = exp(X) ( 1 + X*delta + ... ),
+ * which shows that a 1 lsb error in representing X produces
+ * a relative error of X times 1 lsb in the function.
+ * While the routine gives an accurate result for arguments
+ * that are exactly represented by a long double precision
+ * computer number, the result contains amplified roundoff
+ * error for large arguments not exactly represented.
+ *
+ *
+ * ERROR MESSAGES:
+ *
+ * message condition value returned
+ * exp underflow x < MINLOG 0.0
+ * exp overflow x > MAXLOG MAXNUM
+ *
+ */
+
+#include "libm.h"
+
+#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
+long double expl(long double x)
+{
+ return exp(x);
+}
+#elif LDBL_MANT_DIG == 64 && LDBL_MAX_EXP == 16384
+
+static const long double P[3] = {
+ 1.2617719307481059087798E-4L,
+ 3.0299440770744196129956E-2L,
+ 9.9999999999999999991025E-1L,
+};
+static const long double Q[4] = {
+ 3.0019850513866445504159E-6L,
+ 2.5244834034968410419224E-3L,
+ 2.2726554820815502876593E-1L,
+ 2.0000000000000000000897E0L,
+};
+static const long double
+LN2HI = 6.9314575195312500000000E-1L,
+LN2LO = 1.4286068203094172321215E-6L,
+LOG2E = 1.4426950408889634073599E0L;
+
+long double expl(long double x)
+{
+ long double px, xx;
+ int k;
+
+ if (isnan(x))
+ return x;
+ if (x > 11356.5234062941439488L) /* x > ln(2^16384 - 0.5) */
+ return x * 0x1p16383L;
+ if (x < -11399.4985314888605581L) /* x < ln(2^-16446) */
+ return -0x1p-16445L/x;
+
+ /* Express e**x = e**f 2**k
+ * = e**(f + k ln(2))
+ */
+ px = floorl(LOG2E * x + 0.5);
+ k = px;
+ x -= px * LN2HI;
+ x -= px * LN2LO;
+
+ /* rational approximation of the fractional part:
+ * e**x = 1 + 2x P(x**2)/(Q(x**2) - x P(x**2))
+ */
+ xx = x * x;
+ px = x * __polevll(xx, P, 2);
+ x = px/(__polevll(xx, Q, 3) - px);
+ x = 1.0 + 2.0 * x;
+ return scalbnl(x, k);
+}
+#elif LDBL_MANT_DIG == 113 && LDBL_MAX_EXP == 16384
+// TODO: broken implementation to make things compile
+long double expl(long double x)
+{
+ return exp(x);
+}
+#endif
diff --git a/lib/libc/src/musl-math/expm1.c b/lib/libc/src/musl-math/expm1.c
new file mode 100644
index 0000000..ac1e61e
--- /dev/null
+++ b/lib/libc/src/musl-math/expm1.c
@@ -0,0 +1,201 @@
+/* origin: FreeBSD /usr/src/lib/msun/src/s_expm1.c */
+/*
+ * ====================================================
+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ *
+ * Developed at SunPro, a Sun Microsystems, Inc. business.
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ */
+/* expm1(x)
+ * Returns exp(x)-1, the exponential of x minus 1.
+ *
+ * Method
+ * 1. Argument reduction:
+ * Given x, find r and integer k such that
+ *
+ * x = k*ln2 + r, |r| <= 0.5*ln2 ~ 0.34658
+ *
+ * Here a correction term c will be computed to compensate
+ * the error in r when rounded to a floating-point number.
+ *
+ * 2. Approximating expm1(r) by a special rational function on
+ * the interval [0,0.34658]:
+ * Since
+ * r*(exp(r)+1)/(exp(r)-1) = 2+ r^2/6 - r^4/360 + ...
+ * we define R1(r*r) by
+ * r*(exp(r)+1)/(exp(r)-1) = 2+ r^2/6 * R1(r*r)
+ * That is,
+ * R1(r**2) = 6/r *((exp(r)+1)/(exp(r)-1) - 2/r)
+ * = 6/r * ( 1 + 2.0*(1/(exp(r)-1) - 1/r))
+ * = 1 - r^2/60 + r^4/2520 - r^6/100800 + ...
+ * We use a special Remez algorithm on [0,0.347] to generate
+ * a polynomial of degree 5 in r*r to approximate R1. The
+ * maximum error of this polynomial approximation is bounded
+ * by 2**-61. In other words,
+ * R1(z) ~ 1.0 + Q1*z + Q2*z**2 + Q3*z**3 + Q4*z**4 + Q5*z**5
+ * where Q1 = -1.6666666666666567384E-2,
+ * Q2 = 3.9682539681370365873E-4,
+ * Q3 = -9.9206344733435987357E-6,
+ * Q4 = 2.5051361420808517002E-7,
+ * Q5 = -6.2843505682382617102E-9;
+ * z = r*r,
+ * with error bounded by
+ * | 5 | -61
+ * | 1.0+Q1*z+...+Q5*z - R1(z) | <= 2
+ * | |
+ *
+ * expm1(r) = exp(r)-1 is then computed by the following
+ * specific way which minimize the accumulation rounding error:
+ * 2 3
+ * r r [ 3 - (R1 + R1*r/2) ]
+ * expm1(r) = r + --- + --- * [--------------------]
+ * 2 2 [ 6 - r*(3 - R1*r/2) ]
+ *
+ * To compensate the error in the argument reduction, we use
+ * expm1(r+c) = expm1(r) + c + expm1(r)*c
+ * ~ expm1(r) + c + r*c
+ * Thus c+r*c will be added in as the correction terms for
+ * expm1(r+c). Now rearrange the term to avoid optimization
+ * screw up:
+ * ( 2 2 )
+ * ({ ( r [ R1 - (3 - R1*r/2) ] ) } r )
+ * expm1(r+c)~r - ({r*(--- * [--------------------]-c)-c} - --- )
+ * ({ ( 2 [ 6 - r*(3 - R1*r/2) ] ) } 2 )
+ * ( )
+ *
+ * = r - E
+ * 3. Scale back to obtain expm1(x):
+ * From step 1, we have
+ * expm1(x) = either 2^k*[expm1(r)+1] - 1
+ * = or 2^k*[expm1(r) + (1-2^-k)]
+ * 4. Implementation notes:
+ * (A). To save one multiplication, we scale the coefficient Qi
+ * to Qi*2^i, and replace z by (x^2)/2.
+ * (B). To achieve maximum accuracy, we compute expm1(x) by
+ * (i) if x < -56*ln2, return -1.0, (raise inexact if x!=inf)
+ * (ii) if k=0, return r-E
+ * (iii) if k=-1, return 0.5*(r-E)-0.5
+ * (iv) if k=1 if r < -0.25, return 2*((r+0.5)- E)
+ * else return 1.0+2.0*(r-E);
+ * (v) if (k<-2||k>56) return 2^k(1-(E-r)) - 1 (or exp(x)-1)
+ * (vi) if k <= 20, return 2^k((1-2^-k)-(E-r)), else
+ * (vii) return 2^k(1-((E+2^-k)-r))
+ *
+ * Special cases:
+ * expm1(INF) is INF, expm1(NaN) is NaN;
+ * expm1(-INF) is -1, and
+ * for finite argument, only expm1(0)=0 is exact.
+ *
+ * Accuracy:
+ * according to an error analysis, the error is always less than
+ * 1 ulp (unit in the last place).
+ *
+ * Misc. info.
+ * For IEEE double
+ * if x > 7.09782712893383973096e+02 then expm1(x) overflow
+ *
+ * Constants:
+ * The hexadecimal values are the intended ones for the following
+ * constants. The decimal values may be used, provided that the
+ * compiler will convert from decimal to binary accurately enough
+ * to produce the hexadecimal values shown.
+ */
+
+#include "libm.h"
+
+static const double
+o_threshold = 7.09782712893383973096e+02, /* 0x40862E42, 0xFEFA39EF */
+ln2_hi = 6.93147180369123816490e-01, /* 0x3fe62e42, 0xfee00000 */
+ln2_lo = 1.90821492927058770002e-10, /* 0x3dea39ef, 0x35793c76 */
+invln2 = 1.44269504088896338700e+00, /* 0x3ff71547, 0x652b82fe */
+/* Scaled Q's: Qn_here = 2**n * Qn_above, for R(2*z) where z = hxs = x*x/2: */
+Q1 = -3.33333333333331316428e-02, /* BFA11111 111110F4 */
+Q2 = 1.58730158725481460165e-03, /* 3F5A01A0 19FE5585 */
+Q3 = -7.93650757867487942473e-05, /* BF14CE19 9EAADBB7 */
+Q4 = 4.00821782732936239552e-06, /* 3ED0CFCA 86E65239 */
+Q5 = -2.01099218183624371326e-07; /* BE8AFDB7 6E09C32D */
+
+double expm1(double x)
+{
+ double_t y,hi,lo,c,t,e,hxs,hfx,r1,twopk;
+ union {double f; uint64_t i;} u = {x};
+ uint32_t hx = u.i>>32 & 0x7fffffff;
+ int k, sign = u.i>>63;
+
+ /* filter out huge and non-finite argument */
+ if (hx >= 0x4043687A) { /* if |x|>=56*ln2 */
+ if (isnan(x))
+ return x;
+ if (sign)
+ return -1;
+ if (x > o_threshold) {
+ x *= 0x1p1023;
+ return x;
+ }
+ }
+
+ /* argument reduction */
+ if (hx > 0x3fd62e42) { /* if |x| > 0.5 ln2 */
+ if (hx < 0x3FF0A2B2) { /* and |x| < 1.5 ln2 */
+ if (!sign) {
+ hi = x - ln2_hi;
+ lo = ln2_lo;
+ k = 1;
+ } else {
+ hi = x + ln2_hi;
+ lo = -ln2_lo;
+ k = -1;
+ }
+ } else {
+ k = invln2*x + (sign ? -0.5 : 0.5);
+ t = k;
+ hi = x - t*ln2_hi; /* t*ln2_hi is exact here */
+ lo = t*ln2_lo;
+ }
+ x = hi-lo;
+ c = (hi-x)-lo;
+ } else if (hx < 0x3c900000) { /* |x| < 2**-54, return x */
+ if (hx < 0x00100000)
+ FORCE_EVAL((float)x);
+ return x;
+ } else
+ k = 0;
+
+ /* x is now in primary range */
+ hfx = 0.5*x;
+ hxs = x*hfx;
+ r1 = 1.0+hxs*(Q1+hxs*(Q2+hxs*(Q3+hxs*(Q4+hxs*Q5))));
+ t = 3.0-r1*hfx;
+ e = hxs*((r1-t)/(6.0 - x*t));
+ if (k == 0) /* c is 0 */
+ return x - (x*e-hxs);
+ e = x*(e-c) - c;
+ e -= hxs;
+ /* exp(x) ~ 2^k (x_reduced - e + 1) */
+ if (k == -1)
+ return 0.5*(x-e) - 0.5;
+ if (k == 1) {
+ if (x < -0.25)
+ return -2.0*(e-(x+0.5));
+ return 1.0+2.0*(x-e);
+ }
+ u.i = (uint64_t)(0x3ff + k)<<52; /* 2^k */
+ twopk = u.f;
+ if (k < 0 || k > 56) { /* suffice to return exp(x)-1 */
+ y = x - e + 1.0;
+ if (k == 1024)
+ y = y*2.0*0x1p1023;
+ else
+ y = y*twopk;
+ return y - 1.0;
+ }
+ u.i = (uint64_t)(0x3ff - k)<<52; /* 2^-k */
+ if (k < 20)
+ y = (x-e+(1-u.f))*twopk;
+ else
+ y = (x-(e+u.f)+1)*twopk;
+ return y;
+}
diff --git a/lib/libc/src/musl-math/expm1f.c b/lib/libc/src/musl-math/expm1f.c
new file mode 100644
index 0000000..297e0b4
--- /dev/null
+++ b/lib/libc/src/musl-math/expm1f.c
@@ -0,0 +1,111 @@
+/* origin: FreeBSD /usr/src/lib/msun/src/s_expm1f.c */
+/*
+ * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com.
+ */
+/*
+ * ====================================================
+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ *
+ * Developed at SunPro, a Sun Microsystems, Inc. business.
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ */
+
+#include "libm.h"
+
+static const float
+o_threshold = 8.8721679688e+01, /* 0x42b17180 */
+ln2_hi = 6.9313812256e-01, /* 0x3f317180 */
+ln2_lo = 9.0580006145e-06, /* 0x3717f7d1 */
+invln2 = 1.4426950216e+00, /* 0x3fb8aa3b */
+/*
+ * Domain [-0.34568, 0.34568], range ~[-6.694e-10, 6.696e-10]:
+ * |6 / x * (1 + 2 * (1 / (exp(x) - 1) - 1 / x)) - q(x)| < 2**-30.04
+ * Scaled coefficients: Qn_here = 2**n * Qn_for_q (see s_expm1.c):
+ */
+Q1 = -3.3333212137e-2, /* -0x888868.0p-28 */
+Q2 = 1.5807170421e-3; /* 0xcf3010.0p-33 */
+
+float expm1f(float x)
+{
+ float_t y,hi,lo,c,t,e,hxs,hfx,r1,twopk;
+ union {float f; uint32_t i;} u = {x};
+ uint32_t hx = u.i & 0x7fffffff;
+ int k, sign = u.i >> 31;
+
+ /* filter out huge and non-finite argument */
+ if (hx >= 0x4195b844) { /* if |x|>=27*ln2 */
+ if (hx > 0x7f800000) /* NaN */
+ return x;
+ if (sign)
+ return -1;
+ if (x > o_threshold) {
+ x *= 0x1p127f;
+ return x;
+ }
+ }
+
+ /* argument reduction */
+ if (hx > 0x3eb17218) { /* if |x| > 0.5 ln2 */
+ if (hx < 0x3F851592) { /* and |x| < 1.5 ln2 */
+ if (!sign) {
+ hi = x - ln2_hi;
+ lo = ln2_lo;
+ k = 1;
+ } else {
+ hi = x + ln2_hi;
+ lo = -ln2_lo;
+ k = -1;
+ }
+ } else {
+ k = invln2*x + (sign ? -0.5f : 0.5f);
+ t = k;
+ hi = x - t*ln2_hi; /* t*ln2_hi is exact here */
+ lo = t*ln2_lo;
+ }
+ x = hi-lo;
+ c = (hi-x)-lo;
+ } else if (hx < 0x33000000) { /* when |x|<2**-25, return x */
+ if (hx < 0x00800000)
+ FORCE_EVAL(x*x);
+ return x;
+ } else
+ k = 0;
+
+ /* x is now in primary range */
+ hfx = 0.5f*x;
+ hxs = x*hfx;
+ r1 = 1.0f+hxs*(Q1+hxs*Q2);
+ t = 3.0f - r1*hfx;
+ e = hxs*((r1-t)/(6.0f - x*t));
+ if (k == 0) /* c is 0 */
+ return x - (x*e-hxs);
+ e = x*(e-c) - c;
+ e -= hxs;
+ /* exp(x) ~ 2^k (x_reduced - e + 1) */
+ if (k == -1)
+ return 0.5f*(x-e) - 0.5f;
+ if (k == 1) {
+ if (x < -0.25f)
+ return -2.0f*(e-(x+0.5f));
+ return 1.0f + 2.0f*(x-e);
+ }
+ u.i = (0x7f+k)<<23; /* 2^k */
+ twopk = u.f;
+ if (k < 0 || k > 56) { /* suffice to return exp(x)-1 */
+ y = x - e + 1.0f;
+ if (k == 128)
+ y = y*2.0f*0x1p127f;
+ else
+ y = y*twopk;
+ return y - 1.0f;
+ }
+ u.i = (0x7f-k)<<23; /* 2^-k */
+ if (k < 23)
+ y = (x-e+(1-u.f))*twopk;
+ else
+ y = (x-(e+u.f)+1)*twopk;
+ return y;
+}
diff --git a/lib/libc/src/musl-math/expm1l.c b/lib/libc/src/musl-math/expm1l.c
new file mode 100644
index 0000000..d171507
--- /dev/null
+++ b/lib/libc/src/musl-math/expm1l.c
@@ -0,0 +1,123 @@
+/* origin: OpenBSD /usr/src/lib/libm/src/ld80/e_expm1l.c */
+/*
+ * Copyright (c) 2008 Stephen L. Moshier <steve@moshier.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+/*
+ * Exponential function, minus 1
+ * Long double precision
+ *
+ *
+ * SYNOPSIS:
+ *
+ * long double x, y, expm1l();
+ *
+ * y = expm1l( x );
+ *
+ *
+ * DESCRIPTION:
+ *
+ * Returns e (2.71828...) raised to the x power, minus 1.
+ *
+ * Range reduction is accomplished by separating the argument
+ * into an integer k and fraction f such that
+ *
+ * x k f
+ * e = 2 e.
+ *
+ * An expansion x + .5 x^2 + x^3 R(x) approximates exp(f) - 1
+ * in the basic range [-0.5 ln 2, 0.5 ln 2].
+ *
+ *
+ * ACCURACY:
+ *
+ * Relative error:
+ * arithmetic domain # trials peak rms
+ * IEEE -45,+maxarg 200,000 1.2e-19 2.5e-20
+ */
+
+#include "libm.h"
+
+#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
+long double expm1l(long double x)
+{
+ return expm1(x);
+}
+#elif LDBL_MANT_DIG == 64 && LDBL_MAX_EXP == 16384
+
+/* exp(x) - 1 = x + 0.5 x^2 + x^3 P(x)/Q(x)
+ -.5 ln 2 < x < .5 ln 2
+ Theoretical peak relative error = 3.4e-22 */
+static const long double
+P0 = -1.586135578666346600772998894928250240826E4L,
+P1 = 2.642771505685952966904660652518429479531E3L,
+P2 = -3.423199068835684263987132888286791620673E2L,
+P3 = 1.800826371455042224581246202420972737840E1L,
+P4 = -5.238523121205561042771939008061958820811E-1L,
+Q0 = -9.516813471998079611319047060563358064497E4L,
+Q1 = 3.964866271411091674556850458227710004570E4L,
+Q2 = -7.207678383830091850230366618190187434796E3L,
+Q3 = 7.206038318724600171970199625081491823079E2L,
+Q4 = -4.002027679107076077238836622982900945173E1L,
+/* Q5 = 1.000000000000000000000000000000000000000E0 */
+/* C1 + C2 = ln 2 */
+C1 = 6.93145751953125E-1L,
+C2 = 1.428606820309417232121458176568075500134E-6L,
+/* ln 2^-65 */
+minarg = -4.5054566736396445112120088E1L,
+/* ln 2^16384 */
+maxarg = 1.1356523406294143949492E4L;
+
+long double expm1l(long double x)
+{
+ long double px, qx, xx;
+ int k;
+
+ if (isnan(x))
+ return x;
+ if (x > maxarg)
+ return x*0x1p16383L; /* overflow, unless x==inf */
+ if (x == 0.0)
+ return x;
+ if (x < minarg)
+ return -1.0;
+
+ xx = C1 + C2;
+ /* Express x = ln 2 (k + remainder), remainder not exceeding 1/2. */
+ px = floorl(0.5 + x / xx);
+ k = px;
+ /* remainder times ln 2 */
+ x -= px * C1;
+ x -= px * C2;
+
+ /* Approximate exp(remainder ln 2).*/
+ px = (((( P4 * x + P3) * x + P2) * x + P1) * x + P0) * x;
+ qx = (((( x + Q4) * x + Q3) * x + Q2) * x + Q1) * x + Q0;
+ xx = x * x;
+ qx = x + (0.5 * xx + xx * px / qx);
+
+ /* exp(x) = exp(k ln 2) exp(remainder ln 2) = 2^k exp(remainder ln 2).
+ We have qx = exp(remainder ln 2) - 1, so
+ exp(x) - 1 = 2^k (qx + 1) - 1 = 2^k qx + 2^k - 1. */
+ px = scalbnl(1.0, k);
+ x = px * qx + (px - 1.0);
+ return x;
+}
+#elif LDBL_MANT_DIG == 113 && LDBL_MAX_EXP == 16384
+// TODO: broken implementation to make things compile
+long double expm1l(long double x)
+{
+ return expm1(x);
+}
+#endif
diff --git a/lib/libc/src/musl-math/fabs.c b/lib/libc/src/musl-math/fabs.c
new file mode 100644
index 0000000..e8258cf
--- /dev/null
+++ b/lib/libc/src/musl-math/fabs.c
@@ -0,0 +1,9 @@
+#include <math.h>
+#include <stdint.h>
+
+double fabs(double x)
+{
+ union {double f; uint64_t i;} u = {x};
+ u.i &= -1ULL/2;
+ return u.f;
+}
diff --git a/lib/libc/src/musl-math/fabsf.c b/lib/libc/src/musl-math/fabsf.c
new file mode 100644
index 0000000..4efc8d6
--- /dev/null
+++ b/lib/libc/src/musl-math/fabsf.c
@@ -0,0 +1,9 @@
+#include <math.h>
+#include <stdint.h>
+
+float fabsf(float x)
+{
+ union {float f; uint32_t i;} u = {x};
+ u.i &= 0x7fffffff;
+ return u.f;
+}
diff --git a/lib/libc/src/musl-math/fabsl.c b/lib/libc/src/musl-math/fabsl.c
new file mode 100644
index 0000000..c4f36ec
--- /dev/null
+++ b/lib/libc/src/musl-math/fabsl.c
@@ -0,0 +1,15 @@
+#include "libm.h"
+#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
+long double fabsl(long double x)
+{
+ return fabs(x);
+}
+#elif (LDBL_MANT_DIG == 64 || LDBL_MANT_DIG == 113) && LDBL_MAX_EXP == 16384
+long double fabsl(long double x)
+{
+ union ldshape u = {x};
+
+ u.i.se &= 0x7fff;
+ return u.f;
+}
+#endif
diff --git a/lib/libc/src/musl-math/fdim.c b/lib/libc/src/musl-math/fdim.c
new file mode 100644
index 0000000..9585460
--- /dev/null
+++ b/lib/libc/src/musl-math/fdim.c
@@ -0,0 +1,10 @@
+#include <math.h>
+
+double fdim(double x, double y)
+{
+ if (isnan(x))
+ return x;
+ if (isnan(y))
+ return y;
+ return x > y ? x - y : 0;
+}
diff --git a/lib/libc/src/musl-math/fdimf.c b/lib/libc/src/musl-math/fdimf.c
new file mode 100644
index 0000000..543c364
--- /dev/null
+++ b/lib/libc/src/musl-math/fdimf.c
@@ -0,0 +1,10 @@
+#include <math.h>
+
+float fdimf(float x, float y)
+{
+ if (isnan(x))
+ return x;
+ if (isnan(y))
+ return y;
+ return x > y ? x - y : 0;
+}
diff --git a/lib/libc/src/musl-math/fdiml.c b/lib/libc/src/musl-math/fdiml.c
new file mode 100644
index 0000000..62e29b7
--- /dev/null
+++ b/lib/libc/src/musl-math/fdiml.c
@@ -0,0 +1,18 @@
+#include <math.h>
+#include <float.h>
+
+#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
+long double fdiml(long double x, long double y)
+{
+ return fdim(x, y);
+}
+#else
+long double fdiml(long double x, long double y)
+{
+ if (isnan(x))
+ return x;
+ if (isnan(y))
+ return y;
+ return x > y ? x - y : 0;
+}
+#endif
diff --git a/lib/libc/src/musl-math/finite.c b/lib/libc/src/musl-math/finite.c
new file mode 100644
index 0000000..25a0575
--- /dev/null
+++ b/lib/libc/src/musl-math/finite.c
@@ -0,0 +1,7 @@
+#define _GNU_SOURCE
+#include <math.h>
+
+int finite(double x)
+{
+ return isfinite(x);
+}
diff --git a/lib/libc/src/musl-math/finitef.c b/lib/libc/src/musl-math/finitef.c
new file mode 100644
index 0000000..2c4c771
--- /dev/null
+++ b/lib/libc/src/musl-math/finitef.c
@@ -0,0 +1,7 @@
+#define _GNU_SOURCE
+#include <math.h>
+
+int finitef(float x)
+{
+ return isfinite(x);
+}
diff --git a/lib/libc/src/musl-math/floor.c b/lib/libc/src/musl-math/floor.c
new file mode 100644
index 0000000..14a31cd
--- /dev/null
+++ b/lib/libc/src/musl-math/floor.c
@@ -0,0 +1,31 @@
+#include "libm.h"
+
+#if FLT_EVAL_METHOD==0 || FLT_EVAL_METHOD==1
+#define EPS DBL_EPSILON
+#elif FLT_EVAL_METHOD==2
+#define EPS LDBL_EPSILON
+#endif
+static const double_t toint = 1/EPS;
+
+double floor(double x)
+{
+ union {double f; uint64_t i;} u = {x};
+ int e = u.i >> 52 & 0x7ff;
+ double_t y;
+
+ if (e >= 0x3ff+52 || x == 0)
+ return x;
+ /* y = int(x) - x, where int(x) is an integer neighbor of x */
+ if (u.i >> 63)
+ y = x - toint + toint - x;
+ else
+ y = x + toint - toint - x;
+ /* special case because of non-nearest rounding modes */
+ if (e <= 0x3ff-1) {
+ FORCE_EVAL(y);
+ return u.i >> 63 ? -1 : 0;
+ }
+ if (y > 0)
+ return x + y - 1;
+ return x + y;
+}
diff --git a/lib/libc/src/musl-math/floorf.c b/lib/libc/src/musl-math/floorf.c
new file mode 100644
index 0000000..dceec73
--- /dev/null
+++ b/lib/libc/src/musl-math/floorf.c
@@ -0,0 +1,27 @@
+#include "libm.h"
+
+float floorf(float x)
+{
+ union {float f; uint32_t i;} u = {x};
+ int e = (int)(u.i >> 23 & 0xff) - 0x7f;
+ uint32_t m;
+
+ if (e >= 23)
+ return x;
+ if (e >= 0) {
+ m = 0x007fffff >> e;
+ if ((u.i & m) == 0)
+ return x;
+ FORCE_EVAL(x + 0x1p120f);
+ if (u.i >> 31)
+ u.i += m;
+ u.i &= ~m;
+ } else {
+ FORCE_EVAL(x + 0x1p120f);
+ if (u.i >> 31 == 0)
+ u.i = 0;
+ else if (u.i << 1)
+ u.f = -1.0;
+ }
+ return u.f;
+}
diff --git a/lib/libc/src/musl-math/floorl.c b/lib/libc/src/musl-math/floorl.c
new file mode 100644
index 0000000..16aaec4
--- /dev/null
+++ b/lib/libc/src/musl-math/floorl.c
@@ -0,0 +1,34 @@
+#include "libm.h"
+
+#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
+long double floorl(long double x)
+{
+ return floor(x);
+}
+#elif (LDBL_MANT_DIG == 64 || LDBL_MANT_DIG == 113) && LDBL_MAX_EXP == 16384
+
+static const long double toint = 1/LDBL_EPSILON;
+
+long double floorl(long double x)
+{
+ union ldshape u = {x};
+ int e = u.i.se & 0x7fff;
+ long double y;
+
+ if (e >= 0x3fff+LDBL_MANT_DIG-1 || x == 0)
+ return x;
+ /* y = int(x) - x, where int(x) is an integer neighbor of x */
+ if (u.i.se >> 15)
+ y = x - toint + toint - x;
+ else
+ y = x + toint - toint - x;
+ /* special case because of non-nearest rounding modes */
+ if (e <= 0x3fff-1) {
+ FORCE_EVAL(y);
+ return u.i.se >> 15 ? -1 : 0;
+ }
+ if (y > 0)
+ return x + y - 1;
+ return x + y;
+}
+#endif
diff --git a/lib/libc/src/musl-math/fmaf.c b/lib/libc/src/musl-math/fmaf.c
new file mode 100644
index 0000000..aa57feb
--- /dev/null
+++ b/lib/libc/src/musl-math/fmaf.c
@@ -0,0 +1,93 @@
+/* origin: FreeBSD /usr/src/lib/msun/src/s_fmaf.c */
+/*-
+ * Copyright (c) 2005-2011 David Schultz <das@FreeBSD.ORG>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <fenv.h>
+#include <math.h>
+#include <stdint.h>
+
+/*
+ * Fused multiply-add: Compute x * y + z with a single rounding error.
+ *
+ * A double has more than twice as much precision than a float, so
+ * direct double-precision arithmetic suffices, except where double
+ * rounding occurs.
+ */
+float fmaf(float x, float y, float z)
+{
+ #pragma STDC FENV_ACCESS ON
+ double xy, result;
+ union {double f; uint64_t i;} u;
+ int e;
+
+ xy = (double)x * y;
+ result = xy + z;
+ u.f = result;
+ e = u.i>>52 & 0x7ff;
+ /* Common case: The double precision result is fine. */
+ if ((u.i & 0x1fffffff) != 0x10000000 || /* not a halfway case */
+ e == 0x7ff || /* NaN */
+ result - xy == z || /* exact */
+ fegetround() != FE_TONEAREST) /* not round-to-nearest */
+ {
+ /*
+ underflow may not be raised correctly, example:
+ fmaf(0x1p-120f, 0x1p-120f, 0x1p-149f)
+ */
+#if defined(FE_INEXACT) && defined(FE_UNDERFLOW)
+ if (e < 0x3ff-126 && e >= 0x3ff-149 && fetestexcept(FE_INEXACT)) {
+ feclearexcept(FE_INEXACT);
+ /* TODO: gcc and clang bug workaround */
+ volatile float vz = z;
+ result = xy + vz;
+ if (fetestexcept(FE_INEXACT))
+ feraiseexcept(FE_UNDERFLOW);
+ else
+ feraiseexcept(FE_INEXACT);
+ }
+#endif
+ z = result;
+ return z;
+ }
+
+ /*
+ * If result is inexact, and exactly halfway between two float values,
+ * we need to adjust the low-order bit in the direction of the error.
+ */
+#ifdef FE_TOWARDZERO
+ fesetround(FE_TOWARDZERO);
+#endif
+ volatile double vxy = xy; /* XXX work around gcc CSE bug */
+ double adjusted_result = vxy + z;
+ fesetround(FE_TONEAREST);
+ if (result == adjusted_result) {
+ u.f = adjusted_result;
+ u.i++;
+ adjusted_result = u.f;
+ }
+ z = adjusted_result;
+ return z;
+}
diff --git a/lib/libc/src/musl-math/fmal.c b/lib/libc/src/musl-math/fmal.c
new file mode 100644
index 0000000..4506aac
--- /dev/null
+++ b/lib/libc/src/musl-math/fmal.c
@@ -0,0 +1,293 @@
+/* origin: FreeBSD /usr/src/lib/msun/src/s_fmal.c */
+/*-
+ * Copyright (c) 2005-2011 David Schultz <das@FreeBSD.ORG>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#include "libm.h"
+#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
+long double fmal(long double x, long double y, long double z)
+{
+ return fma(x, y, z);
+}
+#elif (LDBL_MANT_DIG == 64 || LDBL_MANT_DIG == 113) && LDBL_MAX_EXP == 16384
+#include <fenv.h>
+#if LDBL_MANT_DIG == 64
+#define LASTBIT(u) (u.i.m & 1)
+#define SPLIT (0x1p32L + 1)
+#elif LDBL_MANT_DIG == 113
+#define LASTBIT(u) (u.i.lo & 1)
+#define SPLIT (0x1p57L + 1)
+#endif
+
+/*
+ * A struct dd represents a floating-point number with twice the precision
+ * of a long double. We maintain the invariant that "hi" stores the high-order
+ * bits of the result.
+ */
+struct dd {
+ long double hi;
+ long double lo;
+};
+
+/*
+ * Compute a+b exactly, returning the exact result in a struct dd. We assume
+ * that both a and b are finite, but make no assumptions about their relative
+ * magnitudes.
+ */
+static inline struct dd dd_add(long double a, long double b)
+{
+ struct dd ret;
+ long double s;
+
+ ret.hi = a + b;
+ s = ret.hi - a;
+ ret.lo = (a - (ret.hi - s)) + (b - s);
+ return (ret);
+}
+
+/*
+ * Compute a+b, with a small tweak: The least significant bit of the
+ * result is adjusted into a sticky bit summarizing all the bits that
+ * were lost to rounding. This adjustment negates the effects of double
+ * rounding when the result is added to another number with a higher
+ * exponent. For an explanation of round and sticky bits, see any reference
+ * on FPU design, e.g.,
+ *
+ * J. Coonen. An Implementation Guide to a Proposed Standard for
+ * Floating-Point Arithmetic. Computer, vol. 13, no. 1, Jan 1980.
+ */
+static inline long double add_adjusted(long double a, long double b)
+{
+ struct dd sum;
+ union ldshape u;
+
+ sum = dd_add(a, b);
+ if (sum.lo != 0) {
+ u.f = sum.hi;
+ if (!LASTBIT(u))
+ sum.hi = nextafterl(sum.hi, INFINITY * sum.lo);
+ }
+ return (sum.hi);
+}
+
+/*
+ * Compute ldexp(a+b, scale) with a single rounding error. It is assumed
+ * that the result will be subnormal, and care is taken to ensure that
+ * double rounding does not occur.
+ */
+static inline long double add_and_denormalize(long double a, long double b, int scale)
+{
+ struct dd sum;
+ int bits_lost;
+ union ldshape u;
+
+ sum = dd_add(a, b);
+
+ /*
+ * If we are losing at least two bits of accuracy to denormalization,
+ * then the first lost bit becomes a round bit, and we adjust the
+ * lowest bit of sum.hi to make it a sticky bit summarizing all the
+ * bits in sum.lo. With the sticky bit adjusted, the hardware will
+ * break any ties in the correct direction.
+ *
+ * If we are losing only one bit to denormalization, however, we must
+ * break the ties manually.
+ */
+ if (sum.lo != 0) {
+ u.f = sum.hi;
+ bits_lost = -u.i.se - scale + 1;
+ if ((bits_lost != 1) ^ LASTBIT(u))
+ sum.hi = nextafterl(sum.hi, INFINITY * sum.lo);
+ }
+ return scalbnl(sum.hi, scale);
+}
+
+/*
+ * Compute a*b exactly, returning the exact result in a struct dd. We assume
+ * that both a and b are normalized, so no underflow or overflow will occur.
+ * The current rounding mode must be round-to-nearest.
+ */
+static inline struct dd dd_mul(long double a, long double b)
+{
+ struct dd ret;
+ long double ha, hb, la, lb, p, q;
+
+ p = a * SPLIT;
+ ha = a - p;
+ ha += p;
+ la = a - ha;
+
+ p = b * SPLIT;
+ hb = b - p;
+ hb += p;
+ lb = b - hb;
+
+ p = ha * hb;
+ q = ha * lb + la * hb;
+
+ ret.hi = p + q;
+ ret.lo = p - ret.hi + q + la * lb;
+ return (ret);
+}
+
+/*
+ * Fused multiply-add: Compute x * y + z with a single rounding error.
+ *
+ * We use scaling to avoid overflow/underflow, along with the
+ * canonical precision-doubling technique adapted from:
+ *
+ * Dekker, T. A Floating-Point Technique for Extending the
+ * Available Precision. Numer. Math. 18, 224-242 (1971).
+ */
+long double fmal(long double x, long double y, long double z)
+{
+ #pragma STDC FENV_ACCESS ON
+ long double xs, ys, zs, adj;
+ struct dd xy, r;
+ int oround;
+ int ex, ey, ez;
+ int spread;
+
+ /*
+ * Handle special cases. The order of operations and the particular
+ * return values here are crucial in handling special cases involving
+ * infinities, NaNs, overflows, and signed zeroes correctly.
+ */
+ if (!isfinite(x) || !isfinite(y))
+ return (x * y + z);
+ if (!isfinite(z))
+ return (z);
+ if (x == 0.0 || y == 0.0)
+ return (x * y + z);
+ if (z == 0.0)
+ return (x * y);
+
+ xs = frexpl(x, &ex);
+ ys = frexpl(y, &ey);
+ zs = frexpl(z, &ez);
+ oround = fegetround();
+ spread = ex + ey - ez;
+
+ /*
+ * If x * y and z are many orders of magnitude apart, the scaling
+ * will overflow, so we handle these cases specially. Rounding
+ * modes other than FE_TONEAREST are painful.
+ */
+ if (spread < -LDBL_MANT_DIG) {
+#ifdef FE_INEXACT
+ feraiseexcept(FE_INEXACT);
+#endif
+#ifdef FE_UNDERFLOW
+ if (!isnormal(z))
+ feraiseexcept(FE_UNDERFLOW);
+#endif
+ switch (oround) {
+ default: /* FE_TONEAREST */
+ return (z);
+#ifdef FE_TOWARDZERO
+ case FE_TOWARDZERO:
+ if (x > 0.0 ^ y < 0.0 ^ z < 0.0)
+ return (z);
+ else
+ return (nextafterl(z, 0));
+#endif
+#ifdef FE_DOWNWARD
+ case FE_DOWNWARD:
+ if (x > 0.0 ^ y < 0.0)
+ return (z);
+ else
+ return (nextafterl(z, -INFINITY));
+#endif
+#ifdef FE_UPWARD
+ case FE_UPWARD:
+ if (x > 0.0 ^ y < 0.0)
+ return (nextafterl(z, INFINITY));
+ else
+ return (z);
+#endif
+ }
+ }
+ if (spread <= LDBL_MANT_DIG * 2)
+ zs = scalbnl(zs, -spread);
+ else
+ zs = copysignl(LDBL_MIN, zs);
+
+ fesetround(FE_TONEAREST);
+
+ /*
+ * Basic approach for round-to-nearest:
+ *
+ * (xy.hi, xy.lo) = x * y (exact)
+ * (r.hi, r.lo) = xy.hi + z (exact)
+ * adj = xy.lo + r.lo (inexact; low bit is sticky)
+ * result = r.hi + adj (correctly rounded)
+ */
+ xy = dd_mul(xs, ys);
+ r = dd_add(xy.hi, zs);
+
+ spread = ex + ey;
+
+ if (r.hi == 0.0) {
+ /*
+ * When the addends cancel to 0, ensure that the result has
+ * the correct sign.
+ */
+ fesetround(oround);
+ volatile long double vzs = zs; /* XXX gcc CSE bug workaround */
+ return xy.hi + vzs + scalbnl(xy.lo, spread);
+ }
+
+ if (oround != FE_TONEAREST) {
+ /*
+ * There is no need to worry about double rounding in directed
+ * rounding modes.
+ * But underflow may not be raised correctly, example in downward rounding:
+ * fmal(0x1.0000000001p-16000L, 0x1.0000000001p-400L, -0x1p-16440L)
+ */
+ long double ret;
+#if defined(FE_INEXACT) && defined(FE_UNDERFLOW)
+ int e = fetestexcept(FE_INEXACT);
+ feclearexcept(FE_INEXACT);
+#endif
+ fesetround(oround);
+ adj = r.lo + xy.lo;
+ ret = scalbnl(r.hi + adj, spread);
+#if defined(FE_INEXACT) && defined(FE_UNDERFLOW)
+ if (ilogbl(ret) < -16382 && fetestexcept(FE_INEXACT))
+ feraiseexcept(FE_UNDERFLOW);
+ else if (e)
+ feraiseexcept(FE_INEXACT);
+#endif
+ return ret;
+ }
+
+ adj = add_adjusted(r.lo, xy.lo);
+ if (spread + ilogbl(r.hi) > -16383)
+ return scalbnl(r.hi + adj, spread);
+ else
+ return add_and_denormalize(r.hi, adj, spread);
+}
+#endif
diff --git a/lib/libc/src/musl-math/fmax.c b/lib/libc/src/musl-math/fmax.c
new file mode 100644
index 0000000..94f0caa
--- /dev/null
+++ b/lib/libc/src/musl-math/fmax.c
@@ -0,0 +1,13 @@
+#include <math.h>
+
+double fmax(double x, double y)
+{
+ if (isnan(x))
+ return y;
+ if (isnan(y))
+ return x;
+ /* handle signed zeros, see C99 Annex F.9.9.2 */
+ if (signbit(x) != signbit(y))
+ return signbit(x) ? y : x;
+ return x < y ? y : x;
+}
diff --git a/lib/libc/src/musl-math/fmaxf.c b/lib/libc/src/musl-math/fmaxf.c
new file mode 100644
index 0000000..695d817
--- /dev/null
+++ b/lib/libc/src/musl-math/fmaxf.c
@@ -0,0 +1,13 @@
+#include <math.h>
+
+float fmaxf(float x, float y)
+{
+ if (isnan(x))
+ return y;
+ if (isnan(y))
+ return x;
+ /* handle signed zeroes, see C99 Annex F.9.9.2 */
+ if (signbit(x) != signbit(y))
+ return signbit(x) ? y : x;
+ return x < y ? y : x;
+}
diff --git a/lib/libc/src/musl-math/fmaxl.c b/lib/libc/src/musl-math/fmaxl.c
new file mode 100644
index 0000000..4b03158
--- /dev/null
+++ b/lib/libc/src/musl-math/fmaxl.c
@@ -0,0 +1,21 @@
+#include <math.h>
+#include <float.h>
+
+#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
+long double fmaxl(long double x, long double y)
+{
+ return fmax(x, y);
+}
+#else
+long double fmaxl(long double x, long double y)
+{
+ if (isnan(x))
+ return y;
+ if (isnan(y))
+ return x;
+ /* handle signed zeros, see C99 Annex F.9.9.2 */
+ if (signbit(x) != signbit(y))
+ return signbit(x) ? y : x;
+ return x < y ? y : x;
+}
+#endif
diff --git a/lib/libc/src/musl-math/fmin.c b/lib/libc/src/musl-math/fmin.c
new file mode 100644
index 0000000..08a8fd1
--- /dev/null
+++ b/lib/libc/src/musl-math/fmin.c
@@ -0,0 +1,13 @@
+#include <math.h>
+
+double fmin(double x, double y)
+{
+ if (isnan(x))
+ return y;
+ if (isnan(y))
+ return x;
+ /* handle signed zeros, see C99 Annex F.9.9.2 */
+ if (signbit(x) != signbit(y))
+ return signbit(x) ? x : y;
+ return x < y ? x : y;
+}
diff --git a/lib/libc/src/musl-math/fminf.c b/lib/libc/src/musl-math/fminf.c
new file mode 100644
index 0000000..3573c7d
--- /dev/null
+++ b/lib/libc/src/musl-math/fminf.c
@@ -0,0 +1,13 @@
+#include <math.h>
+
+float fminf(float x, float y)
+{
+ if (isnan(x))
+ return y;
+ if (isnan(y))
+ return x;
+ /* handle signed zeros, see C99 Annex F.9.9.2 */
+ if (signbit(x) != signbit(y))
+ return signbit(x) ? x : y;
+ return x < y ? x : y;
+}
diff --git a/lib/libc/src/musl-math/fminl.c b/lib/libc/src/musl-math/fminl.c
new file mode 100644
index 0000000..69bc24a
--- /dev/null
+++ b/lib/libc/src/musl-math/fminl.c
@@ -0,0 +1,21 @@
+#include <math.h>
+#include <float.h>
+
+#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
+long double fminl(long double x, long double y)
+{
+ return fmin(x, y);
+}
+#else
+long double fminl(long double x, long double y)
+{
+ if (isnan(x))
+ return y;
+ if (isnan(y))
+ return x;
+ /* handle signed zeros, see C99 Annex F.9.9.2 */
+ if (signbit(x) != signbit(y))
+ return signbit(x) ? x : y;
+ return x < y ? x : y;
+}
+#endif
diff --git a/lib/libc/src/musl-math/fmod.c b/lib/libc/src/musl-math/fmod.c
new file mode 100644
index 0000000..6849722
--- /dev/null
+++ b/lib/libc/src/musl-math/fmod.c
@@ -0,0 +1,68 @@
+#include <math.h>
+#include <stdint.h>
+
+double fmod(double x, double y)
+{
+ union {double f; uint64_t i;} ux = {x}, uy = {y};
+ int ex = ux.i>>52 & 0x7ff;
+ int ey = uy.i>>52 & 0x7ff;
+ int sx = ux.i>>63;
+ uint64_t i;
+
+ /* in the followings uxi should be ux.i, but then gcc wrongly adds */
+ /* float load/store to inner loops ruining performance and code size */
+ uint64_t uxi = ux.i;
+
+ if (uy.i<<1 == 0 || isnan(y) || ex == 0x7ff)
+ return (x*y)/(x*y);
+ if (uxi<<1 <= uy.i<<1) {
+ if (uxi<<1 == uy.i<<1)
+ return 0*x;
+ return x;
+ }
+
+ /* normalize x and y */
+ if (!ex) {
+ for (i = uxi<<12; i>>63 == 0; ex--, i <<= 1);
+ uxi <<= -ex + 1;
+ } else {
+ uxi &= -1ULL >> 12;
+ uxi |= 1ULL << 52;
+ }
+ if (!ey) {
+ for (i = uy.i<<12; i>>63 == 0; ey--, i <<= 1);
+ uy.i <<= -ey + 1;
+ } else {
+ uy.i &= -1ULL >> 12;
+ uy.i |= 1ULL << 52;
+ }
+
+ /* x mod y */
+ for (; ex > ey; ex--) {
+ i = uxi - uy.i;
+ if (i >> 63 == 0) {
+ if (i == 0)
+ return 0*x;
+ uxi = i;
+ }
+ uxi <<= 1;
+ }
+ i = uxi - uy.i;
+ if (i >> 63 == 0) {
+ if (i == 0)
+ return 0*x;
+ uxi = i;
+ }
+ for (; uxi>>52 == 0; uxi <<= 1, ex--);
+
+ /* scale result */
+ if (ex > 0) {
+ uxi -= 1ULL << 52;
+ uxi |= (uint64_t)ex << 52;
+ } else {
+ uxi >>= -ex + 1;
+ }
+ uxi |= (uint64_t)sx << 63;
+ ux.i = uxi;
+ return ux.f;
+}
diff --git a/lib/libc/src/musl-math/fmodf.c b/lib/libc/src/musl-math/fmodf.c
new file mode 100644
index 0000000..ff58f93
--- /dev/null
+++ b/lib/libc/src/musl-math/fmodf.c
@@ -0,0 +1,65 @@
+#include <math.h>
+#include <stdint.h>
+
+float fmodf(float x, float y)
+{
+ union {float f; uint32_t i;} ux = {x}, uy = {y};
+ int ex = ux.i>>23 & 0xff;
+ int ey = uy.i>>23 & 0xff;
+ uint32_t sx = ux.i & 0x80000000;
+ uint32_t i;
+ uint32_t uxi = ux.i;
+
+ if (uy.i<<1 == 0 || isnan(y) || ex == 0xff)
+ return (x*y)/(x*y);
+ if (uxi<<1 <= uy.i<<1) {
+ if (uxi<<1 == uy.i<<1)
+ return 0*x;
+ return x;
+ }
+
+ /* normalize x and y */
+ if (!ex) {
+ for (i = uxi<<9; i>>31 == 0; ex--, i <<= 1);
+ uxi <<= -ex + 1;
+ } else {
+ uxi &= -1U >> 9;
+ uxi |= 1U << 23;
+ }
+ if (!ey) {
+ for (i = uy.i<<9; i>>31 == 0; ey--, i <<= 1);
+ uy.i <<= -ey + 1;
+ } else {
+ uy.i &= -1U >> 9;
+ uy.i |= 1U << 23;
+ }
+
+ /* x mod y */
+ for (; ex > ey; ex--) {
+ i = uxi - uy.i;
+ if (i >> 31 == 0) {
+ if (i == 0)
+ return 0*x;
+ uxi = i;
+ }
+ uxi <<= 1;
+ }
+ i = uxi - uy.i;
+ if (i >> 31 == 0) {
+ if (i == 0)
+ return 0*x;
+ uxi = i;
+ }
+ for (; uxi>>23 == 0; uxi <<= 1, ex--);
+
+ /* scale result up */
+ if (ex > 0) {
+ uxi -= 1U << 23;
+ uxi |= (uint32_t)ex << 23;
+ } else {
+ uxi >>= -ex + 1;
+ }
+ uxi |= sx;
+ ux.i = uxi;
+ return ux.f;
+}
diff --git a/lib/libc/src/musl-math/fmodl.c b/lib/libc/src/musl-math/fmodl.c
new file mode 100644
index 0000000..9f5b873
--- /dev/null
+++ b/lib/libc/src/musl-math/fmodl.c
@@ -0,0 +1,105 @@
+#include "libm.h"
+
+#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
+long double fmodl(long double x, long double y)
+{
+ return fmod(x, y);
+}
+#elif (LDBL_MANT_DIG == 64 || LDBL_MANT_DIG == 113) && LDBL_MAX_EXP == 16384
+long double fmodl(long double x, long double y)
+{
+ union ldshape ux = {x}, uy = {y};
+ int ex = ux.i.se & 0x7fff;
+ int ey = uy.i.se & 0x7fff;
+ int sx = ux.i.se & 0x8000;
+
+ if (y == 0 || isnan(y) || ex == 0x7fff)
+ return (x*y)/(x*y);
+ ux.i.se = ex;
+ uy.i.se = ey;
+ if (ux.f <= uy.f) {
+ if (ux.f == uy.f)
+ return 0*x;
+ return x;
+ }
+
+ /* normalize x and y */
+ if (!ex) {
+ ux.f *= 0x1p120f;
+ ex = ux.i.se - 120;
+ }
+ if (!ey) {
+ uy.f *= 0x1p120f;
+ ey = uy.i.se - 120;
+ }
+
+ /* x mod y */
+#if LDBL_MANT_DIG == 64
+ uint64_t i, mx, my;
+ mx = ux.i.m;
+ my = uy.i.m;
+ for (; ex > ey; ex--) {
+ i = mx - my;
+ if (mx >= my) {
+ if (i == 0)
+ return 0*x;
+ mx = 2*i;
+ } else if (2*mx < mx) {
+ mx = 2*mx - my;
+ } else {
+ mx = 2*mx;
+ }
+ }
+ i = mx - my;
+ if (mx >= my) {
+ if (i == 0)
+ return 0*x;
+ mx = i;
+ }
+ for (; mx >> 63 == 0; mx *= 2, ex--);
+ ux.i.m = mx;
+#elif LDBL_MANT_DIG == 113
+ uint64_t hi, lo, xhi, xlo, yhi, ylo;
+ xhi = (ux.i2.hi & -1ULL>>16) | 1ULL<<48;
+ yhi = (uy.i2.hi & -1ULL>>16) | 1ULL<<48;
+ xlo = ux.i2.lo;
+ ylo = uy.i2.lo;
+ for (; ex > ey; ex--) {
+ hi = xhi - yhi;
+ lo = xlo - ylo;
+ if (xlo < ylo)
+ hi -= 1;
+ if (hi >> 63 == 0) {
+ if ((hi|lo) == 0)
+ return 0*x;
+ xhi = 2*hi + (lo>>63);
+ xlo = 2*lo;
+ } else {
+ xhi = 2*xhi + (xlo>>63);
+ xlo = 2*xlo;
+ }
+ }
+ hi = xhi - yhi;
+ lo = xlo - ylo;
+ if (xlo < ylo)
+ hi -= 1;
+ if (hi >> 63 == 0) {
+ if ((hi|lo) == 0)
+ return 0*x;
+ xhi = hi;
+ xlo = lo;
+ }
+ for (; xhi >> 48 == 0; xhi = 2*xhi + (xlo>>63), xlo = 2*xlo, ex--);
+ ux.i2.hi = xhi;
+ ux.i2.lo = xlo;
+#endif
+
+ /* scale result */
+ if (ex <= 0) {
+ ux.i.se = (ex+120)|sx;
+ ux.f *= 0x1p-120f;
+ } else
+ ux.i.se = ex|sx;
+ return ux.f;
+}
+#endif
diff --git a/lib/libc/src/musl-math/frexp.c b/lib/libc/src/musl-math/frexp.c
new file mode 100644
index 0000000..abac0ea
--- /dev/null
+++ b/lib/libc/src/musl-math/frexp.c
@@ -0,0 +1,24 @@
+#include <math.h>
+#include <stdint.h>
+
+double frexp(double x, int *e)
+{
+ union { double d; uint64_t i; } y = { x };
+ int ee = y.i>>52 & 0x7ff;
+
+ if (!ee) {
+ if (x) {
+ x = frexp(x*0x1p64, e);
+ *e -= 64;
+ } else *e = 0;
+ return x;
+ } else if (ee == 0x7ff) {
+ *e = 0;
+ return x;
+ }
+
+ *e = ee - 0x3fe;
+ y.i &= 0x800fffffffffffffull;
+ y.i |= 0x3fe0000000000000ull;
+ return y.d;
+}
diff --git a/lib/libc/src/musl-math/frexpf.c b/lib/libc/src/musl-math/frexpf.c
new file mode 100644
index 0000000..2dabe37
--- /dev/null
+++ b/lib/libc/src/musl-math/frexpf.c
@@ -0,0 +1,24 @@
+#include <math.h>
+#include <stdint.h>
+
+float frexpf(float x, int *e)
+{
+ union { float f; uint32_t i; } y = { x };
+ int ee = y.i>>23 & 0xff;
+
+ if (!ee) {
+ if (x) {
+ x = frexpf(x*0x1p64, e);
+ *e -= 64;
+ } else *e = 0;
+ return x;
+ } else if (ee == 0xff) {
+ *e = 0;
+ return x;
+ }
+
+ *e = ee - 0x7e;
+ y.i &= 0x807ffffful;
+ y.i |= 0x3f000000ul;
+ return y.f;
+}
diff --git a/lib/libc/src/musl-math/frexpl.c b/lib/libc/src/musl-math/frexpl.c
new file mode 100644
index 0000000..e05cf75
--- /dev/null
+++ b/lib/libc/src/musl-math/frexpl.c
@@ -0,0 +1,30 @@
+#include "libm.h"
+
+#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
+long double frexpl(long double x, int *e)
+{
+ return frexp(x, e);
+}
+#elif (LDBL_MANT_DIG == 64 || LDBL_MANT_DIG == 113) && LDBL_MAX_EXP == 16384
+long double frexpl(long double x, int *e)
+{
+ union ldshape u = {x};
+ int ee = u.i.se & 0x7fff;
+
+ if (!ee) {
+ if (x) {
+ x = frexpl(x*0x1p120, e);
+ *e -= 120;
+ } else *e = 0;
+ return x;
+ } else if (ee == 0x7fff) {
+ *e = 0;
+ return x;
+ }
+
+ *e = ee - 0x3ffe;
+ u.i.se &= 0x8000;
+ u.i.se |= 0x3ffe;
+ return u.f;
+}
+#endif
diff --git a/lib/libc/src/musl-math/hypot.c b/lib/libc/src/musl-math/hypot.c
new file mode 100644
index 0000000..6071bf1
--- /dev/null
+++ b/lib/libc/src/musl-math/hypot.c
@@ -0,0 +1,67 @@
+#include <math.h>
+#include <stdint.h>
+#include <float.h>
+
+#if FLT_EVAL_METHOD > 1U && LDBL_MANT_DIG == 64
+#define SPLIT (0x1p32 + 1)
+#else
+#define SPLIT (0x1p27 + 1)
+#endif
+
+static void sq(double_t *hi, double_t *lo, double x)
+{
+ double_t xh, xl, xc;
+
+ xc = (double_t)x*SPLIT;
+ xh = x - xc + xc;
+ xl = x - xh;
+ *hi = (double_t)x*x;
+ *lo = xh*xh - *hi + 2*xh*xl + xl*xl;
+}
+
+double hypot(double x, double y)
+{
+ union {double f; uint64_t i;} ux = {x}, uy = {y}, ut;
+ int ex, ey;
+ double_t hx, lx, hy, ly, z;
+
+ /* arrange |x| >= |y| */
+ ux.i &= -1ULL>>1;
+ uy.i &= -1ULL>>1;
+ if (ux.i < uy.i) {
+ ut = ux;
+ ux = uy;
+ uy = ut;
+ }
+
+ /* special cases */
+ ex = ux.i>>52;
+ ey = uy.i>>52;
+ x = ux.f;
+ y = uy.f;
+ /* note: hypot(inf,nan) == inf */
+ if (ey == 0x7ff)
+ return y;
+ if (ex == 0x7ff || uy.i == 0)
+ return x;
+ /* note: hypot(x,y) ~= x + y*y/x/2 with inexact for small y/x */
+ /* 64 difference is enough for ld80 double_t */
+ if (ex - ey > 64)
+ return x + y;
+
+ /* precise sqrt argument in nearest rounding mode without overflow */
+ /* xh*xh must not overflow and xl*xl must not underflow in sq */
+ z = 1;
+ if (ex > 0x3ff+510) {
+ z = 0x1p700;
+ x *= 0x1p-700;
+ y *= 0x1p-700;
+ } else if (ey < 0x3ff-450) {
+ z = 0x1p-700;
+ x *= 0x1p700;
+ y *= 0x1p700;
+ }
+ sq(&hx, &lx, x);
+ sq(&hy, &ly, y);
+ return z*sqrt(ly+lx+hy+hx);
+}
diff --git a/lib/libc/src/musl-math/hypotf.c b/lib/libc/src/musl-math/hypotf.c
new file mode 100644
index 0000000..2fc214b
--- /dev/null
+++ b/lib/libc/src/musl-math/hypotf.c
@@ -0,0 +1,35 @@
+#include <math.h>
+#include <stdint.h>
+
+float hypotf(float x, float y)
+{
+ union {float f; uint32_t i;} ux = {x}, uy = {y}, ut;
+ float_t z;
+
+ ux.i &= -1U>>1;
+ uy.i &= -1U>>1;
+ if (ux.i < uy.i) {
+ ut = ux;
+ ux = uy;
+ uy = ut;
+ }
+
+ x = ux.f;
+ y = uy.f;
+ if (uy.i == 0xff<<23)
+ return y;
+ if (ux.i >= 0xff<<23 || uy.i == 0 || ux.i - uy.i >= 25<<23)
+ return x + y;
+
+ z = 1;
+ if (ux.i >= (0x7f+60)<<23) {
+ z = 0x1p90f;
+ x *= 0x1p-90f;
+ y *= 0x1p-90f;
+ } else if (uy.i < (0x7f-60)<<23) {
+ z = 0x1p-90f;
+ x *= 0x1p90f;
+ y *= 0x1p90f;
+ }
+ return z*sqrtf((double)x*x + (double)y*y);
+}
diff --git a/lib/libc/src/musl-math/hypotl.c b/lib/libc/src/musl-math/hypotl.c
new file mode 100644
index 0000000..479aa92
--- /dev/null
+++ b/lib/libc/src/musl-math/hypotl.c
@@ -0,0 +1,66 @@
+#include "libm.h"
+
+#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
+long double hypotl(long double x, long double y)
+{
+ return hypot(x, y);
+}
+#elif (LDBL_MANT_DIG == 64 || LDBL_MANT_DIG == 113) && LDBL_MAX_EXP == 16384
+#if LDBL_MANT_DIG == 64
+#define SPLIT (0x1p32L+1)
+#elif LDBL_MANT_DIG == 113
+#define SPLIT (0x1p57L+1)
+#endif
+
+static void sq(long double *hi, long double *lo, long double x)
+{
+ long double xh, xl, xc;
+ xc = x*SPLIT;
+ xh = x - xc + xc;
+ xl = x - xh;
+ *hi = x*x;
+ *lo = xh*xh - *hi + 2*xh*xl + xl*xl;
+}
+
+long double hypotl(long double x, long double y)
+{
+ union ldshape ux = {x}, uy = {y};
+ int ex, ey;
+ long double hx, lx, hy, ly, z;
+
+ ux.i.se &= 0x7fff;
+ uy.i.se &= 0x7fff;
+ if (ux.i.se < uy.i.se) {
+ ex = uy.i.se;
+ ey = ux.i.se;
+ x = uy.f;
+ y = ux.f;
+ } else {
+ ex = ux.i.se;
+ ey = uy.i.se;
+ x = ux.f;
+ y = uy.f;
+ }
+
+ if (ex == 0x7fff && isinf(y))
+ return y;
+ if (ex == 0x7fff || y == 0)
+ return x;
+ if (ex - ey > LDBL_MANT_DIG)
+ return x + y;
+
+ z = 1;
+ if (ex > 0x3fff+8000) {
+ z = 0x1p10000L;
+ x *= 0x1p-10000L;
+ y *= 0x1p-10000L;
+ } else if (ey < 0x3fff-8000) {
+ z = 0x1p-10000L;
+ x *= 0x1p10000L;
+ y *= 0x1p10000L;
+ }
+ sq(&hx, &lx, x);
+ sq(&hy, &ly, y);
+ return z*sqrtl(ly+lx+hy+hx);
+}
+#endif
diff --git a/lib/libc/src/musl-math/ilogb.c b/lib/libc/src/musl-math/ilogb.c
new file mode 100644
index 0000000..64d4015
--- /dev/null
+++ b/lib/libc/src/musl-math/ilogb.c
@@ -0,0 +1,26 @@
+#include <limits.h>
+#include "libm.h"
+
+int ilogb(double x)
+{
+ #pragma STDC FENV_ACCESS ON
+ union {double f; uint64_t i;} u = {x};
+ uint64_t i = u.i;
+ int e = i>>52 & 0x7ff;
+
+ if (!e) {
+ i <<= 12;
+ if (i == 0) {
+ FORCE_EVAL(0/0.0f);
+ return FP_ILOGB0;
+ }
+ /* subnormal x */
+ for (e = -0x3ff; i>>63 == 0; e--, i<<=1);
+ return e;
+ }
+ if (e == 0x7ff) {
+ FORCE_EVAL(0/0.0f);
+ return i<<12 ? FP_ILOGBNAN : INT_MAX;
+ }
+ return e - 0x3ff;
+}
diff --git a/lib/libc/src/musl-math/ilogbf.c b/lib/libc/src/musl-math/ilogbf.c
new file mode 100644
index 0000000..e23ba20
--- /dev/null
+++ b/lib/libc/src/musl-math/ilogbf.c
@@ -0,0 +1,26 @@
+#include <limits.h>
+#include "libm.h"
+
+int ilogbf(float x)
+{
+ #pragma STDC FENV_ACCESS ON
+ union {float f; uint32_t i;} u = {x};
+ uint32_t i = u.i;
+ int e = i>>23 & 0xff;
+
+ if (!e) {
+ i <<= 9;
+ if (i == 0) {
+ FORCE_EVAL(0/0.0f);
+ return FP_ILOGB0;
+ }
+ /* subnormal x */
+ for (e = -0x7f; i>>31 == 0; e--, i<<=1);
+ return e;
+ }
+ if (e == 0xff) {
+ FORCE_EVAL(0/0.0f);
+ return i<<9 ? FP_ILOGBNAN : INT_MAX;
+ }
+ return e - 0x7f;
+}
diff --git a/lib/libc/src/musl-math/ilogbl.c b/lib/libc/src/musl-math/ilogbl.c
new file mode 100644
index 0000000..7b1a9cf
--- /dev/null
+++ b/lib/libc/src/musl-math/ilogbl.c
@@ -0,0 +1,55 @@
+#include <limits.h>
+#include "libm.h"
+
+#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
+int ilogbl(long double x)
+{
+ return ilogb(x);
+}
+#elif LDBL_MANT_DIG == 64 && LDBL_MAX_EXP == 16384
+int ilogbl(long double x)
+{
+ #pragma STDC FENV_ACCESS ON
+ union ldshape u = {x};
+ uint64_t m = u.i.m;
+ int e = u.i.se & 0x7fff;
+
+ if (!e) {
+ if (m == 0) {
+ FORCE_EVAL(0/0.0f);
+ return FP_ILOGB0;
+ }
+ /* subnormal x */
+ for (e = -0x3fff+1; m>>63 == 0; e--, m<<=1);
+ return e;
+ }
+ if (e == 0x7fff) {
+ FORCE_EVAL(0/0.0f);
+ return m<<1 ? FP_ILOGBNAN : INT_MAX;
+ }
+ return e - 0x3fff;
+}
+#elif LDBL_MANT_DIG == 113 && LDBL_MAX_EXP == 16384
+int ilogbl(long double x)
+{
+ #pragma STDC FENV_ACCESS ON
+ union ldshape u = {x};
+ int e = u.i.se & 0x7fff;
+
+ if (!e) {
+ if (x == 0) {
+ FORCE_EVAL(0/0.0f);
+ return FP_ILOGB0;
+ }
+ /* subnormal x */
+ x *= 0x1p120;
+ return ilogbl(x) - 120;
+ }
+ if (e == 0x7fff) {
+ FORCE_EVAL(0/0.0f);
+ u.i.se = 0;
+ return u.f ? FP_ILOGBNAN : INT_MAX;
+ }
+ return e - 0x3fff;
+}
+#endif
diff --git a/lib/libc/src/musl-math/j0.c b/lib/libc/src/musl-math/j0.c
new file mode 100644
index 0000000..d722d94
--- /dev/null
+++ b/lib/libc/src/musl-math/j0.c
@@ -0,0 +1,375 @@
+/* origin: FreeBSD /usr/src/lib/msun/src/e_j0.c */
+/*
+ * ====================================================
+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ *
+ * Developed at SunSoft, a Sun Microsystems, Inc. business.
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ */
+/* j0(x), y0(x)
+ * Bessel function of the first and second kinds of order zero.
+ * Method -- j0(x):
+ * 1. For tiny x, we use j0(x) = 1 - x^2/4 + x^4/64 - ...
+ * 2. Reduce x to |x| since j0(x)=j0(-x), and
+ * for x in (0,2)
+ * j0(x) = 1-z/4+ z^2*R0/S0, where z = x*x;
+ * (precision: |j0-1+z/4-z^2R0/S0 |<2**-63.67 )
+ * for x in (2,inf)
+ * j0(x) = sqrt(2/(pi*x))*(p0(x)*cos(x0)-q0(x)*sin(x0))
+ * where x0 = x-pi/4. It is better to compute sin(x0),cos(x0)
+ * as follow:
+ * cos(x0) = cos(x)cos(pi/4)+sin(x)sin(pi/4)
+ * = 1/sqrt(2) * (cos(x) + sin(x))
+ * sin(x0) = sin(x)cos(pi/4)-cos(x)sin(pi/4)
+ * = 1/sqrt(2) * (sin(x) - cos(x))
+ * (To avoid cancellation, use
+ * sin(x) +- cos(x) = -cos(2x)/(sin(x) -+ cos(x))
+ * to compute the worse one.)
+ *
+ * 3 Special cases
+ * j0(nan)= nan
+ * j0(0) = 1
+ * j0(inf) = 0
+ *
+ * Method -- y0(x):
+ * 1. For x<2.
+ * Since
+ * y0(x) = 2/pi*(j0(x)*(ln(x/2)+Euler) + x^2/4 - ...)
+ * therefore y0(x)-2/pi*j0(x)*ln(x) is an even function.
+ * We use the following function to approximate y0,
+ * y0(x) = U(z)/V(z) + (2/pi)*(j0(x)*ln(x)), z= x^2
+ * where
+ * U(z) = u00 + u01*z + ... + u06*z^6
+ * V(z) = 1 + v01*z + ... + v04*z^4
+ * with absolute approximation error bounded by 2**-72.
+ * Note: For tiny x, U/V = u0 and j0(x)~1, hence
+ * y0(tiny) = u0 + (2/pi)*ln(tiny), (choose tiny<2**-27)
+ * 2. For x>=2.
+ * y0(x) = sqrt(2/(pi*x))*(p0(x)*cos(x0)+q0(x)*sin(x0))
+ * where x0 = x-pi/4. It is better to compute sin(x0),cos(x0)
+ * by the method mentioned above.
+ * 3. Special cases: y0(0)=-inf, y0(x<0)=NaN, y0(inf)=0.
+ */
+
+#include "libm.h"
+
+static double pzero(double), qzero(double);
+
+static const double
+invsqrtpi = 5.64189583547756279280e-01, /* 0x3FE20DD7, 0x50429B6D */
+tpi = 6.36619772367581382433e-01; /* 0x3FE45F30, 0x6DC9C883 */
+
+/* common method when |x|>=2 */
+static double common(uint32_t ix, double x, int y0)
+{
+ double s,c,ss,cc,z;
+
+ /*
+ * j0(x) = sqrt(2/(pi*x))*(p0(x)*cos(x-pi/4)-q0(x)*sin(x-pi/4))
+ * y0(x) = sqrt(2/(pi*x))*(p0(x)*sin(x-pi/4)+q0(x)*cos(x-pi/4))
+ *
+ * sin(x-pi/4) = (sin(x) - cos(x))/sqrt(2)
+ * cos(x-pi/4) = (sin(x) + cos(x))/sqrt(2)
+ * sin(x) +- cos(x) = -cos(2x)/(sin(x) -+ cos(x))
+ */
+ s = sin(x);
+ c = cos(x);
+ if (y0)
+ c = -c;
+ cc = s+c;
+ /* avoid overflow in 2*x, big ulp error when x>=0x1p1023 */
+ if (ix < 0x7fe00000) {
+ ss = s-c;
+ z = -cos(2*x);
+ if (s*c < 0)
+ cc = z/ss;
+ else
+ ss = z/cc;
+ if (ix < 0x48000000) {
+ if (y0)
+ ss = -ss;
+ cc = pzero(x)*cc-qzero(x)*ss;
+ }
+ }
+ return invsqrtpi*cc/sqrt(x);
+}
+
+/* R0/S0 on [0, 2.00] */
+static const double
+R02 = 1.56249999999999947958e-02, /* 0x3F8FFFFF, 0xFFFFFFFD */
+R03 = -1.89979294238854721751e-04, /* 0xBF28E6A5, 0xB61AC6E9 */
+R04 = 1.82954049532700665670e-06, /* 0x3EBEB1D1, 0x0C503919 */
+R05 = -4.61832688532103189199e-09, /* 0xBE33D5E7, 0x73D63FCE */
+S01 = 1.56191029464890010492e-02, /* 0x3F8FFCE8, 0x82C8C2A4 */
+S02 = 1.16926784663337450260e-04, /* 0x3F1EA6D2, 0xDD57DBF4 */
+S03 = 5.13546550207318111446e-07, /* 0x3EA13B54, 0xCE84D5A9 */
+S04 = 1.16614003333790000205e-09; /* 0x3E1408BC, 0xF4745D8F */
+
+double j0(double x)
+{
+ double z,r,s;
+ uint32_t ix;
+
+ GET_HIGH_WORD(ix, x);
+ ix &= 0x7fffffff;
+
+ /* j0(+-inf)=0, j0(nan)=nan */
+ if (ix >= 0x7ff00000)
+ return 1/(x*x);
+ x = fabs(x);
+
+ if (ix >= 0x40000000) { /* |x| >= 2 */
+ /* large ulp error near zeros: 2.4, 5.52, 8.6537,.. */
+ return common(ix,x,0);
+ }
+
+ /* 1 - x*x/4 + x*x*R(x^2)/S(x^2) */
+ if (ix >= 0x3f200000) { /* |x| >= 2**-13 */
+ /* up to 4ulp error close to 2 */
+ z = x*x;
+ r = z*(R02+z*(R03+z*(R04+z*R05)));
+ s = 1+z*(S01+z*(S02+z*(S03+z*S04)));
+ return (1+x/2)*(1-x/2) + z*(r/s);
+ }
+
+ /* 1 - x*x/4 */
+ /* prevent underflow */
+ /* inexact should be raised when x!=0, this is not done correctly */
+ if (ix >= 0x38000000) /* |x| >= 2**-127 */
+ x = 0.25*x*x;
+ return 1 - x;
+}
+
+static const double
+u00 = -7.38042951086872317523e-02, /* 0xBFB2E4D6, 0x99CBD01F */
+u01 = 1.76666452509181115538e-01, /* 0x3FC69D01, 0x9DE9E3FC */
+u02 = -1.38185671945596898896e-02, /* 0xBF8C4CE8, 0xB16CFA97 */
+u03 = 3.47453432093683650238e-04, /* 0x3F36C54D, 0x20B29B6B */
+u04 = -3.81407053724364161125e-06, /* 0xBECFFEA7, 0x73D25CAD */
+u05 = 1.95590137035022920206e-08, /* 0x3E550057, 0x3B4EABD4 */
+u06 = -3.98205194132103398453e-11, /* 0xBDC5E43D, 0x693FB3C8 */
+v01 = 1.27304834834123699328e-02, /* 0x3F8A1270, 0x91C9C71A */
+v02 = 7.60068627350353253702e-05, /* 0x3F13ECBB, 0xF578C6C1 */
+v03 = 2.59150851840457805467e-07, /* 0x3E91642D, 0x7FF202FD */
+v04 = 4.41110311332675467403e-10; /* 0x3DFE5018, 0x3BD6D9EF */
+
+double y0(double x)
+{
+ double z,u,v;
+ uint32_t ix,lx;
+
+ EXTRACT_WORDS(ix, lx, x);
+
+ /* y0(nan)=nan, y0(<0)=nan, y0(0)=-inf, y0(inf)=0 */
+ if ((ix<<1 | lx) == 0)
+ return -1/0.0;
+ if (ix>>31)
+ return 0/0.0;
+ if (ix >= 0x7ff00000)
+ return 1/x;
+
+ if (ix >= 0x40000000) { /* x >= 2 */
+ /* large ulp errors near zeros: 3.958, 7.086,.. */
+ return common(ix,x,1);
+ }
+
+ /* U(x^2)/V(x^2) + (2/pi)*j0(x)*log(x) */
+ if (ix >= 0x3e400000) { /* x >= 2**-27 */
+ /* large ulp error near the first zero, x ~= 0.89 */
+ z = x*x;
+ u = u00+z*(u01+z*(u02+z*(u03+z*(u04+z*(u05+z*u06)))));
+ v = 1.0+z*(v01+z*(v02+z*(v03+z*v04)));
+ return u/v + tpi*(j0(x)*log(x));
+ }
+ return u00 + tpi*log(x);
+}
+
+/* The asymptotic expansions of pzero is
+ * 1 - 9/128 s^2 + 11025/98304 s^4 - ..., where s = 1/x.
+ * For x >= 2, We approximate pzero by
+ * pzero(x) = 1 + (R/S)
+ * where R = pR0 + pR1*s^2 + pR2*s^4 + ... + pR5*s^10
+ * S = 1 + pS0*s^2 + ... + pS4*s^10
+ * and
+ * | pzero(x)-1-R/S | <= 2 ** ( -60.26)
+ */
+static const double pR8[6] = { /* for x in [inf, 8]=1/[0,0.125] */
+ 0.00000000000000000000e+00, /* 0x00000000, 0x00000000 */
+ -7.03124999999900357484e-02, /* 0xBFB1FFFF, 0xFFFFFD32 */
+ -8.08167041275349795626e+00, /* 0xC02029D0, 0xB44FA779 */
+ -2.57063105679704847262e+02, /* 0xC0701102, 0x7B19E863 */
+ -2.48521641009428822144e+03, /* 0xC0A36A6E, 0xCD4DCAFC */
+ -5.25304380490729545272e+03, /* 0xC0B4850B, 0x36CC643D */
+};
+static const double pS8[5] = {
+ 1.16534364619668181717e+02, /* 0x405D2233, 0x07A96751 */
+ 3.83374475364121826715e+03, /* 0x40ADF37D, 0x50596938 */
+ 4.05978572648472545552e+04, /* 0x40E3D2BB, 0x6EB6B05F */
+ 1.16752972564375915681e+05, /* 0x40FC810F, 0x8F9FA9BD */
+ 4.76277284146730962675e+04, /* 0x40E74177, 0x4F2C49DC */
+};
+
+static const double pR5[6] = { /* for x in [8,4.5454]=1/[0.125,0.22001] */
+ -1.14125464691894502584e-11, /* 0xBDA918B1, 0x47E495CC */
+ -7.03124940873599280078e-02, /* 0xBFB1FFFF, 0xE69AFBC6 */
+ -4.15961064470587782438e+00, /* 0xC010A370, 0xF90C6BBF */
+ -6.76747652265167261021e+01, /* 0xC050EB2F, 0x5A7D1783 */
+ -3.31231299649172967747e+02, /* 0xC074B3B3, 0x6742CC63 */
+ -3.46433388365604912451e+02, /* 0xC075A6EF, 0x28A38BD7 */
+};
+static const double pS5[5] = {
+ 6.07539382692300335975e+01, /* 0x404E6081, 0x0C98C5DE */
+ 1.05125230595704579173e+03, /* 0x40906D02, 0x5C7E2864 */
+ 5.97897094333855784498e+03, /* 0x40B75AF8, 0x8FBE1D60 */
+ 9.62544514357774460223e+03, /* 0x40C2CCB8, 0xFA76FA38 */
+ 2.40605815922939109441e+03, /* 0x40A2CC1D, 0xC70BE864 */
+};
+
+static const double pR3[6] = {/* for x in [4.547,2.8571]=1/[0.2199,0.35001] */
+ -2.54704601771951915620e-09, /* 0xBE25E103, 0x6FE1AA86 */
+ -7.03119616381481654654e-02, /* 0xBFB1FFF6, 0xF7C0E24B */
+ -2.40903221549529611423e+00, /* 0xC00345B2, 0xAEA48074 */
+ -2.19659774734883086467e+01, /* 0xC035F74A, 0x4CB94E14 */
+ -5.80791704701737572236e+01, /* 0xC04D0A22, 0x420A1A45 */
+ -3.14479470594888503854e+01, /* 0xC03F72AC, 0xA892D80F */
+};
+static const double pS3[5] = {
+ 3.58560338055209726349e+01, /* 0x4041ED92, 0x84077DD3 */
+ 3.61513983050303863820e+02, /* 0x40769839, 0x464A7C0E */
+ 1.19360783792111533330e+03, /* 0x4092A66E, 0x6D1061D6 */
+ 1.12799679856907414432e+03, /* 0x40919FFC, 0xB8C39B7E */
+ 1.73580930813335754692e+02, /* 0x4065B296, 0xFC379081 */
+};
+
+static const double pR2[6] = {/* for x in [2.8570,2]=1/[0.3499,0.5] */
+ -8.87534333032526411254e-08, /* 0xBE77D316, 0xE927026D */
+ -7.03030995483624743247e-02, /* 0xBFB1FF62, 0x495E1E42 */
+ -1.45073846780952986357e+00, /* 0xBFF73639, 0x8A24A843 */
+ -7.63569613823527770791e+00, /* 0xC01E8AF3, 0xEDAFA7F3 */
+ -1.11931668860356747786e+01, /* 0xC02662E6, 0xC5246303 */
+ -3.23364579351335335033e+00, /* 0xC009DE81, 0xAF8FE70F */
+};
+static const double pS2[5] = {
+ 2.22202997532088808441e+01, /* 0x40363865, 0x908B5959 */
+ 1.36206794218215208048e+02, /* 0x4061069E, 0x0EE8878F */
+ 2.70470278658083486789e+02, /* 0x4070E786, 0x42EA079B */
+ 1.53875394208320329881e+02, /* 0x40633C03, 0x3AB6FAFF */
+ 1.46576176948256193810e+01, /* 0x402D50B3, 0x44391809 */
+};
+
+static double pzero(double x)
+{
+ const double *p,*q;
+ double_t z,r,s;
+ uint32_t ix;
+
+ GET_HIGH_WORD(ix, x);
+ ix &= 0x7fffffff;
+ if (ix >= 0x40200000){p = pR8; q = pS8;}
+ else if (ix >= 0x40122E8B){p = pR5; q = pS5;}
+ else if (ix >= 0x4006DB6D){p = pR3; q = pS3;}
+ else /*ix >= 0x40000000*/ {p = pR2; q = pS2;}
+ z = 1.0/(x*x);
+ r = p[0]+z*(p[1]+z*(p[2]+z*(p[3]+z*(p[4]+z*p[5]))));
+ s = 1.0+z*(q[0]+z*(q[1]+z*(q[2]+z*(q[3]+z*q[4]))));
+ return 1.0 + r/s;
+}
+
+
+/* For x >= 8, the asymptotic expansions of qzero is
+ * -1/8 s + 75/1024 s^3 - ..., where s = 1/x.
+ * We approximate pzero by
+ * qzero(x) = s*(-1.25 + (R/S))
+ * where R = qR0 + qR1*s^2 + qR2*s^4 + ... + qR5*s^10
+ * S = 1 + qS0*s^2 + ... + qS5*s^12
+ * and
+ * | qzero(x)/s +1.25-R/S | <= 2 ** ( -61.22)
+ */
+static const double qR8[6] = { /* for x in [inf, 8]=1/[0,0.125] */
+ 0.00000000000000000000e+00, /* 0x00000000, 0x00000000 */
+ 7.32421874999935051953e-02, /* 0x3FB2BFFF, 0xFFFFFE2C */
+ 1.17682064682252693899e+01, /* 0x40278952, 0x5BB334D6 */
+ 5.57673380256401856059e+02, /* 0x40816D63, 0x15301825 */
+ 8.85919720756468632317e+03, /* 0x40C14D99, 0x3E18F46D */
+ 3.70146267776887834771e+04, /* 0x40E212D4, 0x0E901566 */
+};
+static const double qS8[6] = {
+ 1.63776026895689824414e+02, /* 0x406478D5, 0x365B39BC */
+ 8.09834494656449805916e+03, /* 0x40BFA258, 0x4E6B0563 */
+ 1.42538291419120476348e+05, /* 0x41016652, 0x54D38C3F */
+ 8.03309257119514397345e+05, /* 0x412883DA, 0x83A52B43 */
+ 8.40501579819060512818e+05, /* 0x4129A66B, 0x28DE0B3D */
+ -3.43899293537866615225e+05, /* 0xC114FD6D, 0x2C9530C5 */
+};
+
+static const double qR5[6] = { /* for x in [8,4.5454]=1/[0.125,0.22001] */
+ 1.84085963594515531381e-11, /* 0x3DB43D8F, 0x29CC8CD9 */
+ 7.32421766612684765896e-02, /* 0x3FB2BFFF, 0xD172B04C */
+ 5.83563508962056953777e+00, /* 0x401757B0, 0xB9953DD3 */
+ 1.35111577286449829671e+02, /* 0x4060E392, 0x0A8788E9 */
+ 1.02724376596164097464e+03, /* 0x40900CF9, 0x9DC8C481 */
+ 1.98997785864605384631e+03, /* 0x409F17E9, 0x53C6E3A6 */
+};
+static const double qS5[6] = {
+ 8.27766102236537761883e+01, /* 0x4054B1B3, 0xFB5E1543 */
+ 2.07781416421392987104e+03, /* 0x40A03BA0, 0xDA21C0CE */
+ 1.88472887785718085070e+04, /* 0x40D267D2, 0x7B591E6D */
+ 5.67511122894947329769e+04, /* 0x40EBB5E3, 0x97E02372 */
+ 3.59767538425114471465e+04, /* 0x40E19118, 0x1F7A54A0 */
+ -5.35434275601944773371e+03, /* 0xC0B4EA57, 0xBEDBC609 */
+};
+
+static const double qR3[6] = {/* for x in [4.547,2.8571]=1/[0.2199,0.35001] */
+ 4.37741014089738620906e-09, /* 0x3E32CD03, 0x6ADECB82 */
+ 7.32411180042911447163e-02, /* 0x3FB2BFEE, 0x0E8D0842 */
+ 3.34423137516170720929e+00, /* 0x400AC0FC, 0x61149CF5 */
+ 4.26218440745412650017e+01, /* 0x40454F98, 0x962DAEDD */
+ 1.70808091340565596283e+02, /* 0x406559DB, 0xE25EFD1F */
+ 1.66733948696651168575e+02, /* 0x4064D77C, 0x81FA21E0 */
+};
+static const double qS3[6] = {
+ 4.87588729724587182091e+01, /* 0x40486122, 0xBFE343A6 */
+ 7.09689221056606015736e+02, /* 0x40862D83, 0x86544EB3 */
+ 3.70414822620111362994e+03, /* 0x40ACF04B, 0xE44DFC63 */
+ 6.46042516752568917582e+03, /* 0x40B93C6C, 0xD7C76A28 */
+ 2.51633368920368957333e+03, /* 0x40A3A8AA, 0xD94FB1C0 */
+ -1.49247451836156386662e+02, /* 0xC062A7EB, 0x201CF40F */
+};
+
+static const double qR2[6] = {/* for x in [2.8570,2]=1/[0.3499,0.5] */
+ 1.50444444886983272379e-07, /* 0x3E84313B, 0x54F76BDB */
+ 7.32234265963079278272e-02, /* 0x3FB2BEC5, 0x3E883E34 */
+ 1.99819174093815998816e+00, /* 0x3FFFF897, 0xE727779C */
+ 1.44956029347885735348e+01, /* 0x402CFDBF, 0xAAF96FE5 */
+ 3.16662317504781540833e+01, /* 0x403FAA8E, 0x29FBDC4A */
+ 1.62527075710929267416e+01, /* 0x403040B1, 0x71814BB4 */
+};
+static const double qS2[6] = {
+ 3.03655848355219184498e+01, /* 0x403E5D96, 0xF7C07AED */
+ 2.69348118608049844624e+02, /* 0x4070D591, 0xE4D14B40 */
+ 8.44783757595320139444e+02, /* 0x408A6645, 0x22B3BF22 */
+ 8.82935845112488550512e+02, /* 0x408B977C, 0x9C5CC214 */
+ 2.12666388511798828631e+02, /* 0x406A9553, 0x0E001365 */
+ -5.31095493882666946917e+00, /* 0xC0153E6A, 0xF8B32931 */
+};
+
+static double qzero(double x)
+{
+ const double *p,*q;
+ double_t s,r,z;
+ uint32_t ix;
+
+ GET_HIGH_WORD(ix, x);
+ ix &= 0x7fffffff;
+ if (ix >= 0x40200000){p = qR8; q = qS8;}
+ else if (ix >= 0x40122E8B){p = qR5; q = qS5;}
+ else if (ix >= 0x4006DB6D){p = qR3; q = qS3;}
+ else /*ix >= 0x40000000*/ {p = qR2; q = qS2;}
+ z = 1.0/(x*x);
+ r = p[0]+z*(p[1]+z*(p[2]+z*(p[3]+z*(p[4]+z*p[5]))));
+ s = 1.0+z*(q[0]+z*(q[1]+z*(q[2]+z*(q[3]+z*(q[4]+z*q[5])))));
+ return (-.125 + r/s)/x;
+}
diff --git a/lib/libc/src/musl-math/j0f.c b/lib/libc/src/musl-math/j0f.c
new file mode 100644
index 0000000..fab554a
--- /dev/null
+++ b/lib/libc/src/musl-math/j0f.c
@@ -0,0 +1,314 @@
+/* origin: FreeBSD /usr/src/lib/msun/src/e_j0f.c */
+/*
+ * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com.
+ */
+/*
+ * ====================================================
+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ *
+ * Developed at SunPro, a Sun Microsystems, Inc. business.
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ */
+
+#define _GNU_SOURCE
+#include "libm.h"
+
+static float pzerof(float), qzerof(float);
+
+static const float
+invsqrtpi = 5.6418961287e-01, /* 0x3f106ebb */
+tpi = 6.3661974669e-01; /* 0x3f22f983 */
+
+static float common(uint32_t ix, float x, int y0)
+{
+ float z,s,c,ss,cc;
+ /*
+ * j0(x) = 1/sqrt(pi) * (P(0,x)*cc - Q(0,x)*ss) / sqrt(x)
+ * y0(x) = 1/sqrt(pi) * (P(0,x)*ss + Q(0,x)*cc) / sqrt(x)
+ */
+ s = sinf(x);
+ c = cosf(x);
+ if (y0)
+ c = -c;
+ cc = s+c;
+ if (ix < 0x7f000000) {
+ ss = s-c;
+ z = -cosf(2*x);
+ if (s*c < 0)
+ cc = z/ss;
+ else
+ ss = z/cc;
+ if (ix < 0x58800000) {
+ if (y0)
+ ss = -ss;
+ cc = pzerof(x)*cc-qzerof(x)*ss;
+ }
+ }
+ return invsqrtpi*cc/sqrtf(x);
+}
+
+/* R0/S0 on [0, 2.00] */
+static const float
+R02 = 1.5625000000e-02, /* 0x3c800000 */
+R03 = -1.8997929874e-04, /* 0xb947352e */
+R04 = 1.8295404516e-06, /* 0x35f58e88 */
+R05 = -4.6183270541e-09, /* 0xb19eaf3c */
+S01 = 1.5619102865e-02, /* 0x3c7fe744 */
+S02 = 1.1692678527e-04, /* 0x38f53697 */
+S03 = 5.1354652442e-07, /* 0x3509daa6 */
+S04 = 1.1661400734e-09; /* 0x30a045e8 */
+
+float j0f(float x)
+{
+ float z,r,s;
+ uint32_t ix;
+
+ GET_FLOAT_WORD(ix, x);
+ ix &= 0x7fffffff;
+ if (ix >= 0x7f800000)
+ return 1/(x*x);
+ x = fabsf(x);
+
+ if (ix >= 0x40000000) { /* |x| >= 2 */
+ /* large ulp error near zeros */
+ return common(ix, x, 0);
+ }
+ if (ix >= 0x3a000000) { /* |x| >= 2**-11 */
+ /* up to 4ulp error near 2 */
+ z = x*x;
+ r = z*(R02+z*(R03+z*(R04+z*R05)));
+ s = 1+z*(S01+z*(S02+z*(S03+z*S04)));
+ return (1+x/2)*(1-x/2) + z*(r/s);
+ }
+ if (ix >= 0x21800000) /* |x| >= 2**-60 */
+ x = 0.25f*x*x;
+ return 1 - x;
+}
+
+static const float
+u00 = -7.3804296553e-02, /* 0xbd9726b5 */
+u01 = 1.7666645348e-01, /* 0x3e34e80d */
+u02 = -1.3818567619e-02, /* 0xbc626746 */
+u03 = 3.4745343146e-04, /* 0x39b62a69 */
+u04 = -3.8140706238e-06, /* 0xb67ff53c */
+u05 = 1.9559013964e-08, /* 0x32a802ba */
+u06 = -3.9820518410e-11, /* 0xae2f21eb */
+v01 = 1.2730483897e-02, /* 0x3c509385 */
+v02 = 7.6006865129e-05, /* 0x389f65e0 */
+v03 = 2.5915085189e-07, /* 0x348b216c */
+v04 = 4.4111031494e-10; /* 0x2ff280c2 */
+
+float y0f(float x)
+{
+ float z,u,v;
+ uint32_t ix;
+
+ GET_FLOAT_WORD(ix, x);
+ if ((ix & 0x7fffffff) == 0)
+ return -1/0.0f;
+ if (ix>>31)
+ return 0/0.0f;
+ if (ix >= 0x7f800000)
+ return 1/x;
+ if (ix >= 0x40000000) { /* |x| >= 2.0 */
+ /* large ulp error near zeros */
+ return common(ix,x,1);
+ }
+ if (ix >= 0x39000000) { /* x >= 2**-13 */
+ /* large ulp error at x ~= 0.89 */
+ z = x*x;
+ u = u00+z*(u01+z*(u02+z*(u03+z*(u04+z*(u05+z*u06)))));
+ v = 1+z*(v01+z*(v02+z*(v03+z*v04)));
+ return u/v + tpi*(j0f(x)*logf(x));
+ }
+ return u00 + tpi*logf(x);
+}
+
+/* The asymptotic expansions of pzero is
+ * 1 - 9/128 s^2 + 11025/98304 s^4 - ..., where s = 1/x.
+ * For x >= 2, We approximate pzero by
+ * pzero(x) = 1 + (R/S)
+ * where R = pR0 + pR1*s^2 + pR2*s^4 + ... + pR5*s^10
+ * S = 1 + pS0*s^2 + ... + pS4*s^10
+ * and
+ * | pzero(x)-1-R/S | <= 2 ** ( -60.26)
+ */
+static const float pR8[6] = { /* for x in [inf, 8]=1/[0,0.125] */
+ 0.0000000000e+00, /* 0x00000000 */
+ -7.0312500000e-02, /* 0xbd900000 */
+ -8.0816707611e+00, /* 0xc1014e86 */
+ -2.5706311035e+02, /* 0xc3808814 */
+ -2.4852163086e+03, /* 0xc51b5376 */
+ -5.2530439453e+03, /* 0xc5a4285a */
+};
+static const float pS8[5] = {
+ 1.1653436279e+02, /* 0x42e91198 */
+ 3.8337448730e+03, /* 0x456f9beb */
+ 4.0597855469e+04, /* 0x471e95db */
+ 1.1675296875e+05, /* 0x47e4087c */
+ 4.7627726562e+04, /* 0x473a0bba */
+};
+static const float pR5[6] = { /* for x in [8,4.5454]=1/[0.125,0.22001] */
+ -1.1412546255e-11, /* 0xad48c58a */
+ -7.0312492549e-02, /* 0xbd8fffff */
+ -4.1596107483e+00, /* 0xc0851b88 */
+ -6.7674766541e+01, /* 0xc287597b */
+ -3.3123129272e+02, /* 0xc3a59d9b */
+ -3.4643338013e+02, /* 0xc3ad3779 */
+};
+static const float pS5[5] = {
+ 6.0753936768e+01, /* 0x42730408 */
+ 1.0512523193e+03, /* 0x44836813 */
+ 5.9789707031e+03, /* 0x45bad7c4 */
+ 9.6254453125e+03, /* 0x461665c8 */
+ 2.4060581055e+03, /* 0x451660ee */
+};
+
+static const float pR3[6] = {/* for x in [4.547,2.8571]=1/[0.2199,0.35001] */
+ -2.5470459075e-09, /* 0xb12f081b */
+ -7.0311963558e-02, /* 0xbd8fffb8 */
+ -2.4090321064e+00, /* 0xc01a2d95 */
+ -2.1965976715e+01, /* 0xc1afba52 */
+ -5.8079170227e+01, /* 0xc2685112 */
+ -3.1447946548e+01, /* 0xc1fb9565 */
+};
+static const float pS3[5] = {
+ 3.5856033325e+01, /* 0x420f6c94 */
+ 3.6151397705e+02, /* 0x43b4c1ca */
+ 1.1936077881e+03, /* 0x44953373 */
+ 1.1279968262e+03, /* 0x448cffe6 */
+ 1.7358093262e+02, /* 0x432d94b8 */
+};
+
+static const float pR2[6] = {/* for x in [2.8570,2]=1/[0.3499,0.5] */
+ -8.8753431271e-08, /* 0xb3be98b7 */
+ -7.0303097367e-02, /* 0xbd8ffb12 */
+ -1.4507384300e+00, /* 0xbfb9b1cc */
+ -7.6356959343e+00, /* 0xc0f4579f */
+ -1.1193166733e+01, /* 0xc1331736 */
+ -3.2336456776e+00, /* 0xc04ef40d */
+};
+static const float pS2[5] = {
+ 2.2220300674e+01, /* 0x41b1c32d */
+ 1.3620678711e+02, /* 0x430834f0 */
+ 2.7047027588e+02, /* 0x43873c32 */
+ 1.5387539673e+02, /* 0x4319e01a */
+ 1.4657617569e+01, /* 0x416a859a */
+};
+
+static float pzerof(float x)
+{
+ const float *p,*q;
+ float_t z,r,s;
+ uint32_t ix;
+
+ GET_FLOAT_WORD(ix, x);
+ ix &= 0x7fffffff;
+ if (ix >= 0x41000000){p = pR8; q = pS8;}
+ else if (ix >= 0x409173eb){p = pR5; q = pS5;}
+ else if (ix >= 0x4036d917){p = pR3; q = pS3;}
+ else /*ix >= 0x40000000*/ {p = pR2; q = pS2;}
+ z = 1.0f/(x*x);
+ r = p[0]+z*(p[1]+z*(p[2]+z*(p[3]+z*(p[4]+z*p[5]))));
+ s = 1.0f+z*(q[0]+z*(q[1]+z*(q[2]+z*(q[3]+z*q[4]))));
+ return 1.0f + r/s;
+}
+
+
+/* For x >= 8, the asymptotic expansions of qzero is
+ * -1/8 s + 75/1024 s^3 - ..., where s = 1/x.
+ * We approximate pzero by
+ * qzero(x) = s*(-1.25 + (R/S))
+ * where R = qR0 + qR1*s^2 + qR2*s^4 + ... + qR5*s^10
+ * S = 1 + qS0*s^2 + ... + qS5*s^12
+ * and
+ * | qzero(x)/s +1.25-R/S | <= 2 ** ( -61.22)
+ */
+static const float qR8[6] = { /* for x in [inf, 8]=1/[0,0.125] */
+ 0.0000000000e+00, /* 0x00000000 */
+ 7.3242187500e-02, /* 0x3d960000 */
+ 1.1768206596e+01, /* 0x413c4a93 */
+ 5.5767340088e+02, /* 0x440b6b19 */
+ 8.8591972656e+03, /* 0x460a6cca */
+ 3.7014625000e+04, /* 0x471096a0 */
+};
+static const float qS8[6] = {
+ 1.6377603149e+02, /* 0x4323c6aa */
+ 8.0983447266e+03, /* 0x45fd12c2 */
+ 1.4253829688e+05, /* 0x480b3293 */
+ 8.0330925000e+05, /* 0x49441ed4 */
+ 8.4050156250e+05, /* 0x494d3359 */
+ -3.4389928125e+05, /* 0xc8a7eb69 */
+};
+
+static const float qR5[6] = { /* for x in [8,4.5454]=1/[0.125,0.22001] */
+ 1.8408595828e-11, /* 0x2da1ec79 */
+ 7.3242180049e-02, /* 0x3d95ffff */
+ 5.8356351852e+00, /* 0x40babd86 */
+ 1.3511157227e+02, /* 0x43071c90 */
+ 1.0272437744e+03, /* 0x448067cd */
+ 1.9899779053e+03, /* 0x44f8bf4b */
+};
+static const float qS5[6] = {
+ 8.2776611328e+01, /* 0x42a58da0 */
+ 2.0778142090e+03, /* 0x4501dd07 */
+ 1.8847289062e+04, /* 0x46933e94 */
+ 5.6751113281e+04, /* 0x475daf1d */
+ 3.5976753906e+04, /* 0x470c88c1 */
+ -5.3543427734e+03, /* 0xc5a752be */
+};
+
+static const float qR3[6] = {/* for x in [4.547,2.8571]=1/[0.2199,0.35001] */
+ 4.3774099900e-09, /* 0x3196681b */
+ 7.3241114616e-02, /* 0x3d95ff70 */
+ 3.3442313671e+00, /* 0x405607e3 */
+ 4.2621845245e+01, /* 0x422a7cc5 */
+ 1.7080809021e+02, /* 0x432acedf */
+ 1.6673394775e+02, /* 0x4326bbe4 */
+};
+static const float qS3[6] = {
+ 4.8758872986e+01, /* 0x42430916 */
+ 7.0968920898e+02, /* 0x44316c1c */
+ 3.7041481934e+03, /* 0x4567825f */
+ 6.4604252930e+03, /* 0x45c9e367 */
+ 2.5163337402e+03, /* 0x451d4557 */
+ -1.4924745178e+02, /* 0xc3153f59 */
+};
+
+static const float qR2[6] = {/* for x in [2.8570,2]=1/[0.3499,0.5] */
+ 1.5044444979e-07, /* 0x342189db */
+ 7.3223426938e-02, /* 0x3d95f62a */
+ 1.9981917143e+00, /* 0x3fffc4bf */
+ 1.4495602608e+01, /* 0x4167edfd */
+ 3.1666231155e+01, /* 0x41fd5471 */
+ 1.6252708435e+01, /* 0x4182058c */
+};
+static const float qS2[6] = {
+ 3.0365585327e+01, /* 0x41f2ecb8 */
+ 2.6934811401e+02, /* 0x4386ac8f */
+ 8.4478375244e+02, /* 0x44533229 */
+ 8.8293585205e+02, /* 0x445cbbe5 */
+ 2.1266638184e+02, /* 0x4354aa98 */
+ -5.3109550476e+00, /* 0xc0a9f358 */
+};
+
+static float qzerof(float x)
+{
+ const float *p,*q;
+ float_t s,r,z;
+ uint32_t ix;
+
+ GET_FLOAT_WORD(ix, x);
+ ix &= 0x7fffffff;
+ if (ix >= 0x41000000){p = qR8; q = qS8;}
+ else if (ix >= 0x409173eb){p = qR5; q = qS5;}
+ else if (ix >= 0x4036d917){p = qR3; q = qS3;}
+ else /*ix >= 0x40000000*/ {p = qR2; q = qS2;}
+ z = 1.0f/(x*x);
+ r = p[0]+z*(p[1]+z*(p[2]+z*(p[3]+z*(p[4]+z*p[5]))));
+ s = 1.0f+z*(q[0]+z*(q[1]+z*(q[2]+z*(q[3]+z*(q[4]+z*q[5])))));
+ return (-.125f + r/s)/x;
+}
diff --git a/lib/libc/src/musl-math/j1.c b/lib/libc/src/musl-math/j1.c
new file mode 100644
index 0000000..df724d1
--- /dev/null
+++ b/lib/libc/src/musl-math/j1.c
@@ -0,0 +1,362 @@
+/* origin: FreeBSD /usr/src/lib/msun/src/e_j1.c */
+/*
+ * ====================================================
+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ *
+ * Developed at SunSoft, a Sun Microsystems, Inc. business.
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ */
+/* j1(x), y1(x)
+ * Bessel function of the first and second kinds of order zero.
+ * Method -- j1(x):
+ * 1. For tiny x, we use j1(x) = x/2 - x^3/16 + x^5/384 - ...
+ * 2. Reduce x to |x| since j1(x)=-j1(-x), and
+ * for x in (0,2)
+ * j1(x) = x/2 + x*z*R0/S0, where z = x*x;
+ * (precision: |j1/x - 1/2 - R0/S0 |<2**-61.51 )
+ * for x in (2,inf)
+ * j1(x) = sqrt(2/(pi*x))*(p1(x)*cos(x1)-q1(x)*sin(x1))
+ * y1(x) = sqrt(2/(pi*x))*(p1(x)*sin(x1)+q1(x)*cos(x1))
+ * where x1 = x-3*pi/4. It is better to compute sin(x1),cos(x1)
+ * as follow:
+ * cos(x1) = cos(x)cos(3pi/4)+sin(x)sin(3pi/4)
+ * = 1/sqrt(2) * (sin(x) - cos(x))
+ * sin(x1) = sin(x)cos(3pi/4)-cos(x)sin(3pi/4)
+ * = -1/sqrt(2) * (sin(x) + cos(x))
+ * (To avoid cancellation, use
+ * sin(x) +- cos(x) = -cos(2x)/(sin(x) -+ cos(x))
+ * to compute the worse one.)
+ *
+ * 3 Special cases
+ * j1(nan)= nan
+ * j1(0) = 0
+ * j1(inf) = 0
+ *
+ * Method -- y1(x):
+ * 1. screen out x<=0 cases: y1(0)=-inf, y1(x<0)=NaN
+ * 2. For x<2.
+ * Since
+ * y1(x) = 2/pi*(j1(x)*(ln(x/2)+Euler)-1/x-x/2+5/64*x^3-...)
+ * therefore y1(x)-2/pi*j1(x)*ln(x)-1/x is an odd function.
+ * We use the following function to approximate y1,
+ * y1(x) = x*U(z)/V(z) + (2/pi)*(j1(x)*ln(x)-1/x), z= x^2
+ * where for x in [0,2] (abs err less than 2**-65.89)
+ * U(z) = U0[0] + U0[1]*z + ... + U0[4]*z^4
+ * V(z) = 1 + v0[0]*z + ... + v0[4]*z^5
+ * Note: For tiny x, 1/x dominate y1 and hence
+ * y1(tiny) = -2/pi/tiny, (choose tiny<2**-54)
+ * 3. For x>=2.
+ * y1(x) = sqrt(2/(pi*x))*(p1(x)*sin(x1)+q1(x)*cos(x1))
+ * where x1 = x-3*pi/4. It is better to compute sin(x1),cos(x1)
+ * by method mentioned above.
+ */
+
+#include "libm.h"
+
+static double pone(double), qone(double);
+
+static const double
+invsqrtpi = 5.64189583547756279280e-01, /* 0x3FE20DD7, 0x50429B6D */
+tpi = 6.36619772367581382433e-01; /* 0x3FE45F30, 0x6DC9C883 */
+
+static double common(uint32_t ix, double x, int y1, int sign)
+{
+ double z,s,c,ss,cc;
+
+ /*
+ * j1(x) = sqrt(2/(pi*x))*(p1(x)*cos(x-3pi/4)-q1(x)*sin(x-3pi/4))
+ * y1(x) = sqrt(2/(pi*x))*(p1(x)*sin(x-3pi/4)+q1(x)*cos(x-3pi/4))
+ *
+ * sin(x-3pi/4) = -(sin(x) + cos(x))/sqrt(2)
+ * cos(x-3pi/4) = (sin(x) - cos(x))/sqrt(2)
+ * sin(x) +- cos(x) = -cos(2x)/(sin(x) -+ cos(x))
+ */
+ s = sin(x);
+ if (y1)
+ s = -s;
+ c = cos(x);
+ cc = s-c;
+ if (ix < 0x7fe00000) {
+ /* avoid overflow in 2*x */
+ ss = -s-c;
+ z = cos(2*x);
+ if (s*c > 0)
+ cc = z/ss;
+ else
+ ss = z/cc;
+ if (ix < 0x48000000) {
+ if (y1)
+ ss = -ss;
+ cc = pone(x)*cc-qone(x)*ss;
+ }
+ }
+ if (sign)
+ cc = -cc;
+ return invsqrtpi*cc/sqrt(x);
+}
+
+/* R0/S0 on [0,2] */
+static const double
+r00 = -6.25000000000000000000e-02, /* 0xBFB00000, 0x00000000 */
+r01 = 1.40705666955189706048e-03, /* 0x3F570D9F, 0x98472C61 */
+r02 = -1.59955631084035597520e-05, /* 0xBEF0C5C6, 0xBA169668 */
+r03 = 4.96727999609584448412e-08, /* 0x3E6AAAFA, 0x46CA0BD9 */
+s01 = 1.91537599538363460805e-02, /* 0x3F939D0B, 0x12637E53 */
+s02 = 1.85946785588630915560e-04, /* 0x3F285F56, 0xB9CDF664 */
+s03 = 1.17718464042623683263e-06, /* 0x3EB3BFF8, 0x333F8498 */
+s04 = 5.04636257076217042715e-09, /* 0x3E35AC88, 0xC97DFF2C */
+s05 = 1.23542274426137913908e-11; /* 0x3DAB2ACF, 0xCFB97ED8 */
+
+double j1(double x)
+{
+ double z,r,s;
+ uint32_t ix;
+ int sign;
+
+ GET_HIGH_WORD(ix, x);
+ sign = ix>>31;
+ ix &= 0x7fffffff;
+ if (ix >= 0x7ff00000)
+ return 1/(x*x);
+ if (ix >= 0x40000000) /* |x| >= 2 */
+ return common(ix, fabs(x), 0, sign);
+ if (ix >= 0x38000000) { /* |x| >= 2**-127 */
+ z = x*x;
+ r = z*(r00+z*(r01+z*(r02+z*r03)));
+ s = 1+z*(s01+z*(s02+z*(s03+z*(s04+z*s05))));
+ z = r/s;
+ } else
+ /* avoid underflow, raise inexact if x!=0 */
+ z = x;
+ return (0.5 + z)*x;
+}
+
+static const double U0[5] = {
+ -1.96057090646238940668e-01, /* 0xBFC91866, 0x143CBC8A */
+ 5.04438716639811282616e-02, /* 0x3FA9D3C7, 0x76292CD1 */
+ -1.91256895875763547298e-03, /* 0xBF5F55E5, 0x4844F50F */
+ 2.35252600561610495928e-05, /* 0x3EF8AB03, 0x8FA6B88E */
+ -9.19099158039878874504e-08, /* 0xBE78AC00, 0x569105B8 */
+};
+static const double V0[5] = {
+ 1.99167318236649903973e-02, /* 0x3F94650D, 0x3F4DA9F0 */
+ 2.02552581025135171496e-04, /* 0x3F2A8C89, 0x6C257764 */
+ 1.35608801097516229404e-06, /* 0x3EB6C05A, 0x894E8CA6 */
+ 6.22741452364621501295e-09, /* 0x3E3ABF1D, 0x5BA69A86 */
+ 1.66559246207992079114e-11, /* 0x3DB25039, 0xDACA772A */
+};
+
+double y1(double x)
+{
+ double z,u,v;
+ uint32_t ix,lx;
+
+ EXTRACT_WORDS(ix, lx, x);
+ /* y1(nan)=nan, y1(<0)=nan, y1(0)=-inf, y1(inf)=0 */
+ if ((ix<<1 | lx) == 0)
+ return -1/0.0;
+ if (ix>>31)
+ return 0/0.0;
+ if (ix >= 0x7ff00000)
+ return 1/x;
+
+ if (ix >= 0x40000000) /* x >= 2 */
+ return common(ix, x, 1, 0);
+ if (ix < 0x3c900000) /* x < 2**-54 */
+ return -tpi/x;
+ z = x*x;
+ u = U0[0]+z*(U0[1]+z*(U0[2]+z*(U0[3]+z*U0[4])));
+ v = 1+z*(V0[0]+z*(V0[1]+z*(V0[2]+z*(V0[3]+z*V0[4]))));
+ return x*(u/v) + tpi*(j1(x)*log(x)-1/x);
+}
+
+/* For x >= 8, the asymptotic expansions of pone is
+ * 1 + 15/128 s^2 - 4725/2^15 s^4 - ..., where s = 1/x.
+ * We approximate pone by
+ * pone(x) = 1 + (R/S)
+ * where R = pr0 + pr1*s^2 + pr2*s^4 + ... + pr5*s^10
+ * S = 1 + ps0*s^2 + ... + ps4*s^10
+ * and
+ * | pone(x)-1-R/S | <= 2 ** ( -60.06)
+ */
+
+static const double pr8[6] = { /* for x in [inf, 8]=1/[0,0.125] */
+ 0.00000000000000000000e+00, /* 0x00000000, 0x00000000 */
+ 1.17187499999988647970e-01, /* 0x3FBDFFFF, 0xFFFFFCCE */
+ 1.32394806593073575129e+01, /* 0x402A7A9D, 0x357F7FCE */
+ 4.12051854307378562225e+02, /* 0x4079C0D4, 0x652EA590 */
+ 3.87474538913960532227e+03, /* 0x40AE457D, 0xA3A532CC */
+ 7.91447954031891731574e+03, /* 0x40BEEA7A, 0xC32782DD */
+};
+static const double ps8[5] = {
+ 1.14207370375678408436e+02, /* 0x405C8D45, 0x8E656CAC */
+ 3.65093083420853463394e+03, /* 0x40AC85DC, 0x964D274F */
+ 3.69562060269033463555e+04, /* 0x40E20B86, 0x97C5BB7F */
+ 9.76027935934950801311e+04, /* 0x40F7D42C, 0xB28F17BB */
+ 3.08042720627888811578e+04, /* 0x40DE1511, 0x697A0B2D */
+};
+
+static const double pr5[6] = { /* for x in [8,4.5454]=1/[0.125,0.22001] */
+ 1.31990519556243522749e-11, /* 0x3DAD0667, 0xDAE1CA7D */
+ 1.17187493190614097638e-01, /* 0x3FBDFFFF, 0xE2C10043 */
+ 6.80275127868432871736e+00, /* 0x401B3604, 0x6E6315E3 */
+ 1.08308182990189109773e+02, /* 0x405B13B9, 0x452602ED */
+ 5.17636139533199752805e+02, /* 0x40802D16, 0xD052D649 */
+ 5.28715201363337541807e+02, /* 0x408085B8, 0xBB7E0CB7 */
+};
+static const double ps5[5] = {
+ 5.92805987221131331921e+01, /* 0x404DA3EA, 0xA8AF633D */
+ 9.91401418733614377743e+02, /* 0x408EFB36, 0x1B066701 */
+ 5.35326695291487976647e+03, /* 0x40B4E944, 0x5706B6FB */
+ 7.84469031749551231769e+03, /* 0x40BEA4B0, 0xB8A5BB15 */
+ 1.50404688810361062679e+03, /* 0x40978030, 0x036F5E51 */
+};
+
+static const double pr3[6] = {
+ 3.02503916137373618024e-09, /* 0x3E29FC21, 0xA7AD9EDD */
+ 1.17186865567253592491e-01, /* 0x3FBDFFF5, 0x5B21D17B */
+ 3.93297750033315640650e+00, /* 0x400F76BC, 0xE85EAD8A */
+ 3.51194035591636932736e+01, /* 0x40418F48, 0x9DA6D129 */
+ 9.10550110750781271918e+01, /* 0x4056C385, 0x4D2C1837 */
+ 4.85590685197364919645e+01, /* 0x4048478F, 0x8EA83EE5 */
+};
+static const double ps3[5] = {
+ 3.47913095001251519989e+01, /* 0x40416549, 0xA134069C */
+ 3.36762458747825746741e+02, /* 0x40750C33, 0x07F1A75F */
+ 1.04687139975775130551e+03, /* 0x40905B7C, 0x5037D523 */
+ 8.90811346398256432622e+02, /* 0x408BD67D, 0xA32E31E9 */
+ 1.03787932439639277504e+02, /* 0x4059F26D, 0x7C2EED53 */
+};
+
+static const double pr2[6] = {/* for x in [2.8570,2]=1/[0.3499,0.5] */
+ 1.07710830106873743082e-07, /* 0x3E7CE9D4, 0xF65544F4 */
+ 1.17176219462683348094e-01, /* 0x3FBDFF42, 0xBE760D83 */
+ 2.36851496667608785174e+00, /* 0x4002F2B7, 0xF98FAEC0 */
+ 1.22426109148261232917e+01, /* 0x40287C37, 0x7F71A964 */
+ 1.76939711271687727390e+01, /* 0x4031B1A8, 0x177F8EE2 */
+ 5.07352312588818499250e+00, /* 0x40144B49, 0xA574C1FE */
+};
+static const double ps2[5] = {
+ 2.14364859363821409488e+01, /* 0x40356FBD, 0x8AD5ECDC */
+ 1.25290227168402751090e+02, /* 0x405F5293, 0x14F92CD5 */
+ 2.32276469057162813669e+02, /* 0x406D08D8, 0xD5A2DBD9 */
+ 1.17679373287147100768e+02, /* 0x405D6B7A, 0xDA1884A9 */
+ 8.36463893371618283368e+00, /* 0x4020BAB1, 0xF44E5192 */
+};
+
+static double pone(double x)
+{
+ const double *p,*q;
+ double_t z,r,s;
+ uint32_t ix;
+
+ GET_HIGH_WORD(ix, x);
+ ix &= 0x7fffffff;
+ if (ix >= 0x40200000){p = pr8; q = ps8;}
+ else if (ix >= 0x40122E8B){p = pr5; q = ps5;}
+ else if (ix >= 0x4006DB6D){p = pr3; q = ps3;}
+ else /*ix >= 0x40000000*/ {p = pr2; q = ps2;}
+ z = 1.0/(x*x);
+ r = p[0]+z*(p[1]+z*(p[2]+z*(p[3]+z*(p[4]+z*p[5]))));
+ s = 1.0+z*(q[0]+z*(q[1]+z*(q[2]+z*(q[3]+z*q[4]))));
+ return 1.0+ r/s;
+}
+
+/* For x >= 8, the asymptotic expansions of qone is
+ * 3/8 s - 105/1024 s^3 - ..., where s = 1/x.
+ * We approximate pone by
+ * qone(x) = s*(0.375 + (R/S))
+ * where R = qr1*s^2 + qr2*s^4 + ... + qr5*s^10
+ * S = 1 + qs1*s^2 + ... + qs6*s^12
+ * and
+ * | qone(x)/s -0.375-R/S | <= 2 ** ( -61.13)
+ */
+
+static const double qr8[6] = { /* for x in [inf, 8]=1/[0,0.125] */
+ 0.00000000000000000000e+00, /* 0x00000000, 0x00000000 */
+ -1.02539062499992714161e-01, /* 0xBFBA3FFF, 0xFFFFFDF3 */
+ -1.62717534544589987888e+01, /* 0xC0304591, 0xA26779F7 */
+ -7.59601722513950107896e+02, /* 0xC087BCD0, 0x53E4B576 */
+ -1.18498066702429587167e+04, /* 0xC0C724E7, 0x40F87415 */
+ -4.84385124285750353010e+04, /* 0xC0E7A6D0, 0x65D09C6A */
+};
+static const double qs8[6] = {
+ 1.61395369700722909556e+02, /* 0x40642CA6, 0xDE5BCDE5 */
+ 7.82538599923348465381e+03, /* 0x40BE9162, 0xD0D88419 */
+ 1.33875336287249578163e+05, /* 0x4100579A, 0xB0B75E98 */
+ 7.19657723683240939863e+05, /* 0x4125F653, 0x72869C19 */
+ 6.66601232617776375264e+05, /* 0x412457D2, 0x7719AD5C */
+ -2.94490264303834643215e+05, /* 0xC111F969, 0x0EA5AA18 */
+};
+
+static const double qr5[6] = { /* for x in [8,4.5454]=1/[0.125,0.22001] */
+ -2.08979931141764104297e-11, /* 0xBDB6FA43, 0x1AA1A098 */
+ -1.02539050241375426231e-01, /* 0xBFBA3FFF, 0xCB597FEF */
+ -8.05644828123936029840e+00, /* 0xC0201CE6, 0xCA03AD4B */
+ -1.83669607474888380239e+02, /* 0xC066F56D, 0x6CA7B9B0 */
+ -1.37319376065508163265e+03, /* 0xC09574C6, 0x6931734F */
+ -2.61244440453215656817e+03, /* 0xC0A468E3, 0x88FDA79D */
+};
+static const double qs5[6] = {
+ 8.12765501384335777857e+01, /* 0x405451B2, 0xFF5A11B2 */
+ 1.99179873460485964642e+03, /* 0x409F1F31, 0xE77BF839 */
+ 1.74684851924908907677e+04, /* 0x40D10F1F, 0x0D64CE29 */
+ 4.98514270910352279316e+04, /* 0x40E8576D, 0xAABAD197 */
+ 2.79480751638918118260e+04, /* 0x40DB4B04, 0xCF7C364B */
+ -4.71918354795128470869e+03, /* 0xC0B26F2E, 0xFCFFA004 */
+};
+
+static const double qr3[6] = {
+ -5.07831226461766561369e-09, /* 0xBE35CFA9, 0xD38FC84F */
+ -1.02537829820837089745e-01, /* 0xBFBA3FEB, 0x51AEED54 */
+ -4.61011581139473403113e+00, /* 0xC01270C2, 0x3302D9FF */
+ -5.78472216562783643212e+01, /* 0xC04CEC71, 0xC25D16DA */
+ -2.28244540737631695038e+02, /* 0xC06C87D3, 0x4718D55F */
+ -2.19210128478909325622e+02, /* 0xC06B66B9, 0x5F5C1BF6 */
+};
+static const double qs3[6] = {
+ 4.76651550323729509273e+01, /* 0x4047D523, 0xCCD367E4 */
+ 6.73865112676699709482e+02, /* 0x40850EEB, 0xC031EE3E */
+ 3.38015286679526343505e+03, /* 0x40AA684E, 0x448E7C9A */
+ 5.54772909720722782367e+03, /* 0x40B5ABBA, 0xA61D54A6 */
+ 1.90311919338810798763e+03, /* 0x409DBC7A, 0x0DD4DF4B */
+ -1.35201191444307340817e+02, /* 0xC060E670, 0x290A311F */
+};
+
+static const double qr2[6] = {/* for x in [2.8570,2]=1/[0.3499,0.5] */
+ -1.78381727510958865572e-07, /* 0xBE87F126, 0x44C626D2 */
+ -1.02517042607985553460e-01, /* 0xBFBA3E8E, 0x9148B010 */
+ -2.75220568278187460720e+00, /* 0xC0060484, 0x69BB4EDA */
+ -1.96636162643703720221e+01, /* 0xC033A9E2, 0xC168907F */
+ -4.23253133372830490089e+01, /* 0xC04529A3, 0xDE104AAA */
+ -2.13719211703704061733e+01, /* 0xC0355F36, 0x39CF6E52 */
+};
+static const double qs2[6] = {
+ 2.95333629060523854548e+01, /* 0x403D888A, 0x78AE64FF */
+ 2.52981549982190529136e+02, /* 0x406F9F68, 0xDB821CBA */
+ 7.57502834868645436472e+02, /* 0x4087AC05, 0xCE49A0F7 */
+ 7.39393205320467245656e+02, /* 0x40871B25, 0x48D4C029 */
+ 1.55949003336666123687e+02, /* 0x40637E5E, 0x3C3ED8D4 */
+ -4.95949898822628210127e+00, /* 0xC013D686, 0xE71BE86B */
+};
+
+static double qone(double x)
+{
+ const double *p,*q;
+ double_t s,r,z;
+ uint32_t ix;
+
+ GET_HIGH_WORD(ix, x);
+ ix &= 0x7fffffff;
+ if (ix >= 0x40200000){p = qr8; q = qs8;}
+ else if (ix >= 0x40122E8B){p = qr5; q = qs5;}
+ else if (ix >= 0x4006DB6D){p = qr3; q = qs3;}
+ else /*ix >= 0x40000000*/ {p = qr2; q = qs2;}
+ z = 1.0/(x*x);
+ r = p[0]+z*(p[1]+z*(p[2]+z*(p[3]+z*(p[4]+z*p[5]))));
+ s = 1.0+z*(q[0]+z*(q[1]+z*(q[2]+z*(q[3]+z*(q[4]+z*q[5])))));
+ return (.375 + r/s)/x;
+}
diff --git a/lib/libc/src/musl-math/j1f.c b/lib/libc/src/musl-math/j1f.c
new file mode 100644
index 0000000..3434c53
--- /dev/null
+++ b/lib/libc/src/musl-math/j1f.c
@@ -0,0 +1,310 @@
+/* origin: FreeBSD /usr/src/lib/msun/src/e_j1f.c */
+/*
+ * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com.
+ */
+/*
+ * ====================================================
+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ *
+ * Developed at SunPro, a Sun Microsystems, Inc. business.
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ */
+
+#define _GNU_SOURCE
+#include "libm.h"
+
+static float ponef(float), qonef(float);
+
+static const float
+invsqrtpi = 5.6418961287e-01, /* 0x3f106ebb */
+tpi = 6.3661974669e-01; /* 0x3f22f983 */
+
+static float common(uint32_t ix, float x, int y1, int sign)
+{
+ double z,s,c,ss,cc;
+
+ s = sinf(x);
+ if (y1)
+ s = -s;
+ c = cosf(x);
+ cc = s-c;
+ if (ix < 0x7f000000) {
+ ss = -s-c;
+ z = cosf(2*x);
+ if (s*c > 0)
+ cc = z/ss;
+ else
+ ss = z/cc;
+ if (ix < 0x58800000) {
+ if (y1)
+ ss = -ss;
+ cc = ponef(x)*cc-qonef(x)*ss;
+ }
+ }
+ if (sign)
+ cc = -cc;
+ return invsqrtpi*cc/sqrtf(x);
+}
+
+/* R0/S0 on [0,2] */
+static const float
+r00 = -6.2500000000e-02, /* 0xbd800000 */
+r01 = 1.4070566976e-03, /* 0x3ab86cfd */
+r02 = -1.5995563444e-05, /* 0xb7862e36 */
+r03 = 4.9672799207e-08, /* 0x335557d2 */
+s01 = 1.9153760746e-02, /* 0x3c9ce859 */
+s02 = 1.8594678841e-04, /* 0x3942fab6 */
+s03 = 1.1771846857e-06, /* 0x359dffc2 */
+s04 = 5.0463624390e-09, /* 0x31ad6446 */
+s05 = 1.2354227016e-11; /* 0x2d59567e */
+
+float j1f(float x)
+{
+ float z,r,s;
+ uint32_t ix;
+ int sign;
+
+ GET_FLOAT_WORD(ix, x);
+ sign = ix>>31;
+ ix &= 0x7fffffff;
+ if (ix >= 0x7f800000)
+ return 1/(x*x);
+ if (ix >= 0x40000000) /* |x| >= 2 */
+ return common(ix, fabsf(x), 0, sign);
+ if (ix >= 0x39000000) { /* |x| >= 2**-13 */
+ z = x*x;
+ r = z*(r00+z*(r01+z*(r02+z*r03)));
+ s = 1+z*(s01+z*(s02+z*(s03+z*(s04+z*s05))));
+ z = 0.5f + r/s;
+ } else
+ z = 0.5f;
+ return z*x;
+}
+
+static const float U0[5] = {
+ -1.9605709612e-01, /* 0xbe48c331 */
+ 5.0443872809e-02, /* 0x3d4e9e3c */
+ -1.9125689287e-03, /* 0xbafaaf2a */
+ 2.3525259166e-05, /* 0x37c5581c */
+ -9.1909917899e-08, /* 0xb3c56003 */
+};
+static const float V0[5] = {
+ 1.9916731864e-02, /* 0x3ca3286a */
+ 2.0255257550e-04, /* 0x3954644b */
+ 1.3560879779e-06, /* 0x35b602d4 */
+ 6.2274145840e-09, /* 0x31d5f8eb */
+ 1.6655924903e-11, /* 0x2d9281cf */
+};
+
+float y1f(float x)
+{
+ float z,u,v;
+ uint32_t ix;
+
+ GET_FLOAT_WORD(ix, x);
+ if ((ix & 0x7fffffff) == 0)
+ return -1/0.0f;
+ if (ix>>31)
+ return 0/0.0f;
+ if (ix >= 0x7f800000)
+ return 1/x;
+ if (ix >= 0x40000000) /* |x| >= 2.0 */
+ return common(ix,x,1,0);
+ if (ix < 0x33000000) /* x < 2**-25 */
+ return -tpi/x;
+ z = x*x;
+ u = U0[0]+z*(U0[1]+z*(U0[2]+z*(U0[3]+z*U0[4])));
+ v = 1.0f+z*(V0[0]+z*(V0[1]+z*(V0[2]+z*(V0[3]+z*V0[4]))));
+ return x*(u/v) + tpi*(j1f(x)*logf(x)-1.0f/x);
+}
+
+/* For x >= 8, the asymptotic expansions of pone is
+ * 1 + 15/128 s^2 - 4725/2^15 s^4 - ..., where s = 1/x.
+ * We approximate pone by
+ * pone(x) = 1 + (R/S)
+ * where R = pr0 + pr1*s^2 + pr2*s^4 + ... + pr5*s^10
+ * S = 1 + ps0*s^2 + ... + ps4*s^10
+ * and
+ * | pone(x)-1-R/S | <= 2 ** ( -60.06)
+ */
+
+static const float pr8[6] = { /* for x in [inf, 8]=1/[0,0.125] */
+ 0.0000000000e+00, /* 0x00000000 */
+ 1.1718750000e-01, /* 0x3df00000 */
+ 1.3239480972e+01, /* 0x4153d4ea */
+ 4.1205184937e+02, /* 0x43ce06a3 */
+ 3.8747453613e+03, /* 0x45722bed */
+ 7.9144794922e+03, /* 0x45f753d6 */
+};
+static const float ps8[5] = {
+ 1.1420736694e+02, /* 0x42e46a2c */
+ 3.6509309082e+03, /* 0x45642ee5 */
+ 3.6956207031e+04, /* 0x47105c35 */
+ 9.7602796875e+04, /* 0x47bea166 */
+ 3.0804271484e+04, /* 0x46f0a88b */
+};
+
+static const float pr5[6] = { /* for x in [8,4.5454]=1/[0.125,0.22001] */
+ 1.3199052094e-11, /* 0x2d68333f */
+ 1.1718749255e-01, /* 0x3defffff */
+ 6.8027510643e+00, /* 0x40d9b023 */
+ 1.0830818176e+02, /* 0x42d89dca */
+ 5.1763616943e+02, /* 0x440168b7 */
+ 5.2871520996e+02, /* 0x44042dc6 */
+};
+static const float ps5[5] = {
+ 5.9280597687e+01, /* 0x426d1f55 */
+ 9.9140142822e+02, /* 0x4477d9b1 */
+ 5.3532670898e+03, /* 0x45a74a23 */
+ 7.8446904297e+03, /* 0x45f52586 */
+ 1.5040468750e+03, /* 0x44bc0180 */
+};
+
+static const float pr3[6] = {
+ 3.0250391081e-09, /* 0x314fe10d */
+ 1.1718686670e-01, /* 0x3defffab */
+ 3.9329774380e+00, /* 0x407bb5e7 */
+ 3.5119403839e+01, /* 0x420c7a45 */
+ 9.1055007935e+01, /* 0x42b61c2a */
+ 4.8559066772e+01, /* 0x42423c7c */
+};
+static const float ps3[5] = {
+ 3.4791309357e+01, /* 0x420b2a4d */
+ 3.3676245117e+02, /* 0x43a86198 */
+ 1.0468714600e+03, /* 0x4482dbe3 */
+ 8.9081134033e+02, /* 0x445eb3ed */
+ 1.0378793335e+02, /* 0x42cf936c */
+};
+
+static const float pr2[6] = {/* for x in [2.8570,2]=1/[0.3499,0.5] */
+ 1.0771083225e-07, /* 0x33e74ea8 */
+ 1.1717621982e-01, /* 0x3deffa16 */
+ 2.3685150146e+00, /* 0x401795c0 */
+ 1.2242610931e+01, /* 0x4143e1bc */
+ 1.7693971634e+01, /* 0x418d8d41 */
+ 5.0735230446e+00, /* 0x40a25a4d */
+};
+static const float ps2[5] = {
+ 2.1436485291e+01, /* 0x41ab7dec */
+ 1.2529022980e+02, /* 0x42fa9499 */
+ 2.3227647400e+02, /* 0x436846c7 */
+ 1.1767937469e+02, /* 0x42eb5bd7 */
+ 8.3646392822e+00, /* 0x4105d590 */
+};
+
+static float ponef(float x)
+{
+ const float *p,*q;
+ float_t z,r,s;
+ uint32_t ix;
+
+ GET_FLOAT_WORD(ix, x);
+ ix &= 0x7fffffff;
+ if (ix >= 0x41000000){p = pr8; q = ps8;}
+ else if (ix >= 0x409173eb){p = pr5; q = ps5;}
+ else if (ix >= 0x4036d917){p = pr3; q = ps3;}
+ else /*ix >= 0x40000000*/ {p = pr2; q = ps2;}
+ z = 1.0f/(x*x);
+ r = p[0]+z*(p[1]+z*(p[2]+z*(p[3]+z*(p[4]+z*p[5]))));
+ s = 1.0f+z*(q[0]+z*(q[1]+z*(q[2]+z*(q[3]+z*q[4]))));
+ return 1.0f + r/s;
+}
+
+/* For x >= 8, the asymptotic expansions of qone is
+ * 3/8 s - 105/1024 s^3 - ..., where s = 1/x.
+ * We approximate pone by
+ * qone(x) = s*(0.375 + (R/S))
+ * where R = qr1*s^2 + qr2*s^4 + ... + qr5*s^10
+ * S = 1 + qs1*s^2 + ... + qs6*s^12
+ * and
+ * | qone(x)/s -0.375-R/S | <= 2 ** ( -61.13)
+ */
+
+static const float qr8[6] = { /* for x in [inf, 8]=1/[0,0.125] */
+ 0.0000000000e+00, /* 0x00000000 */
+ -1.0253906250e-01, /* 0xbdd20000 */
+ -1.6271753311e+01, /* 0xc1822c8d */
+ -7.5960174561e+02, /* 0xc43de683 */
+ -1.1849806641e+04, /* 0xc639273a */
+ -4.8438511719e+04, /* 0xc73d3683 */
+};
+static const float qs8[6] = {
+ 1.6139537048e+02, /* 0x43216537 */
+ 7.8253862305e+03, /* 0x45f48b17 */
+ 1.3387534375e+05, /* 0x4802bcd6 */
+ 7.1965775000e+05, /* 0x492fb29c */
+ 6.6660125000e+05, /* 0x4922be94 */
+ -2.9449025000e+05, /* 0xc88fcb48 */
+};
+
+static const float qr5[6] = { /* for x in [8,4.5454]=1/[0.125,0.22001] */
+ -2.0897993405e-11, /* 0xadb7d219 */
+ -1.0253904760e-01, /* 0xbdd1fffe */
+ -8.0564479828e+00, /* 0xc100e736 */
+ -1.8366960144e+02, /* 0xc337ab6b */
+ -1.3731937256e+03, /* 0xc4aba633 */
+ -2.6124443359e+03, /* 0xc523471c */
+};
+static const float qs5[6] = {
+ 8.1276550293e+01, /* 0x42a28d98 */
+ 1.9917987061e+03, /* 0x44f8f98f */
+ 1.7468484375e+04, /* 0x468878f8 */
+ 4.9851425781e+04, /* 0x4742bb6d */
+ 2.7948074219e+04, /* 0x46da5826 */
+ -4.7191835938e+03, /* 0xc5937978 */
+};
+
+static const float qr3[6] = {
+ -5.0783124372e-09, /* 0xb1ae7d4f */
+ -1.0253783315e-01, /* 0xbdd1ff5b */
+ -4.6101160049e+00, /* 0xc0938612 */
+ -5.7847221375e+01, /* 0xc267638e */
+ -2.2824453735e+02, /* 0xc3643e9a */
+ -2.1921012878e+02, /* 0xc35b35cb */
+};
+static const float qs3[6] = {
+ 4.7665153503e+01, /* 0x423ea91e */
+ 6.7386511230e+02, /* 0x4428775e */
+ 3.3801528320e+03, /* 0x45534272 */
+ 5.5477290039e+03, /* 0x45ad5dd5 */
+ 1.9031191406e+03, /* 0x44ede3d0 */
+ -1.3520118713e+02, /* 0xc3073381 */
+};
+
+static const float qr2[6] = {/* for x in [2.8570,2]=1/[0.3499,0.5] */
+ -1.7838172539e-07, /* 0xb43f8932 */
+ -1.0251704603e-01, /* 0xbdd1f475 */
+ -2.7522056103e+00, /* 0xc0302423 */
+ -1.9663616180e+01, /* 0xc19d4f16 */
+ -4.2325313568e+01, /* 0xc2294d1f */
+ -2.1371921539e+01, /* 0xc1aaf9b2 */
+};
+static const float qs2[6] = {
+ 2.9533363342e+01, /* 0x41ec4454 */
+ 2.5298155212e+02, /* 0x437cfb47 */
+ 7.5750280762e+02, /* 0x443d602e */
+ 7.3939318848e+02, /* 0x4438d92a */
+ 1.5594900513e+02, /* 0x431bf2f2 */
+ -4.9594988823e+00, /* 0xc09eb437 */
+};
+
+static float qonef(float x)
+{
+ const float *p,*q;
+ float_t s,r,z;
+ uint32_t ix;
+
+ GET_FLOAT_WORD(ix, x);
+ ix &= 0x7fffffff;
+ if (ix >= 0x41000000){p = qr8; q = qs8;}
+ else if (ix >= 0x409173eb){p = qr5; q = qs5;}
+ else if (ix >= 0x4036d917){p = qr3; q = qs3;}
+ else /*ix >= 0x40000000*/ {p = qr2; q = qs2;}
+ z = 1.0f/(x*x);
+ r = p[0]+z*(p[1]+z*(p[2]+z*(p[3]+z*(p[4]+z*p[5]))));
+ s = 1.0f+z*(q[0]+z*(q[1]+z*(q[2]+z*(q[3]+z*(q[4]+z*q[5])))));
+ return (.375f + r/s)/x;
+}
diff --git a/lib/libc/src/musl-math/ldexp.c b/lib/libc/src/musl-math/ldexp.c
new file mode 100644
index 0000000..f4d1cd6
--- /dev/null
+++ b/lib/libc/src/musl-math/ldexp.c
@@ -0,0 +1,6 @@
+#include <math.h>
+
+double ldexp(double x, int n)
+{
+ return scalbn(x, n);
+}
diff --git a/lib/libc/src/musl-math/ldexpf.c b/lib/libc/src/musl-math/ldexpf.c
new file mode 100644
index 0000000..3bad5f3
--- /dev/null
+++ b/lib/libc/src/musl-math/ldexpf.c
@@ -0,0 +1,6 @@
+#include <math.h>
+
+float ldexpf(float x, int n)
+{
+ return scalbnf(x, n);
+}
diff --git a/lib/libc/src/musl-math/ldexpl.c b/lib/libc/src/musl-math/ldexpl.c
new file mode 100644
index 0000000..fd145cc
--- /dev/null
+++ b/lib/libc/src/musl-math/ldexpl.c
@@ -0,0 +1,6 @@
+#include <math.h>
+
+long double ldexpl(long double x, int n)
+{
+ return scalbnl(x, n);
+}
diff --git a/lib/libc/src/musl-math/lgamma.c b/lib/libc/src/musl-math/lgamma.c
new file mode 100644
index 0000000..e25ec8e
--- /dev/null
+++ b/lib/libc/src/musl-math/lgamma.c
@@ -0,0 +1,9 @@
+#include <math.h>
+
+extern int __signgam;
+double __lgamma_r(double, int *);
+
+double lgamma(double x)
+{
+ return __lgamma_r(x, &__signgam);
+}
diff --git a/lib/libc/src/musl-math/lgamma_r.c b/lib/libc/src/musl-math/lgamma_r.c
new file mode 100644
index 0000000..84596a3
--- /dev/null
+++ b/lib/libc/src/musl-math/lgamma_r.c
@@ -0,0 +1,285 @@
+/* origin: FreeBSD /usr/src/lib/msun/src/e_lgamma_r.c */
+/*
+ * ====================================================
+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ *
+ * Developed at SunSoft, a Sun Microsystems, Inc. business.
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ *
+ */
+/* lgamma_r(x, signgamp)
+ * Reentrant version of the logarithm of the Gamma function
+ * with user provide pointer for the sign of Gamma(x).
+ *
+ * Method:
+ * 1. Argument Reduction for 0 < x <= 8
+ * Since gamma(1+s)=s*gamma(s), for x in [0,8], we may
+ * reduce x to a number in [1.5,2.5] by
+ * lgamma(1+s) = log(s) + lgamma(s)
+ * for example,
+ * lgamma(7.3) = log(6.3) + lgamma(6.3)
+ * = log(6.3*5.3) + lgamma(5.3)
+ * = log(6.3*5.3*4.3*3.3*2.3) + lgamma(2.3)
+ * 2. Polynomial approximation of lgamma around its
+ * minimun ymin=1.461632144968362245 to maintain monotonicity.
+ * On [ymin-0.23, ymin+0.27] (i.e., [1.23164,1.73163]), use
+ * Let z = x-ymin;
+ * lgamma(x) = -1.214862905358496078218 + z^2*poly(z)
+ * where
+ * poly(z) is a 14 degree polynomial.
+ * 2. Rational approximation in the primary interval [2,3]
+ * We use the following approximation:
+ * s = x-2.0;
+ * lgamma(x) = 0.5*s + s*P(s)/Q(s)
+ * with accuracy
+ * |P/Q - (lgamma(x)-0.5s)| < 2**-61.71
+ * Our algorithms are based on the following observation
+ *
+ * zeta(2)-1 2 zeta(3)-1 3
+ * lgamma(2+s) = s*(1-Euler) + --------- * s - --------- * s + ...
+ * 2 3
+ *
+ * where Euler = 0.5771... is the Euler constant, which is very
+ * close to 0.5.
+ *
+ * 3. For x>=8, we have
+ * lgamma(x)~(x-0.5)log(x)-x+0.5*log(2pi)+1/(12x)-1/(360x**3)+....
+ * (better formula:
+ * lgamma(x)~(x-0.5)*(log(x)-1)-.5*(log(2pi)-1) + ...)
+ * Let z = 1/x, then we approximation
+ * f(z) = lgamma(x) - (x-0.5)(log(x)-1)
+ * by
+ * 3 5 11
+ * w = w0 + w1*z + w2*z + w3*z + ... + w6*z
+ * where
+ * |w - f(z)| < 2**-58.74
+ *
+ * 4. For negative x, since (G is gamma function)
+ * -x*G(-x)*G(x) = pi/sin(pi*x),
+ * we have
+ * G(x) = pi/(sin(pi*x)*(-x)*G(-x))
+ * since G(-x) is positive, sign(G(x)) = sign(sin(pi*x)) for x<0
+ * Hence, for x<0, signgam = sign(sin(pi*x)) and
+ * lgamma(x) = log(|Gamma(x)|)
+ * = log(pi/(|x*sin(pi*x)|)) - lgamma(-x);
+ * Note: one should avoid compute pi*(-x) directly in the
+ * computation of sin(pi*(-x)).
+ *
+ * 5. Special Cases
+ * lgamma(2+s) ~ s*(1-Euler) for tiny s
+ * lgamma(1) = lgamma(2) = 0
+ * lgamma(x) ~ -log(|x|) for tiny x
+ * lgamma(0) = lgamma(neg.integer) = inf and raise divide-by-zero
+ * lgamma(inf) = inf
+ * lgamma(-inf) = inf (bug for bug compatible with C99!?)
+ *
+ */
+
+#include "libm.h"
+#include "weak_alias.h"
+//#include "libc.h"
+
+static const double
+pi = 3.14159265358979311600e+00, /* 0x400921FB, 0x54442D18 */
+a0 = 7.72156649015328655494e-02, /* 0x3FB3C467, 0xE37DB0C8 */
+a1 = 3.22467033424113591611e-01, /* 0x3FD4A34C, 0xC4A60FAD */
+a2 = 6.73523010531292681824e-02, /* 0x3FB13E00, 0x1A5562A7 */
+a3 = 2.05808084325167332806e-02, /* 0x3F951322, 0xAC92547B */
+a4 = 7.38555086081402883957e-03, /* 0x3F7E404F, 0xB68FEFE8 */
+a5 = 2.89051383673415629091e-03, /* 0x3F67ADD8, 0xCCB7926B */
+a6 = 1.19270763183362067845e-03, /* 0x3F538A94, 0x116F3F5D */
+a7 = 5.10069792153511336608e-04, /* 0x3F40B6C6, 0x89B99C00 */
+a8 = 2.20862790713908385557e-04, /* 0x3F2CF2EC, 0xED10E54D */
+a9 = 1.08011567247583939954e-04, /* 0x3F1C5088, 0x987DFB07 */
+a10 = 2.52144565451257326939e-05, /* 0x3EFA7074, 0x428CFA52 */
+a11 = 4.48640949618915160150e-05, /* 0x3F07858E, 0x90A45837 */
+tc = 1.46163214496836224576e+00, /* 0x3FF762D8, 0x6356BE3F */
+tf = -1.21486290535849611461e-01, /* 0xBFBF19B9, 0xBCC38A42 */
+/* tt = -(tail of tf) */
+tt = -3.63867699703950536541e-18, /* 0xBC50C7CA, 0xA48A971F */
+t0 = 4.83836122723810047042e-01, /* 0x3FDEF72B, 0xC8EE38A2 */
+t1 = -1.47587722994593911752e-01, /* 0xBFC2E427, 0x8DC6C509 */
+t2 = 6.46249402391333854778e-02, /* 0x3FB08B42, 0x94D5419B */
+t3 = -3.27885410759859649565e-02, /* 0xBFA0C9A8, 0xDF35B713 */
+t4 = 1.79706750811820387126e-02, /* 0x3F9266E7, 0x970AF9EC */
+t5 = -1.03142241298341437450e-02, /* 0xBF851F9F, 0xBA91EC6A */
+t6 = 6.10053870246291332635e-03, /* 0x3F78FCE0, 0xE370E344 */
+t7 = -3.68452016781138256760e-03, /* 0xBF6E2EFF, 0xB3E914D7 */
+t8 = 2.25964780900612472250e-03, /* 0x3F6282D3, 0x2E15C915 */
+t9 = -1.40346469989232843813e-03, /* 0xBF56FE8E, 0xBF2D1AF1 */
+t10 = 8.81081882437654011382e-04, /* 0x3F4CDF0C, 0xEF61A8E9 */
+t11 = -5.38595305356740546715e-04, /* 0xBF41A610, 0x9C73E0EC */
+t12 = 3.15632070903625950361e-04, /* 0x3F34AF6D, 0x6C0EBBF7 */
+t13 = -3.12754168375120860518e-04, /* 0xBF347F24, 0xECC38C38 */
+t14 = 3.35529192635519073543e-04, /* 0x3F35FD3E, 0xE8C2D3F4 */
+u0 = -7.72156649015328655494e-02, /* 0xBFB3C467, 0xE37DB0C8 */
+u1 = 6.32827064025093366517e-01, /* 0x3FE4401E, 0x8B005DFF */
+u2 = 1.45492250137234768737e+00, /* 0x3FF7475C, 0xD119BD6F */
+u3 = 9.77717527963372745603e-01, /* 0x3FEF4976, 0x44EA8450 */
+u4 = 2.28963728064692451092e-01, /* 0x3FCD4EAE, 0xF6010924 */
+u5 = 1.33810918536787660377e-02, /* 0x3F8B678B, 0xBF2BAB09 */
+v1 = 2.45597793713041134822e+00, /* 0x4003A5D7, 0xC2BD619C */
+v2 = 2.12848976379893395361e+00, /* 0x40010725, 0xA42B18F5 */
+v3 = 7.69285150456672783825e-01, /* 0x3FE89DFB, 0xE45050AF */
+v4 = 1.04222645593369134254e-01, /* 0x3FBAAE55, 0xD6537C88 */
+v5 = 3.21709242282423911810e-03, /* 0x3F6A5ABB, 0x57D0CF61 */
+s0 = -7.72156649015328655494e-02, /* 0xBFB3C467, 0xE37DB0C8 */
+s1 = 2.14982415960608852501e-01, /* 0x3FCB848B, 0x36E20878 */
+s2 = 3.25778796408930981787e-01, /* 0x3FD4D98F, 0x4F139F59 */
+s3 = 1.46350472652464452805e-01, /* 0x3FC2BB9C, 0xBEE5F2F7 */
+s4 = 2.66422703033638609560e-02, /* 0x3F9B481C, 0x7E939961 */
+s5 = 1.84028451407337715652e-03, /* 0x3F5E26B6, 0x7368F239 */
+s6 = 3.19475326584100867617e-05, /* 0x3F00BFEC, 0xDD17E945 */
+r1 = 1.39200533467621045958e+00, /* 0x3FF645A7, 0x62C4AB74 */
+r2 = 7.21935547567138069525e-01, /* 0x3FE71A18, 0x93D3DCDC */
+r3 = 1.71933865632803078993e-01, /* 0x3FC601ED, 0xCCFBDF27 */
+r4 = 1.86459191715652901344e-02, /* 0x3F9317EA, 0x742ED475 */
+r5 = 7.77942496381893596434e-04, /* 0x3F497DDA, 0xCA41A95B */
+r6 = 7.32668430744625636189e-06, /* 0x3EDEBAF7, 0xA5B38140 */
+w0 = 4.18938533204672725052e-01, /* 0x3FDACFE3, 0x90C97D69 */
+w1 = 8.33333333333329678849e-02, /* 0x3FB55555, 0x5555553B */
+w2 = -2.77777777728775536470e-03, /* 0xBF66C16C, 0x16B02E5C */
+w3 = 7.93650558643019558500e-04, /* 0x3F4A019F, 0x98CF38B6 */
+w4 = -5.95187557450339963135e-04, /* 0xBF4380CB, 0x8C0FE741 */
+w5 = 8.36339918996282139126e-04, /* 0x3F4B67BA, 0x4CDAD5D1 */
+w6 = -1.63092934096575273989e-03; /* 0xBF5AB89D, 0x0B9E43E4 */
+
+/* sin(pi*x) assuming x > 2^-100, if sin(pi*x)==0 the sign is arbitrary */
+static double sin_pi(double x)
+{
+ int n;
+
+ /* spurious inexact if odd int */
+ x = 2.0*(x*0.5 - floor(x*0.5)); /* x mod 2.0 */
+
+ n = (int)(x*4.0);
+ n = (n+1)/2;
+ x -= n*0.5f;
+ x *= pi;
+
+ switch (n) {
+ default: /* case 4: */
+ case 0: return __sin(x, 0.0, 0);
+ case 1: return __cos(x, 0.0);
+ case 2: return __sin(-x, 0.0, 0);
+ case 3: return -__cos(x, 0.0);
+ }
+}
+
+double __lgamma_r(double x, int *signgamp)
+{
+ union {double f; uint64_t i;} u = {x};
+ double_t t,y,z,nadj,p,p1,p2,p3,q,r,w;
+ uint32_t ix;
+ int sign,i;
+
+ /* purge off +-inf, NaN, +-0, tiny and negative arguments */
+ *signgamp = 1;
+ sign = u.i>>63;
+ ix = u.i>>32 & 0x7fffffff;
+ if (ix >= 0x7ff00000)
+ return x*x;
+ if (ix < (0x3ff-70)<<20) { /* |x|<2**-70, return -log(|x|) */
+ if(sign) {
+ x = -x;
+ *signgamp = -1;
+ }
+ return -log(x);
+ }
+ if (sign) {
+ x = -x;
+ t = sin_pi(x);
+ if (t == 0.0) /* -integer */
+ return 1.0/(x-x);
+ if (t > 0.0)
+ *signgamp = -1;
+ else
+ t = -t;
+ nadj = log(pi/(t*x));
+ }
+
+ /* purge off 1 and 2 */
+ if ((ix == 0x3ff00000 || ix == 0x40000000) && (uint32_t)u.i == 0)
+ r = 0;
+ /* for x < 2.0 */
+ else if (ix < 0x40000000) {
+ if (ix <= 0x3feccccc) { /* lgamma(x) = lgamma(x+1)-log(x) */
+ r = -log(x);
+ if (ix >= 0x3FE76944) {
+ y = 1.0 - x;
+ i = 0;
+ } else if (ix >= 0x3FCDA661) {
+ y = x - (tc-1.0);
+ i = 1;
+ } else {
+ y = x;
+ i = 2;
+ }
+ } else {
+ r = 0.0;
+ if (ix >= 0x3FFBB4C3) { /* [1.7316,2] */
+ y = 2.0 - x;
+ i = 0;
+ } else if(ix >= 0x3FF3B4C4) { /* [1.23,1.73] */
+ y = x - tc;
+ i = 1;
+ } else {
+ y = x - 1.0;
+ i = 2;
+ }
+ }
+ switch (i) {
+ case 0:
+ z = y*y;
+ p1 = a0+z*(a2+z*(a4+z*(a6+z*(a8+z*a10))));
+ p2 = z*(a1+z*(a3+z*(a5+z*(a7+z*(a9+z*a11)))));
+ p = y*p1+p2;
+ r += (p-0.5*y);
+ break;
+ case 1:
+ z = y*y;
+ w = z*y;
+ p1 = t0+w*(t3+w*(t6+w*(t9 +w*t12))); /* parallel comp */
+ p2 = t1+w*(t4+w*(t7+w*(t10+w*t13)));
+ p3 = t2+w*(t5+w*(t8+w*(t11+w*t14)));
+ p = z*p1-(tt-w*(p2+y*p3));
+ r += tf + p;
+ break;
+ case 2:
+ p1 = y*(u0+y*(u1+y*(u2+y*(u3+y*(u4+y*u5)))));
+ p2 = 1.0+y*(v1+y*(v2+y*(v3+y*(v4+y*v5))));
+ r += -0.5*y + p1/p2;
+ }
+ } else if (ix < 0x40200000) { /* x < 8.0 */
+ i = (int)x;
+ y = x - (double)i;
+ p = y*(s0+y*(s1+y*(s2+y*(s3+y*(s4+y*(s5+y*s6))))));
+ q = 1.0+y*(r1+y*(r2+y*(r3+y*(r4+y*(r5+y*r6)))));
+ r = 0.5*y+p/q;
+ z = 1.0; /* lgamma(1+s) = log(s) + lgamma(s) */
+ switch (i) {
+ case 7: z *= y + 6.0; /* FALLTHRU */
+ case 6: z *= y + 5.0; /* FALLTHRU */
+ case 5: z *= y + 4.0; /* FALLTHRU */
+ case 4: z *= y + 3.0; /* FALLTHRU */
+ case 3: z *= y + 2.0; /* FALLTHRU */
+ r += log(z);
+ break;
+ }
+ } else if (ix < 0x43900000) { /* 8.0 <= x < 2**58 */
+ t = log(x);
+ z = 1.0/x;
+ y = z*z;
+ w = w0+z*(w1+y*(w2+y*(w3+y*(w4+y*(w5+y*w6)))));
+ r = (x-0.5)*(t-1.0)+w;
+ } else /* 2**58 <= x <= inf */
+ r = x*(log(x)-1.0);
+ if (sign)
+ r = nadj - r;
+ return r;
+}
+
+weak_alias(__lgamma_r, lgamma_r);
diff --git a/lib/libc/src/musl-math/lgammaf.c b/lib/libc/src/musl-math/lgammaf.c
new file mode 100644
index 0000000..badb6df
--- /dev/null
+++ b/lib/libc/src/musl-math/lgammaf.c
@@ -0,0 +1,9 @@
+#include <math.h>
+
+extern int __signgam;
+float __lgammaf_r(float, int *);
+
+float lgammaf(float x)
+{
+ return __lgammaf_r(x, &__signgam);
+}
diff --git a/lib/libc/src/musl-math/lgammaf_r.c b/lib/libc/src/musl-math/lgammaf_r.c
new file mode 100644
index 0000000..f73e89d
--- /dev/null
+++ b/lib/libc/src/musl-math/lgammaf_r.c
@@ -0,0 +1,220 @@
+/* origin: FreeBSD /usr/src/lib/msun/src/e_lgammaf_r.c */
+/*
+ * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com.
+ */
+/*
+ * ====================================================
+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ *
+ * Developed at SunPro, a Sun Microsystems, Inc. business.
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ */
+
+#include "libm.h"
+#include "weak_alias.h"
+//#include "libc.h"
+
+static const float
+pi = 3.1415927410e+00, /* 0x40490fdb */
+a0 = 7.7215664089e-02, /* 0x3d9e233f */
+a1 = 3.2246702909e-01, /* 0x3ea51a66 */
+a2 = 6.7352302372e-02, /* 0x3d89f001 */
+a3 = 2.0580807701e-02, /* 0x3ca89915 */
+a4 = 7.3855509982e-03, /* 0x3bf2027e */
+a5 = 2.8905137442e-03, /* 0x3b3d6ec6 */
+a6 = 1.1927076848e-03, /* 0x3a9c54a1 */
+a7 = 5.1006977446e-04, /* 0x3a05b634 */
+a8 = 2.2086278477e-04, /* 0x39679767 */
+a9 = 1.0801156895e-04, /* 0x38e28445 */
+a10 = 2.5214456400e-05, /* 0x37d383a2 */
+a11 = 4.4864096708e-05, /* 0x383c2c75 */
+tc = 1.4616321325e+00, /* 0x3fbb16c3 */
+tf = -1.2148628384e-01, /* 0xbdf8cdcd */
+/* tt = -(tail of tf) */
+tt = 6.6971006518e-09, /* 0x31e61c52 */
+t0 = 4.8383611441e-01, /* 0x3ef7b95e */
+t1 = -1.4758771658e-01, /* 0xbe17213c */
+t2 = 6.4624942839e-02, /* 0x3d845a15 */
+t3 = -3.2788541168e-02, /* 0xbd064d47 */
+t4 = 1.7970675603e-02, /* 0x3c93373d */
+t5 = -1.0314224288e-02, /* 0xbc28fcfe */
+t6 = 6.1005386524e-03, /* 0x3bc7e707 */
+t7 = -3.6845202558e-03, /* 0xbb7177fe */
+t8 = 2.2596477065e-03, /* 0x3b141699 */
+t9 = -1.4034647029e-03, /* 0xbab7f476 */
+t10 = 8.8108185446e-04, /* 0x3a66f867 */
+t11 = -5.3859531181e-04, /* 0xba0d3085 */
+t12 = 3.1563205994e-04, /* 0x39a57b6b */
+t13 = -3.1275415677e-04, /* 0xb9a3f927 */
+t14 = 3.3552918467e-04, /* 0x39afe9f7 */
+u0 = -7.7215664089e-02, /* 0xbd9e233f */
+u1 = 6.3282704353e-01, /* 0x3f2200f4 */
+u2 = 1.4549225569e+00, /* 0x3fba3ae7 */
+u3 = 9.7771751881e-01, /* 0x3f7a4bb2 */
+u4 = 2.2896373272e-01, /* 0x3e6a7578 */
+u5 = 1.3381091878e-02, /* 0x3c5b3c5e */
+v1 = 2.4559779167e+00, /* 0x401d2ebe */
+v2 = 2.1284897327e+00, /* 0x4008392d */
+v3 = 7.6928514242e-01, /* 0x3f44efdf */
+v4 = 1.0422264785e-01, /* 0x3dd572af */
+v5 = 3.2170924824e-03, /* 0x3b52d5db */
+s0 = -7.7215664089e-02, /* 0xbd9e233f */
+s1 = 2.1498242021e-01, /* 0x3e5c245a */
+s2 = 3.2577878237e-01, /* 0x3ea6cc7a */
+s3 = 1.4635047317e-01, /* 0x3e15dce6 */
+s4 = 2.6642270386e-02, /* 0x3cda40e4 */
+s5 = 1.8402845599e-03, /* 0x3af135b4 */
+s6 = 3.1947532989e-05, /* 0x3805ff67 */
+r1 = 1.3920053244e+00, /* 0x3fb22d3b */
+r2 = 7.2193557024e-01, /* 0x3f38d0c5 */
+r3 = 1.7193385959e-01, /* 0x3e300f6e */
+r4 = 1.8645919859e-02, /* 0x3c98bf54 */
+r5 = 7.7794247773e-04, /* 0x3a4beed6 */
+r6 = 7.3266842264e-06, /* 0x36f5d7bd */
+w0 = 4.1893854737e-01, /* 0x3ed67f1d */
+w1 = 8.3333335817e-02, /* 0x3daaaaab */
+w2 = -2.7777778450e-03, /* 0xbb360b61 */
+w3 = 7.9365057172e-04, /* 0x3a500cfd */
+w4 = -5.9518753551e-04, /* 0xba1c065c */
+w5 = 8.3633989561e-04, /* 0x3a5b3dd2 */
+w6 = -1.6309292987e-03; /* 0xbad5c4e8 */
+
+/* sin(pi*x) assuming x > 2^-100, if sin(pi*x)==0 the sign is arbitrary */
+static float sin_pi(float x)
+{
+ double_t y;
+ int n;
+
+ /* spurious inexact if odd int */
+ x = 2*(x*0.5f - floorf(x*0.5f)); /* x mod 2.0 */
+
+ n = (int)(x*4);
+ n = (n+1)/2;
+ y = x - n*0.5f;
+ y *= 3.14159265358979323846;
+ switch (n) {
+ default: /* case 4: */
+ case 0: return __sindf(y);
+ case 1: return __cosdf(y);
+ case 2: return __sindf(-y);
+ case 3: return -__cosdf(y);
+ }
+}
+
+float __lgammaf_r(float x, int *signgamp)
+{
+ union {float f; uint32_t i;} u = {x};
+ float t,y,z,nadj,p,p1,p2,p3,q,r,w;
+ uint32_t ix;
+ int i,sign;
+
+ /* purge off +-inf, NaN, +-0, tiny and negative arguments */
+ *signgamp = 1;
+ sign = u.i>>31;
+ ix = u.i & 0x7fffffff;
+ if (ix >= 0x7f800000)
+ return x*x;
+ if (ix < 0x35000000) { /* |x| < 2**-21, return -log(|x|) */
+ if (sign) {
+ *signgamp = -1;
+ x = -x;
+ }
+ return -logf(x);
+ }
+ if (sign) {
+ x = -x;
+ t = sin_pi(x);
+ if (t == 0.0f) /* -integer */
+ return 1.0f/(x-x);
+ if (t > 0.0f)
+ *signgamp = -1;
+ else
+ t = -t;
+ nadj = logf(pi/(t*x));
+ }
+
+ /* purge off 1 and 2 */
+ if (ix == 0x3f800000 || ix == 0x40000000)
+ r = 0;
+ /* for x < 2.0 */
+ else if (ix < 0x40000000) {
+ if (ix <= 0x3f666666) { /* lgamma(x) = lgamma(x+1)-log(x) */
+ r = -logf(x);
+ if (ix >= 0x3f3b4a20) {
+ y = 1.0f - x;
+ i = 0;
+ } else if (ix >= 0x3e6d3308) {
+ y = x - (tc-1.0f);
+ i = 1;
+ } else {
+ y = x;
+ i = 2;
+ }
+ } else {
+ r = 0.0f;
+ if (ix >= 0x3fdda618) { /* [1.7316,2] */
+ y = 2.0f - x;
+ i = 0;
+ } else if (ix >= 0x3F9da620) { /* [1.23,1.73] */
+ y = x - tc;
+ i = 1;
+ } else {
+ y = x - 1.0f;
+ i = 2;
+ }
+ }
+ switch(i) {
+ case 0:
+ z = y*y;
+ p1 = a0+z*(a2+z*(a4+z*(a6+z*(a8+z*a10))));
+ p2 = z*(a1+z*(a3+z*(a5+z*(a7+z*(a9+z*a11)))));
+ p = y*p1+p2;
+ r += p - 0.5f*y;
+ break;
+ case 1:
+ z = y*y;
+ w = z*y;
+ p1 = t0+w*(t3+w*(t6+w*(t9 +w*t12))); /* parallel comp */
+ p2 = t1+w*(t4+w*(t7+w*(t10+w*t13)));
+ p3 = t2+w*(t5+w*(t8+w*(t11+w*t14)));
+ p = z*p1-(tt-w*(p2+y*p3));
+ r += (tf + p);
+ break;
+ case 2:
+ p1 = y*(u0+y*(u1+y*(u2+y*(u3+y*(u4+y*u5)))));
+ p2 = 1.0f+y*(v1+y*(v2+y*(v3+y*(v4+y*v5))));
+ r += -0.5f*y + p1/p2;
+ }
+ } else if (ix < 0x41000000) { /* x < 8.0 */
+ i = (int)x;
+ y = x - (float)i;
+ p = y*(s0+y*(s1+y*(s2+y*(s3+y*(s4+y*(s5+y*s6))))));
+ q = 1.0f+y*(r1+y*(r2+y*(r3+y*(r4+y*(r5+y*r6)))));
+ r = 0.5f*y+p/q;
+ z = 1.0f; /* lgamma(1+s) = log(s) + lgamma(s) */
+ switch (i) {
+ case 7: z *= y + 6.0f; /* FALLTHRU */
+ case 6: z *= y + 5.0f; /* FALLTHRU */
+ case 5: z *= y + 4.0f; /* FALLTHRU */
+ case 4: z *= y + 3.0f; /* FALLTHRU */
+ case 3: z *= y + 2.0f; /* FALLTHRU */
+ r += logf(z);
+ break;
+ }
+ } else if (ix < 0x5c800000) { /* 8.0 <= x < 2**58 */
+ t = logf(x);
+ z = 1.0f/x;
+ y = z*z;
+ w = w0+z*(w1+y*(w2+y*(w3+y*(w4+y*(w5+y*w6)))));
+ r = (x-0.5f)*(t-1.0f)+w;
+ } else /* 2**58 <= x <= inf */
+ r = x*(logf(x)-1.0f);
+ if (sign)
+ r = nadj - r;
+ return r;
+}
+
+weak_alias(__lgammaf_r, lgammaf_r);
diff --git a/lib/libc/src/musl-math/lgammal.c b/lib/libc/src/musl-math/lgammal.c
new file mode 100644
index 0000000..f0bea36
--- /dev/null
+++ b/lib/libc/src/musl-math/lgammal.c
@@ -0,0 +1,361 @@
+/* origin: OpenBSD /usr/src/lib/libm/src/ld80/e_lgammal.c */
+/*
+ * ====================================================
+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ *
+ * Developed at SunPro, a Sun Microsystems, Inc. business.
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ */
+/*
+ * Copyright (c) 2008 Stephen L. Moshier <steve@moshier.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+/* lgammal(x)
+ * Reentrant version of the logarithm of the Gamma function
+ * with user provide pointer for the sign of Gamma(x).
+ *
+ * Method:
+ * 1. Argument Reduction for 0 < x <= 8
+ * Since gamma(1+s)=s*gamma(s), for x in [0,8], we may
+ * reduce x to a number in [1.5,2.5] by
+ * lgamma(1+s) = log(s) + lgamma(s)
+ * for example,
+ * lgamma(7.3) = log(6.3) + lgamma(6.3)
+ * = log(6.3*5.3) + lgamma(5.3)
+ * = log(6.3*5.3*4.3*3.3*2.3) + lgamma(2.3)
+ * 2. Polynomial approximation of lgamma around its
+ * minimun ymin=1.461632144968362245 to maintain monotonicity.
+ * On [ymin-0.23, ymin+0.27] (i.e., [1.23164,1.73163]), use
+ * Let z = x-ymin;
+ * lgamma(x) = -1.214862905358496078218 + z^2*poly(z)
+ * 2. Rational approximation in the primary interval [2,3]
+ * We use the following approximation:
+ * s = x-2.0;
+ * lgamma(x) = 0.5*s + s*P(s)/Q(s)
+ * Our algorithms are based on the following observation
+ *
+ * zeta(2)-1 2 zeta(3)-1 3
+ * lgamma(2+s) = s*(1-Euler) + --------- * s - --------- * s + ...
+ * 2 3
+ *
+ * where Euler = 0.5771... is the Euler constant, which is very
+ * close to 0.5.
+ *
+ * 3. For x>=8, we have
+ * lgamma(x)~(x-0.5)log(x)-x+0.5*log(2pi)+1/(12x)-1/(360x**3)+....
+ * (better formula:
+ * lgamma(x)~(x-0.5)*(log(x)-1)-.5*(log(2pi)-1) + ...)
+ * Let z = 1/x, then we approximation
+ * f(z) = lgamma(x) - (x-0.5)(log(x)-1)
+ * by
+ * 3 5 11
+ * w = w0 + w1*z + w2*z + w3*z + ... + w6*z
+ *
+ * 4. For negative x, since (G is gamma function)
+ * -x*G(-x)*G(x) = pi/sin(pi*x),
+ * we have
+ * G(x) = pi/(sin(pi*x)*(-x)*G(-x))
+ * since G(-x) is positive, sign(G(x)) = sign(sin(pi*x)) for x<0
+ * Hence, for x<0, signgam = sign(sin(pi*x)) and
+ * lgamma(x) = log(|Gamma(x)|)
+ * = log(pi/(|x*sin(pi*x)|)) - lgamma(-x);
+ * Note: one should avoid compute pi*(-x) directly in the
+ * computation of sin(pi*(-x)).
+ *
+ * 5. Special Cases
+ * lgamma(2+s) ~ s*(1-Euler) for tiny s
+ * lgamma(1)=lgamma(2)=0
+ * lgamma(x) ~ -log(x) for tiny x
+ * lgamma(0) = lgamma(inf) = inf
+ * lgamma(-integer) = +-inf
+ *
+ */
+
+#define _GNU_SOURCE
+#include "libm.h"
+#include "weak_alias.h"
+//#include "libc.h"
+
+#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
+double __lgamma_r(double x, int *sg);
+
+long double __lgammal_r(long double x, int *sg)
+{
+ return __lgamma_r(x, sg);
+}
+#elif LDBL_MANT_DIG == 64 && LDBL_MAX_EXP == 16384
+static const long double
+pi = 3.14159265358979323846264L,
+
+/* lgam(1+x) = 0.5 x + x a(x)/b(x)
+ -0.268402099609375 <= x <= 0
+ peak relative error 6.6e-22 */
+a0 = -6.343246574721079391729402781192128239938E2L,
+a1 = 1.856560238672465796768677717168371401378E3L,
+a2 = 2.404733102163746263689288466865843408429E3L,
+a3 = 8.804188795790383497379532868917517596322E2L,
+a4 = 1.135361354097447729740103745999661157426E2L,
+a5 = 3.766956539107615557608581581190400021285E0L,
+
+b0 = 8.214973713960928795704317259806842490498E3L,
+b1 = 1.026343508841367384879065363925870888012E4L,
+b2 = 4.553337477045763320522762343132210919277E3L,
+b3 = 8.506975785032585797446253359230031874803E2L,
+b4 = 6.042447899703295436820744186992189445813E1L,
+/* b5 = 1.000000000000000000000000000000000000000E0 */
+
+
+tc = 1.4616321449683623412626595423257213284682E0L,
+tf = -1.2148629053584961146050602565082954242826E-1, /* double precision */
+/* tt = (tail of tf), i.e. tf + tt has extended precision. */
+tt = 3.3649914684731379602768989080467587736363E-18L,
+/* lgam ( 1.4616321449683623412626595423257213284682E0 ) =
+-1.2148629053584960809551455717769158215135617312999903886372437313313530E-1 */
+
+/* lgam (x + tc) = tf + tt + x g(x)/h(x)
+ -0.230003726999612341262659542325721328468 <= x
+ <= 0.2699962730003876587373404576742786715318
+ peak relative error 2.1e-21 */
+g0 = 3.645529916721223331888305293534095553827E-18L,
+g1 = 5.126654642791082497002594216163574795690E3L,
+g2 = 8.828603575854624811911631336122070070327E3L,
+g3 = 5.464186426932117031234820886525701595203E3L,
+g4 = 1.455427403530884193180776558102868592293E3L,
+g5 = 1.541735456969245924860307497029155838446E2L,
+g6 = 4.335498275274822298341872707453445815118E0L,
+
+h0 = 1.059584930106085509696730443974495979641E4L,
+h1 = 2.147921653490043010629481226937850618860E4L,
+h2 = 1.643014770044524804175197151958100656728E4L,
+h3 = 5.869021995186925517228323497501767586078E3L,
+h4 = 9.764244777714344488787381271643502742293E2L,
+h5 = 6.442485441570592541741092969581997002349E1L,
+/* h6 = 1.000000000000000000000000000000000000000E0 */
+
+
+/* lgam (x+1) = -0.5 x + x u(x)/v(x)
+ -0.100006103515625 <= x <= 0.231639862060546875
+ peak relative error 1.3e-21 */
+u0 = -8.886217500092090678492242071879342025627E1L,
+u1 = 6.840109978129177639438792958320783599310E2L,
+u2 = 2.042626104514127267855588786511809932433E3L,
+u3 = 1.911723903442667422201651063009856064275E3L,
+u4 = 7.447065275665887457628865263491667767695E2L,
+u5 = 1.132256494121790736268471016493103952637E2L,
+u6 = 4.484398885516614191003094714505960972894E0L,
+
+v0 = 1.150830924194461522996462401210374632929E3L,
+v1 = 3.399692260848747447377972081399737098610E3L,
+v2 = 3.786631705644460255229513563657226008015E3L,
+v3 = 1.966450123004478374557778781564114347876E3L,
+v4 = 4.741359068914069299837355438370682773122E2L,
+v5 = 4.508989649747184050907206782117647852364E1L,
+/* v6 = 1.000000000000000000000000000000000000000E0 */
+
+
+/* lgam (x+2) = .5 x + x s(x)/r(x)
+ 0 <= x <= 1
+ peak relative error 7.2e-22 */
+s0 = 1.454726263410661942989109455292824853344E6L,
+s1 = -3.901428390086348447890408306153378922752E6L,
+s2 = -6.573568698209374121847873064292963089438E6L,
+s3 = -3.319055881485044417245964508099095984643E6L,
+s4 = -7.094891568758439227560184618114707107977E5L,
+s5 = -6.263426646464505837422314539808112478303E4L,
+s6 = -1.684926520999477529949915657519454051529E3L,
+
+r0 = -1.883978160734303518163008696712983134698E7L,
+r1 = -2.815206082812062064902202753264922306830E7L,
+r2 = -1.600245495251915899081846093343626358398E7L,
+r3 = -4.310526301881305003489257052083370058799E6L,
+r4 = -5.563807682263923279438235987186184968542E5L,
+r5 = -3.027734654434169996032905158145259713083E4L,
+r6 = -4.501995652861105629217250715790764371267E2L,
+/* r6 = 1.000000000000000000000000000000000000000E0 */
+
+
+/* lgam(x) = ( x - 0.5 ) * log(x) - x + LS2PI + 1/x w(1/x^2)
+ x >= 8
+ Peak relative error 1.51e-21
+w0 = LS2PI - 0.5 */
+w0 = 4.189385332046727417803e-1L,
+w1 = 8.333333333333331447505E-2L,
+w2 = -2.777777777750349603440E-3L,
+w3 = 7.936507795855070755671E-4L,
+w4 = -5.952345851765688514613E-4L,
+w5 = 8.412723297322498080632E-4L,
+w6 = -1.880801938119376907179E-3L,
+w7 = 4.885026142432270781165E-3L;
+
+/* sin(pi*x) assuming x > 2^-1000, if sin(pi*x)==0 the sign is arbitrary */
+static long double sin_pi(long double x)
+{
+ int n;
+
+ /* spurious inexact if odd int */
+ x *= 0.5;
+ x = 2.0*(x - floorl(x)); /* x mod 2.0 */
+
+ n = (int)(x*4.0);
+ n = (n+1)/2;
+ x -= n*0.5f;
+ x *= pi;
+
+ switch (n) {
+ default: /* case 4: */
+ case 0: return __sinl(x, 0.0, 0);
+ case 1: return __cosl(x, 0.0);
+ case 2: return __sinl(-x, 0.0, 0);
+ case 3: return -__cosl(x, 0.0);
+ }
+}
+
+long double __lgammal_r(long double x, int *sg) {
+ long double t, y, z, nadj, p, p1, p2, q, r, w;
+ union ldshape u = {x};
+ uint32_t ix = (u.i.se & 0x7fffU)<<16 | u.i.m>>48;
+ int sign = u.i.se >> 15;
+ int i;
+
+ *sg = 1;
+
+ /* purge off +-inf, NaN, +-0, tiny and negative arguments */
+ if (ix >= 0x7fff0000)
+ return x * x;
+ if (ix < 0x3fc08000) { /* |x|<2**-63, return -log(|x|) */
+ if (sign) {
+ *sg = -1;
+ x = -x;
+ }
+ return -logl(x);
+ }
+ if (sign) {
+ x = -x;
+ t = sin_pi(x);
+ if (t == 0.0)
+ return 1.0 / (x-x); /* -integer */
+ if (t > 0.0)
+ *sg = -1;
+ else
+ t = -t;
+ nadj = logl(pi / (t * x));
+ }
+
+ /* purge off 1 and 2 (so the sign is ok with downward rounding) */
+ if ((ix == 0x3fff8000 || ix == 0x40008000) && u.i.m == 0) {
+ r = 0;
+ } else if (ix < 0x40008000) { /* x < 2.0 */
+ if (ix <= 0x3ffee666) { /* 8.99993896484375e-1 */
+ /* lgamma(x) = lgamma(x+1) - log(x) */
+ r = -logl(x);
+ if (ix >= 0x3ffebb4a) { /* 7.31597900390625e-1 */
+ y = x - 1.0;
+ i = 0;
+ } else if (ix >= 0x3ffced33) { /* 2.31639862060546875e-1 */
+ y = x - (tc - 1.0);
+ i = 1;
+ } else { /* x < 0.23 */
+ y = x;
+ i = 2;
+ }
+ } else {
+ r = 0.0;
+ if (ix >= 0x3fffdda6) { /* 1.73162841796875 */
+ /* [1.7316,2] */
+ y = x - 2.0;
+ i = 0;
+ } else if (ix >= 0x3fff9da6) { /* 1.23162841796875 */
+ /* [1.23,1.73] */
+ y = x - tc;
+ i = 1;
+ } else {
+ /* [0.9, 1.23] */
+ y = x - 1.0;
+ i = 2;
+ }
+ }
+ switch (i) {
+ case 0:
+ p1 = a0 + y * (a1 + y * (a2 + y * (a3 + y * (a4 + y * a5))));
+ p2 = b0 + y * (b1 + y * (b2 + y * (b3 + y * (b4 + y))));
+ r += 0.5 * y + y * p1/p2;
+ break;
+ case 1:
+ p1 = g0 + y * (g1 + y * (g2 + y * (g3 + y * (g4 + y * (g5 + y * g6)))));
+ p2 = h0 + y * (h1 + y * (h2 + y * (h3 + y * (h4 + y * (h5 + y)))));
+ p = tt + y * p1/p2;
+ r += (tf + p);
+ break;
+ case 2:
+ p1 = y * (u0 + y * (u1 + y * (u2 + y * (u3 + y * (u4 + y * (u5 + y * u6))))));
+ p2 = v0 + y * (v1 + y * (v2 + y * (v3 + y * (v4 + y * (v5 + y)))));
+ r += (-0.5 * y + p1 / p2);
+ }
+ } else if (ix < 0x40028000) { /* 8.0 */
+ /* x < 8.0 */
+ i = (int)x;
+ y = x - (double)i;
+ p = y * (s0 + y * (s1 + y * (s2 + y * (s3 + y * (s4 + y * (s5 + y * s6))))));
+ q = r0 + y * (r1 + y * (r2 + y * (r3 + y * (r4 + y * (r5 + y * (r6 + y))))));
+ r = 0.5 * y + p / q;
+ z = 1.0;
+ /* lgamma(1+s) = log(s) + lgamma(s) */
+ switch (i) {
+ case 7:
+ z *= (y + 6.0); /* FALLTHRU */
+ case 6:
+ z *= (y + 5.0); /* FALLTHRU */
+ case 5:
+ z *= (y + 4.0); /* FALLTHRU */
+ case 4:
+ z *= (y + 3.0); /* FALLTHRU */
+ case 3:
+ z *= (y + 2.0); /* FALLTHRU */
+ r += logl(z);
+ break;
+ }
+ } else if (ix < 0x40418000) { /* 2^66 */
+ /* 8.0 <= x < 2**66 */
+ t = logl(x);
+ z = 1.0 / x;
+ y = z * z;
+ w = w0 + z * (w1 + y * (w2 + y * (w3 + y * (w4 + y * (w5 + y * (w6 + y * w7))))));
+ r = (x - 0.5) * (t - 1.0) + w;
+ } else /* 2**66 <= x <= inf */
+ r = x * (logl(x) - 1.0);
+ if (sign)
+ r = nadj - r;
+ return r;
+}
+#elif LDBL_MANT_DIG == 113 && LDBL_MAX_EXP == 16384
+// TODO: broken implementation to make things compile
+double __lgamma_r(double x, int *sg);
+
+long double __lgammal_r(long double x, int *sg)
+{
+ return __lgamma_r(x, sg);
+}
+#endif
+
+extern int __signgam;
+
+long double lgammal(long double x)
+{
+ return __lgammal_r(x, &__signgam);
+}
+
+weak_alias(__lgammal_r, lgammal_r);
diff --git a/lib/libc/src/musl-math/libm.h b/lib/libc/src/musl-math/libm.h
new file mode 100644
index 0000000..521b3fd
--- /dev/null
+++ b/lib/libc/src/musl-math/libm.h
@@ -0,0 +1,198 @@
+/* origin: FreeBSD /usr/src/lib/msun/src/math_private.h */
+/*
+ * ====================================================
+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ *
+ * Developed at SunPro, a Sun Microsystems, Inc. business.
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ */
+
+#ifndef _LIBM_H
+#define _LIBM_H
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <float.h>
+#include <math.h>
+
+#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
+#elif LDBL_MANT_DIG == 64 && LDBL_MAX_EXP == 16384 && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+union ldshape {
+ long double f;
+ struct {
+ uint64_t m;
+ uint16_t se;
+ } i;
+};
+#elif LDBL_MANT_DIG == 64 && LDBL_MAX_EXP == 16384 && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+/* This is the m68k variant of 80-bit long double, and this definition only works
+ * on archs where the alignment requirement of uint64_t is <= 4. */
+union ldshape {
+ long double f;
+ struct {
+ uint16_t se;
+ uint16_t pad;
+ uint64_t m;
+ } i;
+};
+#elif LDBL_MANT_DIG == 113 && LDBL_MAX_EXP == 16384 && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+union ldshape {
+ long double f;
+ struct {
+ uint64_t lo;
+ uint32_t mid;
+ uint16_t top;
+ uint16_t se;
+ } i;
+ struct {
+ uint64_t lo;
+ uint64_t hi;
+ } i2;
+};
+#elif LDBL_MANT_DIG == 113 && LDBL_MAX_EXP == 16384 && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+union ldshape {
+ long double f;
+ struct {
+ uint16_t se;
+ uint16_t top;
+ uint32_t mid;
+ uint64_t lo;
+ } i;
+ struct {
+ uint64_t hi;
+ uint64_t lo;
+ } i2;
+};
+#else
+#error Unsupported long double representation
+#endif
+
+#define FORCE_EVAL(x) do { \
+ if (sizeof(x) == sizeof(float)) { \
+ volatile float __x; \
+ __x = (x); \
+ } else if (sizeof(x) == sizeof(double)) { \
+ volatile double __x; \
+ __x = (x); \
+ } else { \
+ volatile long double __x; \
+ __x = (x); \
+ } \
+} while(0)
+
+/* Get two 32 bit ints from a double. */
+#define EXTRACT_WORDS(hi,lo,d) \
+do { \
+ union {double f; uint64_t i;} __u; \
+ __u.f = (d); \
+ (hi) = __u.i >> 32; \
+ (lo) = (uint32_t)__u.i; \
+} while (0)
+
+/* Get the more significant 32 bit int from a double. */
+#define GET_HIGH_WORD(hi,d) \
+do { \
+ union {double f; uint64_t i;} __u; \
+ __u.f = (d); \
+ (hi) = __u.i >> 32; \
+} while (0)
+
+/* Get the less significant 32 bit int from a double. */
+#define GET_LOW_WORD(lo,d) \
+do { \
+ union {double f; uint64_t i;} __u; \
+ __u.f = (d); \
+ (lo) = (uint32_t)__u.i; \
+} while (0)
+
+/* Set a double from two 32 bit ints. */
+#define INSERT_WORDS(d,hi,lo) \
+do { \
+ union {double f; uint64_t i;} __u; \
+ __u.i = ((uint64_t)(hi)<<32) | (uint32_t)(lo); \
+ (d) = __u.f; \
+} while (0)
+
+/* Set the more significant 32 bits of a double from an int. */
+#define SET_HIGH_WORD(d,hi) \
+do { \
+ union {double f; uint64_t i;} __u; \
+ __u.f = (d); \
+ __u.i &= 0xffffffff; \
+ __u.i |= (uint64_t)(hi) << 32; \
+ (d) = __u.f; \
+} while (0)
+
+/* Set the less significant 32 bits of a double from an int. */
+#define SET_LOW_WORD(d,lo) \
+do { \
+ union {double f; uint64_t i;} __u; \
+ __u.f = (d); \
+ __u.i &= 0xffffffff00000000ull; \
+ __u.i |= (uint32_t)(lo); \
+ (d) = __u.f; \
+} while (0)
+
+/* Get a 32 bit int from a float. */
+#define GET_FLOAT_WORD(w,d) \
+do { \
+ union {float f; uint32_t i;} __u; \
+ __u.f = (d); \
+ (w) = __u.i; \
+} while (0)
+
+/* Set a float from a 32 bit int. */
+#define SET_FLOAT_WORD(d,w) \
+do { \
+ union {float f; uint32_t i;} __u; \
+ __u.i = (w); \
+ (d) = __u.f; \
+} while (0)
+
+#undef __CMPLX
+#undef CMPLX
+#undef CMPLXF
+#undef CMPLXL
+
+#define __CMPLX(x, y, t) \
+ ((union { _Complex t __z; t __xy[2]; }){.__xy = {(x),(y)}}.__z)
+
+#define CMPLX(x, y) __CMPLX(x, y, double)
+#define CMPLXF(x, y) __CMPLX(x, y, float)
+#define CMPLXL(x, y) __CMPLX(x, y, long double)
+
+#ifndef __MLIBC_ABI_ONLY
+
+/* fdlibm kernel functions */
+
+int __rem_pio2_large(double*,double*,int,int,int);
+
+int __rem_pio2(double,double*);
+double __sin(double,double,int);
+double __cos(double,double);
+double __tan(double,double,int);
+double __expo2(double);
+/*double complex __ldexp_cexp(double complex,int); */
+
+int __rem_pio2f(float,double*);
+float __sindf(double);
+float __cosdf(double);
+float __tandf(double,int);
+float __expo2f(float);
+/*float complex __ldexp_cexpf(float complex,int); */
+
+int __rem_pio2l(long double, long double *);
+long double __sinl(long double, long double, int);
+long double __cosl(long double, long double);
+long double __tanl(long double, long double, int);
+
+/* polynomial evaluation */
+long double __polevll(long double, const long double *, int);
+long double __p1evll(long double, const long double *, int);
+
+#endif /* !__MLIBC_ABI_ONLY */
+
+#endif
diff --git a/lib/libc/src/musl-math/llrint.c b/lib/libc/src/musl-math/llrint.c
new file mode 100644
index 0000000..4f583ae
--- /dev/null
+++ b/lib/libc/src/musl-math/llrint.c
@@ -0,0 +1,8 @@
+#include <math.h>
+
+/* uses LLONG_MAX > 2^53, see comments in lrint.c */
+
+long long llrint(double x)
+{
+ return rint(x);
+}
diff --git a/lib/libc/src/musl-math/llrintf.c b/lib/libc/src/musl-math/llrintf.c
new file mode 100644
index 0000000..96949a0
--- /dev/null
+++ b/lib/libc/src/musl-math/llrintf.c
@@ -0,0 +1,8 @@
+#include <math.h>
+
+/* uses LLONG_MAX > 2^24, see comments in lrint.c */
+
+long long llrintf(float x)
+{
+ return rintf(x);
+}
diff --git a/lib/libc/src/musl-math/llrintl.c b/lib/libc/src/musl-math/llrintl.c
new file mode 100644
index 0000000..3449f6f
--- /dev/null
+++ b/lib/libc/src/musl-math/llrintl.c
@@ -0,0 +1,36 @@
+#include <limits.h>
+#include <fenv.h>
+#include "libm.h"
+
+
+#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
+long long llrintl(long double x)
+{
+ return llrint(x);
+}
+#elif defined(FE_INEXACT)
+/*
+see comments in lrint.c
+
+Note that if LLONG_MAX == 0x7fffffffffffffff && LDBL_MANT_DIG == 64
+then x == 2**63 - 0.5 is the only input that overflows and
+raises inexact (with tonearest or upward rounding mode)
+*/
+long long llrintl(long double x)
+{
+ #pragma STDC FENV_ACCESS ON
+ int e;
+
+ e = fetestexcept(FE_INEXACT);
+ x = rintl(x);
+ if (!e && (x > LLONG_MAX || x < LLONG_MIN))
+ feclearexcept(FE_INEXACT);
+ /* conversion */
+ return x;
+}
+#else
+long long llrintl(long double x)
+{
+ return rintl(x);
+}
+#endif
diff --git a/lib/libc/src/musl-math/llround.c b/lib/libc/src/musl-math/llround.c
new file mode 100644
index 0000000..4d94787
--- /dev/null
+++ b/lib/libc/src/musl-math/llround.c
@@ -0,0 +1,6 @@
+#include <math.h>
+
+long long llround(double x)
+{
+ return round(x);
+}
diff --git a/lib/libc/src/musl-math/llroundf.c b/lib/libc/src/musl-math/llroundf.c
new file mode 100644
index 0000000..19eb77e
--- /dev/null
+++ b/lib/libc/src/musl-math/llroundf.c
@@ -0,0 +1,6 @@
+#include <math.h>
+
+long long llroundf(float x)
+{
+ return roundf(x);
+}
diff --git a/lib/libc/src/musl-math/llroundl.c b/lib/libc/src/musl-math/llroundl.c
new file mode 100644
index 0000000..2c2ee5e
--- /dev/null
+++ b/lib/libc/src/musl-math/llroundl.c
@@ -0,0 +1,6 @@
+#include <math.h>
+
+long long llroundl(long double x)
+{
+ return roundl(x);
+}
diff --git a/lib/libc/src/musl-math/log.c b/lib/libc/src/musl-math/log.c
new file mode 100644
index 0000000..e61e113
--- /dev/null
+++ b/lib/libc/src/musl-math/log.c
@@ -0,0 +1,118 @@
+/* origin: FreeBSD /usr/src/lib/msun/src/e_log.c */
+/*
+ * ====================================================
+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ *
+ * Developed at SunSoft, a Sun Microsystems, Inc. business.
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ */
+/* log(x)
+ * Return the logarithm of x
+ *
+ * Method :
+ * 1. Argument Reduction: find k and f such that
+ * x = 2^k * (1+f),
+ * where sqrt(2)/2 < 1+f < sqrt(2) .
+ *
+ * 2. Approximation of log(1+f).
+ * Let s = f/(2+f) ; based on log(1+f) = log(1+s) - log(1-s)
+ * = 2s + 2/3 s**3 + 2/5 s**5 + .....,
+ * = 2s + s*R
+ * We use a special Remez algorithm on [0,0.1716] to generate
+ * a polynomial of degree 14 to approximate R The maximum error
+ * of this polynomial approximation is bounded by 2**-58.45. In
+ * other words,
+ * 2 4 6 8 10 12 14
+ * R(z) ~ Lg1*s +Lg2*s +Lg3*s +Lg4*s +Lg5*s +Lg6*s +Lg7*s
+ * (the values of Lg1 to Lg7 are listed in the program)
+ * and
+ * | 2 14 | -58.45
+ * | Lg1*s +...+Lg7*s - R(z) | <= 2
+ * | |
+ * Note that 2s = f - s*f = f - hfsq + s*hfsq, where hfsq = f*f/2.
+ * In order to guarantee error in log below 1ulp, we compute log
+ * by
+ * log(1+f) = f - s*(f - R) (if f is not too large)
+ * log(1+f) = f - (hfsq - s*(hfsq+R)). (better accuracy)
+ *
+ * 3. Finally, log(x) = k*ln2 + log(1+f).
+ * = k*ln2_hi+(f-(hfsq-(s*(hfsq+R)+k*ln2_lo)))
+ * Here ln2 is split into two floating point number:
+ * ln2_hi + ln2_lo,
+ * where n*ln2_hi is always exact for |n| < 2000.
+ *
+ * Special cases:
+ * log(x) is NaN with signal if x < 0 (including -INF) ;
+ * log(+INF) is +INF; log(0) is -INF with signal;
+ * log(NaN) is that NaN with no signal.
+ *
+ * Accuracy:
+ * according to an error analysis, the error is always less than
+ * 1 ulp (unit in the last place).
+ *
+ * Constants:
+ * The hexadecimal values are the intended ones for the following
+ * constants. The decimal values may be used, provided that the
+ * compiler will convert from decimal to binary accurately enough
+ * to produce the hexadecimal values shown.
+ */
+
+#include <math.h>
+#include <stdint.h>
+
+static const double
+ln2_hi = 6.93147180369123816490e-01, /* 3fe62e42 fee00000 */
+ln2_lo = 1.90821492927058770002e-10, /* 3dea39ef 35793c76 */
+Lg1 = 6.666666666666735130e-01, /* 3FE55555 55555593 */
+Lg2 = 3.999999999940941908e-01, /* 3FD99999 9997FA04 */
+Lg3 = 2.857142874366239149e-01, /* 3FD24924 94229359 */
+Lg4 = 2.222219843214978396e-01, /* 3FCC71C5 1D8E78AF */
+Lg5 = 1.818357216161805012e-01, /* 3FC74664 96CB03DE */
+Lg6 = 1.531383769920937332e-01, /* 3FC39A09 D078C69F */
+Lg7 = 1.479819860511658591e-01; /* 3FC2F112 DF3E5244 */
+
+double log(double x)
+{
+ union {double f; uint64_t i;} u = {x};
+ double_t hfsq,f,s,z,R,w,t1,t2,dk;
+ uint32_t hx;
+ int k;
+
+ hx = u.i>>32;
+ k = 0;
+ if (hx < 0x00100000 || hx>>31) {
+ if (u.i<<1 == 0)
+ return -1/(x*x); /* log(+-0)=-inf */
+ if (hx>>31)
+ return (x-x)/0.0; /* log(-#) = NaN */
+ /* subnormal number, scale x up */
+ k -= 54;
+ x *= 0x1p54;
+ u.f = x;
+ hx = u.i>>32;
+ } else if (hx >= 0x7ff00000) {
+ return x;
+ } else if (hx == 0x3ff00000 && u.i<<32 == 0)
+ return 0;
+
+ /* reduce x into [sqrt(2)/2, sqrt(2)] */
+ hx += 0x3ff00000 - 0x3fe6a09e;
+ k += (int)(hx>>20) - 0x3ff;
+ hx = (hx&0x000fffff) + 0x3fe6a09e;
+ u.i = (uint64_t)hx<<32 | (u.i&0xffffffff);
+ x = u.f;
+
+ f = x - 1.0;
+ hfsq = 0.5*f*f;
+ s = f/(2.0+f);
+ z = s*s;
+ w = z*z;
+ t1 = w*(Lg2+w*(Lg4+w*Lg6));
+ t2 = z*(Lg1+w*(Lg3+w*(Lg5+w*Lg7)));
+ R = t2 + t1;
+ dk = k;
+ return s*(hfsq+R) + dk*ln2_lo - hfsq + f + dk*ln2_hi;
+}
diff --git a/lib/libc/src/musl-math/log10.c b/lib/libc/src/musl-math/log10.c
new file mode 100644
index 0000000..8102687
--- /dev/null
+++ b/lib/libc/src/musl-math/log10.c
@@ -0,0 +1,101 @@
+/* origin: FreeBSD /usr/src/lib/msun/src/e_log10.c */
+/*
+ * ====================================================
+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ *
+ * Developed at SunSoft, a Sun Microsystems, Inc. business.
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ */
+/*
+ * Return the base 10 logarithm of x. See log.c for most comments.
+ *
+ * Reduce x to 2^k (1+f) and calculate r = log(1+f) - f + f*f/2
+ * as in log.c, then combine and scale in extra precision:
+ * log10(x) = (f - f*f/2 + r)/log(10) + k*log10(2)
+ */
+
+#include <math.h>
+#include <stdint.h>
+
+static const double
+ivln10hi = 4.34294481878168880939e-01, /* 0x3fdbcb7b, 0x15200000 */
+ivln10lo = 2.50829467116452752298e-11, /* 0x3dbb9438, 0xca9aadd5 */
+log10_2hi = 3.01029995663611771306e-01, /* 0x3FD34413, 0x509F6000 */
+log10_2lo = 3.69423907715893078616e-13, /* 0x3D59FEF3, 0x11F12B36 */
+Lg1 = 6.666666666666735130e-01, /* 3FE55555 55555593 */
+Lg2 = 3.999999999940941908e-01, /* 3FD99999 9997FA04 */
+Lg3 = 2.857142874366239149e-01, /* 3FD24924 94229359 */
+Lg4 = 2.222219843214978396e-01, /* 3FCC71C5 1D8E78AF */
+Lg5 = 1.818357216161805012e-01, /* 3FC74664 96CB03DE */
+Lg6 = 1.531383769920937332e-01, /* 3FC39A09 D078C69F */
+Lg7 = 1.479819860511658591e-01; /* 3FC2F112 DF3E5244 */
+
+double log10(double x)
+{
+ union {double f; uint64_t i;} u = {x};
+ double_t hfsq,f,s,z,R,w,t1,t2,dk,y,hi,lo,val_hi,val_lo;
+ uint32_t hx;
+ int k;
+
+ hx = u.i>>32;
+ k = 0;
+ if (hx < 0x00100000 || hx>>31) {
+ if (u.i<<1 == 0)
+ return -1/(x*x); /* log(+-0)=-inf */
+ if (hx>>31)
+ return (x-x)/0.0; /* log(-#) = NaN */
+ /* subnormal number, scale x up */
+ k -= 54;
+ x *= 0x1p54;
+ u.f = x;
+ hx = u.i>>32;
+ } else if (hx >= 0x7ff00000) {
+ return x;
+ } else if (hx == 0x3ff00000 && u.i<<32 == 0)
+ return 0;
+
+ /* reduce x into [sqrt(2)/2, sqrt(2)] */
+ hx += 0x3ff00000 - 0x3fe6a09e;
+ k += (int)(hx>>20) - 0x3ff;
+ hx = (hx&0x000fffff) + 0x3fe6a09e;
+ u.i = (uint64_t)hx<<32 | (u.i&0xffffffff);
+ x = u.f;
+
+ f = x - 1.0;
+ hfsq = 0.5*f*f;
+ s = f/(2.0+f);
+ z = s*s;
+ w = z*z;
+ t1 = w*(Lg2+w*(Lg4+w*Lg6));
+ t2 = z*(Lg1+w*(Lg3+w*(Lg5+w*Lg7)));
+ R = t2 + t1;
+
+ /* See log2.c for details. */
+ /* hi+lo = f - hfsq + s*(hfsq+R) ~ log(1+f) */
+ hi = f - hfsq;
+ u.f = hi;
+ u.i &= (uint64_t)-1<<32;
+ hi = u.f;
+ lo = f - hi - hfsq + s*(hfsq+R);
+
+ /* val_hi+val_lo ~ log10(1+f) + k*log10(2) */
+ val_hi = hi*ivln10hi;
+ dk = k;
+ y = dk*log10_2hi;
+ val_lo = dk*log10_2lo + (lo+hi)*ivln10lo + lo*ivln10hi;
+
+ /*
+ * Extra precision in for adding y is not strictly needed
+ * since there is no very large cancellation near x = sqrt(2) or
+ * x = 1/sqrt(2), but we do it anyway since it costs little on CPUs
+ * with some parallelism and it reduces the error for many args.
+ */
+ w = y + val_hi;
+ val_lo += (y - w) + val_hi;
+ val_hi = w;
+
+ return val_lo + val_hi;
+}
diff --git a/lib/libc/src/musl-math/log10f.c b/lib/libc/src/musl-math/log10f.c
new file mode 100644
index 0000000..9ca2f01
--- /dev/null
+++ b/lib/libc/src/musl-math/log10f.c
@@ -0,0 +1,77 @@
+/* origin: FreeBSD /usr/src/lib/msun/src/e_log10f.c */
+/*
+ * ====================================================
+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ *
+ * Developed at SunPro, a Sun Microsystems, Inc. business.
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ */
+/*
+ * See comments in log10.c.
+ */
+
+#include <math.h>
+#include <stdint.h>
+
+static const float
+ivln10hi = 4.3432617188e-01, /* 0x3ede6000 */
+ivln10lo = -3.1689971365e-05, /* 0xb804ead9 */
+log10_2hi = 3.0102920532e-01, /* 0x3e9a2080 */
+log10_2lo = 7.9034151668e-07, /* 0x355427db */
+/* |(log(1+s)-log(1-s))/s - Lg(s)| < 2**-34.24 (~[-4.95e-11, 4.97e-11]). */
+Lg1 = 0xaaaaaa.0p-24, /* 0.66666662693 */
+Lg2 = 0xccce13.0p-25, /* 0.40000972152 */
+Lg3 = 0x91e9ee.0p-25, /* 0.28498786688 */
+Lg4 = 0xf89e26.0p-26; /* 0.24279078841 */
+
+float log10f(float x)
+{
+ union {float f; uint32_t i;} u = {x};
+ float_t hfsq,f,s,z,R,w,t1,t2,dk,hi,lo;
+ uint32_t ix;
+ int k;
+
+ ix = u.i;
+ k = 0;
+ if (ix < 0x00800000 || ix>>31) { /* x < 2**-126 */
+ if (ix<<1 == 0)
+ return -1/(x*x); /* log(+-0)=-inf */
+ if (ix>>31)
+ return (x-x)/0.0f; /* log(-#) = NaN */
+ /* subnormal number, scale up x */
+ k -= 25;
+ x *= 0x1p25f;
+ u.f = x;
+ ix = u.i;
+ } else if (ix >= 0x7f800000) {
+ return x;
+ } else if (ix == 0x3f800000)
+ return 0;
+
+ /* reduce x into [sqrt(2)/2, sqrt(2)] */
+ ix += 0x3f800000 - 0x3f3504f3;
+ k += (int)(ix>>23) - 0x7f;
+ ix = (ix&0x007fffff) + 0x3f3504f3;
+ u.i = ix;
+ x = u.f;
+
+ f = x - 1.0f;
+ s = f/(2.0f + f);
+ z = s*s;
+ w = z*z;
+ t1= w*(Lg2+w*Lg4);
+ t2= z*(Lg1+w*Lg3);
+ R = t2 + t1;
+ hfsq = 0.5f*f*f;
+
+ hi = f - hfsq;
+ u.f = hi;
+ u.i &= 0xfffff000;
+ hi = u.f;
+ lo = f - hi - hfsq + s*(hfsq+R);
+ dk = k;
+ return dk*log10_2lo + (lo+hi)*ivln10lo + lo*ivln10hi + hi*ivln10hi + dk*log10_2hi;
+}
diff --git a/lib/libc/src/musl-math/log10l.c b/lib/libc/src/musl-math/log10l.c
new file mode 100644
index 0000000..63dcc28
--- /dev/null
+++ b/lib/libc/src/musl-math/log10l.c
@@ -0,0 +1,191 @@
+/* origin: OpenBSD /usr/src/lib/libm/src/ld80/e_log10l.c */
+/*
+ * Copyright (c) 2008 Stephen L. Moshier <steve@moshier.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+/*
+ * Common logarithm, long double precision
+ *
+ *
+ * SYNOPSIS:
+ *
+ * long double x, y, log10l();
+ *
+ * y = log10l( x );
+ *
+ *
+ * DESCRIPTION:
+ *
+ * Returns the base 10 logarithm of x.
+ *
+ * The argument is separated into its exponent and fractional
+ * parts. If the exponent is between -1 and +1, the logarithm
+ * of the fraction is approximated by
+ *
+ * log(1+x) = x - 0.5 x**2 + x**3 P(x)/Q(x).
+ *
+ * Otherwise, setting z = 2(x-1)/x+1),
+ *
+ * log(x) = z + z**3 P(z)/Q(z).
+ *
+ *
+ * ACCURACY:
+ *
+ * Relative error:
+ * arithmetic domain # trials peak rms
+ * IEEE 0.5, 2.0 30000 9.0e-20 2.6e-20
+ * IEEE exp(+-10000) 30000 6.0e-20 2.3e-20
+ *
+ * In the tests over the interval exp(+-10000), the logarithms
+ * of the random arguments were uniformly distributed over
+ * [-10000, +10000].
+ *
+ * ERROR MESSAGES:
+ *
+ * log singularity: x = 0; returns MINLOG
+ * log domain: x < 0; returns MINLOG
+ */
+
+#include "libm.h"
+
+#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
+long double log10l(long double x)
+{
+ return log10(x);
+}
+#elif LDBL_MANT_DIG == 64 && LDBL_MAX_EXP == 16384
+/* Coefficients for log(1+x) = x - x**2/2 + x**3 P(x)/Q(x)
+ * 1/sqrt(2) <= x < sqrt(2)
+ * Theoretical peak relative error = 6.2e-22
+ */
+static const long double P[] = {
+ 4.9962495940332550844739E-1L,
+ 1.0767376367209449010438E1L,
+ 7.7671073698359539859595E1L,
+ 2.5620629828144409632571E2L,
+ 4.2401812743503691187826E2L,
+ 3.4258224542413922935104E2L,
+ 1.0747524399916215149070E2L,
+};
+static const long double Q[] = {
+/* 1.0000000000000000000000E0,*/
+ 2.3479774160285863271658E1L,
+ 1.9444210022760132894510E2L,
+ 7.7952888181207260646090E2L,
+ 1.6911722418503949084863E3L,
+ 2.0307734695595183428202E3L,
+ 1.2695660352705325274404E3L,
+ 3.2242573199748645407652E2L,
+};
+
+/* Coefficients for log(x) = z + z^3 P(z^2)/Q(z^2),
+ * where z = 2(x-1)/(x+1)
+ * 1/sqrt(2) <= x < sqrt(2)
+ * Theoretical peak relative error = 6.16e-22
+ */
+static const long double R[4] = {
+ 1.9757429581415468984296E-3L,
+-7.1990767473014147232598E-1L,
+ 1.0777257190312272158094E1L,
+-3.5717684488096787370998E1L,
+};
+static const long double S[4] = {
+/* 1.00000000000000000000E0L,*/
+-2.6201045551331104417768E1L,
+ 1.9361891836232102174846E2L,
+-4.2861221385716144629696E2L,
+};
+/* log10(2) */
+#define L102A 0.3125L
+#define L102B -1.1470004336018804786261e-2L
+/* log10(e) */
+#define L10EA 0.5L
+#define L10EB -6.5705518096748172348871e-2L
+
+#define SQRTH 0.70710678118654752440L
+
+long double log10l(long double x)
+{
+ long double y, z;
+ int e;
+
+ if (isnan(x))
+ return x;
+ if(x <= 0.0) {
+ if(x == 0.0)
+ return -1.0 / (x*x);
+ return (x - x) / 0.0;
+ }
+ if (x == INFINITY)
+ return INFINITY;
+ /* separate mantissa from exponent */
+ /* Note, frexp is used so that denormal numbers
+ * will be handled properly.
+ */
+ x = frexpl(x, &e);
+
+ /* logarithm using log(x) = z + z**3 P(z)/Q(z),
+ * where z = 2(x-1)/x+1)
+ */
+ if (e > 2 || e < -2) {
+ if (x < SQRTH) { /* 2(2x-1)/(2x+1) */
+ e -= 1;
+ z = x - 0.5;
+ y = 0.5 * z + 0.5;
+ } else { /* 2 (x-1)/(x+1) */
+ z = x - 0.5;
+ z -= 0.5;
+ y = 0.5 * x + 0.5;
+ }
+ x = z / y;
+ z = x*x;
+ y = x * (z * __polevll(z, R, 3) / __p1evll(z, S, 3));
+ goto done;
+ }
+
+ /* logarithm using log(1+x) = x - .5x**2 + x**3 P(x)/Q(x) */
+ if (x < SQRTH) {
+ e -= 1;
+ x = 2.0*x - 1.0;
+ } else {
+ x = x - 1.0;
+ }
+ z = x*x;
+ y = x * (z * __polevll(x, P, 6) / __p1evll(x, Q, 7));
+ y = y - 0.5*z;
+
+done:
+ /* Multiply log of fraction by log10(e)
+ * and base 2 exponent by log10(2).
+ *
+ * ***CAUTION***
+ *
+ * This sequence of operations is critical and it may
+ * be horribly defeated by some compiler optimizers.
+ */
+ z = y * (L10EB);
+ z += x * (L10EB);
+ z += e * (L102B);
+ z += y * (L10EA);
+ z += x * (L10EA);
+ z += e * (L102A);
+ return z;
+}
+#elif LDBL_MANT_DIG == 113 && LDBL_MAX_EXP == 16384
+// TODO: broken implementation to make things compile
+long double log10l(long double x)
+{
+ return log10(x);
+}
+#endif
diff --git a/lib/libc/src/musl-math/log1p.c b/lib/libc/src/musl-math/log1p.c
new file mode 100644
index 0000000..0097134
--- /dev/null
+++ b/lib/libc/src/musl-math/log1p.c
@@ -0,0 +1,122 @@
+/* origin: FreeBSD /usr/src/lib/msun/src/s_log1p.c */
+/*
+ * ====================================================
+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ *
+ * Developed at SunPro, a Sun Microsystems, Inc. business.
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ */
+/* double log1p(double x)
+ * Return the natural logarithm of 1+x.
+ *
+ * Method :
+ * 1. Argument Reduction: find k and f such that
+ * 1+x = 2^k * (1+f),
+ * where sqrt(2)/2 < 1+f < sqrt(2) .
+ *
+ * Note. If k=0, then f=x is exact. However, if k!=0, then f
+ * may not be representable exactly. In that case, a correction
+ * term is need. Let u=1+x rounded. Let c = (1+x)-u, then
+ * log(1+x) - log(u) ~ c/u. Thus, we proceed to compute log(u),
+ * and add back the correction term c/u.
+ * (Note: when x > 2**53, one can simply return log(x))
+ *
+ * 2. Approximation of log(1+f): See log.c
+ *
+ * 3. Finally, log1p(x) = k*ln2 + log(1+f) + c/u. See log.c
+ *
+ * Special cases:
+ * log1p(x) is NaN with signal if x < -1 (including -INF) ;
+ * log1p(+INF) is +INF; log1p(-1) is -INF with signal;
+ * log1p(NaN) is that NaN with no signal.
+ *
+ * Accuracy:
+ * according to an error analysis, the error is always less than
+ * 1 ulp (unit in the last place).
+ *
+ * Constants:
+ * The hexadecimal values are the intended ones for the following
+ * constants. The decimal values may be used, provided that the
+ * compiler will convert from decimal to binary accurately enough
+ * to produce the hexadecimal values shown.
+ *
+ * Note: Assuming log() return accurate answer, the following
+ * algorithm can be used to compute log1p(x) to within a few ULP:
+ *
+ * u = 1+x;
+ * if(u==1.0) return x ; else
+ * return log(u)*(x/(u-1.0));
+ *
+ * See HP-15C Advanced Functions Handbook, p.193.
+ */
+
+#include "libm.h"
+
+static const double
+ln2_hi = 6.93147180369123816490e-01, /* 3fe62e42 fee00000 */
+ln2_lo = 1.90821492927058770002e-10, /* 3dea39ef 35793c76 */
+Lg1 = 6.666666666666735130e-01, /* 3FE55555 55555593 */
+Lg2 = 3.999999999940941908e-01, /* 3FD99999 9997FA04 */
+Lg3 = 2.857142874366239149e-01, /* 3FD24924 94229359 */
+Lg4 = 2.222219843214978396e-01, /* 3FCC71C5 1D8E78AF */
+Lg5 = 1.818357216161805012e-01, /* 3FC74664 96CB03DE */
+Lg6 = 1.531383769920937332e-01, /* 3FC39A09 D078C69F */
+Lg7 = 1.479819860511658591e-01; /* 3FC2F112 DF3E5244 */
+
+double log1p(double x)
+{
+ union {double f; uint64_t i;} u = {x};
+ double_t hfsq,f,c,s,z,R,w,t1,t2,dk;
+ uint32_t hx,hu;
+ int k;
+
+ hx = u.i>>32;
+ k = 1;
+ if (hx < 0x3fda827a || hx>>31) { /* 1+x < sqrt(2)+ */
+ if (hx >= 0xbff00000) { /* x <= -1.0 */
+ if (x == -1)
+ return x/0.0; /* log1p(-1) = -inf */
+ return (x-x)/0.0; /* log1p(x<-1) = NaN */
+ }
+ if (hx<<1 < 0x3ca00000<<1) { /* |x| < 2**-53 */
+ /* underflow if subnormal */
+ if ((hx&0x7ff00000) == 0)
+ FORCE_EVAL((float)x);
+ return x;
+ }
+ if (hx <= 0xbfd2bec4) { /* sqrt(2)/2- <= 1+x < sqrt(2)+ */
+ k = 0;
+ c = 0;
+ f = x;
+ }
+ } else if (hx >= 0x7ff00000)
+ return x;
+ if (k) {
+ u.f = 1 + x;
+ hu = u.i>>32;
+ hu += 0x3ff00000 - 0x3fe6a09e;
+ k = (int)(hu>>20) - 0x3ff;
+ /* correction term ~ log(1+x)-log(u), avoid underflow in c/u */
+ if (k < 54) {
+ c = k >= 2 ? 1-(u.f-x) : x-(u.f-1);
+ c /= u.f;
+ } else
+ c = 0;
+ /* reduce u into [sqrt(2)/2, sqrt(2)] */
+ hu = (hu&0x000fffff) + 0x3fe6a09e;
+ u.i = (uint64_t)hu<<32 | (u.i&0xffffffff);
+ f = u.f - 1;
+ }
+ hfsq = 0.5*f*f;
+ s = f/(2.0+f);
+ z = s*s;
+ w = z*z;
+ t1 = w*(Lg2+w*(Lg4+w*Lg6));
+ t2 = z*(Lg1+w*(Lg3+w*(Lg5+w*Lg7)));
+ R = t2 + t1;
+ dk = k;
+ return s*(hfsq+R) + (dk*ln2_lo+c) - hfsq + f + dk*ln2_hi;
+}
diff --git a/lib/libc/src/musl-math/log1pf.c b/lib/libc/src/musl-math/log1pf.c
new file mode 100644
index 0000000..23985c3
--- /dev/null
+++ b/lib/libc/src/musl-math/log1pf.c
@@ -0,0 +1,77 @@
+/* origin: FreeBSD /usr/src/lib/msun/src/s_log1pf.c */
+/*
+ * ====================================================
+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ *
+ * Developed at SunPro, a Sun Microsystems, Inc. business.
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ */
+
+#include "libm.h"
+
+static const float
+ln2_hi = 6.9313812256e-01, /* 0x3f317180 */
+ln2_lo = 9.0580006145e-06, /* 0x3717f7d1 */
+/* |(log(1+s)-log(1-s))/s - Lg(s)| < 2**-34.24 (~[-4.95e-11, 4.97e-11]). */
+Lg1 = 0xaaaaaa.0p-24, /* 0.66666662693 */
+Lg2 = 0xccce13.0p-25, /* 0.40000972152 */
+Lg3 = 0x91e9ee.0p-25, /* 0.28498786688 */
+Lg4 = 0xf89e26.0p-26; /* 0.24279078841 */
+
+float log1pf(float x)
+{
+ union {float f; uint32_t i;} u = {x};
+ float_t hfsq,f,c,s,z,R,w,t1,t2,dk;
+ uint32_t ix,iu;
+ int k;
+
+ ix = u.i;
+ k = 1;
+ if (ix < 0x3ed413d0 || ix>>31) { /* 1+x < sqrt(2)+ */
+ if (ix >= 0xbf800000) { /* x <= -1.0 */
+ if (x == -1)
+ return x/0.0f; /* log1p(-1)=+inf */
+ return (x-x)/0.0f; /* log1p(x<-1)=NaN */
+ }
+ if (ix<<1 < 0x33800000<<1) { /* |x| < 2**-24 */
+ /* underflow if subnormal */
+ if ((ix&0x7f800000) == 0)
+ FORCE_EVAL(x*x);
+ return x;
+ }
+ if (ix <= 0xbe95f619) { /* sqrt(2)/2- <= 1+x < sqrt(2)+ */
+ k = 0;
+ c = 0;
+ f = x;
+ }
+ } else if (ix >= 0x7f800000)
+ return x;
+ if (k) {
+ u.f = 1 + x;
+ iu = u.i;
+ iu += 0x3f800000 - 0x3f3504f3;
+ k = (int)(iu>>23) - 0x7f;
+ /* correction term ~ log(1+x)-log(u), avoid underflow in c/u */
+ if (k < 25) {
+ c = k >= 2 ? 1-(u.f-x) : x-(u.f-1);
+ c /= u.f;
+ } else
+ c = 0;
+ /* reduce u into [sqrt(2)/2, sqrt(2)] */
+ iu = (iu&0x007fffff) + 0x3f3504f3;
+ u.i = iu;
+ f = u.f - 1;
+ }
+ s = f/(2.0f + f);
+ z = s*s;
+ w = z*z;
+ t1= w*(Lg2+w*Lg4);
+ t2= z*(Lg1+w*Lg3);
+ R = t2 + t1;
+ hfsq = 0.5f*f*f;
+ dk = k;
+ return s*(hfsq+R) + (dk*ln2_lo+c) - hfsq + f + dk*ln2_hi;
+}
diff --git a/lib/libc/src/musl-math/log1pl.c b/lib/libc/src/musl-math/log1pl.c
new file mode 100644
index 0000000..141b5f0
--- /dev/null
+++ b/lib/libc/src/musl-math/log1pl.c
@@ -0,0 +1,177 @@
+/* origin: OpenBSD /usr/src/lib/libm/src/ld80/s_log1pl.c */
+/*
+ * Copyright (c) 2008 Stephen L. Moshier <steve@moshier.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+/*
+ * Relative error logarithm
+ * Natural logarithm of 1+x, long double precision
+ *
+ *
+ * SYNOPSIS:
+ *
+ * long double x, y, log1pl();
+ *
+ * y = log1pl( x );
+ *
+ *
+ * DESCRIPTION:
+ *
+ * Returns the base e (2.718...) logarithm of 1+x.
+ *
+ * The argument 1+x is separated into its exponent and fractional
+ * parts. If the exponent is between -1 and +1, the logarithm
+ * of the fraction is approximated by
+ *
+ * log(1+x) = x - 0.5 x^2 + x^3 P(x)/Q(x).
+ *
+ * Otherwise, setting z = 2(x-1)/x+1),
+ *
+ * log(x) = z + z^3 P(z)/Q(z).
+ *
+ *
+ * ACCURACY:
+ *
+ * Relative error:
+ * arithmetic domain # trials peak rms
+ * IEEE -1.0, 9.0 100000 8.2e-20 2.5e-20
+ */
+
+#include "libm.h"
+
+#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
+long double log1pl(long double x)
+{
+ return log1p(x);
+}
+#elif LDBL_MANT_DIG == 64 && LDBL_MAX_EXP == 16384
+/* Coefficients for log(1+x) = x - x^2 / 2 + x^3 P(x)/Q(x)
+ * 1/sqrt(2) <= x < sqrt(2)
+ * Theoretical peak relative error = 2.32e-20
+ */
+static const long double P[] = {
+ 4.5270000862445199635215E-5L,
+ 4.9854102823193375972212E-1L,
+ 6.5787325942061044846969E0L,
+ 2.9911919328553073277375E1L,
+ 6.0949667980987787057556E1L,
+ 5.7112963590585538103336E1L,
+ 2.0039553499201281259648E1L,
+};
+static const long double Q[] = {
+/* 1.0000000000000000000000E0,*/
+ 1.5062909083469192043167E1L,
+ 8.3047565967967209469434E1L,
+ 2.2176239823732856465394E2L,
+ 3.0909872225312059774938E2L,
+ 2.1642788614495947685003E2L,
+ 6.0118660497603843919306E1L,
+};
+
+/* Coefficients for log(x) = z + z^3 P(z^2)/Q(z^2),
+ * where z = 2(x-1)/(x+1)
+ * 1/sqrt(2) <= x < sqrt(2)
+ * Theoretical peak relative error = 6.16e-22
+ */
+static const long double R[4] = {
+ 1.9757429581415468984296E-3L,
+-7.1990767473014147232598E-1L,
+ 1.0777257190312272158094E1L,
+-3.5717684488096787370998E1L,
+};
+static const long double S[4] = {
+/* 1.00000000000000000000E0L,*/
+-2.6201045551331104417768E1L,
+ 1.9361891836232102174846E2L,
+-4.2861221385716144629696E2L,
+};
+static const long double C1 = 6.9314575195312500000000E-1L;
+static const long double C2 = 1.4286068203094172321215E-6L;
+
+#define SQRTH 0.70710678118654752440L
+
+long double log1pl(long double xm1)
+{
+ long double x, y, z;
+ int e;
+
+ if (isnan(xm1))
+ return xm1;
+ if (xm1 == INFINITY)
+ return xm1;
+ if (xm1 == 0.0)
+ return xm1;
+
+ x = xm1 + 1.0;
+
+ /* Test for domain errors. */
+ if (x <= 0.0) {
+ if (x == 0.0)
+ return -1/(x*x); /* -inf with divbyzero */
+ return 0/0.0f; /* nan with invalid */
+ }
+
+ /* Separate mantissa from exponent.
+ Use frexp so that denormal numbers will be handled properly. */
+ x = frexpl(x, &e);
+
+ /* logarithm using log(x) = z + z^3 P(z)/Q(z),
+ where z = 2(x-1)/x+1) */
+ if (e > 2 || e < -2) {
+ if (x < SQRTH) { /* 2(2x-1)/(2x+1) */
+ e -= 1;
+ z = x - 0.5;
+ y = 0.5 * z + 0.5;
+ } else { /* 2 (x-1)/(x+1) */
+ z = x - 0.5;
+ z -= 0.5;
+ y = 0.5 * x + 0.5;
+ }
+ x = z / y;
+ z = x*x;
+ z = x * (z * __polevll(z, R, 3) / __p1evll(z, S, 3));
+ z = z + e * C2;
+ z = z + x;
+ z = z + e * C1;
+ return z;
+ }
+
+ /* logarithm using log(1+x) = x - .5x**2 + x**3 P(x)/Q(x) */
+ if (x < SQRTH) {
+ e -= 1;
+ if (e != 0)
+ x = 2.0 * x - 1.0;
+ else
+ x = xm1;
+ } else {
+ if (e != 0)
+ x = x - 1.0;
+ else
+ x = xm1;
+ }
+ z = x*x;
+ y = x * (z * __polevll(x, P, 6) / __p1evll(x, Q, 6));
+ y = y + e * C2;
+ z = y - 0.5 * z;
+ z = z + x;
+ z = z + e * C1;
+ return z;
+}
+#elif LDBL_MANT_DIG == 113 && LDBL_MAX_EXP == 16384
+// TODO: broken implementation to make things compile
+long double log1pl(long double x)
+{
+ return log1p(x);
+}
+#endif
diff --git a/lib/libc/src/musl-math/log2.c b/lib/libc/src/musl-math/log2.c
new file mode 100644
index 0000000..0aafad4
--- /dev/null
+++ b/lib/libc/src/musl-math/log2.c
@@ -0,0 +1,122 @@
+/* origin: FreeBSD /usr/src/lib/msun/src/e_log2.c */
+/*
+ * ====================================================
+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ *
+ * Developed at SunSoft, a Sun Microsystems, Inc. business.
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ */
+/*
+ * Return the base 2 logarithm of x. See log.c for most comments.
+ *
+ * Reduce x to 2^k (1+f) and calculate r = log(1+f) - f + f*f/2
+ * as in log.c, then combine and scale in extra precision:
+ * log2(x) = (f - f*f/2 + r)/log(2) + k
+ */
+
+#include <math.h>
+#include <stdint.h>
+
+static const double
+ivln2hi = 1.44269504072144627571e+00, /* 0x3ff71547, 0x65200000 */
+ivln2lo = 1.67517131648865118353e-10, /* 0x3de705fc, 0x2eefa200 */
+Lg1 = 6.666666666666735130e-01, /* 3FE55555 55555593 */
+Lg2 = 3.999999999940941908e-01, /* 3FD99999 9997FA04 */
+Lg3 = 2.857142874366239149e-01, /* 3FD24924 94229359 */
+Lg4 = 2.222219843214978396e-01, /* 3FCC71C5 1D8E78AF */
+Lg5 = 1.818357216161805012e-01, /* 3FC74664 96CB03DE */
+Lg6 = 1.531383769920937332e-01, /* 3FC39A09 D078C69F */
+Lg7 = 1.479819860511658591e-01; /* 3FC2F112 DF3E5244 */
+
+double log2(double x)
+{
+ union {double f; uint64_t i;} u = {x};
+ double_t hfsq,f,s,z,R,w,t1,t2,y,hi,lo,val_hi,val_lo;
+ uint32_t hx;
+ int k;
+
+ hx = u.i>>32;
+ k = 0;
+ if (hx < 0x00100000 || hx>>31) {
+ if (u.i<<1 == 0)
+ return -1/(x*x); /* log(+-0)=-inf */
+ if (hx>>31)
+ return (x-x)/0.0; /* log(-#) = NaN */
+ /* subnormal number, scale x up */
+ k -= 54;
+ x *= 0x1p54;
+ u.f = x;
+ hx = u.i>>32;
+ } else if (hx >= 0x7ff00000) {
+ return x;
+ } else if (hx == 0x3ff00000 && u.i<<32 == 0)
+ return 0;
+
+ /* reduce x into [sqrt(2)/2, sqrt(2)] */
+ hx += 0x3ff00000 - 0x3fe6a09e;
+ k += (int)(hx>>20) - 0x3ff;
+ hx = (hx&0x000fffff) + 0x3fe6a09e;
+ u.i = (uint64_t)hx<<32 | (u.i&0xffffffff);
+ x = u.f;
+
+ f = x - 1.0;
+ hfsq = 0.5*f*f;
+ s = f/(2.0+f);
+ z = s*s;
+ w = z*z;
+ t1 = w*(Lg2+w*(Lg4+w*Lg6));
+ t2 = z*(Lg1+w*(Lg3+w*(Lg5+w*Lg7)));
+ R = t2 + t1;
+
+ /*
+ * f-hfsq must (for args near 1) be evaluated in extra precision
+ * to avoid a large cancellation when x is near sqrt(2) or 1/sqrt(2).
+ * This is fairly efficient since f-hfsq only depends on f, so can
+ * be evaluated in parallel with R. Not combining hfsq with R also
+ * keeps R small (though not as small as a true `lo' term would be),
+ * so that extra precision is not needed for terms involving R.
+ *
+ * Compiler bugs involving extra precision used to break Dekker's
+ * theorem for spitting f-hfsq as hi+lo, unless double_t was used
+ * or the multi-precision calculations were avoided when double_t
+ * has extra precision. These problems are now automatically
+ * avoided as a side effect of the optimization of combining the
+ * Dekker splitting step with the clear-low-bits step.
+ *
+ * y must (for args near sqrt(2) and 1/sqrt(2)) be added in extra
+ * precision to avoid a very large cancellation when x is very near
+ * these values. Unlike the above cancellations, this problem is
+ * specific to base 2. It is strange that adding +-1 is so much
+ * harder than adding +-ln2 or +-log10_2.
+ *
+ * This uses Dekker's theorem to normalize y+val_hi, so the
+ * compiler bugs are back in some configurations, sigh. And I
+ * don't want to used double_t to avoid them, since that gives a
+ * pessimization and the support for avoiding the pessimization
+ * is not yet available.
+ *
+ * The multi-precision calculations for the multiplications are
+ * routine.
+ */
+
+ /* hi+lo = f - hfsq + s*(hfsq+R) ~ log(1+f) */
+ hi = f - hfsq;
+ u.f = hi;
+ u.i &= (uint64_t)-1<<32;
+ hi = u.f;
+ lo = f - hi - hfsq + s*(hfsq+R);
+
+ val_hi = hi*ivln2hi;
+ val_lo = (lo+hi)*ivln2lo + lo*ivln2hi;
+
+ /* spadd(val_hi, val_lo, y), except for not using double_t: */
+ y = k;
+ w = y + val_hi;
+ val_lo += (y - w) + val_hi;
+ val_hi = w;
+
+ return val_lo + val_hi;
+}
diff --git a/lib/libc/src/musl-math/log2f.c b/lib/libc/src/musl-math/log2f.c
new file mode 100644
index 0000000..b3e305f
--- /dev/null
+++ b/lib/libc/src/musl-math/log2f.c
@@ -0,0 +1,74 @@
+/* origin: FreeBSD /usr/src/lib/msun/src/e_log2f.c */
+/*
+ * ====================================================
+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ *
+ * Developed at SunPro, a Sun Microsystems, Inc. business.
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ */
+/*
+ * See comments in log2.c.
+ */
+
+#include <math.h>
+#include <stdint.h>
+
+static const float
+ivln2hi = 1.4428710938e+00, /* 0x3fb8b000 */
+ivln2lo = -1.7605285393e-04, /* 0xb9389ad4 */
+/* |(log(1+s)-log(1-s))/s - Lg(s)| < 2**-34.24 (~[-4.95e-11, 4.97e-11]). */
+Lg1 = 0xaaaaaa.0p-24, /* 0.66666662693 */
+Lg2 = 0xccce13.0p-25, /* 0.40000972152 */
+Lg3 = 0x91e9ee.0p-25, /* 0.28498786688 */
+Lg4 = 0xf89e26.0p-26; /* 0.24279078841 */
+
+float log2f(float x)
+{
+ union {float f; uint32_t i;} u = {x};
+ float_t hfsq,f,s,z,R,w,t1,t2,hi,lo;
+ uint32_t ix;
+ int k;
+
+ ix = u.i;
+ k = 0;
+ if (ix < 0x00800000 || ix>>31) { /* x < 2**-126 */
+ if (ix<<1 == 0)
+ return -1/(x*x); /* log(+-0)=-inf */
+ if (ix>>31)
+ return (x-x)/0.0f; /* log(-#) = NaN */
+ /* subnormal number, scale up x */
+ k -= 25;
+ x *= 0x1p25f;
+ u.f = x;
+ ix = u.i;
+ } else if (ix >= 0x7f800000) {
+ return x;
+ } else if (ix == 0x3f800000)
+ return 0;
+
+ /* reduce x into [sqrt(2)/2, sqrt(2)] */
+ ix += 0x3f800000 - 0x3f3504f3;
+ k += (int)(ix>>23) - 0x7f;
+ ix = (ix&0x007fffff) + 0x3f3504f3;
+ u.i = ix;
+ x = u.f;
+
+ f = x - 1.0f;
+ s = f/(2.0f + f);
+ z = s*s;
+ w = z*z;
+ t1= w*(Lg2+w*Lg4);
+ t2= z*(Lg1+w*Lg3);
+ R = t2 + t1;
+ hfsq = 0.5f*f*f;
+
+ hi = f - hfsq;
+ u.f = hi;
+ u.i &= 0xfffff000;
+ hi = u.f;
+ lo = f - hi - hfsq + s*(hfsq+R);
+ return (lo+hi)*ivln2lo + lo*ivln2hi + hi*ivln2hi + k;
+}
diff --git a/lib/libc/src/musl-math/log2l.c b/lib/libc/src/musl-math/log2l.c
new file mode 100644
index 0000000..722b451
--- /dev/null
+++ b/lib/libc/src/musl-math/log2l.c
@@ -0,0 +1,182 @@
+/* origin: OpenBSD /usr/src/lib/libm/src/ld80/e_log2l.c */
+/*
+ * Copyright (c) 2008 Stephen L. Moshier <steve@moshier.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+/*
+ * Base 2 logarithm, long double precision
+ *
+ *
+ * SYNOPSIS:
+ *
+ * long double x, y, log2l();
+ *
+ * y = log2l( x );
+ *
+ *
+ * DESCRIPTION:
+ *
+ * Returns the base 2 logarithm of x.
+ *
+ * The argument is separated into its exponent and fractional
+ * parts. If the exponent is between -1 and +1, the (natural)
+ * logarithm of the fraction is approximated by
+ *
+ * log(1+x) = x - 0.5 x**2 + x**3 P(x)/Q(x).
+ *
+ * Otherwise, setting z = 2(x-1)/x+1),
+ *
+ * log(x) = z + z**3 P(z)/Q(z).
+ *
+ *
+ * ACCURACY:
+ *
+ * Relative error:
+ * arithmetic domain # trials peak rms
+ * IEEE 0.5, 2.0 30000 9.8e-20 2.7e-20
+ * IEEE exp(+-10000) 70000 5.4e-20 2.3e-20
+ *
+ * In the tests over the interval exp(+-10000), the logarithms
+ * of the random arguments were uniformly distributed over
+ * [-10000, +10000].
+ */
+
+#include "libm.h"
+
+#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
+long double log2l(long double x)
+{
+ return log2(x);
+}
+#elif LDBL_MANT_DIG == 64 && LDBL_MAX_EXP == 16384
+/* Coefficients for ln(1+x) = x - x**2/2 + x**3 P(x)/Q(x)
+ * 1/sqrt(2) <= x < sqrt(2)
+ * Theoretical peak relative error = 6.2e-22
+ */
+static const long double P[] = {
+ 4.9962495940332550844739E-1L,
+ 1.0767376367209449010438E1L,
+ 7.7671073698359539859595E1L,
+ 2.5620629828144409632571E2L,
+ 4.2401812743503691187826E2L,
+ 3.4258224542413922935104E2L,
+ 1.0747524399916215149070E2L,
+};
+static const long double Q[] = {
+/* 1.0000000000000000000000E0,*/
+ 2.3479774160285863271658E1L,
+ 1.9444210022760132894510E2L,
+ 7.7952888181207260646090E2L,
+ 1.6911722418503949084863E3L,
+ 2.0307734695595183428202E3L,
+ 1.2695660352705325274404E3L,
+ 3.2242573199748645407652E2L,
+};
+
+/* Coefficients for log(x) = z + z^3 P(z^2)/Q(z^2),
+ * where z = 2(x-1)/(x+1)
+ * 1/sqrt(2) <= x < sqrt(2)
+ * Theoretical peak relative error = 6.16e-22
+ */
+static const long double R[4] = {
+ 1.9757429581415468984296E-3L,
+-7.1990767473014147232598E-1L,
+ 1.0777257190312272158094E1L,
+-3.5717684488096787370998E1L,
+};
+static const long double S[4] = {
+/* 1.00000000000000000000E0L,*/
+-2.6201045551331104417768E1L,
+ 1.9361891836232102174846E2L,
+-4.2861221385716144629696E2L,
+};
+/* log2(e) - 1 */
+#define LOG2EA 4.4269504088896340735992e-1L
+
+#define SQRTH 0.70710678118654752440L
+
+long double log2l(long double x)
+{
+ long double y, z;
+ int e;
+
+ if (isnan(x))
+ return x;
+ if (x == INFINITY)
+ return x;
+ if (x <= 0.0) {
+ if (x == 0.0)
+ return -1/(x*x); /* -inf with divbyzero */
+ return 0/0.0f; /* nan with invalid */
+ }
+
+ /* separate mantissa from exponent */
+ /* Note, frexp is used so that denormal numbers
+ * will be handled properly.
+ */
+ x = frexpl(x, &e);
+
+ /* logarithm using log(x) = z + z**3 P(z)/Q(z),
+ * where z = 2(x-1)/x+1)
+ */
+ if (e > 2 || e < -2) {
+ if (x < SQRTH) { /* 2(2x-1)/(2x+1) */
+ e -= 1;
+ z = x - 0.5;
+ y = 0.5 * z + 0.5;
+ } else { /* 2 (x-1)/(x+1) */
+ z = x - 0.5;
+ z -= 0.5;
+ y = 0.5 * x + 0.5;
+ }
+ x = z / y;
+ z = x*x;
+ y = x * (z * __polevll(z, R, 3) / __p1evll(z, S, 3));
+ goto done;
+ }
+
+ /* logarithm using log(1+x) = x - .5x**2 + x**3 P(x)/Q(x) */
+ if (x < SQRTH) {
+ e -= 1;
+ x = 2.0*x - 1.0;
+ } else {
+ x = x - 1.0;
+ }
+ z = x*x;
+ y = x * (z * __polevll(x, P, 6) / __p1evll(x, Q, 7));
+ y = y - 0.5*z;
+
+done:
+ /* Multiply log of fraction by log2(e)
+ * and base 2 exponent by 1
+ *
+ * ***CAUTION***
+ *
+ * This sequence of operations is critical and it may
+ * be horribly defeated by some compiler optimizers.
+ */
+ z = y * LOG2EA;
+ z += x * LOG2EA;
+ z += y;
+ z += x;
+ z += e;
+ return z;
+}
+#elif LDBL_MANT_DIG == 113 && LDBL_MAX_EXP == 16384
+// TODO: broken implementation to make things compile
+long double log2l(long double x)
+{
+ return log2(x);
+}
+#endif
diff --git a/lib/libc/src/musl-math/logb.c b/lib/libc/src/musl-math/logb.c
new file mode 100644
index 0000000..7f8bdfa
--- /dev/null
+++ b/lib/libc/src/musl-math/logb.c
@@ -0,0 +1,17 @@
+#include <math.h>
+
+/*
+special cases:
+ logb(+-0) = -inf, and raise divbyzero
+ logb(+-inf) = +inf
+ logb(nan) = nan
+*/
+
+double logb(double x)
+{
+ if (!isfinite(x))
+ return x * x;
+ if (x == 0)
+ return -1/(x*x);
+ return ilogb(x);
+}
diff --git a/lib/libc/src/musl-math/logbf.c b/lib/libc/src/musl-math/logbf.c
new file mode 100644
index 0000000..a0a0b5e
--- /dev/null
+++ b/lib/libc/src/musl-math/logbf.c
@@ -0,0 +1,10 @@
+#include <math.h>
+
+float logbf(float x)
+{
+ if (!isfinite(x))
+ return x * x;
+ if (x == 0)
+ return -1/(x*x);
+ return ilogbf(x);
+}
diff --git a/lib/libc/src/musl-math/logbl.c b/lib/libc/src/musl-math/logbl.c
new file mode 100644
index 0000000..962973a
--- /dev/null
+++ b/lib/libc/src/musl-math/logbl.c
@@ -0,0 +1,16 @@
+#include <math.h>
+#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
+long double logbl(long double x)
+{
+ return logb(x);
+}
+#else
+long double logbl(long double x)
+{
+ if (!isfinite(x))
+ return x * x;
+ if (x == 0)
+ return -1/(x*x);
+ return ilogbl(x);
+}
+#endif
diff --git a/lib/libc/src/musl-math/logf.c b/lib/libc/src/musl-math/logf.c
new file mode 100644
index 0000000..52230a1
--- /dev/null
+++ b/lib/libc/src/musl-math/logf.c
@@ -0,0 +1,69 @@
+/* origin: FreeBSD /usr/src/lib/msun/src/e_logf.c */
+/*
+ * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com.
+ */
+/*
+ * ====================================================
+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ *
+ * Developed at SunPro, a Sun Microsystems, Inc. business.
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ */
+
+#include <math.h>
+#include <stdint.h>
+
+static const float
+ln2_hi = 6.9313812256e-01, /* 0x3f317180 */
+ln2_lo = 9.0580006145e-06, /* 0x3717f7d1 */
+/* |(log(1+s)-log(1-s))/s - Lg(s)| < 2**-34.24 (~[-4.95e-11, 4.97e-11]). */
+Lg1 = 0xaaaaaa.0p-24, /* 0.66666662693 */
+Lg2 = 0xccce13.0p-25, /* 0.40000972152 */
+Lg3 = 0x91e9ee.0p-25, /* 0.28498786688 */
+Lg4 = 0xf89e26.0p-26; /* 0.24279078841 */
+
+float logf(float x)
+{
+ union {float f; uint32_t i;} u = {x};
+ float_t hfsq,f,s,z,R,w,t1,t2,dk;
+ uint32_t ix;
+ int k;
+
+ ix = u.i;
+ k = 0;
+ if (ix < 0x00800000 || ix>>31) { /* x < 2**-126 */
+ if (ix<<1 == 0)
+ return -1/(x*x); /* log(+-0)=-inf */
+ if (ix>>31)
+ return (x-x)/0.0f; /* log(-#) = NaN */
+ /* subnormal number, scale up x */
+ k -= 25;
+ x *= 0x1p25f;
+ u.f = x;
+ ix = u.i;
+ } else if (ix >= 0x7f800000) {
+ return x;
+ } else if (ix == 0x3f800000)
+ return 0;
+
+ /* reduce x into [sqrt(2)/2, sqrt(2)] */
+ ix += 0x3f800000 - 0x3f3504f3;
+ k += (int)(ix>>23) - 0x7f;
+ ix = (ix&0x007fffff) + 0x3f3504f3;
+ u.i = ix;
+ x = u.f;
+
+ f = x - 1.0f;
+ s = f/(2.0f + f);
+ z = s*s;
+ w = z*z;
+ t1= w*(Lg2+w*Lg4);
+ t2= z*(Lg1+w*Lg3);
+ R = t2 + t1;
+ hfsq = 0.5f*f*f;
+ dk = k;
+ return s*(hfsq+R) + dk*ln2_lo - hfsq + f + dk*ln2_hi;
+}
diff --git a/lib/libc/src/musl-math/logl.c b/lib/libc/src/musl-math/logl.c
new file mode 100644
index 0000000..5d53659
--- /dev/null
+++ b/lib/libc/src/musl-math/logl.c
@@ -0,0 +1,175 @@
+/* origin: OpenBSD /usr/src/lib/libm/src/ld80/e_logl.c */
+/*
+ * Copyright (c) 2008 Stephen L. Moshier <steve@moshier.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+/*
+ * Natural logarithm, long double precision
+ *
+ *
+ * SYNOPSIS:
+ *
+ * long double x, y, logl();
+ *
+ * y = logl( x );
+ *
+ *
+ * DESCRIPTION:
+ *
+ * Returns the base e (2.718...) logarithm of x.
+ *
+ * The argument is separated into its exponent and fractional
+ * parts. If the exponent is between -1 and +1, the logarithm
+ * of the fraction is approximated by
+ *
+ * log(1+x) = x - 0.5 x**2 + x**3 P(x)/Q(x).
+ *
+ * Otherwise, setting z = 2(x-1)/(x+1),
+ *
+ * log(x) = log(1+z/2) - log(1-z/2) = z + z**3 P(z)/Q(z).
+ *
+ *
+ * ACCURACY:
+ *
+ * Relative error:
+ * arithmetic domain # trials peak rms
+ * IEEE 0.5, 2.0 150000 8.71e-20 2.75e-20
+ * IEEE exp(+-10000) 100000 5.39e-20 2.34e-20
+ *
+ * In the tests over the interval exp(+-10000), the logarithms
+ * of the random arguments were uniformly distributed over
+ * [-10000, +10000].
+ */
+
+#include "libm.h"
+
+#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
+long double logl(long double x)
+{
+ return log(x);
+}
+#elif LDBL_MANT_DIG == 64 && LDBL_MAX_EXP == 16384
+/* Coefficients for log(1+x) = x - x**2/2 + x**3 P(x)/Q(x)
+ * 1/sqrt(2) <= x < sqrt(2)
+ * Theoretical peak relative error = 2.32e-20
+ */
+static const long double P[] = {
+ 4.5270000862445199635215E-5L,
+ 4.9854102823193375972212E-1L,
+ 6.5787325942061044846969E0L,
+ 2.9911919328553073277375E1L,
+ 6.0949667980987787057556E1L,
+ 5.7112963590585538103336E1L,
+ 2.0039553499201281259648E1L,
+};
+static const long double Q[] = {
+/* 1.0000000000000000000000E0,*/
+ 1.5062909083469192043167E1L,
+ 8.3047565967967209469434E1L,
+ 2.2176239823732856465394E2L,
+ 3.0909872225312059774938E2L,
+ 2.1642788614495947685003E2L,
+ 6.0118660497603843919306E1L,
+};
+
+/* Coefficients for log(x) = z + z^3 P(z^2)/Q(z^2),
+ * where z = 2(x-1)/(x+1)
+ * 1/sqrt(2) <= x < sqrt(2)
+ * Theoretical peak relative error = 6.16e-22
+ */
+static const long double R[4] = {
+ 1.9757429581415468984296E-3L,
+-7.1990767473014147232598E-1L,
+ 1.0777257190312272158094E1L,
+-3.5717684488096787370998E1L,
+};
+static const long double S[4] = {
+/* 1.00000000000000000000E0L,*/
+-2.6201045551331104417768E1L,
+ 1.9361891836232102174846E2L,
+-4.2861221385716144629696E2L,
+};
+static const long double C1 = 6.9314575195312500000000E-1L;
+static const long double C2 = 1.4286068203094172321215E-6L;
+
+#define SQRTH 0.70710678118654752440L
+
+long double logl(long double x)
+{
+ long double y, z;
+ int e;
+
+ if (isnan(x))
+ return x;
+ if (x == INFINITY)
+ return x;
+ if (x <= 0.0) {
+ if (x == 0.0)
+ return -1/(x*x); /* -inf with divbyzero */
+ return 0/0.0f; /* nan with invalid */
+ }
+
+ /* separate mantissa from exponent */
+ /* Note, frexp is used so that denormal numbers
+ * will be handled properly.
+ */
+ x = frexpl(x, &e);
+
+ /* logarithm using log(x) = z + z**3 P(z)/Q(z),
+ * where z = 2(x-1)/(x+1)
+ */
+ if (e > 2 || e < -2) {
+ if (x < SQRTH) { /* 2(2x-1)/(2x+1) */
+ e -= 1;
+ z = x - 0.5;
+ y = 0.5 * z + 0.5;
+ } else { /* 2 (x-1)/(x+1) */
+ z = x - 0.5;
+ z -= 0.5;
+ y = 0.5 * x + 0.5;
+ }
+ x = z / y;
+ z = x*x;
+ z = x * (z * __polevll(z, R, 3) / __p1evll(z, S, 3));
+ z = z + e * C2;
+ z = z + x;
+ z = z + e * C1;
+ return z;
+ }
+
+ /* logarithm using log(1+x) = x - .5x**2 + x**3 P(x)/Q(x) */
+ if (x < SQRTH) {
+ e -= 1;
+ x = 2.0*x - 1.0;
+ } else {
+ x = x - 1.0;
+ }
+ z = x*x;
+ y = x * (z * __polevll(x, P, 6) / __p1evll(x, Q, 6));
+ y = y + e * C2;
+ z = y - 0.5*z;
+ /* Note, the sum of above terms does not exceed x/4,
+ * so it contributes at most about 1/4 lsb to the error.
+ */
+ z = z + x;
+ z = z + e * C1; /* This sum has an error of 1/2 lsb. */
+ return z;
+}
+#elif LDBL_MANT_DIG == 113 && LDBL_MAX_EXP == 16384
+// TODO: broken implementation to make things compile
+long double logl(long double x)
+{
+ return log(x);
+}
+#endif
diff --git a/lib/libc/src/musl-math/lrint.c b/lib/libc/src/musl-math/lrint.c
new file mode 100644
index 0000000..bdca8b7
--- /dev/null
+++ b/lib/libc/src/musl-math/lrint.c
@@ -0,0 +1,46 @@
+#include <limits.h>
+#include <fenv.h>
+#include "libm.h"
+
+/*
+If the result cannot be represented (overflow, nan), then
+lrint raises the invalid exception.
+
+Otherwise if the input was not an integer then the inexact
+exception is raised.
+
+C99 is a bit vague about whether inexact exception is
+allowed to be raised when invalid is raised.
+(F.9 explicitly allows spurious inexact exceptions, F.9.6.5
+does not make it clear if that rule applies to lrint, but
+IEEE 754r 7.8 seems to forbid spurious inexact exception in
+the ineger conversion functions)
+
+So we try to make sure that no spurious inexact exception is
+raised in case of an overflow.
+
+If the bit size of long > precision of double, then there
+cannot be inexact rounding in case the result overflows,
+otherwise LONG_MAX and LONG_MIN can be represented exactly
+as a double.
+*/
+
+#if LONG_MAX < 1U<<53 && defined(FE_INEXACT)
+long lrint(double x)
+{
+ #pragma STDC FENV_ACCESS ON
+ int e;
+
+ e = fetestexcept(FE_INEXACT);
+ x = rint(x);
+ if (!e && (x > LONG_MAX || x < LONG_MIN))
+ feclearexcept(FE_INEXACT);
+ /* conversion */
+ return x;
+}
+#else
+long lrint(double x)
+{
+ return rint(x);
+}
+#endif
diff --git a/lib/libc/src/musl-math/lrintf.c b/lib/libc/src/musl-math/lrintf.c
new file mode 100644
index 0000000..ca0b6a4
--- /dev/null
+++ b/lib/libc/src/musl-math/lrintf.c
@@ -0,0 +1,8 @@
+#include <math.h>
+
+/* uses LONG_MAX > 2^24, see comments in lrint.c */
+
+long lrintf(float x)
+{
+ return rintf(x);
+}
diff --git a/lib/libc/src/musl-math/lrintl.c b/lib/libc/src/musl-math/lrintl.c
new file mode 100644
index 0000000..b2a8106
--- /dev/null
+++ b/lib/libc/src/musl-math/lrintl.c
@@ -0,0 +1,36 @@
+#include <limits.h>
+#include <fenv.h>
+#include "libm.h"
+
+
+#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
+long lrintl(long double x)
+{
+ return lrint(x);
+}
+#elif defined(FE_INEXACT)
+/*
+see comments in lrint.c
+
+Note that if LONG_MAX == 0x7fffffffffffffff && LDBL_MANT_DIG == 64
+then x == 2**63 - 0.5 is the only input that overflows and
+raises inexact (with tonearest or upward rounding mode)
+*/
+long lrintl(long double x)
+{
+ #pragma STDC FENV_ACCESS ON
+ int e;
+
+ e = fetestexcept(FE_INEXACT);
+ x = rintl(x);
+ if (!e && (x > LONG_MAX || x < LONG_MIN))
+ feclearexcept(FE_INEXACT);
+ /* conversion */
+ return x;
+}
+#else
+long lrintl(long double x)
+{
+ return rintl(x);
+}
+#endif
diff --git a/lib/libc/src/musl-math/lround.c b/lib/libc/src/musl-math/lround.c
new file mode 100644
index 0000000..b8b7954
--- /dev/null
+++ b/lib/libc/src/musl-math/lround.c
@@ -0,0 +1,6 @@
+#include <math.h>
+
+long lround(double x)
+{
+ return round(x);
+}
diff --git a/lib/libc/src/musl-math/lroundf.c b/lib/libc/src/musl-math/lroundf.c
new file mode 100644
index 0000000..c4707e7
--- /dev/null
+++ b/lib/libc/src/musl-math/lroundf.c
@@ -0,0 +1,6 @@
+#include <math.h>
+
+long lroundf(float x)
+{
+ return roundf(x);
+}
diff --git a/lib/libc/src/musl-math/lroundl.c b/lib/libc/src/musl-math/lroundl.c
new file mode 100644
index 0000000..094fdf6
--- /dev/null
+++ b/lib/libc/src/musl-math/lroundl.c
@@ -0,0 +1,6 @@
+#include <math.h>
+
+long lroundl(long double x)
+{
+ return roundl(x);
+}
diff --git a/lib/libc/src/musl-math/modf.c b/lib/libc/src/musl-math/modf.c
new file mode 100644
index 0000000..1c8a1db
--- /dev/null
+++ b/lib/libc/src/musl-math/modf.c
@@ -0,0 +1,34 @@
+#include "libm.h"
+
+double modf(double x, double *iptr)
+{
+ union {double f; uint64_t i;} u = {x};
+ uint64_t mask;
+ int e = (int)(u.i>>52 & 0x7ff) - 0x3ff;
+
+ /* no fractional part */
+ if (e >= 52) {
+ *iptr = x;
+ if (e == 0x400 && u.i<<12 != 0) /* nan */
+ return x;
+ u.i &= 1ULL<<63;
+ return u.f;
+ }
+
+ /* no integral part*/
+ if (e < 0) {
+ u.i &= 1ULL<<63;
+ *iptr = u.f;
+ return x;
+ }
+
+ mask = -1ULL>>12>>e;
+ if ((u.i & mask) == 0) {
+ *iptr = x;
+ u.i &= 1ULL<<63;
+ return u.f;
+ }
+ u.i &= ~mask;
+ *iptr = u.f;
+ return x - u.f;
+}
diff --git a/lib/libc/src/musl-math/modff.c b/lib/libc/src/musl-math/modff.c
new file mode 100644
index 0000000..639514e
--- /dev/null
+++ b/lib/libc/src/musl-math/modff.c
@@ -0,0 +1,34 @@
+#include "libm.h"
+
+float modff(float x, float *iptr)
+{
+ union {float f; uint32_t i;} u = {x};
+ uint32_t mask;
+ int e = (int)(u.i>>23 & 0xff) - 0x7f;
+
+ /* no fractional part */
+ if (e >= 23) {
+ *iptr = x;
+ if (e == 0x80 && u.i<<9 != 0) { /* nan */
+ return x;
+ }
+ u.i &= 0x80000000;
+ return u.f;
+ }
+ /* no integral part */
+ if (e < 0) {
+ u.i &= 0x80000000;
+ *iptr = u.f;
+ return x;
+ }
+
+ mask = 0x007fffff>>e;
+ if ((u.i & mask) == 0) {
+ *iptr = x;
+ u.i &= 0x80000000;
+ return u.f;
+ }
+ u.i &= ~mask;
+ *iptr = u.f;
+ return x - u.f;
+}
diff --git a/lib/libc/src/musl-math/modfl.c b/lib/libc/src/musl-math/modfl.c
new file mode 100644
index 0000000..a47b192
--- /dev/null
+++ b/lib/libc/src/musl-math/modfl.c
@@ -0,0 +1,53 @@
+#include "libm.h"
+
+#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
+long double modfl(long double x, long double *iptr)
+{
+ double d;
+ long double r;
+
+ r = modf(x, &d);
+ *iptr = d;
+ return r;
+}
+#elif (LDBL_MANT_DIG == 64 || LDBL_MANT_DIG == 113) && LDBL_MAX_EXP == 16384
+
+static const long double toint = 1/LDBL_EPSILON;
+
+long double modfl(long double x, long double *iptr)
+{
+ union ldshape u = {x};
+ int e = (u.i.se & 0x7fff) - 0x3fff;
+ int s = u.i.se >> 15;
+ long double absx;
+ long double y;
+
+ /* no fractional part */
+ if (e >= LDBL_MANT_DIG-1) {
+ *iptr = x;
+ if (isnan(x))
+ return x;
+ return s ? -0.0 : 0.0;
+ }
+
+ /* no integral part*/
+ if (e < 0) {
+ *iptr = s ? -0.0 : 0.0;
+ return x;
+ }
+
+ /* raises spurious inexact */
+ absx = s ? -x : x;
+ y = absx + toint - toint - absx;
+ if (y == 0) {
+ *iptr = x;
+ return s ? -0.0 : 0.0;
+ }
+ if (y > 0)
+ y -= 1;
+ if (s)
+ y = -y;
+ *iptr = x + y;
+ return -y;
+}
+#endif
diff --git a/lib/libc/src/musl-math/nan.c b/lib/libc/src/musl-math/nan.c
new file mode 100644
index 0000000..9e0826c
--- /dev/null
+++ b/lib/libc/src/musl-math/nan.c
@@ -0,0 +1,6 @@
+#include <math.h>
+
+double nan(const char *s)
+{
+ return NAN;
+}
diff --git a/lib/libc/src/musl-math/nanf.c b/lib/libc/src/musl-math/nanf.c
new file mode 100644
index 0000000..752ce54
--- /dev/null
+++ b/lib/libc/src/musl-math/nanf.c
@@ -0,0 +1,6 @@
+#include <math.h>
+
+float nanf(const char *s)
+{
+ return NAN;
+}
diff --git a/lib/libc/src/musl-math/nanl.c b/lib/libc/src/musl-math/nanl.c
new file mode 100644
index 0000000..969af56
--- /dev/null
+++ b/lib/libc/src/musl-math/nanl.c
@@ -0,0 +1,6 @@
+#include <math.h>
+
+long double nanl(const char *s)
+{
+ return NAN;
+}
diff --git a/lib/libc/src/musl-math/nearbyint.c b/lib/libc/src/musl-math/nearbyint.c
new file mode 100644
index 0000000..f4e8aac
--- /dev/null
+++ b/lib/libc/src/musl-math/nearbyint.c
@@ -0,0 +1,20 @@
+#include <fenv.h>
+#include <math.h>
+
+/* nearbyint is the same as rint, but it must not raise the inexact exception */
+
+double nearbyint(double x)
+{
+#ifdef FE_INEXACT
+ #pragma STDC FENV_ACCESS ON
+ int e;
+
+ e = fetestexcept(FE_INEXACT);
+#endif
+ x = rint(x);
+#ifdef FE_INEXACT
+ if (!e)
+ feclearexcept(FE_INEXACT);
+#endif
+ return x;
+}
diff --git a/lib/libc/src/musl-math/nearbyintf.c b/lib/libc/src/musl-math/nearbyintf.c
new file mode 100644
index 0000000..092e9ff
--- /dev/null
+++ b/lib/libc/src/musl-math/nearbyintf.c
@@ -0,0 +1,18 @@
+#include <fenv.h>
+#include <math.h>
+
+float nearbyintf(float x)
+{
+#ifdef FE_INEXACT
+ #pragma STDC FENV_ACCESS ON
+ int e;
+
+ e = fetestexcept(FE_INEXACT);
+#endif
+ x = rintf(x);
+#ifdef FE_INEXACT
+ if (!e)
+ feclearexcept(FE_INEXACT);
+#endif
+ return x;
+}
diff --git a/lib/libc/src/musl-math/nearbyintl.c b/lib/libc/src/musl-math/nearbyintl.c
new file mode 100644
index 0000000..8285249
--- /dev/null
+++ b/lib/libc/src/musl-math/nearbyintl.c
@@ -0,0 +1,26 @@
+#include <math.h>
+#include <float.h>
+
+#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
+long double nearbyintl(long double x)
+{
+ return nearbyint(x);
+}
+#else
+#include <fenv.h>
+long double nearbyintl(long double x)
+{
+#ifdef FE_INEXACT
+ #pragma STDC FENV_ACCESS ON
+ int e;
+
+ e = fetestexcept(FE_INEXACT);
+#endif
+ x = rintl(x);
+#ifdef FE_INEXACT
+ if (!e)
+ feclearexcept(FE_INEXACT);
+#endif
+ return x;
+}
+#endif
diff --git a/lib/libc/src/musl-math/nextafter.c b/lib/libc/src/musl-math/nextafter.c
new file mode 100644
index 0000000..ab5795a
--- /dev/null
+++ b/lib/libc/src/musl-math/nextafter.c
@@ -0,0 +1,31 @@
+#include "libm.h"
+
+double nextafter(double x, double y)
+{
+ union {double f; uint64_t i;} ux={x}, uy={y};
+ uint64_t ax, ay;
+ int e;
+
+ if (isnan(x) || isnan(y))
+ return x + y;
+ if (ux.i == uy.i)
+ return y;
+ ax = ux.i & -1ULL/2;
+ ay = uy.i & -1ULL/2;
+ if (ax == 0) {
+ if (ay == 0)
+ return y;
+ ux.i = (uy.i & 1ULL<<63) | 1;
+ } else if (ax > ay || ((ux.i ^ uy.i) & 1ULL<<63))
+ ux.i--;
+ else
+ ux.i++;
+ e = ux.i >> 52 & 0x7ff;
+ /* raise overflow if ux.f is infinite and x is finite */
+ if (e == 0x7ff)
+ FORCE_EVAL(x+x);
+ /* raise underflow if ux.f is subnormal or zero */
+ if (e == 0)
+ FORCE_EVAL(x*x + ux.f*ux.f);
+ return ux.f;
+}
diff --git a/lib/libc/src/musl-math/nextafterf.c b/lib/libc/src/musl-math/nextafterf.c
new file mode 100644
index 0000000..75a09f7
--- /dev/null
+++ b/lib/libc/src/musl-math/nextafterf.c
@@ -0,0 +1,30 @@
+#include "libm.h"
+
+float nextafterf(float x, float y)
+{
+ union {float f; uint32_t i;} ux={x}, uy={y};
+ uint32_t ax, ay, e;
+
+ if (isnan(x) || isnan(y))
+ return x + y;
+ if (ux.i == uy.i)
+ return y;
+ ax = ux.i & 0x7fffffff;
+ ay = uy.i & 0x7fffffff;
+ if (ax == 0) {
+ if (ay == 0)
+ return y;
+ ux.i = (uy.i & 0x80000000) | 1;
+ } else if (ax > ay || ((ux.i ^ uy.i) & 0x80000000))
+ ux.i--;
+ else
+ ux.i++;
+ e = ux.i & 0x7f800000;
+ /* raise overflow if ux.f is infinite and x is finite */
+ if (e == 0x7f800000)
+ FORCE_EVAL(x+x);
+ /* raise underflow if ux.f is subnormal or zero */
+ if (e == 0)
+ FORCE_EVAL(x*x + ux.f*ux.f);
+ return ux.f;
+}
diff --git a/lib/libc/src/musl-math/nextafterl.c b/lib/libc/src/musl-math/nextafterl.c
new file mode 100644
index 0000000..37e858f
--- /dev/null
+++ b/lib/libc/src/musl-math/nextafterl.c
@@ -0,0 +1,75 @@
+#include "libm.h"
+
+#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
+long double nextafterl(long double x, long double y)
+{
+ return nextafter(x, y);
+}
+#elif LDBL_MANT_DIG == 64 && LDBL_MAX_EXP == 16384
+long double nextafterl(long double x, long double y)
+{
+ union ldshape ux, uy;
+
+ if (isnan(x) || isnan(y))
+ return x + y;
+ if (x == y)
+ return y;
+ ux.f = x;
+ if (x == 0) {
+ uy.f = y;
+ ux.i.m = 1;
+ ux.i.se = uy.i.se & 0x8000;
+ } else if ((x < y) == !(ux.i.se & 0x8000)) {
+ ux.i.m++;
+ if (ux.i.m << 1 == 0) {
+ ux.i.m = 1ULL << 63;
+ ux.i.se++;
+ }
+ } else {
+ if (ux.i.m << 1 == 0) {
+ ux.i.se--;
+ if (ux.i.se)
+ ux.i.m = 0;
+ }
+ ux.i.m--;
+ }
+ /* raise overflow if ux is infinite and x is finite */
+ if ((ux.i.se & 0x7fff) == 0x7fff)
+ return x + x;
+ /* raise underflow if ux is subnormal or zero */
+ if ((ux.i.se & 0x7fff) == 0)
+ FORCE_EVAL(x*x + ux.f*ux.f);
+ return ux.f;
+}
+#elif LDBL_MANT_DIG == 113 && LDBL_MAX_EXP == 16384
+long double nextafterl(long double x, long double y)
+{
+ union ldshape ux, uy;
+
+ if (isnan(x) || isnan(y))
+ return x + y;
+ if (x == y)
+ return y;
+ ux.f = x;
+ if (x == 0) {
+ uy.f = y;
+ ux.i.lo = 1;
+ ux.i.se = uy.i.se & 0x8000;
+ } else if ((x < y) == !(ux.i.se & 0x8000)) {
+ ux.i2.lo++;
+ if (ux.i2.lo == 0)
+ ux.i2.hi++;
+ } else {
+ if (ux.i2.lo == 0)
+ ux.i2.hi--;
+ ux.i2.lo--;
+ }
+ /* raise overflow if ux is infinite and x is finite */
+ if ((ux.i.se & 0x7fff) == 0x7fff)
+ return x + x;
+ /* raise underflow if ux is subnormal or zero */
+ if ((ux.i.se & 0x7fff) == 0)
+ FORCE_EVAL(x*x + ux.f*ux.f);
+ return ux.f;
+}
+#endif
diff --git a/lib/libc/src/musl-math/nexttoward.c b/lib/libc/src/musl-math/nexttoward.c
new file mode 100644
index 0000000..827ee5c
--- /dev/null
+++ b/lib/libc/src/musl-math/nexttoward.c
@@ -0,0 +1,42 @@
+#include "libm.h"
+
+#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
+double nexttoward(double x, long double y)
+{
+ return nextafter(x, y);
+}
+#else
+double nexttoward(double x, long double y)
+{
+ union {double f; uint64_t i;} ux = {x};
+ int e;
+
+ if (isnan(x) || isnan(y))
+ return x + y;
+ if (x == y)
+ return y;
+ if (x == 0) {
+ ux.i = 1;
+ if (signbit(y))
+ ux.i |= 1ULL<<63;
+ } else if (x < y) {
+ if (signbit(x))
+ ux.i--;
+ else
+ ux.i++;
+ } else {
+ if (signbit(x))
+ ux.i++;
+ else
+ ux.i--;
+ }
+ e = ux.i>>52 & 0x7ff;
+ /* raise overflow if ux.f is infinite and x is finite */
+ if (e == 0x7ff)
+ FORCE_EVAL(x+x);
+ /* raise underflow if ux.f is subnormal or zero */
+ if (e == 0)
+ FORCE_EVAL(x*x + ux.f*ux.f);
+ return ux.f;
+}
+#endif
diff --git a/lib/libc/src/musl-math/nexttowardf.c b/lib/libc/src/musl-math/nexttowardf.c
new file mode 100644
index 0000000..bbf172f
--- /dev/null
+++ b/lib/libc/src/musl-math/nexttowardf.c
@@ -0,0 +1,35 @@
+#include "libm.h"
+
+float nexttowardf(float x, long double y)
+{
+ union {float f; uint32_t i;} ux = {x};
+ uint32_t e;
+
+ if (isnan(x) || isnan(y))
+ return x + y;
+ if (x == y)
+ return y;
+ if (x == 0) {
+ ux.i = 1;
+ if (signbit(y))
+ ux.i |= 0x80000000;
+ } else if (x < y) {
+ if (signbit(x))
+ ux.i--;
+ else
+ ux.i++;
+ } else {
+ if (signbit(x))
+ ux.i++;
+ else
+ ux.i--;
+ }
+ e = ux.i & 0x7f800000;
+ /* raise overflow if ux.f is infinite and x is finite */
+ if (e == 0x7f800000)
+ FORCE_EVAL(x+x);
+ /* raise underflow if ux.f is subnormal or zero */
+ if (e == 0)
+ FORCE_EVAL(x*x + ux.f*ux.f);
+ return ux.f;
+}
diff --git a/lib/libc/src/musl-math/nexttowardl.c b/lib/libc/src/musl-math/nexttowardl.c
new file mode 100644
index 0000000..67a6340
--- /dev/null
+++ b/lib/libc/src/musl-math/nexttowardl.c
@@ -0,0 +1,6 @@
+#include <math.h>
+
+long double nexttowardl(long double x, long double y)
+{
+ return nextafterl(x, y);
+}
diff --git a/lib/libc/src/musl-math/pow.c b/lib/libc/src/musl-math/pow.c
new file mode 100644
index 0000000..3ddc1b6
--- /dev/null
+++ b/lib/libc/src/musl-math/pow.c
@@ -0,0 +1,328 @@
+/* origin: FreeBSD /usr/src/lib/msun/src/e_pow.c */
+/*
+ * ====================================================
+ * Copyright (C) 2004 by Sun Microsystems, Inc. All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ */
+/* pow(x,y) return x**y
+ *
+ * n
+ * Method: Let x = 2 * (1+f)
+ * 1. Compute and return log2(x) in two pieces:
+ * log2(x) = w1 + w2,
+ * where w1 has 53-24 = 29 bit trailing zeros.
+ * 2. Perform y*log2(x) = n+y' by simulating muti-precision
+ * arithmetic, where |y'|<=0.5.
+ * 3. Return x**y = 2**n*exp(y'*log2)
+ *
+ * Special cases:
+ * 1. (anything) ** 0 is 1
+ * 2. 1 ** (anything) is 1
+ * 3. (anything except 1) ** NAN is NAN
+ * 4. NAN ** (anything except 0) is NAN
+ * 5. +-(|x| > 1) ** +INF is +INF
+ * 6. +-(|x| > 1) ** -INF is +0
+ * 7. +-(|x| < 1) ** +INF is +0
+ * 8. +-(|x| < 1) ** -INF is +INF
+ * 9. -1 ** +-INF is 1
+ * 10. +0 ** (+anything except 0, NAN) is +0
+ * 11. -0 ** (+anything except 0, NAN, odd integer) is +0
+ * 12. +0 ** (-anything except 0, NAN) is +INF, raise divbyzero
+ * 13. -0 ** (-anything except 0, NAN, odd integer) is +INF, raise divbyzero
+ * 14. -0 ** (+odd integer) is -0
+ * 15. -0 ** (-odd integer) is -INF, raise divbyzero
+ * 16. +INF ** (+anything except 0,NAN) is +INF
+ * 17. +INF ** (-anything except 0,NAN) is +0
+ * 18. -INF ** (+odd integer) is -INF
+ * 19. -INF ** (anything) = -0 ** (-anything), (anything except odd integer)
+ * 20. (anything) ** 1 is (anything)
+ * 21. (anything) ** -1 is 1/(anything)
+ * 22. (-anything) ** (integer) is (-1)**(integer)*(+anything**integer)
+ * 23. (-anything except 0 and inf) ** (non-integer) is NAN
+ *
+ * Accuracy:
+ * pow(x,y) returns x**y nearly rounded. In particular
+ * pow(integer,integer)
+ * always returns the correct integer provided it is
+ * representable.
+ *
+ * Constants :
+ * The hexadecimal values are the intended ones for the following
+ * constants. The decimal values may be used, provided that the
+ * compiler will convert from decimal to binary accurately enough
+ * to produce the hexadecimal values shown.
+ */
+
+#include "libm.h"
+
+static const double
+bp[] = {1.0, 1.5,},
+dp_h[] = { 0.0, 5.84962487220764160156e-01,}, /* 0x3FE2B803, 0x40000000 */
+dp_l[] = { 0.0, 1.35003920212974897128e-08,}, /* 0x3E4CFDEB, 0x43CFD006 */
+two53 = 9007199254740992.0, /* 0x43400000, 0x00000000 */
+huge = 1.0e300,
+tiny = 1.0e-300,
+/* poly coefs for (3/2)*(log(x)-2s-2/3*s**3 */
+L1 = 5.99999999999994648725e-01, /* 0x3FE33333, 0x33333303 */
+L2 = 4.28571428578550184252e-01, /* 0x3FDB6DB6, 0xDB6FABFF */
+L3 = 3.33333329818377432918e-01, /* 0x3FD55555, 0x518F264D */
+L4 = 2.72728123808534006489e-01, /* 0x3FD17460, 0xA91D4101 */
+L5 = 2.30660745775561754067e-01, /* 0x3FCD864A, 0x93C9DB65 */
+L6 = 2.06975017800338417784e-01, /* 0x3FCA7E28, 0x4A454EEF */
+P1 = 1.66666666666666019037e-01, /* 0x3FC55555, 0x5555553E */
+P2 = -2.77777777770155933842e-03, /* 0xBF66C16C, 0x16BEBD93 */
+P3 = 6.61375632143793436117e-05, /* 0x3F11566A, 0xAF25DE2C */
+P4 = -1.65339022054652515390e-06, /* 0xBEBBBD41, 0xC5D26BF1 */
+P5 = 4.13813679705723846039e-08, /* 0x3E663769, 0x72BEA4D0 */
+lg2 = 6.93147180559945286227e-01, /* 0x3FE62E42, 0xFEFA39EF */
+lg2_h = 6.93147182464599609375e-01, /* 0x3FE62E43, 0x00000000 */
+lg2_l = -1.90465429995776804525e-09, /* 0xBE205C61, 0x0CA86C39 */
+ovt = 8.0085662595372944372e-017, /* -(1024-log2(ovfl+.5ulp)) */
+cp = 9.61796693925975554329e-01, /* 0x3FEEC709, 0xDC3A03FD =2/(3ln2) */
+cp_h = 9.61796700954437255859e-01, /* 0x3FEEC709, 0xE0000000 =(float)cp */
+cp_l = -7.02846165095275826516e-09, /* 0xBE3E2FE0, 0x145B01F5 =tail of cp_h*/
+ivln2 = 1.44269504088896338700e+00, /* 0x3FF71547, 0x652B82FE =1/ln2 */
+ivln2_h = 1.44269502162933349609e+00, /* 0x3FF71547, 0x60000000 =24b 1/ln2*/
+ivln2_l = 1.92596299112661746887e-08; /* 0x3E54AE0B, 0xF85DDF44 =1/ln2 tail*/
+
+double pow(double x, double y)
+{
+ double z,ax,z_h,z_l,p_h,p_l;
+ double y1,t1,t2,r,s,t,u,v,w;
+ int32_t i,j,k,yisint,n;
+ int32_t hx,hy,ix,iy;
+ uint32_t lx,ly;
+
+ EXTRACT_WORDS(hx, lx, x);
+ EXTRACT_WORDS(hy, ly, y);
+ ix = hx & 0x7fffffff;
+ iy = hy & 0x7fffffff;
+
+ /* x**0 = 1, even if x is NaN */
+ if ((iy|ly) == 0)
+ return 1.0;
+ /* 1**y = 1, even if y is NaN */
+ if (hx == 0x3ff00000 && lx == 0)
+ return 1.0;
+ /* NaN if either arg is NaN */
+ if (ix > 0x7ff00000 || (ix == 0x7ff00000 && lx != 0) ||
+ iy > 0x7ff00000 || (iy == 0x7ff00000 && ly != 0))
+ return x + y;
+
+ /* determine if y is an odd int when x < 0
+ * yisint = 0 ... y is not an integer
+ * yisint = 1 ... y is an odd int
+ * yisint = 2 ... y is an even int
+ */
+ yisint = 0;
+ if (hx < 0) {
+ if (iy >= 0x43400000)
+ yisint = 2; /* even integer y */
+ else if (iy >= 0x3ff00000) {
+ k = (iy>>20) - 0x3ff; /* exponent */
+ if (k > 20) {
+ uint32_t j = ly>>(52-k);
+ if ((j<<(52-k)) == ly)
+ yisint = 2 - (j&1);
+ } else if (ly == 0) {
+ uint32_t j = iy>>(20-k);
+ if ((j<<(20-k)) == iy)
+ yisint = 2 - (j&1);
+ }
+ }
+ }
+
+ /* special value of y */
+ if (ly == 0) {
+ if (iy == 0x7ff00000) { /* y is +-inf */
+ if (((ix-0x3ff00000)|lx) == 0) /* (-1)**+-inf is 1 */
+ return 1.0;
+ else if (ix >= 0x3ff00000) /* (|x|>1)**+-inf = inf,0 */
+ return hy >= 0 ? y : 0.0;
+ else /* (|x|<1)**+-inf = 0,inf */
+ return hy >= 0 ? 0.0 : -y;
+ }
+ if (iy == 0x3ff00000) { /* y is +-1 */
+ if (hy >= 0)
+ return x;
+ y = 1/x;
+#if FLT_EVAL_METHOD!=0
+ {
+ union {double f; uint64_t i;} u = {y};
+ uint64_t i = u.i & -1ULL/2;
+ if (i>>52 == 0 && (i&(i-1)))
+ FORCE_EVAL((float)y);
+ }
+#endif
+ return y;
+ }
+ if (hy == 0x40000000) /* y is 2 */
+ return x*x;
+ if (hy == 0x3fe00000) { /* y is 0.5 */
+ if (hx >= 0) /* x >= +0 */
+ return sqrt(x);
+ }
+ }
+
+ ax = fabs(x);
+ /* special value of x */
+ if (lx == 0) {
+ if (ix == 0x7ff00000 || ix == 0 || ix == 0x3ff00000) { /* x is +-0,+-inf,+-1 */
+ z = ax;
+ if (hy < 0) /* z = (1/|x|) */
+ z = 1.0/z;
+ if (hx < 0) {
+ if (((ix-0x3ff00000)|yisint) == 0) {
+ z = (z-z)/(z-z); /* (-1)**non-int is NaN */
+ } else if (yisint == 1)
+ z = -z; /* (x<0)**odd = -(|x|**odd) */
+ }
+ return z;
+ }
+ }
+
+ s = 1.0; /* sign of result */
+ if (hx < 0) {
+ if (yisint == 0) /* (x<0)**(non-int) is NaN */
+ return (x-x)/(x-x);
+ if (yisint == 1) /* (x<0)**(odd int) */
+ s = -1.0;
+ }
+
+ /* |y| is huge */
+ if (iy > 0x41e00000) { /* if |y| > 2**31 */
+ if (iy > 0x43f00000) { /* if |y| > 2**64, must o/uflow */
+ if (ix <= 0x3fefffff)
+ return hy < 0 ? huge*huge : tiny*tiny;
+ if (ix >= 0x3ff00000)
+ return hy > 0 ? huge*huge : tiny*tiny;
+ }
+ /* over/underflow if x is not close to one */
+ if (ix < 0x3fefffff)
+ return hy < 0 ? s*huge*huge : s*tiny*tiny;
+ if (ix > 0x3ff00000)
+ return hy > 0 ? s*huge*huge : s*tiny*tiny;
+ /* now |1-x| is tiny <= 2**-20, suffice to compute
+ log(x) by x-x^2/2+x^3/3-x^4/4 */
+ t = ax - 1.0; /* t has 20 trailing zeros */
+ w = (t*t)*(0.5 - t*(0.3333333333333333333333-t*0.25));
+ u = ivln2_h*t; /* ivln2_h has 21 sig. bits */
+ v = t*ivln2_l - w*ivln2;
+ t1 = u + v;
+ SET_LOW_WORD(t1, 0);
+ t2 = v - (t1-u);
+ } else {
+ double ss,s2,s_h,s_l,t_h,t_l;
+ n = 0;
+ /* take care subnormal number */
+ if (ix < 0x00100000) {
+ ax *= two53;
+ n -= 53;
+ GET_HIGH_WORD(ix,ax);
+ }
+ n += ((ix)>>20) - 0x3ff;
+ j = ix & 0x000fffff;
+ /* determine interval */
+ ix = j | 0x3ff00000; /* normalize ix */
+ if (j <= 0x3988E) /* |x|<sqrt(3/2) */
+ k = 0;
+ else if (j < 0xBB67A) /* |x|<sqrt(3) */
+ k = 1;
+ else {
+ k = 0;
+ n += 1;
+ ix -= 0x00100000;
+ }
+ SET_HIGH_WORD(ax, ix);
+
+ /* compute ss = s_h+s_l = (x-1)/(x+1) or (x-1.5)/(x+1.5) */
+ u = ax - bp[k]; /* bp[0]=1.0, bp[1]=1.5 */
+ v = 1.0/(ax+bp[k]);
+ ss = u*v;
+ s_h = ss;
+ SET_LOW_WORD(s_h, 0);
+ /* t_h=ax+bp[k] High */
+ t_h = 0.0;
+ SET_HIGH_WORD(t_h, ((ix>>1)|0x20000000) + 0x00080000 + (k<<18));
+ t_l = ax - (t_h-bp[k]);
+ s_l = v*((u-s_h*t_h)-s_h*t_l);
+ /* compute log(ax) */
+ s2 = ss*ss;
+ r = s2*s2*(L1+s2*(L2+s2*(L3+s2*(L4+s2*(L5+s2*L6)))));
+ r += s_l*(s_h+ss);
+ s2 = s_h*s_h;
+ t_h = 3.0 + s2 + r;
+ SET_LOW_WORD(t_h, 0);
+ t_l = r - ((t_h-3.0)-s2);
+ /* u+v = ss*(1+...) */
+ u = s_h*t_h;
+ v = s_l*t_h + t_l*ss;
+ /* 2/(3log2)*(ss+...) */
+ p_h = u + v;
+ SET_LOW_WORD(p_h, 0);
+ p_l = v - (p_h-u);
+ z_h = cp_h*p_h; /* cp_h+cp_l = 2/(3*log2) */
+ z_l = cp_l*p_h+p_l*cp + dp_l[k];
+ /* log2(ax) = (ss+..)*2/(3*log2) = n + dp_h + z_h + z_l */
+ t = (double)n;
+ t1 = ((z_h + z_l) + dp_h[k]) + t;
+ SET_LOW_WORD(t1, 0);
+ t2 = z_l - (((t1 - t) - dp_h[k]) - z_h);
+ }
+
+ /* split up y into y1+y2 and compute (y1+y2)*(t1+t2) */
+ y1 = y;
+ SET_LOW_WORD(y1, 0);
+ p_l = (y-y1)*t1 + y*t2;
+ p_h = y1*t1;
+ z = p_l + p_h;
+ EXTRACT_WORDS(j, i, z);
+ if (j >= 0x40900000) { /* z >= 1024 */
+ if (((j-0x40900000)|i) != 0) /* if z > 1024 */
+ return s*huge*huge; /* overflow */
+ if (p_l + ovt > z - p_h)
+ return s*huge*huge; /* overflow */
+ } else if ((j&0x7fffffff) >= 0x4090cc00) { /* z <= -1075 */ // FIXME: instead of abs(j) use unsigned j
+ if (((j-0xc090cc00)|i) != 0) /* z < -1075 */
+ return s*tiny*tiny; /* underflow */
+ if (p_l <= z - p_h)
+ return s*tiny*tiny; /* underflow */
+ }
+ /*
+ * compute 2**(p_h+p_l)
+ */
+ i = j & 0x7fffffff;
+ k = (i>>20) - 0x3ff;
+ n = 0;
+ if (i > 0x3fe00000) { /* if |z| > 0.5, set n = [z+0.5] */
+ n = j + (0x00100000>>(k+1));
+ k = ((n&0x7fffffff)>>20) - 0x3ff; /* new k for n */
+ t = 0.0;
+ SET_HIGH_WORD(t, n & ~(0x000fffff>>k));
+ n = ((n&0x000fffff)|0x00100000)>>(20-k);
+ if (j < 0)
+ n = -n;
+ p_h -= t;
+ }
+ t = p_l + p_h;
+ SET_LOW_WORD(t, 0);
+ u = t*lg2_h;
+ v = (p_l-(t-p_h))*lg2 + t*lg2_l;
+ z = u + v;
+ w = v - (z-u);
+ t = z*z;
+ t1 = z - t*(P1+t*(P2+t*(P3+t*(P4+t*P5))));
+ r = (z*t1)/(t1-2.0) - (w + z*w);
+ z = 1.0 - (r-z);
+ GET_HIGH_WORD(j, z);
+ j += n<<20;
+ if ((j>>20) <= 0) /* subnormal output */
+ z = scalbn(z,n);
+ else
+ SET_HIGH_WORD(z, j);
+ return s*z;
+}
diff --git a/lib/libc/src/musl-math/powf.c b/lib/libc/src/musl-math/powf.c
new file mode 100644
index 0000000..427c896
--- /dev/null
+++ b/lib/libc/src/musl-math/powf.c
@@ -0,0 +1,259 @@
+/* origin: FreeBSD /usr/src/lib/msun/src/e_powf.c */
+/*
+ * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com.
+ */
+/*
+ * ====================================================
+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ *
+ * Developed at SunPro, a Sun Microsystems, Inc. business.
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ */
+
+#include "libm.h"
+
+static const float
+bp[] = {1.0, 1.5,},
+dp_h[] = { 0.0, 5.84960938e-01,}, /* 0x3f15c000 */
+dp_l[] = { 0.0, 1.56322085e-06,}, /* 0x35d1cfdc */
+two24 = 16777216.0, /* 0x4b800000 */
+huge = 1.0e30,
+tiny = 1.0e-30,
+/* poly coefs for (3/2)*(log(x)-2s-2/3*s**3 */
+L1 = 6.0000002384e-01, /* 0x3f19999a */
+L2 = 4.2857143283e-01, /* 0x3edb6db7 */
+L3 = 3.3333334327e-01, /* 0x3eaaaaab */
+L4 = 2.7272811532e-01, /* 0x3e8ba305 */
+L5 = 2.3066075146e-01, /* 0x3e6c3255 */
+L6 = 2.0697501302e-01, /* 0x3e53f142 */
+P1 = 1.6666667163e-01, /* 0x3e2aaaab */
+P2 = -2.7777778450e-03, /* 0xbb360b61 */
+P3 = 6.6137559770e-05, /* 0x388ab355 */
+P4 = -1.6533901999e-06, /* 0xb5ddea0e */
+P5 = 4.1381369442e-08, /* 0x3331bb4c */
+lg2 = 6.9314718246e-01, /* 0x3f317218 */
+lg2_h = 6.93145752e-01, /* 0x3f317200 */
+lg2_l = 1.42860654e-06, /* 0x35bfbe8c */
+ovt = 4.2995665694e-08, /* -(128-log2(ovfl+.5ulp)) */
+cp = 9.6179670095e-01, /* 0x3f76384f =2/(3ln2) */
+cp_h = 9.6191406250e-01, /* 0x3f764000 =12b cp */
+cp_l = -1.1736857402e-04, /* 0xb8f623c6 =tail of cp_h */
+ivln2 = 1.4426950216e+00, /* 0x3fb8aa3b =1/ln2 */
+ivln2_h = 1.4426879883e+00, /* 0x3fb8aa00 =16b 1/ln2*/
+ivln2_l = 7.0526075433e-06; /* 0x36eca570 =1/ln2 tail*/
+
+float powf(float x, float y)
+{
+ float z,ax,z_h,z_l,p_h,p_l;
+ float y1,t1,t2,r,s,sn,t,u,v,w;
+ int32_t i,j,k,yisint,n;
+ int32_t hx,hy,ix,iy,is;
+
+ GET_FLOAT_WORD(hx, x);
+ GET_FLOAT_WORD(hy, y);
+ ix = hx & 0x7fffffff;
+ iy = hy & 0x7fffffff;
+
+ /* x**0 = 1, even if x is NaN */
+ if (iy == 0)
+ return 1.0f;
+ /* 1**y = 1, even if y is NaN */
+ if (hx == 0x3f800000)
+ return 1.0f;
+ /* NaN if either arg is NaN */
+ if (ix > 0x7f800000 || iy > 0x7f800000)
+ return x + y;
+
+ /* determine if y is an odd int when x < 0
+ * yisint = 0 ... y is not an integer
+ * yisint = 1 ... y is an odd int
+ * yisint = 2 ... y is an even int
+ */
+ yisint = 0;
+ if (hx < 0) {
+ if (iy >= 0x4b800000)
+ yisint = 2; /* even integer y */
+ else if (iy >= 0x3f800000) {
+ k = (iy>>23) - 0x7f; /* exponent */
+ j = iy>>(23-k);
+ if ((j<<(23-k)) == iy)
+ yisint = 2 - (j & 1);
+ }
+ }
+
+ /* special value of y */
+ if (iy == 0x7f800000) { /* y is +-inf */
+ if (ix == 0x3f800000) /* (-1)**+-inf is 1 */
+ return 1.0f;
+ else if (ix > 0x3f800000) /* (|x|>1)**+-inf = inf,0 */
+ return hy >= 0 ? y : 0.0f;
+ else /* (|x|<1)**+-inf = 0,inf */
+ return hy >= 0 ? 0.0f: -y;
+ }
+ if (iy == 0x3f800000) /* y is +-1 */
+ return hy >= 0 ? x : 1.0f/x;
+ if (hy == 0x40000000) /* y is 2 */
+ return x*x;
+ if (hy == 0x3f000000) { /* y is 0.5 */
+ if (hx >= 0) /* x >= +0 */
+ return sqrtf(x);
+ }
+
+ ax = fabsf(x);
+ /* special value of x */
+ if (ix == 0x7f800000 || ix == 0 || ix == 0x3f800000) { /* x is +-0,+-inf,+-1 */
+ z = ax;
+ if (hy < 0) /* z = (1/|x|) */
+ z = 1.0f/z;
+ if (hx < 0) {
+ if (((ix-0x3f800000)|yisint) == 0) {
+ z = (z-z)/(z-z); /* (-1)**non-int is NaN */
+ } else if (yisint == 1)
+ z = -z; /* (x<0)**odd = -(|x|**odd) */
+ }
+ return z;
+ }
+
+ sn = 1.0f; /* sign of result */
+ if (hx < 0) {
+ if (yisint == 0) /* (x<0)**(non-int) is NaN */
+ return (x-x)/(x-x);
+ if (yisint == 1) /* (x<0)**(odd int) */
+ sn = -1.0f;
+ }
+
+ /* |y| is huge */
+ if (iy > 0x4d000000) { /* if |y| > 2**27 */
+ /* over/underflow if x is not close to one */
+ if (ix < 0x3f7ffff8)
+ return hy < 0 ? sn*huge*huge : sn*tiny*tiny;
+ if (ix > 0x3f800007)
+ return hy > 0 ? sn*huge*huge : sn*tiny*tiny;
+ /* now |1-x| is tiny <= 2**-20, suffice to compute
+ log(x) by x-x^2/2+x^3/3-x^4/4 */
+ t = ax - 1; /* t has 20 trailing zeros */
+ w = (t*t)*(0.5f - t*(0.333333333333f - t*0.25f));
+ u = ivln2_h*t; /* ivln2_h has 16 sig. bits */
+ v = t*ivln2_l - w*ivln2;
+ t1 = u + v;
+ GET_FLOAT_WORD(is, t1);
+ SET_FLOAT_WORD(t1, is & 0xfffff000);
+ t2 = v - (t1-u);
+ } else {
+ float s2,s_h,s_l,t_h,t_l;
+ n = 0;
+ /* take care subnormal number */
+ if (ix < 0x00800000) {
+ ax *= two24;
+ n -= 24;
+ GET_FLOAT_WORD(ix, ax);
+ }
+ n += ((ix)>>23) - 0x7f;
+ j = ix & 0x007fffff;
+ /* determine interval */
+ ix = j | 0x3f800000; /* normalize ix */
+ if (j <= 0x1cc471) /* |x|<sqrt(3/2) */
+ k = 0;
+ else if (j < 0x5db3d7) /* |x|<sqrt(3) */
+ k = 1;
+ else {
+ k = 0;
+ n += 1;
+ ix -= 0x00800000;
+ }
+ SET_FLOAT_WORD(ax, ix);
+
+ /* compute s = s_h+s_l = (x-1)/(x+1) or (x-1.5)/(x+1.5) */
+ u = ax - bp[k]; /* bp[0]=1.0, bp[1]=1.5 */
+ v = 1.0f/(ax+bp[k]);
+ s = u*v;
+ s_h = s;
+ GET_FLOAT_WORD(is, s_h);
+ SET_FLOAT_WORD(s_h, is & 0xfffff000);
+ /* t_h=ax+bp[k] High */
+ is = ((ix>>1) & 0xfffff000) | 0x20000000;
+ SET_FLOAT_WORD(t_h, is + 0x00400000 + (k<<21));
+ t_l = ax - (t_h - bp[k]);
+ s_l = v*((u - s_h*t_h) - s_h*t_l);
+ /* compute log(ax) */
+ s2 = s*s;
+ r = s2*s2*(L1+s2*(L2+s2*(L3+s2*(L4+s2*(L5+s2*L6)))));
+ r += s_l*(s_h+s);
+ s2 = s_h*s_h;
+ t_h = 3.0f + s2 + r;
+ GET_FLOAT_WORD(is, t_h);
+ SET_FLOAT_WORD(t_h, is & 0xfffff000);
+ t_l = r - ((t_h - 3.0f) - s2);
+ /* u+v = s*(1+...) */
+ u = s_h*t_h;
+ v = s_l*t_h + t_l*s;
+ /* 2/(3log2)*(s+...) */
+ p_h = u + v;
+ GET_FLOAT_WORD(is, p_h);
+ SET_FLOAT_WORD(p_h, is & 0xfffff000);
+ p_l = v - (p_h - u);
+ z_h = cp_h*p_h; /* cp_h+cp_l = 2/(3*log2) */
+ z_l = cp_l*p_h + p_l*cp+dp_l[k];
+ /* log2(ax) = (s+..)*2/(3*log2) = n + dp_h + z_h + z_l */
+ t = (float)n;
+ t1 = (((z_h + z_l) + dp_h[k]) + t);
+ GET_FLOAT_WORD(is, t1);
+ SET_FLOAT_WORD(t1, is & 0xfffff000);
+ t2 = z_l - (((t1 - t) - dp_h[k]) - z_h);
+ }
+
+ /* split up y into y1+y2 and compute (y1+y2)*(t1+t2) */
+ GET_FLOAT_WORD(is, y);
+ SET_FLOAT_WORD(y1, is & 0xfffff000);
+ p_l = (y-y1)*t1 + y*t2;
+ p_h = y1*t1;
+ z = p_l + p_h;
+ GET_FLOAT_WORD(j, z);
+ if (j > 0x43000000) /* if z > 128 */
+ return sn*huge*huge; /* overflow */
+ else if (j == 0x43000000) { /* if z == 128 */
+ if (p_l + ovt > z - p_h)
+ return sn*huge*huge; /* overflow */
+ } else if ((j&0x7fffffff) > 0x43160000) /* z < -150 */ // FIXME: check should be (uint32_t)j > 0xc3160000
+ return sn*tiny*tiny; /* underflow */
+ else if (j == 0xc3160000) { /* z == -150 */
+ if (p_l <= z-p_h)
+ return sn*tiny*tiny; /* underflow */
+ }
+ /*
+ * compute 2**(p_h+p_l)
+ */
+ i = j & 0x7fffffff;
+ k = (i>>23) - 0x7f;
+ n = 0;
+ if (i > 0x3f000000) { /* if |z| > 0.5, set n = [z+0.5] */
+ n = j + (0x00800000>>(k+1));
+ k = ((n&0x7fffffff)>>23) - 0x7f; /* new k for n */
+ SET_FLOAT_WORD(t, n & ~(0x007fffff>>k));
+ n = ((n&0x007fffff)|0x00800000)>>(23-k);
+ if (j < 0)
+ n = -n;
+ p_h -= t;
+ }
+ t = p_l + p_h;
+ GET_FLOAT_WORD(is, t);
+ SET_FLOAT_WORD(t, is & 0xffff8000);
+ u = t*lg2_h;
+ v = (p_l-(t-p_h))*lg2 + t*lg2_l;
+ z = u + v;
+ w = v - (z - u);
+ t = z*z;
+ t1 = z - t*(P1+t*(P2+t*(P3+t*(P4+t*P5))));
+ r = (z*t1)/(t1-2.0f) - (w+z*w);
+ z = 1.0f - (r - z);
+ GET_FLOAT_WORD(j, z);
+ j += n<<23;
+ if ((j>>23) <= 0) /* subnormal output */
+ z = scalbnf(z, n);
+ else
+ SET_FLOAT_WORD(z, j);
+ return sn*z;
+}
diff --git a/lib/libc/src/musl-math/powl.c b/lib/libc/src/musl-math/powl.c
new file mode 100644
index 0000000..5b6da07
--- /dev/null
+++ b/lib/libc/src/musl-math/powl.c
@@ -0,0 +1,522 @@
+/* origin: OpenBSD /usr/src/lib/libm/src/ld80/e_powl.c */
+/*
+ * Copyright (c) 2008 Stephen L. Moshier <steve@moshier.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+/* powl.c
+ *
+ * Power function, long double precision
+ *
+ *
+ * SYNOPSIS:
+ *
+ * long double x, y, z, powl();
+ *
+ * z = powl( x, y );
+ *
+ *
+ * DESCRIPTION:
+ *
+ * Computes x raised to the yth power. Analytically,
+ *
+ * x**y = exp( y log(x) ).
+ *
+ * Following Cody and Waite, this program uses a lookup table
+ * of 2**-i/32 and pseudo extended precision arithmetic to
+ * obtain several extra bits of accuracy in both the logarithm
+ * and the exponential.
+ *
+ *
+ * ACCURACY:
+ *
+ * The relative error of pow(x,y) can be estimated
+ * by y dl ln(2), where dl is the absolute error of
+ * the internally computed base 2 logarithm. At the ends
+ * of the approximation interval the logarithm equal 1/32
+ * and its relative error is about 1 lsb = 1.1e-19. Hence
+ * the predicted relative error in the result is 2.3e-21 y .
+ *
+ * Relative error:
+ * arithmetic domain # trials peak rms
+ *
+ * IEEE +-1000 40000 2.8e-18 3.7e-19
+ * .001 < x < 1000, with log(x) uniformly distributed.
+ * -1000 < y < 1000, y uniformly distributed.
+ *
+ * IEEE 0,8700 60000 6.5e-18 1.0e-18
+ * 0.99 < x < 1.01, 0 < y < 8700, uniformly distributed.
+ *
+ *
+ * ERROR MESSAGES:
+ *
+ * message condition value returned
+ * pow overflow x**y > MAXNUM INFINITY
+ * pow underflow x**y < 1/MAXNUM 0.0
+ * pow domain x<0 and y noninteger 0.0
+ *
+ */
+
+#include "libm.h"
+
+#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
+long double powl(long double x, long double y)
+{
+ return pow(x, y);
+}
+#elif LDBL_MANT_DIG == 64 && LDBL_MAX_EXP == 16384
+
+/* Table size */
+#define NXT 32
+
+/* log(1+x) = x - .5x^2 + x^3 * P(z)/Q(z)
+ * on the domain 2^(-1/32) - 1 <= x <= 2^(1/32) - 1
+ */
+static const long double P[] = {
+ 8.3319510773868690346226E-4L,
+ 4.9000050881978028599627E-1L,
+ 1.7500123722550302671919E0L,
+ 1.4000100839971580279335E0L,
+};
+static const long double Q[] = {
+/* 1.0000000000000000000000E0L,*/
+ 5.2500282295834889175431E0L,
+ 8.4000598057587009834666E0L,
+ 4.2000302519914740834728E0L,
+};
+/* A[i] = 2^(-i/32), rounded to IEEE long double precision.
+ * If i is even, A[i] + B[i/2] gives additional accuracy.
+ */
+static const long double A[33] = {
+ 1.0000000000000000000000E0L,
+ 9.7857206208770013448287E-1L,
+ 9.5760328069857364691013E-1L,
+ 9.3708381705514995065011E-1L,
+ 9.1700404320467123175367E-1L,
+ 8.9735453750155359320742E-1L,
+ 8.7812608018664974155474E-1L,
+ 8.5930964906123895780165E-1L,
+ 8.4089641525371454301892E-1L,
+ 8.2287773907698242225554E-1L,
+ 8.0524516597462715409607E-1L,
+ 7.8799042255394324325455E-1L,
+ 7.7110541270397041179298E-1L,
+ 7.5458221379671136985669E-1L,
+ 7.3841307296974965571198E-1L,
+ 7.2259040348852331001267E-1L,
+ 7.0710678118654752438189E-1L,
+ 6.9195494098191597746178E-1L,
+ 6.7712777346844636413344E-1L,
+ 6.6261832157987064729696E-1L,
+ 6.4841977732550483296079E-1L,
+ 6.3452547859586661129850E-1L,
+ 6.2092890603674202431705E-1L,
+ 6.0762367999023443907803E-1L,
+ 5.9460355750136053334378E-1L,
+ 5.8186242938878875689693E-1L,
+ 5.6939431737834582684856E-1L,
+ 5.5719337129794626814472E-1L,
+ 5.4525386633262882960438E-1L,
+ 5.3357020033841180906486E-1L,
+ 5.2213689121370692017331E-1L,
+ 5.1094857432705833910408E-1L,
+ 5.0000000000000000000000E-1L,
+};
+static const long double B[17] = {
+ 0.0000000000000000000000E0L,
+ 2.6176170809902549338711E-20L,
+-1.0126791927256478897086E-20L,
+ 1.3438228172316276937655E-21L,
+ 1.2207982955417546912101E-20L,
+-6.3084814358060867200133E-21L,
+ 1.3164426894366316434230E-20L,
+-1.8527916071632873716786E-20L,
+ 1.8950325588932570796551E-20L,
+ 1.5564775779538780478155E-20L,
+ 6.0859793637556860974380E-21L,
+-2.0208749253662532228949E-20L,
+ 1.4966292219224761844552E-20L,
+ 3.3540909728056476875639E-21L,
+-8.6987564101742849540743E-22L,
+-1.2327176863327626135542E-20L,
+ 0.0000000000000000000000E0L,
+};
+
+/* 2^x = 1 + x P(x),
+ * on the interval -1/32 <= x <= 0
+ */
+static const long double R[] = {
+ 1.5089970579127659901157E-5L,
+ 1.5402715328927013076125E-4L,
+ 1.3333556028915671091390E-3L,
+ 9.6181291046036762031786E-3L,
+ 5.5504108664798463044015E-2L,
+ 2.4022650695910062854352E-1L,
+ 6.9314718055994530931447E-1L,
+};
+
+#define MEXP (NXT*16384.0L)
+/* The following if denormal numbers are supported, else -MEXP: */
+#define MNEXP (-NXT*(16384.0L+64.0L))
+/* log2(e) - 1 */
+#define LOG2EA 0.44269504088896340735992L
+
+#define F W
+#define Fa Wa
+#define Fb Wb
+#define G W
+#define Ga Wa
+#define Gb u
+#define H W
+#define Ha Wb
+#define Hb Wb
+
+static const long double MAXLOGL = 1.1356523406294143949492E4L;
+static const long double MINLOGL = -1.13994985314888605586758E4L;
+static const long double LOGE2L = 6.9314718055994530941723E-1L;
+static const long double huge = 0x1p10000L;
+/* XXX Prevent gcc from erroneously constant folding this. */
+static const volatile long double twom10000 = 0x1p-10000L;
+
+static long double reducl(long double);
+static long double powil(long double, int);
+
+long double powl(long double x, long double y)
+{
+ /* double F, Fa, Fb, G, Ga, Gb, H, Ha, Hb */
+ int i, nflg, iyflg, yoddint;
+ long e;
+ volatile long double z=0;
+ long double w=0, W=0, Wa=0, Wb=0, ya=0, yb=0, u=0;
+
+ /* make sure no invalid exception is raised by nan comparision */
+ if (isnan(x)) {
+ if (!isnan(y) && y == 0.0)
+ return 1.0;
+ return x;
+ }
+ if (isnan(y)) {
+ if (x == 1.0)
+ return 1.0;
+ return y;
+ }
+ if (x == 1.0)
+ return 1.0; /* 1**y = 1, even if y is nan */
+ if (x == -1.0 && !isfinite(y))
+ return 1.0; /* -1**inf = 1 */
+ if (y == 0.0)
+ return 1.0; /* x**0 = 1, even if x is nan */
+ if (y == 1.0)
+ return x;
+ if (y >= LDBL_MAX) {
+ if (x > 1.0 || x < -1.0)
+ return INFINITY;
+ if (x != 0.0)
+ return 0.0;
+ }
+ if (y <= -LDBL_MAX) {
+ if (x > 1.0 || x < -1.0)
+ return 0.0;
+ if (x != 0.0 || y == -INFINITY)
+ return INFINITY;
+ }
+ if (x >= LDBL_MAX) {
+ if (y > 0.0)
+ return INFINITY;
+ return 0.0;
+ }
+
+ w = floorl(y);
+
+ /* Set iyflg to 1 if y is an integer. */
+ iyflg = 0;
+ if (w == y)
+ iyflg = 1;
+
+ /* Test for odd integer y. */
+ yoddint = 0;
+ if (iyflg) {
+ ya = fabsl(y);
+ ya = floorl(0.5 * ya);
+ yb = 0.5 * fabsl(w);
+ if( ya != yb )
+ yoddint = 1;
+ }
+
+ if (x <= -LDBL_MAX) {
+ if (y > 0.0) {
+ if (yoddint)
+ return -INFINITY;
+ return INFINITY;
+ }
+ if (y < 0.0) {
+ if (yoddint)
+ return -0.0;
+ return 0.0;
+ }
+ }
+ nflg = 0; /* (x<0)**(odd int) */
+ if (x <= 0.0) {
+ if (x == 0.0) {
+ if (y < 0.0) {
+ if (signbit(x) && yoddint)
+ /* (-0.0)**(-odd int) = -inf, divbyzero */
+ return -1.0/0.0;
+ /* (+-0.0)**(negative) = inf, divbyzero */
+ return 1.0/0.0;
+ }
+ if (signbit(x) && yoddint)
+ return -0.0;
+ return 0.0;
+ }
+ if (iyflg == 0)
+ return (x - x) / (x - x); /* (x<0)**(non-int) is NaN */
+ /* (x<0)**(integer) */
+ if (yoddint)
+ nflg = 1; /* negate result */
+ x = -x;
+ }
+ /* (+integer)**(integer) */
+ if (iyflg && floorl(x) == x && fabsl(y) < 32768.0) {
+ w = powil(x, (int)y);
+ return nflg ? -w : w;
+ }
+
+ /* separate significand from exponent */
+ x = frexpl(x, &i);
+ e = i;
+
+ /* find significand in antilog table A[] */
+ i = 1;
+ if (x <= A[17])
+ i = 17;
+ if (x <= A[i+8])
+ i += 8;
+ if (x <= A[i+4])
+ i += 4;
+ if (x <= A[i+2])
+ i += 2;
+ if (x >= A[1])
+ i = -1;
+ i += 1;
+
+ /* Find (x - A[i])/A[i]
+ * in order to compute log(x/A[i]):
+ *
+ * log(x) = log( a x/a ) = log(a) + log(x/a)
+ *
+ * log(x/a) = log(1+v), v = x/a - 1 = (x-a)/a
+ */
+ x -= A[i];
+ x -= B[i/2];
+ x /= A[i];
+
+ /* rational approximation for log(1+v):
+ *
+ * log(1+v) = v - v**2/2 + v**3 P(v) / Q(v)
+ */
+ z = x*x;
+ w = x * (z * __polevll(x, P, 3) / __p1evll(x, Q, 3));
+ w = w - 0.5*z;
+
+ /* Convert to base 2 logarithm:
+ * multiply by log2(e) = 1 + LOG2EA
+ */
+ z = LOG2EA * w;
+ z += w;
+ z += LOG2EA * x;
+ z += x;
+
+ /* Compute exponent term of the base 2 logarithm. */
+ w = -i;
+ w /= NXT;
+ w += e;
+ /* Now base 2 log of x is w + z. */
+
+ /* Multiply base 2 log by y, in extended precision. */
+
+ /* separate y into large part ya
+ * and small part yb less than 1/NXT
+ */
+ ya = reducl(y);
+ yb = y - ya;
+
+ /* (w+z)(ya+yb)
+ * = w*ya + w*yb + z*y
+ */
+ F = z * y + w * yb;
+ Fa = reducl(F);
+ Fb = F - Fa;
+
+ G = Fa + w * ya;
+ Ga = reducl(G);
+ Gb = G - Ga;
+
+ H = Fb + Gb;
+ Ha = reducl(H);
+ w = (Ga + Ha) * NXT;
+
+ /* Test the power of 2 for overflow */
+ if (w > MEXP)
+ return huge * huge; /* overflow */
+ if (w < MNEXP)
+ return twom10000 * twom10000; /* underflow */
+
+ e = w;
+ Hb = H - Ha;
+
+ if (Hb > 0.0) {
+ e += 1;
+ Hb -= 1.0/NXT; /*0.0625L;*/
+ }
+
+ /* Now the product y * log2(x) = Hb + e/NXT.
+ *
+ * Compute base 2 exponential of Hb,
+ * where -0.0625 <= Hb <= 0.
+ */
+ z = Hb * __polevll(Hb, R, 6); /* z = 2**Hb - 1 */
+
+ /* Express e/NXT as an integer plus a negative number of (1/NXT)ths.
+ * Find lookup table entry for the fractional power of 2.
+ */
+ if (e < 0)
+ i = 0;
+ else
+ i = 1;
+ i = e/NXT + i;
+ e = NXT*i - e;
+ w = A[e];
+ z = w * z; /* 2**-e * ( 1 + (2**Hb-1) ) */
+ z = z + w;
+ z = scalbnl(z, i); /* multiply by integer power of 2 */
+
+ if (nflg)
+ z = -z;
+ return z;
+}
+
+
+/* Find a multiple of 1/NXT that is within 1/NXT of x. */
+static long double reducl(long double x)
+{
+ long double t;
+
+ t = x * NXT;
+ t = floorl(t);
+ t = t / NXT;
+ return t;
+}
+
+/*
+ * Positive real raised to integer power, long double precision
+ *
+ *
+ * SYNOPSIS:
+ *
+ * long double x, y, powil();
+ * int n;
+ *
+ * y = powil( x, n );
+ *
+ *
+ * DESCRIPTION:
+ *
+ * Returns argument x>0 raised to the nth power.
+ * The routine efficiently decomposes n as a sum of powers of
+ * two. The desired power is a product of two-to-the-kth
+ * powers of x. Thus to compute the 32767 power of x requires
+ * 28 multiplications instead of 32767 multiplications.
+ *
+ *
+ * ACCURACY:
+ *
+ * Relative error:
+ * arithmetic x domain n domain # trials peak rms
+ * IEEE .001,1000 -1022,1023 50000 4.3e-17 7.8e-18
+ * IEEE 1,2 -1022,1023 20000 3.9e-17 7.6e-18
+ * IEEE .99,1.01 0,8700 10000 3.6e-16 7.2e-17
+ *
+ * Returns MAXNUM on overflow, zero on underflow.
+ */
+
+static long double powil(long double x, int nn)
+{
+ long double ww, y;
+ long double s;
+ int n, e, sign, lx;
+
+ if (nn == 0)
+ return 1.0;
+
+ if (nn < 0) {
+ sign = -1;
+ n = -nn;
+ } else {
+ sign = 1;
+ n = nn;
+ }
+
+ /* Overflow detection */
+
+ /* Calculate approximate logarithm of answer */
+ s = x;
+ s = frexpl( s, &lx);
+ e = (lx - 1)*n;
+ if ((e == 0) || (e > 64) || (e < -64)) {
+ s = (s - 7.0710678118654752e-1L) / (s + 7.0710678118654752e-1L);
+ s = (2.9142135623730950L * s - 0.5 + lx) * nn * LOGE2L;
+ } else {
+ s = LOGE2L * e;
+ }
+
+ if (s > MAXLOGL)
+ return huge * huge; /* overflow */
+
+ if (s < MINLOGL)
+ return twom10000 * twom10000; /* underflow */
+ /* Handle tiny denormal answer, but with less accuracy
+ * since roundoff error in 1.0/x will be amplified.
+ * The precise demarcation should be the gradual underflow threshold.
+ */
+ if (s < -MAXLOGL+2.0) {
+ x = 1.0/x;
+ sign = -sign;
+ }
+
+ /* First bit of the power */
+ if (n & 1)
+ y = x;
+ else
+ y = 1.0;
+
+ ww = x;
+ n >>= 1;
+ while (n) {
+ ww = ww * ww; /* arg to the 2-to-the-kth power */
+ if (n & 1) /* if that bit is set, then include in product */
+ y *= ww;
+ n >>= 1;
+ }
+
+ if (sign < 0)
+ y = 1.0/y;
+ return y;
+}
+#elif LDBL_MANT_DIG == 113 && LDBL_MAX_EXP == 16384
+// TODO: broken implementation to make things compile
+long double powl(long double x, long double y)
+{
+ return pow(x, y);
+}
+#endif
diff --git a/lib/libc/src/musl-math/remainder.c b/lib/libc/src/musl-math/remainder.c
new file mode 100644
index 0000000..e4abcd7
--- /dev/null
+++ b/lib/libc/src/musl-math/remainder.c
@@ -0,0 +1,11 @@
+#include <math.h>
+#include "weak_alias.h"
+//#include "libc.h"
+
+double remainder(double x, double y)
+{
+ int q;
+ return remquo(x, y, &q);
+}
+
+weak_alias(remainder, drem);
diff --git a/lib/libc/src/musl-math/remainderf.c b/lib/libc/src/musl-math/remainderf.c
new file mode 100644
index 0000000..e1fcdaa
--- /dev/null
+++ b/lib/libc/src/musl-math/remainderf.c
@@ -0,0 +1,11 @@
+#include <math.h>
+#include "weak_alias.h"
+//#include "libc.h"
+
+float remainderf(float x, float y)
+{
+ int q;
+ return remquof(x, y, &q);
+}
+
+weak_alias(remainderf, dremf);
diff --git a/lib/libc/src/musl-math/remainderl.c b/lib/libc/src/musl-math/remainderl.c
new file mode 100644
index 0000000..2a13c1d
--- /dev/null
+++ b/lib/libc/src/musl-math/remainderl.c
@@ -0,0 +1,15 @@
+#include <math.h>
+#include <float.h>
+
+#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
+long double remainderl(long double x, long double y)
+{
+ return remainder(x, y);
+}
+#else
+long double remainderl(long double x, long double y)
+{
+ int q;
+ return remquol(x, y, &q);
+}
+#endif
diff --git a/lib/libc/src/musl-math/remquo.c b/lib/libc/src/musl-math/remquo.c
new file mode 100644
index 0000000..59d5ad5
--- /dev/null
+++ b/lib/libc/src/musl-math/remquo.c
@@ -0,0 +1,82 @@
+#include <math.h>
+#include <stdint.h>
+
+double remquo(double x, double y, int *quo)
+{
+ union {double f; uint64_t i;} ux = {x}, uy = {y};
+ int ex = ux.i>>52 & 0x7ff;
+ int ey = uy.i>>52 & 0x7ff;
+ int sx = ux.i>>63;
+ int sy = uy.i>>63;
+ uint32_t q;
+ uint64_t i;
+ uint64_t uxi = ux.i;
+
+ *quo = 0;
+ if (uy.i<<1 == 0 || isnan(y) || ex == 0x7ff)
+ return (x*y)/(x*y);
+ if (ux.i<<1 == 0)
+ return x;
+
+ /* normalize x and y */
+ if (!ex) {
+ for (i = uxi<<12; i>>63 == 0; ex--, i <<= 1);
+ uxi <<= -ex + 1;
+ } else {
+ uxi &= -1ULL >> 12;
+ uxi |= 1ULL << 52;
+ }
+ if (!ey) {
+ for (i = uy.i<<12; i>>63 == 0; ey--, i <<= 1);
+ uy.i <<= -ey + 1;
+ } else {
+ uy.i &= -1ULL >> 12;
+ uy.i |= 1ULL << 52;
+ }
+
+ q = 0;
+ if (ex < ey) {
+ if (ex+1 == ey)
+ goto end;
+ return x;
+ }
+
+ /* x mod y */
+ for (; ex > ey; ex--) {
+ i = uxi - uy.i;
+ if (i >> 63 == 0) {
+ uxi = i;
+ q++;
+ }
+ uxi <<= 1;
+ q <<= 1;
+ }
+ i = uxi - uy.i;
+ if (i >> 63 == 0) {
+ uxi = i;
+ q++;
+ }
+ if (uxi == 0)
+ ex = -60;
+ else
+ for (; uxi>>52 == 0; uxi <<= 1, ex--);
+end:
+ /* scale result and decide between |x| and |x|-|y| */
+ if (ex > 0) {
+ uxi -= 1ULL << 52;
+ uxi |= (uint64_t)ex << 52;
+ } else {
+ uxi >>= -ex + 1;
+ }
+ ux.i = uxi;
+ x = ux.f;
+ if (sy)
+ y = -y;
+ if (ex == ey || (ex+1 == ey && (2*x > y || (2*x == y && q%2)))) {
+ x -= y;
+ q++;
+ }
+ q &= 0x7fffffff;
+ *quo = sx^sy ? -(int)q : (int)q;
+ return sx ? -x : x;
+}
diff --git a/lib/libc/src/musl-math/remquof.c b/lib/libc/src/musl-math/remquof.c
new file mode 100644
index 0000000..2f41ff7
--- /dev/null
+++ b/lib/libc/src/musl-math/remquof.c
@@ -0,0 +1,82 @@
+#include <math.h>
+#include <stdint.h>
+
+float remquof(float x, float y, int *quo)
+{
+ union {float f; uint32_t i;} ux = {x}, uy = {y};
+ int ex = ux.i>>23 & 0xff;
+ int ey = uy.i>>23 & 0xff;
+ int sx = ux.i>>31;
+ int sy = uy.i>>31;
+ uint32_t q;
+ uint32_t i;
+ uint32_t uxi = ux.i;
+
+ *quo = 0;
+ if (uy.i<<1 == 0 || isnan(y) || ex == 0xff)
+ return (x*y)/(x*y);
+ if (ux.i<<1 == 0)
+ return x;
+
+ /* normalize x and y */
+ if (!ex) {
+ for (i = uxi<<9; i>>31 == 0; ex--, i <<= 1);
+ uxi <<= -ex + 1;
+ } else {
+ uxi &= -1U >> 9;
+ uxi |= 1U << 23;
+ }
+ if (!ey) {
+ for (i = uy.i<<9; i>>31 == 0; ey--, i <<= 1);
+ uy.i <<= -ey + 1;
+ } else {
+ uy.i &= -1U >> 9;
+ uy.i |= 1U << 23;
+ }
+
+ q = 0;
+ if (ex < ey) {
+ if (ex+1 == ey)
+ goto end;
+ return x;
+ }
+
+ /* x mod y */
+ for (; ex > ey; ex--) {
+ i = uxi - uy.i;
+ if (i >> 31 == 0) {
+ uxi = i;
+ q++;
+ }
+ uxi <<= 1;
+ q <<= 1;
+ }
+ i = uxi - uy.i;
+ if (i >> 31 == 0) {
+ uxi = i;
+ q++;
+ }
+ if (uxi == 0)
+ ex = -30;
+ else
+ for (; uxi>>23 == 0; uxi <<= 1, ex--);
+end:
+ /* scale result and decide between |x| and |x|-|y| */
+ if (ex > 0) {
+ uxi -= 1U << 23;
+ uxi |= (uint32_t)ex << 23;
+ } else {
+ uxi >>= -ex + 1;
+ }
+ ux.i = uxi;
+ x = ux.f;
+ if (sy)
+ y = -y;
+ if (ex == ey || (ex+1 == ey && (2*x > y || (2*x == y && q%2)))) {
+ x -= y;
+ q++;
+ }
+ q &= 0x7fffffff;
+ *quo = sx^sy ? -(int)q : (int)q;
+ return sx ? -x : x;
+}
diff --git a/lib/libc/src/musl-math/remquol.c b/lib/libc/src/musl-math/remquol.c
new file mode 100644
index 0000000..9b065c0
--- /dev/null
+++ b/lib/libc/src/musl-math/remquol.c
@@ -0,0 +1,124 @@
+#include "libm.h"
+
+#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
+long double remquol(long double x, long double y, int *quo)
+{
+ return remquo(x, y, quo);
+}
+#elif (LDBL_MANT_DIG == 64 || LDBL_MANT_DIG == 113) && LDBL_MAX_EXP == 16384
+long double remquol(long double x, long double y, int *quo)
+{
+ union ldshape ux = {x}, uy = {y};
+ int ex = ux.i.se & 0x7fff;
+ int ey = uy.i.se & 0x7fff;
+ int sx = ux.i.se >> 15;
+ int sy = uy.i.se >> 15;
+ uint32_t q;
+
+ *quo = 0;
+ if (y == 0 || isnan(y) || ex == 0x7fff)
+ return (x*y)/(x*y);
+ if (x == 0)
+ return x;
+
+ /* normalize x and y */
+ if (!ex) {
+ ux.i.se = ex;
+ ux.f *= 0x1p120f;
+ ex = ux.i.se - 120;
+ }
+ if (!ey) {
+ uy.i.se = ey;
+ uy.f *= 0x1p120f;
+ ey = uy.i.se - 120;
+ }
+
+ q = 0;
+ if (ex >= ey) {
+ /* x mod y */
+#if LDBL_MANT_DIG == 64
+ uint64_t i, mx, my;
+ mx = ux.i.m;
+ my = uy.i.m;
+ for (; ex > ey; ex--) {
+ i = mx - my;
+ if (mx >= my) {
+ mx = 2*i;
+ q++;
+ q <<= 1;
+ } else if (2*mx < mx) {
+ mx = 2*mx - my;
+ q <<= 1;
+ q++;
+ } else {
+ mx = 2*mx;
+ q <<= 1;
+ }
+ }
+ i = mx - my;
+ if (mx >= my) {
+ mx = i;
+ q++;
+ }
+ if (mx == 0)
+ ex = -120;
+ else
+ for (; mx >> 63 == 0; mx *= 2, ex--);
+ ux.i.m = mx;
+#elif LDBL_MANT_DIG == 113
+ uint64_t hi, lo, xhi, xlo, yhi, ylo;
+ xhi = (ux.i2.hi & -1ULL>>16) | 1ULL<<48;
+ yhi = (uy.i2.hi & -1ULL>>16) | 1ULL<<48;
+ xlo = ux.i2.lo;
+ ylo = ux.i2.lo;
+ for (; ex > ey; ex--) {
+ hi = xhi - yhi;
+ lo = xlo - ylo;
+ if (xlo < ylo)
+ hi -= 1;
+ if (hi >> 63 == 0) {
+ xhi = 2*hi + (lo>>63);
+ xlo = 2*lo;
+ q++;
+ } else {
+ xhi = 2*xhi + (xlo>>63);
+ xlo = 2*xlo;
+ }
+ q <<= 1;
+ }
+ hi = xhi - yhi;
+ lo = xlo - ylo;
+ if (xlo < ylo)
+ hi -= 1;
+ if (hi >> 63 == 0) {
+ xhi = hi;
+ xlo = lo;
+ q++;
+ }
+ if ((xhi|xlo) == 0)
+ ex = -120;
+ else
+ for (; xhi >> 48 == 0; xhi = 2*xhi + (xlo>>63), xlo = 2*xlo, ex--);
+ ux.i2.hi = xhi;
+ ux.i2.lo = xlo;
+#endif
+ }
+
+ /* scale result and decide between |x| and |x|-|y| */
+ if (ex <= 0) {
+ ux.i.se = ex + 120;
+ ux.f *= 0x1p-120f;
+ } else
+ ux.i.se = ex;
+ x = ux.f;
+ if (sy)
+ y = -y;
+ if (ex == ey || (ex+1 == ey && (2*x > y || (2*x == y && q%2)))) {
+ x -= y;
+ q++;
+ }
+ q &= 0x7fffffff;
+ *quo = sx^sy ? -(int)q : (int)q;
+ return sx ? -x : x;
+}
+#endif
diff --git a/lib/libc/src/musl-math/rint.c b/lib/libc/src/musl-math/rint.c
new file mode 100644
index 0000000..fbba390
--- /dev/null
+++ b/lib/libc/src/musl-math/rint.c
@@ -0,0 +1,28 @@
+#include <float.h>
+#include <math.h>
+#include <stdint.h>
+
+#if FLT_EVAL_METHOD==0 || FLT_EVAL_METHOD==1
+#define EPS DBL_EPSILON
+#elif FLT_EVAL_METHOD==2
+#define EPS LDBL_EPSILON
+#endif
+static const double_t toint = 1/EPS;
+
+double rint(double x)
+{
+ union {double f; uint64_t i;} u = {x};
+ int e = u.i>>52 & 0x7ff;
+ int s = u.i>>63;
+ double_t y;
+
+ if (e >= 0x3ff+52)
+ return x;
+ if (s)
+ y = x - toint + toint;
+ else
+ y = x + toint - toint;
+ if (y == 0)
+ return s ? -0.0 : 0;
+ return y;
+}
diff --git a/lib/libc/src/musl-math/rintf.c b/lib/libc/src/musl-math/rintf.c
new file mode 100644
index 0000000..9047688
--- /dev/null
+++ b/lib/libc/src/musl-math/rintf.c
@@ -0,0 +1,30 @@
+#include <float.h>
+#include <math.h>
+#include <stdint.h>
+
+#if FLT_EVAL_METHOD==0
+#define EPS FLT_EPSILON
+#elif FLT_EVAL_METHOD==1
+#define EPS DBL_EPSILON
+#elif FLT_EVAL_METHOD==2
+#define EPS LDBL_EPSILON
+#endif
+static const float_t toint = 1/EPS;
+
+float rintf(float x)
+{
+ union {float f; uint32_t i;} u = {x};
+ int e = u.i>>23 & 0xff;
+ int s = u.i>>31;
+ float_t y;
+
+ if (e >= 0x7f+23)
+ return x;
+ if (s)
+ y = x - toint + toint;
+ else
+ y = x + toint - toint;
+ if (y == 0)
+ return s ? -0.0f : 0.0f;
+ return y;
+}
diff --git a/lib/libc/src/musl-math/rintl.c b/lib/libc/src/musl-math/rintl.c
new file mode 100644
index 0000000..374327d
--- /dev/null
+++ b/lib/libc/src/musl-math/rintl.c
@@ -0,0 +1,29 @@
+#include "libm.h"
+
+#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
+long double rintl(long double x)
+{
+ return rint(x);
+}
+#elif (LDBL_MANT_DIG == 64 || LDBL_MANT_DIG == 113) && LDBL_MAX_EXP == 16384
+
+static const long double toint = 1/LDBL_EPSILON;
+
+long double rintl(long double x)
+{
+ union ldshape u = {x};
+ int e = u.i.se & 0x7fff;
+ int s = u.i.se >> 15;
+ long double y;
+
+ if (e >= 0x3fff+LDBL_MANT_DIG-1)
+ return x;
+ if (s)
+ y = x - toint + toint;
+ else
+ y = x + toint - toint;
+ if (y == 0)
+ return 0*x;
+ return y;
+}
+#endif
diff --git a/lib/libc/src/musl-math/round.c b/lib/libc/src/musl-math/round.c
new file mode 100644
index 0000000..130d58d
--- /dev/null
+++ b/lib/libc/src/musl-math/round.c
@@ -0,0 +1,35 @@
+#include "libm.h"
+
+#if FLT_EVAL_METHOD==0 || FLT_EVAL_METHOD==1
+#define EPS DBL_EPSILON
+#elif FLT_EVAL_METHOD==2
+#define EPS LDBL_EPSILON
+#endif
+static const double_t toint = 1/EPS;
+
+double round(double x)
+{
+ union {double f; uint64_t i;} u = {x};
+ int e = u.i >> 52 & 0x7ff;
+ double_t y;
+
+ if (e >= 0x3ff+52)
+ return x;
+ if (u.i >> 63)
+ x = -x;
+ if (e < 0x3ff-1) {
+ /* raise inexact if x!=0 */
+ FORCE_EVAL(x + toint);
+ return 0*u.f;
+ }
+ y = x + toint - toint - x;
+ if (y > 0.5)
+ y = y + x - 1;
+ else if (y <= -0.5)
+ y = y + x + 1;
+ else
+ y = y + x;
+ if (u.i >> 63)
+ y = -y;
+ return y;
+}
diff --git a/lib/libc/src/musl-math/roundf.c b/lib/libc/src/musl-math/roundf.c
new file mode 100644
index 0000000..e8210af
--- /dev/null
+++ b/lib/libc/src/musl-math/roundf.c
@@ -0,0 +1,36 @@
+#include "libm.h"
+
+#if FLT_EVAL_METHOD==0
+#define EPS FLT_EPSILON
+#elif FLT_EVAL_METHOD==1
+#define EPS DBL_EPSILON
+#elif FLT_EVAL_METHOD==2
+#define EPS LDBL_EPSILON
+#endif
+static const float_t toint = 1/EPS;
+
+float roundf(float x)
+{
+ union {float f; uint32_t i;} u = {x};
+ int e = u.i >> 23 & 0xff;
+ float_t y;
+
+ if (e >= 0x7f+23)
+ return x;
+ if (u.i >> 31)
+ x = -x;
+ if (e < 0x7f-1) {
+ FORCE_EVAL(x + toint);
+ return 0*u.f;
+ }
+ y = x + toint - toint - x;
+ if (y > 0.5f)
+ y = y + x - 1;
+ else if (y <= -0.5f)
+ y = y + x + 1;
+ else
+ y = y + x;
+ if (u.i >> 31)
+ y = -y;
+ return y;
+}
diff --git a/lib/libc/src/musl-math/roundl.c b/lib/libc/src/musl-math/roundl.c
new file mode 100644
index 0000000..f4ff682
--- /dev/null
+++ b/lib/libc/src/musl-math/roundl.c
@@ -0,0 +1,37 @@
+#include "libm.h"
+
+#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
+long double roundl(long double x)
+{
+ return round(x);
+}
+#elif (LDBL_MANT_DIG == 64 || LDBL_MANT_DIG == 113) && LDBL_MAX_EXP == 16384
+
+static const long double toint = 1/LDBL_EPSILON;
+
+long double roundl(long double x)
+{
+ union ldshape u = {x};
+ int e = u.i.se & 0x7fff;
+ long double y;
+
+ if (e >= 0x3fff+LDBL_MANT_DIG-1)
+ return x;
+ if (u.i.se >> 15)
+ x = -x;
+ if (e < 0x3fff-1) {
+ FORCE_EVAL(x + toint);
+ return 0*u.f;
+ }
+ y = x + toint - toint - x;
+ if (y > 0.5)
+ y = y + x - 1;
+ else if (y <= -0.5)
+ y = y + x + 1;
+ else
+ y = y + x;
+ if (u.i.se >> 15)
+ y = -y;
+ return y;
+}
+#endif
diff --git a/lib/libc/src/musl-math/scalb.c b/lib/libc/src/musl-math/scalb.c
new file mode 100644
index 0000000..efe69e6
--- /dev/null
+++ b/lib/libc/src/musl-math/scalb.c
@@ -0,0 +1,35 @@
+/* origin: FreeBSD /usr/src/lib/msun/src/e_scalb.c */
+/*
+ * ====================================================
+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ *
+ * Developed at SunSoft, a Sun Microsystems, Inc. business.
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ */
+/*
+ * scalb(x, fn) is provide for
+ * passing various standard test suite. One
+ * should use scalbn() instead.
+ */
+
+#define _GNU_SOURCE
+#include <math.h>
+
+double scalb(double x, double fn)
+{
+ if (isnan(x) || isnan(fn))
+ return x*fn;
+ if (!isfinite(fn)) {
+ if (fn > 0.0)
+ return x*fn;
+ else
+ return x/(-fn);
+ }
+ if (rint(fn) != fn) return (fn-fn)/(fn-fn);
+ if ( fn > 65000.0) return scalbn(x, 65000);
+ if (-fn > 65000.0) return scalbn(x,-65000);
+ return scalbn(x,(int)fn);
+}
diff --git a/lib/libc/src/musl-math/scalbf.c b/lib/libc/src/musl-math/scalbf.c
new file mode 100644
index 0000000..f44ed5b
--- /dev/null
+++ b/lib/libc/src/musl-math/scalbf.c
@@ -0,0 +1,32 @@
+/* origin: FreeBSD /usr/src/lib/msun/src/e_scalbf.c */
+/*
+ * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com.
+ */
+/*
+ * ====================================================
+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ *
+ * Developed at SunPro, a Sun Microsystems, Inc. business.
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ */
+
+#define _GNU_SOURCE
+#include <math.h>
+
+float scalbf(float x, float fn)
+{
+ if (isnan(x) || isnan(fn)) return x*fn;
+ if (!isfinite(fn)) {
+ if (fn > 0.0f)
+ return x*fn;
+ else
+ return x/(-fn);
+ }
+ if (rintf(fn) != fn) return (fn-fn)/(fn-fn);
+ if ( fn > 65000.0f) return scalbnf(x, 65000);
+ if (-fn > 65000.0f) return scalbnf(x,-65000);
+ return scalbnf(x,(int)fn);
+}
diff --git a/lib/libc/src/musl-math/scalbln.c b/lib/libc/src/musl-math/scalbln.c
new file mode 100644
index 0000000..4fb3d06
--- /dev/null
+++ b/lib/libc/src/musl-math/scalbln.c
@@ -0,0 +1,12 @@
+#include <limits.h>
+#include <math.h>
+#include "libm.h"
+
+double scalbln(double x, long n)
+{
+ if (n > INT_MAX)
+ n = INT_MAX;
+ else if (n < INT_MIN)
+ n = INT_MIN;
+ return scalbn(x, n);
+}
diff --git a/lib/libc/src/musl-math/scalblnf.c b/lib/libc/src/musl-math/scalblnf.c
new file mode 100644
index 0000000..b6bdeed
--- /dev/null
+++ b/lib/libc/src/musl-math/scalblnf.c
@@ -0,0 +1,12 @@
+#include <limits.h>
+#include <math.h>
+#include "libm.h"
+
+float scalblnf(float x, long n)
+{
+ if (n > INT_MAX)
+ n = INT_MAX;
+ else if (n < INT_MIN)
+ n = INT_MIN;
+ return scalbnf(x, n);
+}
diff --git a/lib/libc/src/musl-math/scalblnl.c b/lib/libc/src/musl-math/scalblnl.c
new file mode 100644
index 0000000..b1a0f7f
--- /dev/null
+++ b/lib/libc/src/musl-math/scalblnl.c
@@ -0,0 +1,20 @@
+#include <limits.h>
+#include <math.h>
+#include <float.h>
+#include "libm.h"
+
+#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
+long double scalblnl(long double x, long n)
+{
+ return scalbln(x, n);
+}
+#else
+long double scalblnl(long double x, long n)
+{
+ if (n > INT_MAX)
+ n = INT_MAX;
+ else if (n < INT_MIN)
+ n = INT_MIN;
+ return scalbnl(x, n);
+}
+#endif
diff --git a/lib/libc/src/musl-math/scalbn.c b/lib/libc/src/musl-math/scalbn.c
new file mode 100644
index 0000000..182f561
--- /dev/null
+++ b/lib/libc/src/musl-math/scalbn.c
@@ -0,0 +1,33 @@
+#include <math.h>
+#include <stdint.h>
+
+double scalbn(double x, int n)
+{
+ union {double f; uint64_t i;} u;
+ double_t y = x;
+
+ if (n > 1023) {
+ y *= 0x1p1023;
+ n -= 1023;
+ if (n > 1023) {
+ y *= 0x1p1023;
+ n -= 1023;
+ if (n > 1023)
+ n = 1023;
+ }
+ } else if (n < -1022) {
+ /* make sure final n < -53 to avoid double
+ rounding in the subnormal range */
+ y *= 0x1p-1022 * 0x1p53;
+ n += 1022 - 53;
+ if (n < -1022) {
+ y *= 0x1p-1022 * 0x1p53;
+ n += 1022 - 53;
+ if (n < -1022)
+ n = -1022;
+ }
+ }
+ u.i = (uint64_t)(0x3ff+n)<<52;
+ x = y * u.f;
+ return x;
+}
diff --git a/lib/libc/src/musl-math/scalbnf.c b/lib/libc/src/musl-math/scalbnf.c
new file mode 100644
index 0000000..a5ad208
--- /dev/null
+++ b/lib/libc/src/musl-math/scalbnf.c
@@ -0,0 +1,31 @@
+#include <math.h>
+#include <stdint.h>
+
+float scalbnf(float x, int n)
+{
+ union {float f; uint32_t i;} u;
+ float_t y = x;
+
+ if (n > 127) {
+ y *= 0x1p127f;
+ n -= 127;
+ if (n > 127) {
+ y *= 0x1p127f;
+ n -= 127;
+ if (n > 127)
+ n = 127;
+ }
+ } else if (n < -126) {
+ y *= 0x1p-126f * 0x1p24f;
+ n += 126 - 24;
+ if (n < -126) {
+ y *= 0x1p-126f * 0x1p24f;
+ n += 126 - 24;
+ if (n < -126)
+ n = -126;
+ }
+ }
+ u.i = (uint32_t)(0x7f+n)<<23;
+ x = y * u.f;
+ return x;
+}
diff --git a/lib/libc/src/musl-math/scalbnl.c b/lib/libc/src/musl-math/scalbnl.c
new file mode 100644
index 0000000..db44dab
--- /dev/null
+++ b/lib/libc/src/musl-math/scalbnl.c
@@ -0,0 +1,36 @@
+#include "libm.h"
+
+#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
+long double scalbnl(long double x, int n)
+{
+ return scalbn(x, n);
+}
+#elif (LDBL_MANT_DIG == 64 || LDBL_MANT_DIG == 113) && LDBL_MAX_EXP == 16384
+long double scalbnl(long double x, int n)
+{
+ union ldshape u;
+
+ if (n > 16383) {
+ x *= 0x1p16383L;
+ n -= 16383;
+ if (n > 16383) {
+ x *= 0x1p16383L;
+ n -= 16383;
+ if (n > 16383)
+ n = 16383;
+ }
+ } else if (n < -16382) {
+ x *= 0x1p-16382L * 0x1p113L;
+ n += 16382 - 113;
+ if (n < -16382) {
+ x *= 0x1p-16382L * 0x1p113L;
+ n += 16382 - 113;
+ if (n < -16382)
+ n = -16382;
+ }
+ }
+ u.f = 1.0;
+ u.i.se = 0x3fff + n;
+ return x * u.f;
+}
+#endif
diff --git a/lib/libc/src/musl-math/signgam.c b/lib/libc/src/musl-math/signgam.c
new file mode 100644
index 0000000..3a5b9f7
--- /dev/null
+++ b/lib/libc/src/musl-math/signgam.c
@@ -0,0 +1,5 @@
+#include <math.h>
+#include "weak_alias.h"
+//#include "libc.h"
+
+int signgam = 0;
diff --git a/lib/libc/src/musl-math/significand.c b/lib/libc/src/musl-math/significand.c
new file mode 100644
index 0000000..40d9aa9
--- /dev/null
+++ b/lib/libc/src/musl-math/significand.c
@@ -0,0 +1,7 @@
+#define _GNU_SOURCE
+#include <math.h>
+
+double significand(double x)
+{
+ return scalbn(x, -ilogb(x));
+}
diff --git a/lib/libc/src/musl-math/significandf.c b/lib/libc/src/musl-math/significandf.c
new file mode 100644
index 0000000..8a697e1
--- /dev/null
+++ b/lib/libc/src/musl-math/significandf.c
@@ -0,0 +1,7 @@
+#define _GNU_SOURCE
+#include <math.h>
+
+float significandf(float x)
+{
+ return scalbnf(x, -ilogbf(x));
+}
diff --git a/lib/libc/src/musl-math/sin.c b/lib/libc/src/musl-math/sin.c
new file mode 100644
index 0000000..055e215
--- /dev/null
+++ b/lib/libc/src/musl-math/sin.c
@@ -0,0 +1,78 @@
+/* origin: FreeBSD /usr/src/lib/msun/src/s_sin.c */
+/*
+ * ====================================================
+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ *
+ * Developed at SunPro, a Sun Microsystems, Inc. business.
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ */
+/* sin(x)
+ * Return sine function of x.
+ *
+ * kernel function:
+ * __sin ... sine function on [-pi/4,pi/4]
+ * __cos ... cose function on [-pi/4,pi/4]
+ * __rem_pio2 ... argument reduction routine
+ *
+ * Method.
+ * Let S,C and T denote the sin, cos and tan respectively on
+ * [-PI/4, +PI/4]. Reduce the argument x to y1+y2 = x-k*pi/2
+ * in [-pi/4 , +pi/4], and let n = k mod 4.
+ * We have
+ *
+ * n sin(x) cos(x) tan(x)
+ * ----------------------------------------------------------
+ * 0 S C T
+ * 1 C -S -1/T
+ * 2 -S -C T
+ * 3 -C S -1/T
+ * ----------------------------------------------------------
+ *
+ * Special cases:
+ * Let trig be any of sin, cos, or tan.
+ * trig(+-INF) is NaN, with signals;
+ * trig(NaN) is that NaN;
+ *
+ * Accuracy:
+ * TRIG(x) returns trig(x) nearly rounded
+ */
+
+#include "libm.h"
+
+double sin(double x)
+{
+ double y[2];
+ uint32_t ix;
+ unsigned n;
+
+ /* High word of x. */
+ GET_HIGH_WORD(ix, x);
+ ix &= 0x7fffffff;
+
+ /* |x| ~< pi/4 */
+ if (ix <= 0x3fe921fb) {
+ if (ix < 0x3e500000) { /* |x| < 2**-26 */
+ /* raise inexact if x != 0 and underflow if subnormal*/
+ FORCE_EVAL(ix < 0x00100000 ? x/0x1p120f : x+0x1p120f);
+ return x;
+ }
+ return __sin(x, 0.0, 0);
+ }
+
+ /* sin(Inf or NaN) is NaN */
+ if (ix >= 0x7ff00000)
+ return x - x;
+
+ /* argument reduction needed */
+ n = __rem_pio2(x, y);
+ switch (n&3) {
+ case 0: return __sin(y[0], y[1], 1);
+ case 1: return __cos(y[0], y[1]);
+ case 2: return -__sin(y[0], y[1], 1);
+ default:
+ return -__cos(y[0], y[1]);
+ }
+}
diff --git a/lib/libc/src/musl-math/sincos.c b/lib/libc/src/musl-math/sincos.c
new file mode 100644
index 0000000..35b2d92
--- /dev/null
+++ b/lib/libc/src/musl-math/sincos.c
@@ -0,0 +1,69 @@
+/* origin: FreeBSD /usr/src/lib/msun/src/s_sin.c */
+/*
+ * ====================================================
+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ *
+ * Developed at SunPro, a Sun Microsystems, Inc. business.
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ */
+
+#define _GNU_SOURCE
+#include "libm.h"
+
+void sincos(double x, double *sin, double *cos)
+{
+ double y[2], s, c;
+ uint32_t ix;
+ unsigned n;
+
+ GET_HIGH_WORD(ix, x);
+ ix &= 0x7fffffff;
+
+ /* |x| ~< pi/4 */
+ if (ix <= 0x3fe921fb) {
+ /* if |x| < 2**-27 * sqrt(2) */
+ if (ix < 0x3e46a09e) {
+ /* raise inexact if x!=0 and underflow if subnormal */
+ FORCE_EVAL(ix < 0x00100000 ? x/0x1p120f : x+0x1p120f);
+ *sin = x;
+ *cos = 1.0;
+ return;
+ }
+ *sin = __sin(x, 0.0, 0);
+ *cos = __cos(x, 0.0);
+ return;
+ }
+
+ /* sincos(Inf or NaN) is NaN */
+ if (ix >= 0x7ff00000) {
+ *sin = *cos = x - x;
+ return;
+ }
+
+ /* argument reduction needed */
+ n = __rem_pio2(x, y);
+ s = __sin(y[0], y[1], 1);
+ c = __cos(y[0], y[1]);
+ switch (n&3) {
+ case 0:
+ *sin = s;
+ *cos = c;
+ break;
+ case 1:
+ *sin = c;
+ *cos = -s;
+ break;
+ case 2:
+ *sin = -s;
+ *cos = -c;
+ break;
+ case 3:
+ default:
+ *sin = -c;
+ *cos = s;
+ break;
+ }
+}
diff --git a/lib/libc/src/musl-math/sincosf.c b/lib/libc/src/musl-math/sincosf.c
new file mode 100644
index 0000000..f8ca723
--- /dev/null
+++ b/lib/libc/src/musl-math/sincosf.c
@@ -0,0 +1,117 @@
+/* origin: FreeBSD /usr/src/lib/msun/src/s_sinf.c */
+/*
+ * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com.
+ * Optimized by Bruce D. Evans.
+ */
+/*
+ * ====================================================
+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ *
+ * Developed at SunPro, a Sun Microsystems, Inc. business.
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ */
+
+#define _GNU_SOURCE
+#include "libm.h"
+
+/* Small multiples of pi/2 rounded to double precision. */
+static const double
+s1pio2 = 1*M_PI_2, /* 0x3FF921FB, 0x54442D18 */
+s2pio2 = 2*M_PI_2, /* 0x400921FB, 0x54442D18 */
+s3pio2 = 3*M_PI_2, /* 0x4012D97C, 0x7F3321D2 */
+s4pio2 = 4*M_PI_2; /* 0x401921FB, 0x54442D18 */
+
+void sincosf(float x, float *sin, float *cos)
+{
+ double y;
+ float_t s, c;
+ uint32_t ix;
+ unsigned n, sign;
+
+ GET_FLOAT_WORD(ix, x);
+ sign = ix >> 31;
+ ix &= 0x7fffffff;
+
+ /* |x| ~<= pi/4 */
+ if (ix <= 0x3f490fda) {
+ /* |x| < 2**-12 */
+ if (ix < 0x39800000) {
+ /* raise inexact if x!=0 and underflow if subnormal */
+ FORCE_EVAL(ix < 0x00100000 ? x/0x1p120f : x+0x1p120f);
+ *sin = x;
+ *cos = 1.0f;
+ return;
+ }
+ *sin = __sindf(x);
+ *cos = __cosdf(x);
+ return;
+ }
+
+ /* |x| ~<= 5*pi/4 */
+ if (ix <= 0x407b53d1) {
+ if (ix <= 0x4016cbe3) { /* |x| ~<= 3pi/4 */
+ if (sign) {
+ *sin = -__cosdf(x + s1pio2);
+ *cos = __sindf(x + s1pio2);
+ } else {
+ *sin = __cosdf(s1pio2 - x);
+ *cos = __sindf(s1pio2 - x);
+ }
+ return;
+ }
+ /* -sin(x+c) is not correct if x+c could be 0: -0 vs +0 */
+ *sin = -__sindf(sign ? x + s2pio2 : x - s2pio2);
+ *cos = -__cosdf(sign ? x + s2pio2 : x - s2pio2);
+ return;
+ }
+
+ /* |x| ~<= 9*pi/4 */
+ if (ix <= 0x40e231d5) {
+ if (ix <= 0x40afeddf) { /* |x| ~<= 7*pi/4 */
+ if (sign) {
+ *sin = __cosdf(x + s3pio2);
+ *cos = -__sindf(x + s3pio2);
+ } else {
+ *sin = -__cosdf(x - s3pio2);
+ *cos = __sindf(x - s3pio2);
+ }
+ return;
+ }
+ *sin = __sindf(sign ? x + s4pio2 : x - s4pio2);
+ *cos = __cosdf(sign ? x + s4pio2 : x - s4pio2);
+ return;
+ }
+
+ /* sin(Inf or NaN) is NaN */
+ if (ix >= 0x7f800000) {
+ *sin = *cos = x - x;
+ return;
+ }
+
+ /* general argument reduction needed */
+ n = __rem_pio2f(x, &y);
+ s = __sindf(y);
+ c = __cosdf(y);
+ switch (n&3) {
+ case 0:
+ *sin = s;
+ *cos = c;
+ break;
+ case 1:
+ *sin = c;
+ *cos = -s;
+ break;
+ case 2:
+ *sin = -s;
+ *cos = -c;
+ break;
+ case 3:
+ default:
+ *sin = -c;
+ *cos = s;
+ break;
+ }
+}
diff --git a/lib/libc/src/musl-math/sincosl.c b/lib/libc/src/musl-math/sincosl.c
new file mode 100644
index 0000000..d3ac1c4
--- /dev/null
+++ b/lib/libc/src/musl-math/sincosl.c
@@ -0,0 +1,60 @@
+#define _GNU_SOURCE
+#include "libm.h"
+
+#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
+void sincosl(long double x, long double *sin, long double *cos)
+{
+ double sind, cosd;
+ sincos(x, &sind, &cosd);
+ *sin = sind;
+ *cos = cosd;
+}
+#elif (LDBL_MANT_DIG == 64 || LDBL_MANT_DIG == 113) && LDBL_MAX_EXP == 16384
+void sincosl(long double x, long double *sin, long double *cos)
+{
+ union ldshape u = {x};
+ unsigned n;
+ long double y[2], s, c;
+
+ u.i.se &= 0x7fff;
+ if (u.i.se == 0x7fff) {
+ *sin = *cos = x - x;
+ return;
+ }
+ if (u.f < M_PI_4) {
+ if (u.i.se < 0x3fff - LDBL_MANT_DIG) {
+ /* raise underflow if subnormal */
+ if (u.i.se == 0) FORCE_EVAL(x*0x1p-120f);
+ *sin = x;
+ /* raise inexact if x!=0 */
+ *cos = 1.0 + x;
+ return;
+ }
+ *sin = __sinl(x, 0, 0);
+ *cos = __cosl(x, 0);
+ return;
+ }
+ n = __rem_pio2l(x, y);
+ s = __sinl(y[0], y[1], 1);
+ c = __cosl(y[0], y[1]);
+ switch (n & 3) {
+ case 0:
+ *sin = s;
+ *cos = c;
+ break;
+ case 1:
+ *sin = c;
+ *cos = -s;
+ break;
+ case 2:
+ *sin = -s;
+ *cos = -c;
+ break;
+ case 3:
+ default:
+ *sin = -c;
+ *cos = s;
+ break;
+ }
+}
+#endif
diff --git a/lib/libc/src/musl-math/sinf.c b/lib/libc/src/musl-math/sinf.c
new file mode 100644
index 0000000..64e39f5
--- /dev/null
+++ b/lib/libc/src/musl-math/sinf.c
@@ -0,0 +1,76 @@
+/* origin: FreeBSD /usr/src/lib/msun/src/s_sinf.c */
+/*
+ * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com.
+ * Optimized by Bruce D. Evans.
+ */
+/*
+ * ====================================================
+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ *
+ * Developed at SunPro, a Sun Microsystems, Inc. business.
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ */
+
+#include "libm.h"
+
+/* Small multiples of pi/2 rounded to double precision. */
+static const double
+s1pio2 = 1*M_PI_2, /* 0x3FF921FB, 0x54442D18 */
+s2pio2 = 2*M_PI_2, /* 0x400921FB, 0x54442D18 */
+s3pio2 = 3*M_PI_2, /* 0x4012D97C, 0x7F3321D2 */
+s4pio2 = 4*M_PI_2; /* 0x401921FB, 0x54442D18 */
+
+float sinf(float x)
+{
+ double y;
+ uint32_t ix;
+ int n, sign;
+
+ GET_FLOAT_WORD(ix, x);
+ sign = ix >> 31;
+ ix &= 0x7fffffff;
+
+ if (ix <= 0x3f490fda) { /* |x| ~<= pi/4 */
+ if (ix < 0x39800000) { /* |x| < 2**-12 */
+ /* raise inexact if x!=0 and underflow if subnormal */
+ FORCE_EVAL(ix < 0x00800000 ? x/0x1p120f : x+0x1p120f);
+ return x;
+ }
+ return __sindf(x);
+ }
+ if (ix <= 0x407b53d1) { /* |x| ~<= 5*pi/4 */
+ if (ix <= 0x4016cbe3) { /* |x| ~<= 3pi/4 */
+ if (sign)
+ return -__cosdf(x + s1pio2);
+ else
+ return __cosdf(x - s1pio2);
+ }
+ return __sindf(sign ? -(x + s2pio2) : -(x - s2pio2));
+ }
+ if (ix <= 0x40e231d5) { /* |x| ~<= 9*pi/4 */
+ if (ix <= 0x40afeddf) { /* |x| ~<= 7*pi/4 */
+ if (sign)
+ return __cosdf(x + s3pio2);
+ else
+ return -__cosdf(x - s3pio2);
+ }
+ return __sindf(sign ? x + s4pio2 : x - s4pio2);
+ }
+
+ /* sin(Inf or NaN) is NaN */
+ if (ix >= 0x7f800000)
+ return x - x;
+
+ /* general argument reduction needed */
+ n = __rem_pio2f(x, &y);
+ switch (n&3) {
+ case 0: return __sindf(y);
+ case 1: return __cosdf(y);
+ case 2: return __sindf(-y);
+ default:
+ return -__cosdf(y);
+ }
+}
diff --git a/lib/libc/src/musl-math/sinh.c b/lib/libc/src/musl-math/sinh.c
new file mode 100644
index 0000000..00022c4
--- /dev/null
+++ b/lib/libc/src/musl-math/sinh.c
@@ -0,0 +1,39 @@
+#include "libm.h"
+
+/* sinh(x) = (exp(x) - 1/exp(x))/2
+ * = (exp(x)-1 + (exp(x)-1)/exp(x))/2
+ * = x + x^3/6 + o(x^5)
+ */
+double sinh(double x)
+{
+ union {double f; uint64_t i;} u = {.f = x};
+ uint32_t w;
+ double t, h, absx;
+
+ h = 0.5;
+ if (u.i >> 63)
+ h = -h;
+ /* |x| */
+ u.i &= (uint64_t)-1/2;
+ absx = u.f;
+ w = u.i >> 32;
+
+ /* |x| < log(DBL_MAX) */
+ if (w < 0x40862e42) {
+ t = expm1(absx);
+ if (w < 0x3ff00000) {
+ if (w < 0x3ff00000 - (26<<20))
+ /* note: inexact and underflow are raised by expm1 */
+ /* note: this branch avoids spurious underflow */
+ return x;
+ return h*(2*t - t*t/(t+1));
+ }
+ /* note: |x|>log(0x1p26)+eps could be just h*exp(x) */
+ return h*(t + t/(t+1));
+ }
+
+ /* |x| > log(DBL_MAX) or nan */
+ /* note: the result is stored to handle overflow */
+ t = 2*h*__expo2(absx);
+ return t;
+}
diff --git a/lib/libc/src/musl-math/sinhf.c b/lib/libc/src/musl-math/sinhf.c
new file mode 100644
index 0000000..6ad19ea
--- /dev/null
+++ b/lib/libc/src/musl-math/sinhf.c
@@ -0,0 +1,31 @@
+#include "libm.h"
+
+float sinhf(float x)
+{
+ union {float f; uint32_t i;} u = {.f = x};
+ uint32_t w;
+ float t, h, absx;
+
+ h = 0.5;
+ if (u.i >> 31)
+ h = -h;
+ /* |x| */
+ u.i &= 0x7fffffff;
+ absx = u.f;
+ w = u.i;
+
+ /* |x| < log(FLT_MAX) */
+ if (w < 0x42b17217) {
+ t = expm1f(absx);
+ if (w < 0x3f800000) {
+ if (w < 0x3f800000 - (12<<23))
+ return x;
+ return h*(2*t - t*t/(t+1));
+ }
+ return h*(t + t/(t+1));
+ }
+
+ /* |x| > logf(FLT_MAX) or nan */
+ t = 2*h*__expo2f(absx);
+ return t;
+}
diff --git a/lib/libc/src/musl-math/sinhl.c b/lib/libc/src/musl-math/sinhl.c
new file mode 100644
index 0000000..b305d4d
--- /dev/null
+++ b/lib/libc/src/musl-math/sinhl.c
@@ -0,0 +1,43 @@
+#include "libm.h"
+
+#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
+long double sinhl(long double x)
+{
+ return sinh(x);
+}
+#elif LDBL_MANT_DIG == 64 && LDBL_MAX_EXP == 16384
+long double sinhl(long double x)
+{
+ union ldshape u = {x};
+ unsigned ex = u.i.se & 0x7fff;
+ long double h, t, absx;
+
+ h = 0.5;
+ if (u.i.se & 0x8000)
+ h = -h;
+ /* |x| */
+ u.i.se = ex;
+ absx = u.f;
+
+ /* |x| < log(LDBL_MAX) */
+ if (ex < 0x3fff+13 || (ex == 0x3fff+13 && u.i.m>>32 < 0xb17217f7)) {
+ t = expm1l(absx);
+ if (ex < 0x3fff) {
+ if (ex < 0x3fff-32)
+ return x;
+ return h*(2*t - t*t/(1+t));
+ }
+ return h*(t + t/(t+1));
+ }
+
+ /* |x| > log(LDBL_MAX) or nan */
+ t = expl(0.5*absx);
+ return h*t*t;
+}
+#elif LDBL_MANT_DIG == 113 && LDBL_MAX_EXP == 16384
+// TODO: broken implementation to make things compile
+long double sinhl(long double x)
+{
+ return sinh(x);
+}
+#endif
diff --git a/lib/libc/src/musl-math/sinl.c b/lib/libc/src/musl-math/sinl.c
new file mode 100644
index 0000000..9c0b16e
--- /dev/null
+++ b/lib/libc/src/musl-math/sinl.c
@@ -0,0 +1,41 @@
+#include "libm.h"
+
+#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
+long double sinl(long double x)
+{
+ return sin(x);
+}
+#elif (LDBL_MANT_DIG == 64 || LDBL_MANT_DIG == 113) && LDBL_MAX_EXP == 16384
+long double sinl(long double x)
+{
+ union ldshape u = {x};
+ unsigned n;
+ long double y[2], hi, lo;
+
+ u.i.se &= 0x7fff;
+ if (u.i.se == 0x7fff)
+ return x - x;
+ if (u.f < M_PI_4) {
+ if (u.i.se < 0x3fff - LDBL_MANT_DIG/2) {
+ /* raise inexact if x!=0 and underflow if subnormal */
+ FORCE_EVAL(u.i.se == 0 ? x*0x1p-120f : x+0x1p120f);
+ return x;
+ }
+ return __sinl(x, 0.0, 0);
+ }
+ n = __rem_pio2l(x, y);
+ hi = y[0];
+ lo = y[1];
+ switch (n & 3) {
+ case 0:
+ return __sinl(hi, lo, 1);
+ case 1:
+ return __cosl(hi, lo);
+ case 2:
+ return -__sinl(hi, lo, 1);
+ case 3:
+ default:
+ return -__cosl(hi, lo);
+ }
+}
+#endif
diff --git a/lib/libc/src/musl-math/sqrt.c b/lib/libc/src/musl-math/sqrt.c
new file mode 100644
index 0000000..b277567
--- /dev/null
+++ b/lib/libc/src/musl-math/sqrt.c
@@ -0,0 +1,185 @@
+/* origin: FreeBSD /usr/src/lib/msun/src/e_sqrt.c */
+/*
+ * ====================================================
+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ *
+ * Developed at SunSoft, a Sun Microsystems, Inc. business.
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ */
+/* sqrt(x)
+ * Return correctly rounded sqrt.
+ * ------------------------------------------
+ * | Use the hardware sqrt if you have one |
+ * ------------------------------------------
+ * Method:
+ * Bit by bit method using integer arithmetic. (Slow, but portable)
+ * 1. Normalization
+ * Scale x to y in [1,4) with even powers of 2:
+ * find an integer k such that 1 <= (y=x*2^(2k)) < 4, then
+ * sqrt(x) = 2^k * sqrt(y)
+ * 2. Bit by bit computation
+ * Let q = sqrt(y) truncated to i bit after binary point (q = 1),
+ * i 0
+ * i+1 2
+ * s = 2*q , and y = 2 * ( y - q ). (1)
+ * i i i i
+ *
+ * To compute q from q , one checks whether
+ * i+1 i
+ *
+ * -(i+1) 2
+ * (q + 2 ) <= y. (2)
+ * i
+ * -(i+1)
+ * If (2) is false, then q = q ; otherwise q = q + 2 .
+ * i+1 i i+1 i
+ *
+ * With some algebric manipulation, it is not difficult to see
+ * that (2) is equivalent to
+ * -(i+1)
+ * s + 2 <= y (3)
+ * i i
+ *
+ * The advantage of (3) is that s and y can be computed by
+ * i i
+ * the following recurrence formula:
+ * if (3) is false
+ *
+ * s = s , y = y ; (4)
+ * i+1 i i+1 i
+ *
+ * otherwise,
+ * -i -(i+1)
+ * s = s + 2 , y = y - s - 2 (5)
+ * i+1 i i+1 i i
+ *
+ * One may easily use induction to prove (4) and (5).
+ * Note. Since the left hand side of (3) contain only i+2 bits,
+ * it does not necessary to do a full (53-bit) comparison
+ * in (3).
+ * 3. Final rounding
+ * After generating the 53 bits result, we compute one more bit.
+ * Together with the remainder, we can decide whether the
+ * result is exact, bigger than 1/2ulp, or less than 1/2ulp
+ * (it will never equal to 1/2ulp).
+ * The rounding mode can be detected by checking whether
+ * huge + tiny is equal to huge, and whether huge - tiny is
+ * equal to huge for some floating point number "huge" and "tiny".
+ *
+ * Special cases:
+ * sqrt(+-0) = +-0 ... exact
+ * sqrt(inf) = inf
+ * sqrt(-ve) = NaN ... with invalid signal
+ * sqrt(NaN) = NaN ... with invalid signal for signaling NaN
+ */
+
+#include "libm.h"
+
+static const double tiny = 1.0e-300;
+
+double sqrt(double x)
+{
+ double z;
+ int32_t sign = (int)0x80000000;
+ int32_t ix0,s0,q,m,t,i;
+ uint32_t r,t1,s1,ix1,q1;
+
+ EXTRACT_WORDS(ix0, ix1, x);
+
+ /* take care of Inf and NaN */
+ if ((ix0&0x7ff00000) == 0x7ff00000) {
+ return x*x + x; /* sqrt(NaN)=NaN, sqrt(+inf)=+inf, sqrt(-inf)=sNaN */
+ }
+ /* take care of zero */
+ if (ix0 <= 0) {
+ if (((ix0&~sign)|ix1) == 0)
+ return x; /* sqrt(+-0) = +-0 */
+ if (ix0 < 0)
+ return (x-x)/(x-x); /* sqrt(-ve) = sNaN */
+ }
+ /* normalize x */
+ m = ix0>>20;
+ if (m == 0) { /* subnormal x */
+ while (ix0 == 0) {
+ m -= 21;
+ ix0 |= (ix1>>11);
+ ix1 <<= 21;
+ }
+ for (i=0; (ix0&0x00100000) == 0; i++)
+ ix0<<=1;
+ m -= i - 1;
+ ix0 |= ix1>>(32-i);
+ ix1 <<= i;
+ }
+ m -= 1023; /* unbias exponent */
+ ix0 = (ix0&0x000fffff)|0x00100000;
+ if (m & 1) { /* odd m, double x to make it even */
+ ix0 += ix0 + ((ix1&sign)>>31);
+ ix1 += ix1;
+ }
+ m >>= 1; /* m = [m/2] */
+
+ /* generate sqrt(x) bit by bit */
+ ix0 += ix0 + ((ix1&sign)>>31);
+ ix1 += ix1;
+ q = q1 = s0 = s1 = 0; /* [q,q1] = sqrt(x) */
+ r = 0x00200000; /* r = moving bit from right to left */
+
+ while (r != 0) {
+ t = s0 + r;
+ if (t <= ix0) {
+ s0 = t + r;
+ ix0 -= t;
+ q += r;
+ }
+ ix0 += ix0 + ((ix1&sign)>>31);
+ ix1 += ix1;
+ r >>= 1;
+ }
+
+ r = sign;
+ while (r != 0) {
+ t1 = s1 + r;
+ t = s0;
+ if (t < ix0 || (t == ix0 && t1 <= ix1)) {
+ s1 = t1 + r;
+ if ((t1&sign) == sign && (s1&sign) == 0)
+ s0++;
+ ix0 -= t;
+ if (ix1 < t1)
+ ix0--;
+ ix1 -= t1;
+ q1 += r;
+ }
+ ix0 += ix0 + ((ix1&sign)>>31);
+ ix1 += ix1;
+ r >>= 1;
+ }
+
+ /* use floating add to find out rounding direction */
+ if ((ix0|ix1) != 0) {
+ z = 1.0 - tiny; /* raise inexact flag */
+ if (z >= 1.0) {
+ z = 1.0 + tiny;
+ if (q1 == (uint32_t)0xffffffff) {
+ q1 = 0;
+ q++;
+ } else if (z > 1.0) {
+ if (q1 == (uint32_t)0xfffffffe)
+ q++;
+ q1 += 2;
+ } else
+ q1 += q1 & 1;
+ }
+ }
+ ix0 = (q>>1) + 0x3fe00000;
+ ix1 = q1>>1;
+ if (q&1)
+ ix1 |= sign;
+ ix0 += m << 20;
+ INSERT_WORDS(z, ix0, ix1);
+ return z;
+}
diff --git a/lib/libc/src/musl-math/sqrtf.c b/lib/libc/src/musl-math/sqrtf.c
new file mode 100644
index 0000000..28cb4ad
--- /dev/null
+++ b/lib/libc/src/musl-math/sqrtf.c
@@ -0,0 +1,84 @@
+/* origin: FreeBSD /usr/src/lib/msun/src/e_sqrtf.c */
+/*
+ * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com.
+ */
+/*
+ * ====================================================
+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ *
+ * Developed at SunPro, a Sun Microsystems, Inc. business.
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ */
+
+#include "libm.h"
+
+static const float tiny = 1.0e-30;
+
+float sqrtf(float x)
+{
+ float z;
+ int32_t sign = (int)0x80000000;
+ int32_t ix,s,q,m,t,i;
+ uint32_t r;
+
+ GET_FLOAT_WORD(ix, x);
+
+ /* take care of Inf and NaN */
+ if ((ix&0x7f800000) == 0x7f800000)
+ return x*x + x; /* sqrt(NaN)=NaN, sqrt(+inf)=+inf, sqrt(-inf)=sNaN */
+
+ /* take care of zero */
+ if (ix <= 0) {
+ if ((ix&~sign) == 0)
+ return x; /* sqrt(+-0) = +-0 */
+ if (ix < 0)
+ return (x-x)/(x-x); /* sqrt(-ve) = sNaN */
+ }
+ /* normalize x */
+ m = ix>>23;
+ if (m == 0) { /* subnormal x */
+ for (i = 0; (ix&0x00800000) == 0; i++)
+ ix<<=1;
+ m -= i - 1;
+ }
+ m -= 127; /* unbias exponent */
+ ix = (ix&0x007fffff)|0x00800000;
+ if (m&1) /* odd m, double x to make it even */
+ ix += ix;
+ m >>= 1; /* m = [m/2] */
+
+ /* generate sqrt(x) bit by bit */
+ ix += ix;
+ q = s = 0; /* q = sqrt(x) */
+ r = 0x01000000; /* r = moving bit from right to left */
+
+ while (r != 0) {
+ t = s + r;
+ if (t <= ix) {
+ s = t+r;
+ ix -= t;
+ q += r;
+ }
+ ix += ix;
+ r >>= 1;
+ }
+
+ /* use floating add to find out rounding direction */
+ if (ix != 0) {
+ z = 1.0f - tiny; /* raise inexact flag */
+ if (z >= 1.0f) {
+ z = 1.0f + tiny;
+ if (z > 1.0f)
+ q += 2;
+ else
+ q += q & 1;
+ }
+ }
+ ix = (q>>1) + 0x3f000000;
+ ix += m << 23;
+ SET_FLOAT_WORD(z, ix);
+ return z;
+}
diff --git a/lib/libc/src/musl-math/sqrtl.c b/lib/libc/src/musl-math/sqrtl.c
new file mode 100644
index 0000000..83a8f80
--- /dev/null
+++ b/lib/libc/src/musl-math/sqrtl.c
@@ -0,0 +1,7 @@
+#include <math.h>
+
+long double sqrtl(long double x)
+{
+ /* FIXME: implement in C, this is for LDBL_MANT_DIG == 64 only */
+ return sqrt(x);
+}
diff --git a/lib/libc/src/musl-math/tan.c b/lib/libc/src/musl-math/tan.c
new file mode 100644
index 0000000..9c724a4
--- /dev/null
+++ b/lib/libc/src/musl-math/tan.c
@@ -0,0 +1,70 @@
+/* origin: FreeBSD /usr/src/lib/msun/src/s_tan.c */
+/*
+ * ====================================================
+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ *
+ * Developed at SunPro, a Sun Microsystems, Inc. business.
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ */
+/* tan(x)
+ * Return tangent function of x.
+ *
+ * kernel function:
+ * __tan ... tangent function on [-pi/4,pi/4]
+ * __rem_pio2 ... argument reduction routine
+ *
+ * Method.
+ * Let S,C and T denote the sin, cos and tan respectively on
+ * [-PI/4, +PI/4]. Reduce the argument x to y1+y2 = x-k*pi/2
+ * in [-pi/4 , +pi/4], and let n = k mod 4.
+ * We have
+ *
+ * n sin(x) cos(x) tan(x)
+ * ----------------------------------------------------------
+ * 0 S C T
+ * 1 C -S -1/T
+ * 2 -S -C T
+ * 3 -C S -1/T
+ * ----------------------------------------------------------
+ *
+ * Special cases:
+ * Let trig be any of sin, cos, or tan.
+ * trig(+-INF) is NaN, with signals;
+ * trig(NaN) is that NaN;
+ *
+ * Accuracy:
+ * TRIG(x) returns trig(x) nearly rounded
+ */
+
+#include "libm.h"
+
+double tan(double x)
+{
+ double y[2];
+ uint32_t ix;
+ unsigned n;
+
+ GET_HIGH_WORD(ix, x);
+ ix &= 0x7fffffff;
+
+ /* |x| ~< pi/4 */
+ if (ix <= 0x3fe921fb) {
+ if (ix < 0x3e400000) { /* |x| < 2**-27 */
+ /* raise inexact if x!=0 and underflow if subnormal */
+ FORCE_EVAL(ix < 0x00100000 ? x/0x1p120f : x+0x1p120f);
+ return x;
+ }
+ return __tan(x, 0.0, 0);
+ }
+
+ /* tan(Inf or NaN) is NaN */
+ if (ix >= 0x7ff00000)
+ return x - x;
+
+ /* argument reduction */
+ n = __rem_pio2(x, y);
+ return __tan(y[0], y[1], n&1);
+}
diff --git a/lib/libc/src/musl-math/tanf.c b/lib/libc/src/musl-math/tanf.c
new file mode 100644
index 0000000..aba1977
--- /dev/null
+++ b/lib/libc/src/musl-math/tanf.c
@@ -0,0 +1,64 @@
+/* origin: FreeBSD /usr/src/lib/msun/src/s_tanf.c */
+/*
+ * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com.
+ * Optimized by Bruce D. Evans.
+ */
+/*
+ * ====================================================
+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ *
+ * Developed at SunPro, a Sun Microsystems, Inc. business.
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ */
+
+#include "libm.h"
+
+/* Small multiples of pi/2 rounded to double precision. */
+static const double
+t1pio2 = 1*M_PI_2, /* 0x3FF921FB, 0x54442D18 */
+t2pio2 = 2*M_PI_2, /* 0x400921FB, 0x54442D18 */
+t3pio2 = 3*M_PI_2, /* 0x4012D97C, 0x7F3321D2 */
+t4pio2 = 4*M_PI_2; /* 0x401921FB, 0x54442D18 */
+
+float tanf(float x)
+{
+ double y;
+ uint32_t ix;
+ unsigned n, sign;
+
+ GET_FLOAT_WORD(ix, x);
+ sign = ix >> 31;
+ ix &= 0x7fffffff;
+
+ if (ix <= 0x3f490fda) { /* |x| ~<= pi/4 */
+ if (ix < 0x39800000) { /* |x| < 2**-12 */
+ /* raise inexact if x!=0 and underflow if subnormal */
+ FORCE_EVAL(ix < 0x00800000 ? x/0x1p120f : x+0x1p120f);
+ return x;
+ }
+ return __tandf(x, 0);
+ }
+ if (ix <= 0x407b53d1) { /* |x| ~<= 5*pi/4 */
+ if (ix <= 0x4016cbe3) /* |x| ~<= 3pi/4 */
+ return __tandf((sign ? x+t1pio2 : x-t1pio2), 1);
+ else
+ return __tandf((sign ? x+t2pio2 : x-t2pio2), 0);
+ }
+ if (ix <= 0x40e231d5) { /* |x| ~<= 9*pi/4 */
+ if (ix <= 0x40afeddf) /* |x| ~<= 7*pi/4 */
+ return __tandf((sign ? x+t3pio2 : x-t3pio2), 1);
+ else
+ return __tandf((sign ? x+t4pio2 : x-t4pio2), 0);
+ }
+
+ /* tan(Inf or NaN) is NaN */
+ if (ix >= 0x7f800000)
+ return x - x;
+
+ /* argument reduction */
+ n = __rem_pio2f(x, &y);
+ return __tandf(y, n&1);
+}
diff --git a/lib/libc/src/musl-math/tanh.c b/lib/libc/src/musl-math/tanh.c
new file mode 100644
index 0000000..20d6dbc
--- /dev/null
+++ b/lib/libc/src/musl-math/tanh.c
@@ -0,0 +1,45 @@
+#include "libm.h"
+
+/* tanh(x) = (exp(x) - exp(-x))/(exp(x) + exp(-x))
+ * = (exp(2*x) - 1)/(exp(2*x) - 1 + 2)
+ * = (1 - exp(-2*x))/(exp(-2*x) - 1 + 2)
+ */
+double tanh(double x)
+{
+ union {double f; uint64_t i;} u = {.f = x};
+ uint32_t w;
+ int sign;
+ double_t t;
+
+ /* x = |x| */
+ sign = u.i >> 63;
+ u.i &= (uint64_t)-1/2;
+ x = u.f;
+ w = u.i >> 32;
+
+ if (w > 0x3fe193ea) {
+ /* |x| > log(3)/2 ~= 0.5493 or nan */
+ if (w > 0x40340000) {
+ /* |x| > 20 or nan */
+ /* note: this branch avoids raising overflow */
+ t = 1 - 0/x;
+ } else {
+ t = expm1(2*x);
+ t = 1 - 2/(t+2);
+ }
+ } else if (w > 0x3fd058ae) {
+ /* |x| > log(5/3)/2 ~= 0.2554 */
+ t = expm1(2*x);
+ t = t/(t+2);
+ } else if (w >= 0x00100000) {
+ /* |x| >= 0x1p-1022, up to 2ulp error in [0.1,0.2554] */
+ t = expm1(-2*x);
+ t = -t/(t+2);
+ } else {
+ /* |x| is subnormal */
+ /* note: the branch above would not raise underflow in [0x1p-1023,0x1p-1022) */
+ FORCE_EVAL((float)x);
+ t = x;
+ }
+ return sign ? -t : t;
+}
diff --git a/lib/libc/src/musl-math/tanhf.c b/lib/libc/src/musl-math/tanhf.c
new file mode 100644
index 0000000..10636fb
--- /dev/null
+++ b/lib/libc/src/musl-math/tanhf.c
@@ -0,0 +1,39 @@
+#include "libm.h"
+
+float tanhf(float x)
+{
+ union {float f; uint32_t i;} u = {.f = x};
+ uint32_t w;
+ int sign;
+ float t;
+
+ /* x = |x| */
+ sign = u.i >> 31;
+ u.i &= 0x7fffffff;
+ x = u.f;
+ w = u.i;
+
+ if (w > 0x3f0c9f54) {
+ /* |x| > log(3)/2 ~= 0.5493 or nan */
+ if (w > 0x41200000) {
+ /* |x| > 10 */
+ t = 1 + 0/x;
+ } else {
+ t = expm1f(2*x);
+ t = 1 - 2/(t+2);
+ }
+ } else if (w > 0x3e82c578) {
+ /* |x| > log(5/3)/2 ~= 0.2554 */
+ t = expm1f(2*x);
+ t = t/(t+2);
+ } else if (w >= 0x00800000) {
+ /* |x| >= 0x1p-126 */
+ t = expm1f(-2*x);
+ t = -t/(t+2);
+ } else {
+ /* |x| is subnormal */
+ FORCE_EVAL(x*x);
+ t = x;
+ }
+ return sign ? -t : t;
+}
diff --git a/lib/libc/src/musl-math/tanhl.c b/lib/libc/src/musl-math/tanhl.c
new file mode 100644
index 0000000..4e1aa9f
--- /dev/null
+++ b/lib/libc/src/musl-math/tanhl.c
@@ -0,0 +1,48 @@
+#include "libm.h"
+
+#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
+long double tanhl(long double x)
+{
+ return tanh(x);
+}
+#elif LDBL_MANT_DIG == 64 && LDBL_MAX_EXP == 16384
+long double tanhl(long double x)
+{
+ union ldshape u = {x};
+ unsigned ex = u.i.se & 0x7fff;
+ unsigned sign = u.i.se & 0x8000;
+ uint32_t w;
+ long double t;
+
+ /* x = |x| */
+ u.i.se = ex;
+ x = u.f;
+ w = u.i.m >> 32;
+
+ if (ex > 0x3ffe || (ex == 0x3ffe && w > 0x8c9f53d5)) {
+ /* |x| > log(3)/2 ~= 0.5493 or nan */
+ if (ex >= 0x3fff+5) {
+ /* |x| >= 32 */
+ t = 1 + 0/(x + 0x1p-120f);
+ } else {
+ t = expm1l(2*x);
+ t = 1 - 2/(t+2);
+ }
+ } else if (ex > 0x3ffd || (ex == 0x3ffd && w > 0x82c577d4)) {
+ /* |x| > log(5/3)/2 ~= 0.2554 */
+ t = expm1l(2*x);
+ t = t/(t+2);
+ } else {
+ /* |x| is small */
+ t = expm1l(-2*x);
+ t = -t/(t+2);
+ }
+ return sign ? -t : t;
+}
+#elif LDBL_MANT_DIG == 113 && LDBL_MAX_EXP == 16384
+// TODO: broken implementation to make things compile
+long double tanhl(long double x)
+{
+ return tanh(x);
+}
+#endif
diff --git a/lib/libc/src/musl-math/tanl.c b/lib/libc/src/musl-math/tanl.c
new file mode 100644
index 0000000..6af0671
--- /dev/null
+++ b/lib/libc/src/musl-math/tanl.c
@@ -0,0 +1,29 @@
+#include "libm.h"
+
+#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
+long double tanl(long double x)
+{
+ return tan(x);
+}
+#elif (LDBL_MANT_DIG == 64 || LDBL_MANT_DIG == 113) && LDBL_MAX_EXP == 16384
+long double tanl(long double x)
+{
+ union ldshape u = {x};
+ long double y[2];
+ unsigned n;
+
+ u.i.se &= 0x7fff;
+ if (u.i.se == 0x7fff)
+ return x - x;
+ if (u.f < M_PI_4) {
+ if (u.i.se < 0x3fff - LDBL_MANT_DIG/2) {
+ /* raise inexact if x!=0 and underflow if subnormal */
+ FORCE_EVAL(u.i.se == 0 ? x*0x1p-120f : x+0x1p120f);
+ return x;
+ }
+ return __tanl(x, 0, 0);
+ }
+ n = __rem_pio2l(x, y);
+ return __tanl(y[0], y[1], n&1);
+}
+#endif
diff --git a/lib/libc/src/musl-math/tgamma.c b/lib/libc/src/musl-math/tgamma.c
new file mode 100644
index 0000000..28f6e0f
--- /dev/null
+++ b/lib/libc/src/musl-math/tgamma.c
@@ -0,0 +1,222 @@
+/*
+"A Precision Approximation of the Gamma Function" - Cornelius Lanczos (1964)
+"Lanczos Implementation of the Gamma Function" - Paul Godfrey (2001)
+"An Analysis of the Lanczos Gamma Approximation" - Glendon Ralph Pugh (2004)
+
+approximation method:
+
+ (x - 0.5) S(x)
+Gamma(x) = (x + g - 0.5) * ----------------
+ exp(x + g - 0.5)
+
+with
+ a1 a2 a3 aN
+S(x) ~= [ a0 + ----- + ----- + ----- + ... + ----- ]
+ x + 1 x + 2 x + 3 x + N
+
+with a0, a1, a2, a3,.. aN constants which depend on g.
+
+for x < 0 the following reflection formula is used:
+
+Gamma(x)*Gamma(-x) = -pi/(x sin(pi x))
+
+most ideas and constants are from boost and python
+*/
+#include "libm.h"
+
+static const double pi = 3.141592653589793238462643383279502884;
+
+/* sin(pi x) with x > 0x1p-100, if sin(pi*x)==0 the sign is arbitrary */
+static double sinpi(double x)
+{
+ int n;
+
+ /* argument reduction: x = |x| mod 2 */
+ /* spurious inexact when x is odd int */
+ x = x * 0.5;
+ x = 2 * (x - floor(x));
+
+ /* reduce x into [-.25,.25] */
+ n = 4 * x;
+ n = (n+1)/2;
+ x -= n * 0.5;
+
+ x *= pi;
+ switch (n) {
+ default: /* case 4 */
+ case 0:
+ return __sin(x, 0, 0);
+ case 1:
+ return __cos(x, 0);
+ case 2:
+ return __sin(-x, 0, 0);
+ case 3:
+ return -__cos(x, 0);
+ }
+}
+
+#define N 12
+//static const double g = 6.024680040776729583740234375;
+static const double gmhalf = 5.524680040776729583740234375;
+static const double Snum[N+1] = {
+ 23531376880.410759688572007674451636754734846804940,
+ 42919803642.649098768957899047001988850926355848959,
+ 35711959237.355668049440185451547166705960488635843,
+ 17921034426.037209699919755754458931112671403265390,
+ 6039542586.3520280050642916443072979210699388420708,
+ 1439720407.3117216736632230727949123939715485786772,
+ 248874557.86205415651146038641322942321632125127801,
+ 31426415.585400194380614231628318205362874684987640,
+ 2876370.6289353724412254090516208496135991145378768,
+ 186056.26539522349504029498971604569928220784236328,
+ 8071.6720023658162106380029022722506138218516325024,
+ 210.82427775157934587250973392071336271166969580291,
+ 2.5066282746310002701649081771338373386264310793408,
+};
+static const double Sden[N+1] = {
+ 0, 39916800, 120543840, 150917976, 105258076, 45995730, 13339535,
+ 2637558, 357423, 32670, 1925, 66, 1,
+};
+/* n! for small integer n */
+static const double fact[] = {
+ 1, 1, 2, 6, 24, 120, 720, 5040.0, 40320.0, 362880.0, 3628800.0, 39916800.0,
+ 479001600.0, 6227020800.0, 87178291200.0, 1307674368000.0, 20922789888000.0,
+ 355687428096000.0, 6402373705728000.0, 121645100408832000.0,
+ 2432902008176640000.0, 51090942171709440000.0, 1124000727777607680000.0,
+};
+
+/* S(x) rational function for positive x */
+static double S(double x)
+{
+ double_t num = 0, den = 0;
+ int i;
+
+ /* to avoid overflow handle large x differently */
+ if (x < 8)
+ for (i = N; i >= 0; i--) {
+ num = num * x + Snum[i];
+ den = den * x + Sden[i];
+ }
+ else
+ for (i = 0; i <= N; i++) {
+ num = num / x + Snum[i];
+ den = den / x + Sden[i];
+ }
+ return num/den;
+}
+
+double tgamma(double x)
+{
+ union {double f; uint64_t i;} u = {x};
+ double absx, y;
+ double_t dy, z, r;
+ uint32_t ix = u.i>>32 & 0x7fffffff;
+ int sign = u.i>>63;
+
+ /* special cases */
+ if (ix >= 0x7ff00000)
+ /* tgamma(nan)=nan, tgamma(inf)=inf, tgamma(-inf)=nan with invalid */
+ return x + INFINITY;
+ if (ix < (0x3ff-54)<<20)
+ /* |x| < 2^-54: tgamma(x) ~ 1/x, +-0 raises div-by-zero */
+ return 1/x;
+
+ /* integer arguments */
+ /* raise inexact when non-integer */
+ if (x == floor(x)) {
+ if (sign)
+ return 0/0.0;
+ if (x <= sizeof fact/sizeof *fact)
+ return fact[(int)x - 1];
+ }
+
+ /* x >= 172: tgamma(x)=inf with overflow */
+ /* x =< -184: tgamma(x)=+-0 with underflow */
+ if (ix >= 0x40670000) { /* |x| >= 184 */
+ if (sign) {
+ FORCE_EVAL((float)(0x1p-126/x));
+ if (floor(x) * 0.5 == floor(x * 0.5))
+ return 0;
+ return -0.0;
+ }
+ x *= 0x1p1023;
+ return x;
+ }
+
+ absx = sign ? -x : x;
+
+ /* handle the error of x + g - 0.5 */
+ y = absx + gmhalf;
+ if (absx > gmhalf) {
+ dy = y - absx;
+ dy -= gmhalf;
+ } else {
+ dy = y - gmhalf;
+ dy -= absx;
+ }
+
+ z = absx - 0.5;
+ r = S(absx) * exp(-y);
+ if (x < 0) {
+ /* reflection formula for negative x */
+ /* sinpi(absx) is not 0, integers are already handled */
+ r = -pi / (sinpi(absx) * absx * r);
+ dy = -dy;
+ z = -z;
+ }
+ r += dy * (gmhalf+0.5) * r / y;
+ z = pow(y, 0.5*z);
+ y = r * z * z;
+ return y;
+}
+
+#if 0
+double __lgamma_r(double x, int *sign)
+{
+ double r, absx;
+
+ *sign = 1;
+
+ /* special cases */
+ if (!isfinite(x))
+ /* lgamma(nan)=nan, lgamma(+-inf)=inf */
+ return x*x;
+
+ /* integer arguments */
+ if (x == floor(x) && x <= 2) {
+ /* n <= 0: lgamma(n)=inf with divbyzero */
+ /* n == 1,2: lgamma(n)=0 */
+ if (x <= 0)
+ return 1/0.0;
+ return 0;
+ }
+
+ absx = fabs(x);
+
+ /* lgamma(x) ~ -log(|x|) for tiny |x| */
+ if (absx < 0x1p-54) {
+ *sign = 1 - 2*!!signbit(x);
+ return -log(absx);
+ }
+
+ /* use tgamma for smaller |x| */
+ if (absx < 128) {
+ x = tgamma(x);
+ *sign = 1 - 2*!!signbit(x);
+ return log(fabs(x));
+ }
+
+ /* second term (log(S)-g) could be more precise here.. */
+ /* or with stirling: (|x|-0.5)*(log(|x|)-1) + poly(1/|x|) */
+ r = (absx-0.5)*(log(absx+gmhalf)-1) + (log(S(absx)) - (gmhalf+0.5));
+ if (x < 0) {
+ /* reflection formula for negative x */
+ x = sinpi(absx);
+ *sign = 2*!!signbit(x) - 1;
+ r = log(pi/(fabs(x)*absx)) - r;
+ }
+ return r;
+}
+
+weak_alias(__lgamma_r, lgamma_r);
+#endif
diff --git a/lib/libc/src/musl-math/tgammaf.c b/lib/libc/src/musl-math/tgammaf.c
new file mode 100644
index 0000000..b4ca51c
--- /dev/null
+++ b/lib/libc/src/musl-math/tgammaf.c
@@ -0,0 +1,6 @@
+#include <math.h>
+
+float tgammaf(float x)
+{
+ return tgamma(x);
+}
diff --git a/lib/libc/src/musl-math/tgammal.c b/lib/libc/src/musl-math/tgammal.c
new file mode 100644
index 0000000..5336c5b
--- /dev/null
+++ b/lib/libc/src/musl-math/tgammal.c
@@ -0,0 +1,281 @@
+/* origin: OpenBSD /usr/src/lib/libm/src/ld80/e_tgammal.c */
+/*
+ * Copyright (c) 2008 Stephen L. Moshier <steve@moshier.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+/*
+ * Gamma function
+ *
+ *
+ * SYNOPSIS:
+ *
+ * long double x, y, tgammal();
+ *
+ * y = tgammal( x );
+ *
+ *
+ * DESCRIPTION:
+ *
+ * Returns gamma function of the argument. The result is
+ * correctly signed.
+ *
+ * Arguments |x| <= 13 are reduced by recurrence and the function
+ * approximated by a rational function of degree 7/8 in the
+ * interval (2,3). Large arguments are handled by Stirling's
+ * formula. Large negative arguments are made positive using
+ * a reflection formula.
+ *
+ *
+ * ACCURACY:
+ *
+ * Relative error:
+ * arithmetic domain # trials peak rms
+ * IEEE -40,+40 10000 3.6e-19 7.9e-20
+ * IEEE -1755,+1755 10000 4.8e-18 6.5e-19
+ *
+ * Accuracy for large arguments is dominated by error in powl().
+ *
+ */
+
+#include "libm.h"
+
+#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
+long double tgammal(long double x)
+{
+ return tgamma(x);
+}
+#elif LDBL_MANT_DIG == 64 && LDBL_MAX_EXP == 16384
+/*
+tgamma(x+2) = tgamma(x+2) P(x)/Q(x)
+0 <= x <= 1
+Relative error
+n=7, d=8
+Peak error = 1.83e-20
+Relative error spread = 8.4e-23
+*/
+static const long double P[8] = {
+ 4.212760487471622013093E-5L,
+ 4.542931960608009155600E-4L,
+ 4.092666828394035500949E-3L,
+ 2.385363243461108252554E-2L,
+ 1.113062816019361559013E-1L,
+ 3.629515436640239168939E-1L,
+ 8.378004301573126728826E-1L,
+ 1.000000000000000000009E0L,
+};
+static const long double Q[9] = {
+-1.397148517476170440917E-5L,
+ 2.346584059160635244282E-4L,
+-1.237799246653152231188E-3L,
+-7.955933682494738320586E-4L,
+ 2.773706565840072979165E-2L,
+-4.633887671244534213831E-2L,
+-2.243510905670329164562E-1L,
+ 4.150160950588455434583E-1L,
+ 9.999999999999999999908E-1L,
+};
+
+/*
+static const long double P[] = {
+-3.01525602666895735709e0L,
+-3.25157411956062339893e1L,
+-2.92929976820724030353e2L,
+-1.70730828800510297666e3L,
+-7.96667499622741999770e3L,
+-2.59780216007146401957e4L,
+-5.99650230220855581642e4L,
+-7.15743521530849602425e4L
+};
+static const long double Q[] = {
+ 1.00000000000000000000e0L,
+-1.67955233807178858919e1L,
+ 8.85946791747759881659e1L,
+ 5.69440799097468430177e1L,
+-1.98526250512761318471e3L,
+ 3.31667508019495079814e3L,
+ 1.60577839621734713377e4L,
+-2.97045081369399940529e4L,
+-7.15743521530849602412e4L
+};
+*/
+#define MAXGAML 1755.455L
+/*static const long double LOGPI = 1.14472988584940017414L;*/
+
+/* Stirling's formula for the gamma function
+tgamma(x) = sqrt(2 pi) x^(x-.5) exp(-x) (1 + 1/x P(1/x))
+z(x) = x
+13 <= x <= 1024
+Relative error
+n=8, d=0
+Peak error = 9.44e-21
+Relative error spread = 8.8e-4
+*/
+static const long double STIR[9] = {
+ 7.147391378143610789273E-4L,
+-2.363848809501759061727E-5L,
+-5.950237554056330156018E-4L,
+ 6.989332260623193171870E-5L,
+ 7.840334842744753003862E-4L,
+-2.294719747873185405699E-4L,
+-2.681327161876304418288E-3L,
+ 3.472222222230075327854E-3L,
+ 8.333333333333331800504E-2L,
+};
+
+#define MAXSTIR 1024.0L
+static const long double SQTPI = 2.50662827463100050242E0L;
+
+/* 1/tgamma(x) = z P(z)
+ * z(x) = 1/x
+ * 0 < x < 0.03125
+ * Peak relative error 4.2e-23
+ */
+static const long double S[9] = {
+-1.193945051381510095614E-3L,
+ 7.220599478036909672331E-3L,
+-9.622023360406271645744E-3L,
+-4.219773360705915470089E-2L,
+ 1.665386113720805206758E-1L,
+-4.200263503403344054473E-2L,
+-6.558780715202540684668E-1L,
+ 5.772156649015328608253E-1L,
+ 1.000000000000000000000E0L,
+};
+
+/* 1/tgamma(-x) = z P(z)
+ * z(x) = 1/x
+ * 0 < x < 0.03125
+ * Peak relative error 5.16e-23
+ * Relative error spread = 2.5e-24
+ */
+static const long double SN[9] = {
+ 1.133374167243894382010E-3L,
+ 7.220837261893170325704E-3L,
+ 9.621911155035976733706E-3L,
+-4.219773343731191721664E-2L,
+-1.665386113944413519335E-1L,
+-4.200263503402112910504E-2L,
+ 6.558780715202536547116E-1L,
+ 5.772156649015328608727E-1L,
+-1.000000000000000000000E0L,
+};
+
+static const long double PIL = 3.1415926535897932384626L;
+
+/* Gamma function computed by Stirling's formula.
+ */
+static long double stirf(long double x)
+{
+ long double y, w, v;
+
+ w = 1.0/x;
+ /* For large x, use rational coefficients from the analytical expansion. */
+ if (x > 1024.0)
+ w = (((((6.97281375836585777429E-5L * w
+ + 7.84039221720066627474E-4L) * w
+ - 2.29472093621399176955E-4L) * w
+ - 2.68132716049382716049E-3L) * w
+ + 3.47222222222222222222E-3L) * w
+ + 8.33333333333333333333E-2L) * w
+ + 1.0;
+ else
+ w = 1.0 + w * __polevll(w, STIR, 8);
+ y = expl(x);
+ if (x > MAXSTIR) { /* Avoid overflow in pow() */
+ v = powl(x, 0.5L * x - 0.25L);
+ y = v * (v / y);
+ } else {
+ y = powl(x, x - 0.5L) / y;
+ }
+ y = SQTPI * y * w;
+ return y;
+}
+
+long double tgammal(long double x)
+{
+ long double p, q, z;
+
+ if (!isfinite(x))
+ return x + INFINITY;
+
+ q = fabsl(x);
+ if (q > 13.0) {
+ if (x < 0.0) {
+ p = floorl(q);
+ z = q - p;
+ if (z == 0)
+ return 0 / z;
+ if (q > MAXGAML) {
+ z = 0;
+ } else {
+ if (z > 0.5) {
+ p += 1.0;
+ z = q - p;
+ }
+ z = q * sinl(PIL * z);
+ z = fabsl(z) * stirf(q);
+ z = PIL/z;
+ }
+ if (0.5 * p == floorl(q * 0.5))
+ z = -z;
+ } else if (x > MAXGAML) {
+ z = x * 0x1p16383L;
+ } else {
+ z = stirf(x);
+ }
+ return z;
+ }
+
+ z = 1.0;
+ while (x >= 3.0) {
+ x -= 1.0;
+ z *= x;
+ }
+ while (x < -0.03125L) {
+ z /= x;
+ x += 1.0;
+ }
+ if (x <= 0.03125L)
+ goto small;
+ while (x < 2.0) {
+ z /= x;
+ x += 1.0;
+ }
+ if (x == 2.0)
+ return z;
+
+ x -= 2.0;
+ p = __polevll(x, P, 7);
+ q = __polevll(x, Q, 8);
+ z = z * p / q;
+ return z;
+
+small:
+ /* z==1 if x was originally +-0 */
+ if (x == 0 && z != 1)
+ return x / x;
+ if (x < 0.0) {
+ x = -x;
+ q = z / (x * __polevll(x, SN, 8));
+ } else
+ q = z / (x * __polevll(x, S, 8));
+ return q;
+}
+#elif LDBL_MANT_DIG == 113 && LDBL_MAX_EXP == 16384
+// TODO: broken implementation to make things compile
+long double tgammal(long double x)
+{
+ return tgamma(x);
+}
+#endif
diff --git a/lib/libc/src/musl-math/trunc.c b/lib/libc/src/musl-math/trunc.c
new file mode 100644
index 0000000..d13711b
--- /dev/null
+++ b/lib/libc/src/musl-math/trunc.c
@@ -0,0 +1,19 @@
+#include "libm.h"
+
+double trunc(double x)
+{
+ union {double f; uint64_t i;} u = {x};
+ int e = (int)(u.i >> 52 & 0x7ff) - 0x3ff + 12;
+ uint64_t m;
+
+ if (e >= 52 + 12)
+ return x;
+ if (e < 12)
+ e = 1;
+ m = -1ULL >> e;
+ if ((u.i & m) == 0)
+ return x;
+ FORCE_EVAL(x + 0x1p120f);
+ u.i &= ~m;
+ return u.f;
+}
diff --git a/lib/libc/src/musl-math/truncf.c b/lib/libc/src/musl-math/truncf.c
new file mode 100644
index 0000000..1a7d03c
--- /dev/null
+++ b/lib/libc/src/musl-math/truncf.c
@@ -0,0 +1,19 @@
+#include "libm.h"
+
+float truncf(float x)
+{
+ union {float f; uint32_t i;} u = {x};
+ int e = (int)(u.i >> 23 & 0xff) - 0x7f + 9;
+ uint32_t m;
+
+ if (e >= 23 + 9)
+ return x;
+ if (e < 9)
+ e = 1;
+ m = -1U >> e;
+ if ((u.i & m) == 0)
+ return x;
+ FORCE_EVAL(x + 0x1p120f);
+ u.i &= ~m;
+ return u.f;
+}
diff --git a/lib/libc/src/musl-math/truncl.c b/lib/libc/src/musl-math/truncl.c
new file mode 100644
index 0000000..f07b193
--- /dev/null
+++ b/lib/libc/src/musl-math/truncl.c
@@ -0,0 +1,34 @@
+#include "libm.h"
+
+#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
+long double truncl(long double x)
+{
+ return trunc(x);
+}
+#elif (LDBL_MANT_DIG == 64 || LDBL_MANT_DIG == 113) && LDBL_MAX_EXP == 16384
+
+static const long double toint = 1/LDBL_EPSILON;
+
+long double truncl(long double x)
+{
+ union ldshape u = {x};
+ int e = u.i.se & 0x7fff;
+ int s = u.i.se >> 15;
+ long double y;
+
+ if (e >= 0x3fff+LDBL_MANT_DIG-1)
+ return x;
+ if (e <= 0x3fff-1) {
+ FORCE_EVAL(x + 0x1p120f);
+ return x*0;
+ }
+ /* y = int(|x|) - |x|, where int(|x|) is an integer neighbor of |x| */
+ if (s)
+ x = -x;
+ y = x + toint - toint - x;
+ if (y > 0)
+ y -= 1;
+ x += y;
+ return s ? -x : x;
+}
+#endif
diff --git a/lib/libc/src/musl-math/weak_alias.h b/lib/libc/src/musl-math/weak_alias.h
new file mode 100644
index 0000000..785f9d1
--- /dev/null
+++ b/lib/libc/src/musl-math/weak_alias.h
@@ -0,0 +1,7 @@
+#ifndef _WEAK_ALIAS_H
+#define _WEAK_ALIAS_H
+
+#define weak_alias(name, alias_to) \
+ extern __typeof (name) alias_to __attribute__((__weak__, __alias__(#name)));
+
+#endif
diff --git a/lib/libc/src/posix/getopt.c b/lib/libc/src/posix/getopt.c
new file mode 100644
index 0000000..d3cd530
--- /dev/null
+++ b/lib/libc/src/posix/getopt.c
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/errno.h>
+#include <unistd.h>
+#include <stdbool.h>
+#include <string.h>
+#include <stdio.h>
+
+char *optarg;
+int optind, opterr, optopt;
+
+int
+getopt(int argc, char *argv[], const char *optstring)
+{
+ size_t optstr_len;
+ char *arg;
+ bool has_arg = false;
+
+ if (argc == 0 || optstring == NULL) {
+ opterr = -EINVAL;
+ return -1;
+ }
+
+ if (optind >= argc) {
+ return -1;
+ }
+
+ arg = argv[optind];
+ optstr_len = strlen(optstring);
+
+ /* Non option argument? */
+ if (arg[0] != '-') {
+ return -1;
+ }
+
+ /*
+ * We will look through each possible flag/option
+ * in the optstring and match it against our arg.
+ */
+ for (size_t i = 0; i < optstr_len; ++i) {
+ if (arg[1] != optstring[i]) {
+ continue;
+ }
+
+ /*
+ * If this option has a ':' right next to it,
+ * it also has an argument.
+ */
+ if (i < optstr_len - 1) {
+ if (optstring[i + 1] == ':') {
+ has_arg = true;
+ }
+ }
+
+ break;
+ }
+
+ /*
+ * Handle cases where the option has an argument
+ * with it (-opt=arg)
+ */
+ if (has_arg && optind < argc ) {
+ if (arg[2] != '=') {
+ opterr = -EINVAL;
+ return -1;
+ }
+ optarg = &arg[3];
+ ++optind;
+ }
+
+ ++optind;
+ return arg[1];
+}
diff --git a/lib/libc/src/stdio/fclose.c b/lib/libc/src/stdio/fclose.c
new file mode 100644
index 0000000..7628973
--- /dev/null
+++ b/lib/libc/src/stdio/fclose.c
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+int
+fclose(FILE *stream)
+{
+ int retval;
+
+ if (stream == NULL) {
+ return -EBADF;
+ }
+
+ retval = close(stream->fd);
+ free(stream);
+ return retval;
+}
diff --git a/lib/libc/src/stdio/fgetc.c b/lib/libc/src/stdio/fgetc.c
new file mode 100644
index 0000000..2ed8496
--- /dev/null
+++ b/lib/libc/src/stdio/fgetc.c
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+
+extern size_t __stdio_read(void *__restrict ptr, size_t size, FILE *__restrict stream);
+
+int
+fgetc(FILE *stream)
+{
+ uint16_t val;
+
+ if (stream == NULL) {
+ return EOF;
+ }
+
+ if (__stdio_read(&val, sizeof(val), stream) != sizeof(val)) {
+ return EOF;
+ }
+
+ return (int)val;
+}
+
+int
+getchar(void)
+{
+ return fgetc(stdin);
+}
diff --git a/lib/libc/src/stdio/fgets.c b/lib/libc/src/stdio/fgets.c
new file mode 100644
index 0000000..fdaad84
--- /dev/null
+++ b/lib/libc/src/stdio/fgets.c
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+
+extern size_t __stdio_read(void *__restrict ptr, size_t size, FILE *__restrict stream);
+
+char *
+fgets(char *__restrict s, int size, FILE *__restrict stream)
+{
+ size_t count;
+ uint8_t tmp;
+ int idx = 0;
+ char c;
+
+ if (stream == NULL) {
+ return NULL;
+ }
+
+ for (;;) {
+ count = __stdio_read(&tmp, sizeof(tmp), stream);
+ if (count <= 0 && idx == 0) {
+ s[idx] = '\0';
+ return NULL;
+ }
+ if (count <= 0 && idx > 0) {
+ s[idx] = '\0';
+ break;
+ }
+
+ c = (char)tmp;
+ s[idx++] = c;
+
+ if (c == '\n') {
+ s[idx] = '\0';
+ break;
+ }
+
+ /* Did we read the entire line? */
+ if (idx >= size - 1) {
+ return s;
+ }
+ }
+
+ return s;
+}
diff --git a/lib/libc/src/stdio/file.c b/lib/libc/src/stdio/file.c
new file mode 100644
index 0000000..5c84c35
--- /dev/null
+++ b/lib/libc/src/stdio/file.c
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+
+int
+fileno(FILE *stream)
+{
+ return stream->fd;
+}
diff --git a/lib/libc/src/stdio/fopen.c b/lib/libc/src/stdio/fopen.c
new file mode 100644
index 0000000..efeb577
--- /dev/null
+++ b/lib/libc/src/stdio/fopen.c
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <fcntl.h>
+
+FILE *
+fopen(const char *__restrict path, const char *__restrict mode)
+{
+ FILE *fhand;
+ int fd;
+ mode_t seal = 0;
+
+ if (path == NULL || mode == NULL) {
+ return NULL;
+ }
+
+ /* Create the seal from mode string */
+ if (strcmp(mode, "r") == 0) {
+ seal |= O_RDONLY;
+ } else if (strcmp(mode, "w") == 0) {
+ seal |= (O_WRONLY | O_CREAT);
+ } else if (strcmp(mode, "r+") == 0) {
+ seal |= O_RDWR;
+ } else if (strcmp(mode, "rb") == 0) {
+ seal |= O_RDONLY;
+ } else {
+ return NULL;
+ }
+
+ /* Try to open the file descriptor */
+ fd = open(path, seal);
+ if (fd < 0) {
+ return NULL;
+ }
+
+ fhand = malloc(sizeof(*fhand));
+ if (fhand == NULL) {
+ return NULL;
+ }
+
+ fhand->fd = fd;
+ fhand->buf_mode = _IONBF;
+ return fhand;
+}
diff --git a/lib/libc/src/stdio/fputc.c b/lib/libc/src/stdio/fputc.c
new file mode 100644
index 0000000..6ac7aac
--- /dev/null
+++ b/lib/libc/src/stdio/fputc.c
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+
+extern size_t __stdio_write(const void *__restrict ptr, size_t size, FILE *__restrict stream);
+
+int
+fputc(int c, FILE *stream)
+{
+ unsigned char val;
+
+ if (stream == NULL) {
+ return EOF;
+ }
+
+ val = (unsigned char)c;
+ if (__stdio_write(&val, sizeof(val), stream) != sizeof(val)) {
+ return EOF;
+ }
+
+ return (int)val;
+}
+
+int
+putchar(int c)
+{
+ return fputc(c, stdout);
+}
diff --git a/lib/libc/src/stdio/fputs.c b/lib/libc/src/stdio/fputs.c
new file mode 100644
index 0000000..357fd52
--- /dev/null
+++ b/lib/libc/src/stdio/fputs.c
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+extern size_t __stdio_write(const void *__restrict ptr, size_t size, FILE *__restrict stream);
+
+int
+fputs(const char *__restrict s, FILE *__restrict stream)
+{
+ size_t len;
+
+ if (s == NULL || stream == NULL) {
+ return EOF;
+ }
+
+ len = strlen(s);
+ if (len < 1) {
+ return 0;
+ }
+
+ if (__stdio_write(s, sizeof(char) * len, stream) != (sizeof(char) * len)) {
+ return EOF;
+ }
+
+ return 0;
+}
+
+int
+puts(const char *s)
+{
+ if (fputs(s, stdout) < 0) {
+ return EOF;
+ }
+
+ if (fputc('\n', stdout) != '\n') {
+ return EOF;
+ }
+
+ return 0;
+}
diff --git a/lib/libc/src/stdio/fread.c b/lib/libc/src/stdio/fread.c
new file mode 100644
index 0000000..036962d
--- /dev/null
+++ b/lib/libc/src/stdio/fread.c
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+
+size_t
+__stdio_read(void *__restrict ptr, size_t size, FILE *__restrict stream)
+{
+ ssize_t count;
+
+ if (stream->buf_mode == _IONBF) {
+ if ((count = read(stream->fd, ptr, size)) < 0) {
+ return 0;
+ }
+
+ return count;
+ }
+
+ /*
+ * TODO: Implement more buffering modes.
+ */
+
+ return 0;
+}
+
+size_t
+fread(void *__restrict ptr, size_t size, size_t n, FILE *__restrict stream)
+{
+ if (ptr == NULL || stream == NULL || (size * n) == 0) {
+ return 0;
+ }
+
+ return __stdio_read(ptr, size * n, stream);
+}
diff --git a/lib/libc/src/stdio/fseek.c b/lib/libc/src/stdio/fseek.c
new file mode 100644
index 0000000..b783081
--- /dev/null
+++ b/lib/libc/src/stdio/fseek.c
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/errno.h>
+#include <stdio.h>
+#include <unistd.h>
+
+int
+fseek(FILE *stream, long offset, int whence)
+{
+ if (stream == NULL) {
+ return -EINVAL;
+ }
+
+ return lseek(stream->fd, offset, whence);
+}
diff --git a/lib/libc/src/stdio/ftell.c b/lib/libc/src/stdio/ftell.c
new file mode 100644
index 0000000..aa0f608
--- /dev/null
+++ b/lib/libc/src/stdio/ftell.c
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+
+long
+ftell(FILE *stream)
+{
+ return lseek(stream->fd, 0, SEEK_CUR);
+}
diff --git a/lib/libc/src/stdio/fwrite.c b/lib/libc/src/stdio/fwrite.c
new file mode 100644
index 0000000..660034e
--- /dev/null
+++ b/lib/libc/src/stdio/fwrite.c
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+
+size_t
+__stdio_write(const void *__restrict ptr, size_t size, FILE *__restrict stream)
+{
+ ssize_t count;
+
+ if (stream->buf_mode == _IONBF) {
+ if ((count = write(stream->fd, ptr, size)) < 0) {
+ return 0;
+ }
+
+ return count;
+ }
+
+ /*
+ * TODO: Implement more buffering modes.
+ */
+
+ return 0;
+}
+
+size_t
+fwrite(const void *__restrict ptr, size_t size, size_t n, FILE *__restrict stream)
+{
+ if (ptr == NULL || stream == NULL || (size * n) == 0) {
+ return 0;
+ }
+
+ return __stdio_write(ptr, size * n, stream);
+}
diff --git a/lib/libc/src/stdio/init.c b/lib/libc/src/stdio/init.c
new file mode 100644
index 0000000..5982d59
--- /dev/null
+++ b/lib/libc/src/stdio/init.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <fcntl.h>
+
+FILE *stdin;
+FILE *stdout;
+FILE *stderr;
+
+static FILE cin, cout, cerr;
+
+int
+__libc_stdio_init(void)
+{
+ int cfd;
+
+ if ((cfd = open("/dev/console", O_RDWR)) < 0) {
+ return cfd;
+ }
+
+ cin.buf_mode = _IONBF;
+ cin.fd = cfd;
+
+ cout.buf_mode = _IONBF;
+ cout.fd = cfd;
+
+ cerr.buf_mode = _IONBF;
+ cerr.fd = cfd;
+
+ stdout = &cout;
+ stdin = &cin;
+ stderr = &cerr;
+
+ return 0;
+}
diff --git a/lib/libc/src/stdio/snprintf.c b/lib/libc/src/stdio/snprintf.c
new file mode 100644
index 0000000..2387950
--- /dev/null
+++ b/lib/libc/src/stdio/snprintf.c
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <unistd.h>
+
+/* TODO FIXME: Use stdarg.h */
+#define __va_start(ap, fmt) __builtin_va_start(ap, fmt)
+#define __va_end(ap) __builtin_va_end(ap)
+
+int
+snprintf(char *s, size_t size, const char *fmt, ...)
+{
+ va_list ap;
+ int ret;
+
+ __va_start(ap, fmt);
+ ret = vsnprintf(s, size, fmt, ap);
+ __va_end(ap);
+ return ret;
+}
+
+int
+printf(const char *__restrict fmt, ...)
+{
+ char buf[512];
+ va_list ap;
+ int ret;
+
+ __va_start(ap, fmt);
+ ret = vsnprintf(buf, sizeof(buf), fmt, ap);
+ write(stdout->fd, buf, ret);
+ __va_end(ap);
+ return ret;
+}
diff --git a/lib/libc/src/stdio/vsnprintf.c b/lib/libc/src/stdio/vsnprintf.c
new file mode 100644
index 0000000..0e3d268
--- /dev/null
+++ b/lib/libc/src/stdio/vsnprintf.c
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <string.h>
+
+/* TODO FIXME: Use stdarg.h */
+#define __va_arg(ap, type) __builtin_va_arg(ap, type)
+
+static inline void
+printc(char *buf, size_t size, size_t *off, char c)
+{
+ if (*off < size - 1) {
+ buf[(*off)++] = c;
+ }
+ buf[*off] = 0;
+}
+
+static void
+printstr(char *buf, size_t size, size_t *off, const char *s)
+{
+ while (*off < size - 1 && *s != '\0') {
+ buf[(*off)++] = *(s)++;
+ }
+ buf[*off] = 0;
+}
+
+int
+vsnprintf(char *s, size_t size, const char *fmt, va_list ap)
+{
+ size_t tmp_len, num_len, off = 0;
+ ssize_t num = 0;
+ char c, c1, num_buf[256] = {0};
+ const char *tmp_str;
+ uint8_t pad_width = 0;
+
+ while (off < (size - 1)) {
+ while (*fmt && *fmt != '%') {
+ printc(s, size, &off, *fmt++);
+ }
+ if (*(fmt)++ == '\0' || off == size - 1) {
+ return off;
+ }
+
+ /*
+ * Handle a case where we have "%0N". For example:
+ * "%04d"
+ */
+ if (*fmt == '0') {
+ ++fmt;
+ while (*fmt >= '0' && *fmt <= '9') {
+ pad_width = pad_width * 10 + (*fmt - '0');
+ ++fmt;
+ }
+ }
+
+ c = *fmt++;
+ switch (c) {
+ case '%':
+ printc(s, size, &off, c);
+ break;
+ case 'c':
+ c1 = (char )__va_arg(ap, int);
+ printc(s, size, &off, c1);
+ break;
+ case 'd':
+ num = __va_arg(ap, int);
+ itoa(num, num_buf, 10);
+
+ if (pad_width > 0) {
+ num_len = strlen(num_buf);
+ for (size_t i = num_len; i < pad_width; ++i)
+ printc(s, size, &off, '0');
+ pad_width = 0;
+ }
+ printstr(s, size, &off, num_buf);
+ break;
+ case 'p':
+ num = __va_arg(ap, uint64_t);
+ itoa(num, num_buf, 16);
+ tmp_len = strlen(num_buf);
+
+ /* Add '0x' prefix */
+ printc(s, size, &off, '0');
+ printc(s, size, &off, 'x');
+ /*
+ * Now we pad this.
+ *
+ * XXX TODO: This assumes 64-bits, should be
+ * cleaned up.
+ */
+ for (size_t i = 0; i < 18 - tmp_len; ++i) {
+ printc(s, size, &off, '0');
+ }
+ printstr(s, size, &off, num_buf + 2);
+ break;
+ case 'x':
+ num = __va_arg(ap, uint64_t);
+ itoa(num, num_buf, 16);
+ tmp_len = strlen(num_buf);
+ if (pad_width > 0) {
+ num_len = strlen(num_buf);
+ for (size_t i = num_len; i < pad_width; ++i)
+ printc(s, size, &off, '0');
+ pad_width = 0;
+ }
+ printstr(s, size, &off, num_buf + 2);
+ break;
+ case 's':
+ tmp_str = __va_arg(ap, const char *);
+ printstr(s, size, &off, tmp_str);
+ break;
+ }
+ }
+
+ return 0;
+}
diff --git a/lib/libc/src/stdlib/_Exit.c b/lib/libc/src/stdlib/_Exit.c
new file mode 100644
index 0000000..e975381
--- /dev/null
+++ b/lib/libc/src/stdlib/_Exit.c
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/syscall.h>
+#include <stdlib.h>
+
+__dead void
+_Exit(int status)
+{
+ syscall(SYS_exit, status);
+ __builtin_unreachable();
+}
diff --git a/lib/libc/src/stdlib/abort.c b/lib/libc/src/stdlib/abort.c
new file mode 100644
index 0000000..bc0a491
--- /dev/null
+++ b/lib/libc/src/stdlib/abort.c
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdlib.h>
+
+__dead void
+abort(void)
+{
+ /*
+ * TODO: Call SIGABRT handler here.
+ */
+
+ _Exit(EXIT_FAILURE);
+}
diff --git a/lib/libc/src/stdlib/exit.c b/lib/libc/src/stdlib/exit.c
new file mode 100644
index 0000000..e5adfac
--- /dev/null
+++ b/lib/libc/src/stdlib/exit.c
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdlib.h>
+
+__dead void
+exit(int status)
+{
+ /*
+ * TODO: Call atexit() handlers and do cleanup here.
+ */
+
+ _Exit(status);
+}
diff --git a/lib/libc/src/stdlib/malloc.c b/lib/libc/src/stdlib/malloc.c
new file mode 100644
index 0000000..4f25c24
--- /dev/null
+++ b/lib/libc/src/stdlib/malloc.c
@@ -0,0 +1,219 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/param.h>
+#include <sys/cdefs.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdatomic.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <string.h>
+
+#define HEAP_SIZE 0x1001A8
+#define HEAP_MAGIC 0x05306A /* "OSMORA" :~) */
+#define HEAP_ALIGN 4
+#define HEAP_PROT PROT_READ | PROT_WRITE
+#define BYTE_PTR(PTR) ((char *)(PTR))
+#define HEAP_NEXT(BLOCKP, SIZE) \
+ PTR_OFFSET((BLOCKP), sizeof(struct mem_block) + (SIZE))
+
+struct __aligned(HEAP_ALIGN) mem_block {
+ uint32_t magic;
+ uint32_t size;
+ uint8_t allocated : 1;
+ struct mem_block *next;
+};
+
+static struct mem_block *mem_head;
+static struct mem_block *mem_tail;
+
+/*
+ * The size of the heap including data on the heap
+ * as well as the sizes of their respective block
+ * headers.
+ */
+static ssize_t heap_len = 0;
+static off_t heap_pos = 0;
+
+/*
+ * During the initial state of malloc() when the C library
+ * first starts up. We can assume that there is zero fragmentation
+ * in our heap pool. This allows us to initially allocate memory
+ * by bumping a pointer which is O(1). During this state, even after
+ * any calls to free(), we can assume that there is more memory ahead
+ * of us that is free (due to the initial zero-fragmentation). However,
+ * once we've reached the end of the pool, if any memory has been previously
+ * freed (indicated by heap_len > 0), we can wrap the tail and start allocating
+ * in a best-fit fashion as we can assume that the heap is now fragmented.
+ */
+static bool wrap = false;
+
+void __malloc_mem_init(void);
+
+/*
+ * Terminate program abnormally due to any heap
+ * errors.
+ *
+ * TODO: Raise SIGABRT instead of using _Exit()
+ */
+__dead static void
+__heap_abort(const char *str)
+{
+ printf(str);
+ _Exit(1);
+ __builtin_unreachable();
+}
+
+/*
+ * Find a free block
+ *
+ * TODO: This is currently a first-fit style
+ * routine. This should be best-fit instead
+ * as it doesn't waste memory.
+ */
+static struct mem_block *
+malloc_find_free(size_t size)
+{
+ struct mem_block *cur = mem_head;
+
+ while (cur != NULL) {
+ if (cur->size >= size) {
+ return cur;
+ }
+
+ cur = cur->next;
+ }
+
+ return NULL;
+}
+
+void *
+malloc(size_t size)
+{
+ struct mem_block *next_block;
+ struct mem_block *tail;
+ size_t inc_len = 0;
+
+ size = ALIGN_UP(size, HEAP_ALIGN);
+ inc_len = sizeof(*next_block) + size;
+
+ if (heap_len < 0) {
+ heap_len = 0;
+ }
+
+ if (heap_len < 0) {
+ heap_len = 0;
+ return NULL;
+ }
+
+ /* Any memory left to allocate? */
+ if ((heap_len + inc_len) >= HEAP_SIZE) {
+ return NULL;
+ }
+
+ if (heap_pos >= HEAP_SIZE - inc_len) {
+ wrap = true;
+ mem_tail = mem_head;
+ }
+
+ tail = wrap ? malloc_find_free(size) : mem_tail;
+ if (tail == NULL) {
+ return NULL;
+ }
+
+ next_block = mem_tail;
+ mem_tail = HEAP_NEXT(mem_tail, size);
+ mem_tail->next = NULL;
+
+ next_block->next = mem_tail;
+ next_block->size = size;
+ next_block->allocated = 1;
+ next_block->magic = HEAP_MAGIC;
+
+ heap_len += inc_len;
+ heap_pos += inc_len;
+ return PTR_OFFSET(next_block, sizeof(*next_block));
+}
+
+void *
+realloc(void *ptr, size_t size)
+{
+ struct mem_block *blk;
+ void *new_buf;
+
+ blk = PTR_NOFFSET(ptr, sizeof(*blk));
+ if (blk->magic != HEAP_MAGIC) {
+ __heap_abort("realloc: bad realloc block detected\n");
+ }
+ if (!blk->allocated) {
+ __heap_abort("realloc: bad realloc\n");
+ }
+
+ new_buf = malloc(size);
+ memcpy(new_buf, ptr, blk->size);
+ free(ptr);
+ return new_buf;
+}
+
+void
+free(void *ptr)
+{
+ struct mem_block *blk;
+
+ blk = PTR_NOFFSET(ptr, sizeof(*blk));
+ if (blk->magic != HEAP_MAGIC) {
+ __heap_abort("free: bad free block detected\n");
+ }
+ if (!blk->allocated) {
+ __heap_abort("free: double free detected\n");
+ }
+
+ blk->allocated = 0;
+ heap_len -= (blk->size + sizeof(*blk));
+ if (heap_len < 0) {
+ heap_len = 0;
+ }
+}
+
+void
+__malloc_mem_init(void)
+{
+ mem_head = mmap(NULL, HEAP_SIZE, HEAP_PROT, MAP_ANON, 0, 0);
+ if (mem_head == NULL) {
+ __heap_abort("__malloc_mem_init: mem_head is NULL, out of memory\n");
+ }
+
+ mem_head->size = 0;
+ mem_head->next = NULL;
+ mem_head->allocated = 0;
+ mem_tail = mem_head;
+}
diff --git a/lib/libc/src/stdlib/rand.c b/lib/libc/src/stdlib/rand.c
new file mode 100644
index 0000000..838ba5f
--- /dev/null
+++ b/lib/libc/src/stdlib/rand.c
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdlib.h>
+
+static unsigned int r_seed = 0x9E3779B9;
+
+void
+srand(unsigned int r)
+{
+ r_seed = r;
+}
+
+int
+rand(void)
+{
+ r_seed = (r_seed >> 0x01U) ^ (-(r_seed & 0x01U) & 0xB400U);
+ return r_seed;
+}
diff --git a/lib/libc/src/string/atoi.c b/lib/libc/src/string/atoi.c
new file mode 100644
index 0000000..920e561
--- /dev/null
+++ b/lib/libc/src/string/atoi.c
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <string.h>
+
+#define IS_DIGIT(C) ((C >= '0' && C <= '9'))
+
+int
+atoi(const char *s)
+{
+ int n, sign;
+
+ while (*s == ' ') {
+ ++s;
+ }
+
+ sign = (*s == '-') ? -1 : 1;
+ if (*s == '+' || *s == '-') {
+ s++;
+ }
+ for (n = 0; IS_DIGIT(*s); ++s) {
+ n = 10 * n + (*s - '0');
+ }
+ return sign * n;
+}
diff --git a/lib/libc/src/string/itoa.c b/lib/libc/src/string/itoa.c
new file mode 100644
index 0000000..cfce406
--- /dev/null
+++ b/lib/libc/src/string/itoa.c
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <string.h>
+#include <stdbool.h>
+
+static char *
+itoa_base10_convert(int64_t value, char *buf)
+{
+ size_t i;
+ uint8_t tmp;
+ bool is_negative;
+
+ i = 0;
+ is_negative = false;
+
+ if (value == 0) {
+ buf[i++] = '0';
+ buf[i++] = '\0';
+ return buf;
+ }
+
+ if (value < 0) {
+ /* Easier to handle positive numbers */
+ value *= -1;
+ is_negative = true;
+ }
+
+ while (value > 0) {
+ buf[i++] = '0' + (value % 10);
+ value /= 10;
+ }
+
+ if (is_negative) {
+ buf[i++] = '-';
+ }
+
+ buf[i--] = '\0';
+
+ /* Result is in reverse */
+ for (int j = 0; j < i; ++j, --i) {
+ tmp = buf[j];
+ buf[j] = buf[i];
+ buf[i] = tmp;
+ }
+
+ return buf;
+}
+
+static char *
+itoa_convert_base16(uint64_t n, char *buffer)
+{
+ bool pad;
+ uint8_t nibble;
+ uint8_t i, j, tmp;
+ const char *ascii_nums = "0123456789ABCDEF";
+
+ i = 0;
+ pad = false;
+
+ if (n == 0) {
+ /* Zero, no need to parse */
+ memcpy(buffer, "0x00\0", 5);
+ return buffer;
+ }
+ /* If one digit, pad out 2 later */
+ if (n < 0x10) {
+ pad = true;
+ }
+
+ while (n > 0) {
+ nibble = (uint8_t)n & 0x0F;
+ nibble = ascii_nums[nibble];
+ buffer[i++] = nibble;
+ n >>= 4; /* Fetch next nibble */
+ }
+
+ if (pad) {
+ buffer[i++] = '0';
+ }
+
+ /* Add "0x" prefix */
+ buffer[i++] = 'x';
+ buffer[i++] = '0';
+ buffer[i--] = '\0';
+
+ /* Unreverse the result */
+ for (j = 0; j < i; ++j, --i) {
+ tmp = buffer[j];
+ buffer[j] = buffer[i];
+ buffer[i] = tmp;
+ }
+ return buffer;
+}
+
+char *
+itoa(int64_t value, char *buf, int base)
+{
+ switch (base) {
+ case 10:
+ return itoa_base10_convert(value, buf);
+ case 16:
+ return itoa_convert_base16(value, buf);
+ default:
+ return NULL;
+ }
+}
diff --git a/lib/libc/src/string/memcpy.c b/lib/libc/src/string/memcpy.c
new file mode 100644
index 0000000..a9bcbe9
--- /dev/null
+++ b/lib/libc/src/string/memcpy.c
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <string.h>
+
+void *
+memcpy(void *dest, const void *src, size_t n)
+{
+ for (size_t i = 0; i < n; ++i) {
+ ((char *)dest)[i] = ((char *)src)[i];
+ }
+
+ return dest;
+}
diff --git a/lib/libc/src/string/memset.c b/lib/libc/src/string/memset.c
new file mode 100644
index 0000000..c95029c
--- /dev/null
+++ b/lib/libc/src/string/memset.c
@@ -0,0 +1,45 @@
+
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <string.h>
+
+void *
+memset(void *dst, int c, size_t n)
+{
+ char *p = dst;
+
+ if (n != 0) {
+ do {
+ *p++ = (unsigned char)c;
+ } while (--n != 0);
+ }
+
+ return dst;
+}
diff --git a/lib/libc/src/string/strdup.c b/lib/libc/src/string/strdup.c
new file mode 100644
index 0000000..e5b0910
--- /dev/null
+++ b/lib/libc/src/string/strdup.c
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+
+char *
+strdup(const char *s)
+{
+ char *new_s;
+ size_t s_len;
+
+ if (s == NULL) {
+ return NULL;
+ }
+
+ s_len = strlen(s);
+ new_s = malloc(s_len + 1);
+ if (new_s == NULL) {
+ return NULL;
+ }
+
+ memcpy(new_s, s, s_len);
+ return new_s;
+}
diff --git a/lib/libc/src/string/strtok.c b/lib/libc/src/string/strtok.c
new file mode 100644
index 0000000..f01dcd6
--- /dev/null
+++ b/lib/libc/src/string/strtok.c
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <string.h>
+
+static char *
+__strtok(char *s, const char *delim, char **last)
+{
+ const char *spanp;
+ char *tok;
+ int c, sc;
+
+ if (s == NULL && (s = *last) == NULL) {
+ return NULL;
+ }
+
+cont:
+ c = *s++;
+ for (spanp = delim; (sc = *spanp++) != 0;) {
+ if (c == sc)
+ goto cont;
+ }
+
+ if (c == 0) {
+ *last = NULL;
+ return NULL;
+ }
+
+ tok = s - 1;
+
+ /* Scan tokens */
+ for (;;) {
+ c = *s++;
+ spanp = delim;
+ do {
+ if ((sc = *spanp++) == c) {
+ if (c == 0)
+ s = NULL;
+ else
+ s[-1] = '\0';
+ *last = s;
+ return (tok);
+ }
+ } while (sc != 0);
+ }
+
+ __builtin_unreachable();
+}
+
+char *
+strtok(char *s, const char *delim)
+{
+ static char *last;
+
+ return __strtok(s, delim, &last);
+}
diff --git a/lib/libc/src/unistd/access.c b/lib/libc/src/unistd/access.c
new file mode 100644
index 0000000..0aeb030
--- /dev/null
+++ b/lib/libc/src/unistd/access.c
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/syscall.h>
+#include <unistd.h>
+
+int
+access(const char *path, int mode)
+{
+ return syscall(SYS_access, (uintptr_t)path, mode);
+}
diff --git a/lib/libc/src/unistd/dup.c b/lib/libc/src/unistd/dup.c
new file mode 100644
index 0000000..887fdc6
--- /dev/null
+++ b/lib/libc/src/unistd/dup.c
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <unistd.h>
+
+int
+dup(int fd)
+{
+ /* TODO: STUB */
+ return -1;
+}
+
+int
+dup2(int fd, int fd1)
+{
+ /* TODO: STUB */
+ return -1;
+}
diff --git a/lib/libc/src/unistd/fork.c b/lib/libc/src/unistd/fork.c
new file mode 100644
index 0000000..970072e
--- /dev/null
+++ b/lib/libc/src/unistd/fork.c
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <unistd.h>
+
+pid_t
+fork(void)
+{
+ /* TODO */
+ return -1;
+}
diff --git a/lib/libc/src/unistd/getcwd.c b/lib/libc/src/unistd/getcwd.c
new file mode 100644
index 0000000..641a49b
--- /dev/null
+++ b/lib/libc/src/unistd/getcwd.c
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <unistd.h>
+
+char *
+getcwd(char *buf, size_t size)
+{
+ if (buf == NULL) {
+ return NULL;
+ }
+
+ /* TODO: STUB */
+ return NULL;
+}
+
+char *
+getwd(char *pathname)
+{
+ if (pathname == NULL) {
+ return NULL;
+ }
+
+ /* TODO: STUB */
+ return NULL;
+}
diff --git a/lib/libc/src/unistd/getlogin.c b/lib/libc/src/unistd/getlogin.c
new file mode 100644
index 0000000..dd74261
--- /dev/null
+++ b/lib/libc/src/unistd/getlogin.c
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+
+#define UNKNOWN_USER "unknown"
+
+static char *ucache = NULL;
+
+static int
+match_entry(uid_t uid, char *entry)
+{
+ char uidstr[16];
+ char *username = NULL;
+ char *p;
+ size_t len;
+ uint8_t row = 0;
+
+ if (itoa(uid, uidstr, 10) == NULL) {
+ return -1;
+ }
+
+ p = strtok(entry, ":");
+ if (p == NULL) {
+ return -1;
+ }
+
+ while (p != NULL) {
+ switch (row) {
+ case 0:
+ username = p;
+ break;
+ case 2:
+ /*
+ * If the user ID matches, we'll cache the
+ * username.
+ */
+ if (strcmp(uidstr, p) == 0) {
+ ucache = strdup(username);
+ return 0;
+ }
+ break;
+ }
+
+ p = strtok(NULL, ":");
+ ++row;
+ }
+
+ return -1;
+}
+
+char *
+getlogin(void)
+{
+ FILE *fp;
+ char entry[256];
+ int retval;
+ uid_t uid = getuid();
+
+ /* Get the user from the cache */
+ if (ucache != NULL) {
+ return ucache;
+ }
+
+ fp = fopen("/etc/passwd", "r");
+ if (fp == NULL) {
+ return UNKNOWN_USER;
+ }
+
+ while (fgets(entry, sizeof(entry), fp) != NULL) {
+ if (match_entry(uid, entry) == 0) {
+ fclose(fp);
+ return ucache;
+ }
+ }
+
+ fclose(fp);
+ return UNKNOWN_USER;
+}
diff --git a/lib/libc/src/unistd/getpid.c b/lib/libc/src/unistd/getpid.c
new file mode 100644
index 0000000..5770495
--- /dev/null
+++ b/lib/libc/src/unistd/getpid.c
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/syscall.h>
+#include <unistd.h>
+
+pid_t
+getpid(void)
+{
+ return syscall(SYS_getpid);
+}
+
+pid_t
+getppid(void)
+{
+ return syscall(SYS_getppid);
+}
diff --git a/lib/libc/src/unistd/getuid.c b/lib/libc/src/unistd/getuid.c
new file mode 100644
index 0000000..644faa5
--- /dev/null
+++ b/lib/libc/src/unistd/getuid.c
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+uid_t
+getuid(void)
+{
+ return syscall(SYS_getuid);
+}
diff --git a/lib/libc/src/unistd/hostname.c b/lib/libc/src/unistd/hostname.c
new file mode 100644
index 0000000..60df9a0
--- /dev/null
+++ b/lib/libc/src/unistd/hostname.c
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/sysctl.h>
+#include <unistd.h>
+
+/*
+ * Internal helper to grab a sysctl
+ * variable.
+ *
+ * @name: Name definition of sysctl variable
+ * @buf: Buffer to read data in
+ * @buflen: Length of buffer
+ *
+ * Returns zero on success, otherwise a less than
+ * zero value.
+ */
+static int
+__sysctl_get(int name, char *buf, size_t buflen)
+{
+ struct sysctl_args args;
+ int error;
+
+ args.name = &name;
+ args.nlen = 1;
+ args.oldp = buf;
+ args.oldlenp = &buflen;
+ args.newp = NULL;
+ args.newlen = 0;
+
+ if ((error = sysctl(&args)) != 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Internal helper to set a sysctl
+ * variable.
+ *
+ * @name: Name definition of sysctl variable
+ * @buf: Buffer with data to set
+ * @buflen: Length of buffer
+ *
+ * Returns zero on success, otherwise a less than
+ * zero value.
+ */
+static int
+__sysctl_set(int name, const char *buf, size_t buflen)
+{
+ struct sysctl_args args;
+ int error;
+
+ args.name = &name;
+ args.nlen = 1;
+ args.oldp = NULL;
+ args.oldlenp = NULL;
+ args.newp = (void *)buf;
+ args.newlen = buflen;
+
+ if ((error = sysctl(&args)) != 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Get the system hostname
+ *
+ * @name: Buffer to read name into
+ * @size: Length of name to read
+ */
+int
+gethostname(char *name, size_t size)
+{
+ if (name == NULL || size == 0) {
+ return -1;
+ }
+
+ return __sysctl_get(KERN_HOSTNAME, name, size);
+}
+
+/*
+ * Set the system hostname
+ *
+ * @name: Name to set
+ * @size: Size of name to set
+ */
+int
+sethostname(const char *name, size_t size)
+{
+ if (name == NULL || size == 0) {
+ return -1;
+ }
+
+ return __sysctl_set(KERN_HOSTNAME, name, size);
+}
diff --git a/lib/libc/src/unistd/lseek.c b/lib/libc/src/unistd/lseek.c
new file mode 100644
index 0000000..d99b0f0
--- /dev/null
+++ b/lib/libc/src/unistd/lseek.c
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/syscall.h>
+#include <unistd.h>
+
+off_t
+lseek(int fildes, off_t offset, int whence)
+{
+ return syscall(SYS_lseek, fildes, offset, whence);
+}
diff --git a/lib/libc/src/unistd/setuid.c b/lib/libc/src/unistd/setuid.c
new file mode 100644
index 0000000..218f1f1
--- /dev/null
+++ b/lib/libc/src/unistd/setuid.c
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/syscall.h>
+#include <unistd.h>
+
+int
+setuid(uid_t new)
+{
+ return syscall(SYS_setuid, new);
+}
diff --git a/lib/libc/src/unistd/symlink.c b/lib/libc/src/unistd/symlink.c
new file mode 100644
index 0000000..2ad24ca
--- /dev/null
+++ b/lib/libc/src/unistd/symlink.c
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/errno.h>
+#include <unistd.h>
+
+int
+symlink(const char *target, const char *linkpath)
+{
+ if (target == NULL || linkpath == NULL) {
+ return -EINVAL;
+ }
+
+ /* TODO: STUB */
+ return -1;
+}
+
+int
+synlinkat(const char *target, int newdirfd, const char *linkpath)
+{
+ if (target == NULL || linkpath == NULL) {
+ return -EINVAL;
+ }
+
+ /* TODO: STUB */
+ return -1;
+}
diff --git a/lib/libc/src/unistd/sysconf.c b/lib/libc/src/unistd/sysconf.c
new file mode 100644
index 0000000..43fab01
--- /dev/null
+++ b/lib/libc/src/unistd/sysconf.c
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <unistd.h>
+
+extern uint64_t __libc_auxv[_AT_MAX];
+
+int
+sysconf(int name)
+{
+ if (name >= _AT_MAX) {
+ return -1;
+ }
+
+ return __libc_auxv[name];
+}
diff --git a/lib/libc/src/unistd/unlink.c b/lib/libc/src/unistd/unlink.c
new file mode 100644
index 0000000..3de0796
--- /dev/null
+++ b/lib/libc/src/unistd/unlink.c
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/errno.h>
+#include <unistd.h>
+
+int
+unlink(const char *path)
+{
+ if (path == NULL) {
+ return -EINVAL;
+ }
+
+ /* TODO: STUB */
+ return -1;
+}
+
+int
+unlinkat(int dirfd, const char *pathname, int flags)
+{
+ if (pathname == NULL || dirfd < 0) {
+ return -EINVAL;
+ }
+
+ /* TODO: STUB */
+ return -1;
+}
diff --git a/lib/libgfx/Makefile b/lib/libgfx/Makefile
new file mode 100644
index 0000000..12fdd9a
--- /dev/null
+++ b/lib/libgfx/Makefile
@@ -0,0 +1,29 @@
+CFLAGS = -c -fno-stack-protector -nostdlib -static \
+ -Iinclude/ -I$(USRDIR)/include/
+CFILES = $(shell find src/ -name "*.c")
+OBJ = $(CFILES:.c=.o)
+
+all: headers $(OBJ) build/libgfx.a
+ echo $(USRDIR)
+ mv build/libgfx.a $(USRDIR)/lib/
+ cp -r include/ $(USRDIR)/include/
+
+build/libgfx.a:
+ mkdir -p build/
+ ar rcs build/libgfx.a $(OBJ)
+
+%.o: %.c
+ $(CC) $(CFLAGS) -Iinclude/ $< -o $@
+
+.PHONY: headers
+headers:
+ cp -rf include/* $(USRDIR)/include/
+
+.PHONY:
+build/:
+ mkdir -p build/
+
+.PHONY: clean
+clean:
+ rm -f $(OBJ)
+ rm -rf build/
diff --git a/lib/libgfx/include/libgfx/draw.h b/lib/libgfx/include/libgfx/draw.h
new file mode 100644
index 0000000..4140593
--- /dev/null
+++ b/lib/libgfx/include/libgfx/draw.h
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _LIBGFX_DRAW_H_
+#define _LIBGFX_DRAW_H_
+
+#include <stdint.h>
+#include <libgfx/gfx.h>
+
+/* Shape types */
+#define SHAPE_SQUARE 0x00000000
+#define SHAPE_SQUARE_BORDER 0x00000001
+
+/* Basic color defines */
+#define GFX_BLACK 0x000000
+#define GFX_RED 0xFF0000
+#define GFX_GREEN 0x00FF00
+#define GFX_BLUE 0x0000FF
+#define GFX_WHITE 0xFFFFFF
+#define GFX_PURPLE 0x800080
+#define GFX_YELLOW 0xFFFF00
+#define GFX_DARK 0x1D2021
+#define GFX_AQUA 0x427B58
+
+/*
+ * Default shape initializer, something that
+ * works and can be tweaked. The idea of this
+ * is so that shapes may be set up like so:
+ *
+ * --
+ * struct gfx_shape blah = GFX_SHAPE_DEFAULT;
+ *
+ * blah.width = width;
+ * blah.heiht = height;
+ * ...
+ * --
+ */
+#define GFX_SHAPE_DEFAULT \
+ { \
+ .type = SHAPE_SQUARE, \
+ .color = 0x00FF00, \
+ .x = 0, \
+ .y = 0, \
+ .width = 50, \
+ .height = 50, \
+ }
+
+/*
+ * Generic shape representation
+ *
+ * @type: Shape type (see SHAPE_*)
+ * @color: Color of the shape
+ * @x: X position of the shape
+ * @y: Y position of the shape
+ * @width: Shape width
+ * @height: Shape height
+ */
+struct gfx_shape {
+ uint32_t type;
+ color_t color;
+ scrpos_t x;
+ scrpos_t y;
+ dimm_t width;
+ dimm_t height;
+};
+
+/*
+ * A point or single pixel that
+ * may be plotted onto the screen.
+ *
+ * @x,y: Position of the point on the screen
+ * @rgb: Color of the point (RGB)
+ */
+struct gfx_point {
+ scrpos_t x, y;
+ color_t rgb;
+};
+
+/*
+ * Represents a rectangular region on
+ * the screen.
+ *
+ * @x,y: Position of this region on the screen
+ * @width: Region width
+ * @heght: Region height
+ */
+struct gfx_region {
+ scrpos_t x, y;
+ dimm_t width;
+ dimm_t height;
+};
+
+int gfx_draw_shape(struct gfx_ctx *ctx, const struct gfx_shape *shape);
+int gfx_plot_point(struct gfx_ctx *ctx, const struct gfx_point *point);
+
+int gfx_copy_region(struct gfx_ctx *ctx, struct gfx_region *r, scrpos_t x, scrpos_t y);
+color_t gfx_get_pix(struct gfx_ctx *ctx, uint32_t x, uint32_t y);
+
+__always_inline static inline size_t
+gfx_io_index(struct gfx_ctx *ctx, scrpos_t x, scrpos_t y)
+{
+ struct fbattr fbdev = ctx->fbdev;
+
+ return x + y * (fbdev.pitch / 4);
+}
+
+#endif /* !_LIBGFX_DRAW_H_ */
diff --git a/lib/libgfx/include/libgfx/gfx.h b/lib/libgfx/include/libgfx/gfx.h
new file mode 100644
index 0000000..67a1006
--- /dev/null
+++ b/lib/libgfx/include/libgfx/gfx.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _LIBGFX_H_
+#define _LIBGFX_H_
+
+#include <sys/fbdev.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#define gfx_log(fmt, ...) printf( "libgfx: " fmt, ##__VA_ARGS__)
+
+/*
+ * Represents a 32-bit pixel value.
+ *
+ * 24:16 15:8 7:0
+ * +-----------------+
+ * | R | B | B |
+ * +-----------------+
+ */
+typedef uint32_t pixel_t;
+typedef pixel_t color_t;
+
+/*
+ * Represents cartesian x/y values
+ */
+typedef uint32_t cartpos_t;
+typedef cartpos_t scrpos_t;
+typedef cartpos_t dimm_t; /* Dimensions */
+
+/*
+ * Graphics context for libgfx
+ *
+ * @fbdev: Framebuffer attributes
+ * @io: Framebuffer pointer
+ * @fbfd: Framebuffer file descriptor
+ */
+struct gfx_ctx {
+ struct fbattr fbdev;
+ size_t fb_size;
+ pixel_t *io;
+ int fbfd;
+};
+
+int gfx_init(struct gfx_ctx *res);
+void gfx_cleanup(struct gfx_ctx *ctx);
+
+#endif /* !_LIBGFX_H_ */
diff --git a/lib/libgfx/src/draw.c b/lib/libgfx/src/draw.c
new file mode 100644
index 0000000..4df64a8
--- /dev/null
+++ b/lib/libgfx/src/draw.c
@@ -0,0 +1,282 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/errno.h>
+#include <stdint.h>
+#include <libgfx/gfx.h>
+#include <libgfx/draw.h>
+
+/*
+ * See if a pixel is within the bounds of
+ * the screen.
+ *
+ * @ctx: Graphics context pointer
+ * @x: X position to check
+ * @y: Y position to check
+ *
+ * Returns 0 if within bounds, otherwise
+ * a negative value.
+ */
+static int
+gfx_pixel_bounds(struct gfx_ctx *ctx, uint32_t x, uint32_t y)
+{
+ scrpos_t scr_width, scr_height;
+ struct fbattr fbdev;
+
+ if (ctx == NULL) {
+ return -1;
+ }
+
+ /* Grab screen dimensions */
+ fbdev = ctx->fbdev;
+ scr_width = fbdev.width;
+ scr_height = fbdev.height;
+
+ if (x >= scr_width || y >= scr_height) {
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Draw a classic square onto the screen.
+ *
+ * @ctx: Graphics context
+ * @shape: Square to draw
+ */
+static int
+gfx_draw_square(struct gfx_ctx *ctx, const struct gfx_shape *shape)
+{
+ struct fbattr fbdev;
+ struct gfx_point p;
+ off_t idx;
+ scrpos_t x, y;
+
+ if (ctx == NULL || shape == NULL) {
+ return -EINVAL;
+ }
+
+ for (x = shape->x; x < shape->x + shape->width; ++x) {
+ for (y = shape->y; y < shape->y + shape->height; ++y) {
+ p.x = x;
+ p.y = y;
+ p.rgb = shape->color;
+ gfx_plot_point(ctx, &p);
+ }
+ }
+ return 0;
+}
+
+/*
+ * Draw a bordered square onto the screen.
+ *
+ * @ctx: Graphics context pointer
+ * @shape: Bordered square to draw
+ */
+static int
+gfx_draw_bsquare(struct gfx_ctx *ctx, const struct gfx_shape *shape)
+{
+ struct gfx_point p;
+ scrpos_t x_i, y_i;
+ scrpos_t x_f, y_f;
+ scrpos_t x, y;
+
+ if (ctx == NULL || shape == NULL) {
+ return -EINVAL;
+ }
+
+ x_i = shape->x;
+ y_i = shape->y;
+ x_f = shape->x + shape->width;
+ y_f = shape->y + shape->height;
+
+ /*
+ * Draw an unfilled square.
+ *
+ * If we are at the `y_i' or `y_f' position, draw
+ * pixels from 'x_i' to 'x_f'. If we are away
+ * from the `y_i' position, draw two pixels,
+ * one at `x_i' and the other at `x_f' for that
+ * current 'y' value.
+ */
+ for (y = y_i; y < y_f; ++y) {
+ for (x = x_i; x < x_f; ++x) {
+ p.x = x;
+ p.y = y;
+ p.rgb = shape->color;
+
+ /* Origin y, draw entire width */
+ if (y == y_i || y == y_f - 1) {
+ gfx_plot_point(ctx, &p);
+ continue;
+ }
+
+ p.x = x_i;
+ gfx_plot_point(ctx, &p);
+
+ p.x = x_f - 1;
+ gfx_plot_point(ctx, &p);
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Plot a single pixel (aka point) onto
+ * the screen.
+ *
+ * @ctx: The graphics context pointer
+ * @point: Point to plot
+ *
+ * Returns 0 on success, otherwise a less
+ * than zero value.
+ */
+int
+gfx_plot_point(struct gfx_ctx *ctx, const struct gfx_point *point)
+{
+ uint32_t index;
+
+ if (ctx == NULL || point == NULL) {
+ return -EINVAL;
+ }
+
+ /*
+ * Is this even a valid point on the screen for
+ * us to plot on?
+ */
+ if (gfx_pixel_bounds(ctx, point->x, point->y) < 0) {
+ return -1;
+ }
+
+ /* Plot it !! */
+ index = gfx_io_index(ctx, point->x, point->y);
+ ctx->io[index] = point->rgb;
+ return 0;
+}
+
+/*
+ * Grab the RGB value of a single pixel on
+ * the scren.
+ *
+ * @ctx: Graphics context pointer
+ * @x: X position to sample
+ * @y: Y position to sample
+ */
+color_t
+gfx_get_pix(struct gfx_ctx *ctx, uint32_t x, uint32_t y)
+{
+ const color_t ERROR_COLOR = GFX_BLACK;
+ uint32_t index;
+
+ /* The 'ctx' argument is required */
+ if (ctx == NULL) {
+ return ERROR_COLOR;
+ }
+
+ /* Are we within bounds of the screen */
+ if (gfx_pixel_bounds(ctx, x, y) < 0) {
+ return ERROR_COLOR;
+ }
+
+ index = gfx_io_index(ctx, x, y);
+ return ctx->io[index];
+}
+
+/*
+ * Draw a shape onto the screen
+ *
+ * @ctx: libgfx graphics context
+ * @shape: Shape to draw
+ *
+ * Returns 0 on success, otherwise a less than zero
+ * value on failure.
+ *
+ * All error codes follow POSIX errno. However if the value
+ * is -1, the requested shape to be drawn is not valid.
+ */
+int
+gfx_draw_shape(struct gfx_ctx *ctx, const struct gfx_shape *shape)
+{
+ if (ctx == NULL || shape == NULL) {
+ return -EINVAL;
+ }
+
+ switch (shape->type) {
+ case SHAPE_SQUARE:
+ return gfx_draw_square(ctx, shape);
+ case SHAPE_SQUARE_BORDER:
+ return gfx_draw_bsquare(ctx, shape);
+ }
+
+ return -1;
+}
+
+/*
+ * Copy a region on one part of a screen to
+ * another part of a screen.
+ *
+ * @ctx: Graphics context pointer
+ * @r: Region to copy
+ * @x: X position for copy dest
+ * @y: Y position for copy dest
+ */
+int
+gfx_copy_region(struct gfx_ctx *ctx, struct gfx_region *r, scrpos_t x, scrpos_t y)
+{
+ struct gfx_point point;
+ color_t pixel;
+ scrpos_t src_cx, src_cy;
+ dimm_t w, h;
+
+ if (ctx == NULL || r == NULL) {
+ return -EINVAL;
+ }
+
+ w = r->width;
+ h = r->height;
+
+ for (int xoff = 0; xoff < w; ++xoff) {
+ for (int yoff = 0; yoff < h; ++yoff) {
+ /* Source position */
+ src_cx = r->x + xoff;
+ src_cy = r->y + yoff;
+
+ /* Plot the new pixel */
+ pixel = gfx_get_pix(ctx, src_cx, src_cy);
+ point.x = x + xoff;
+ point.y = y + yoff;
+ point.rgb = pixel;
+ gfx_plot_point(ctx, &point);
+ }
+ }
+
+ return 0;
+}
diff --git a/lib/libgfx/src/gfx.c b/lib/libgfx/src/gfx.c
new file mode 100644
index 0000000..90dcb79
--- /dev/null
+++ b/lib/libgfx/src/gfx.c
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/errno.h>
+#include <sys/mman.h>
+#include <sys/fbdev.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <libgfx/gfx.h>
+
+/*
+ * Initialize the libgfx context
+ *
+ * @res: Context pointer to be initialized
+ */
+int
+gfx_init(struct gfx_ctx *res)
+{
+ struct fbattr attr;
+ int fd, prot;
+
+ if (res == NULL) {
+ return -EINVAL;
+ }
+
+ /* Get framebuffer attributes */
+ fd = open("/ctl/fb0/attr", O_RDONLY);
+ if (fd < 0) {
+ gfx_log("could not open '/ctl/fb0/attr");
+ return fd;
+ }
+
+ read(fd, &attr, sizeof(attr));
+ close(fd);
+ res->fbdev = attr;
+
+ /* Open the framebuffer file */
+ res->fbfd = open("/dev/fb0", O_RDWR);
+ if (res->fbfd < 0) {
+ gfx_log("could not open '/dev/fb0'\n");
+ return res->fbfd;
+ }
+
+ /* Map the framebuffer into memory */
+ prot = PROT_READ | PROT_WRITE;
+ res->fb_size = attr.height * attr.pitch;
+ res->io = mmap(NULL, res->fb_size, prot, MAP_SHARED, res->fbfd, 0);
+
+ /* Did the mmap() call work? */
+ if (res->io == NULL) {
+ gfx_log("could not map framebuffer\n");
+ close(res->fbfd);
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Cleanup all state and free the gfx
+ * context.
+ */
+void
+gfx_cleanup(struct gfx_ctx *ctx)
+{
+ if (ctx->io != NULL)
+ munmap(ctx->io, ctx->fb_size);
+ if (ctx->fbfd > 0)
+ close(ctx->fbfd);
+}
diff --git a/lib/liboda/Makefile b/lib/liboda/Makefile
new file mode 100644
index 0000000..5b4022c
--- /dev/null
+++ b/lib/liboda/Makefile
@@ -0,0 +1,29 @@
+CFLAGS = -c -fno-stack-protector -nostdlib -static \
+ -Iinclude/ -I$(USRDIR)/include/
+CFILES = $(shell find src/ -name "*.c")
+OBJ = $(CFILES:.c=.o)
+
+all: headers $(OBJ) build/liboda.a
+ echo $(USRDIR)
+ mv build/liboda.a $(USRDIR)/lib/
+ cp -r include/ $(USRDIR)/include/
+
+build/liboda.a:
+ mkdir -p build/
+ ar rcs build/liboda.a $(OBJ)
+
+%.o: %.c
+ $(CC) $(CFLAGS) -Iinclude/ $< -o $@
+
+.PHONY: headers
+headers:
+ cp -rf include/* $(USRDIR)/include/
+
+.PHONY:
+build/:
+ mkdir -p build/
+
+.PHONY: clean
+clean:
+ rm -f $(OBJ)
+ rm -rf build/
diff --git a/lib/liboda/include/liboda/input.h b/lib/liboda/include/liboda/input.h
new file mode 100644
index 0000000..c74a304
--- /dev/null
+++ b/lib/liboda/include/liboda/input.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef LIBODA_INPUT_H
+#define LIBODA_INPUT_H
+
+#include <stdint.h>
+#include <stddef.h>
+
+/*
+ * Macros to help extract scancode and
+ * character
+ */
+#define ODA_SCANCODE(KEY) ((KEY) >> 8)
+#define ODA_KEYCHAR(KEY) ((char )(KEY) & 0xFF)
+
+/*
+ * Key defines
+ */
+#define ODA_KEY_OTHER 0x0000
+#define ODA_KEY_ESCAPE 0x0001
+#define ODA_KEY_TAB 0x0002
+#define ODA_KEY_BACKSPACE 0x0003
+
+/*
+ * Represents a key press
+ *
+ * @type: Key types (see ODA_KEY_*)
+ * @scancode: Scancode
+ * @ch: Character
+ */
+struct oda_key {
+ uint16_t type;
+ uint8_t scancode;
+ char ch;
+};
+
+/*
+ * ODA keyboard object for managing keyboard
+ * input.
+ */
+struct oda_kbd {
+ int(*handle_keyev)(struct oda_kbd *kbd, struct oda_key *key);
+};
+
+int oda_kbd_dispatch(struct oda_kbd *kbd);
+
+#endif /* !LIBODA_INPUT_H */
diff --git a/lib/liboda/include/liboda/oda.h b/lib/liboda/include/liboda/oda.h
new file mode 100644
index 0000000..9d96f2f
--- /dev/null
+++ b/lib/liboda/include/liboda/oda.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef LIBODA_ODA_H
+#define LIBODA_ODA_H 1
+
+#include <sys/queue.h>
+#include <stdint.h>
+#include <stddef.h>
+#include <liboda/types.h>
+#include <libgfx/gfx.h>
+#include <libgfx/draw.h>
+
+/*
+ * ODA representation of a window.
+ *
+ * @wid: Window ID (identifies the window)
+ * @surface: Window surface descriptor
+ * @session: Session this window belongs to
+ */
+struct oda_window {
+ odawid_t wid;
+ struct gfx_shape surface;
+ struct oda_state *session;
+ TAILQ_ENTRY(oda_window) link;
+};
+
+/*
+ * ODA session
+ *
+ * @winq: Window queue
+ * @gctx: Graphics context
+ * @cookie: State cookie (ODA_COOKIE)
+ */
+struct oda_state {
+ TAILQ_HEAD(, oda_window) winq;
+ struct gfx_ctx gctx;
+ uint32_t cookie;
+};
+
+/*
+ * ODA window attributes. Arguments to be
+ * passed to oda_window_new()
+ *
+ * @session: Current ODA session / state
+ * @parent: Window parent (NULL for root)
+ * @pg: Background color (0xRRGGBB)
+ * @x,y: Window position
+ * @w,h: Window width [w] and height [h]
+ */
+struct oda_wattr {
+ struct oda_state *session;
+ struct oda_window *parent;
+ odacolor_t bg;
+ odapos_t x, y;
+ odadimm_t w, h;
+};
+
+/*
+ * Arguments for oda_movewin() are stored
+ * within this structure to minimize the
+ * number of arguments within the function
+ * signature.
+ *
+ * @wp: Window to be moved
+ * @to_x: X position to move window to
+ * @to_y: Y position to move window to
+ */
+struct oda_movewin {
+ struct oda_window *wp;
+ odapos_t to_x;
+ odapos_t to_y;
+};
+
+/*
+ * A pixel point that can be plotted
+ * onto a window.
+ *
+ * @x,y: Point position
+ * @rgb: Color (RGB)
+ * @window: Window this will be plotted to
+ *
+ * Just set x, y, the color (rgb) then point it
+ * to a window!
+ */
+struct oda_point {
+ odapos_t x, y;
+ odacolor_t rgb;
+ struct oda_window *window;
+};
+
+int oda_reqwin(struct oda_wattr *params, struct oda_window **res);
+int oda_termwin(struct oda_state *state, struct oda_window *win);
+
+int oda_plotwin(struct oda_state *state, const struct oda_point *point);
+int oda_movewin(struct oda_state *state, struct oda_movewin *params);
+int oda_start_win(struct oda_state *state, struct oda_window *win);
+
+int oda_init(struct oda_state *res);
+int oda_shutdown(struct oda_state *state);
+
+#endif /* !LIBODA_ODA_H */
diff --git a/lib/liboda/include/liboda/odavar.h b/lib/liboda/include/liboda/odavar.h
new file mode 100644
index 0000000..d2dbe2e
--- /dev/null
+++ b/lib/liboda/include/liboda/odavar.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef LIBODA_ODAVAR_H
+#define LIBODA_ODAVAR_H
+
+#include <sys/param.h>
+#include <sys/errno.h>
+#include <liboda/oda.h>
+
+/*
+ * Default window attributes
+ */
+#define DEFAULT_WIN_HEIGHT 200
+#define DEFAULT_WIN_WIDTH 150
+
+/*
+ * Verify that ODA structures have been properly
+ * initialized before usage to prevent undefined
+ * behaviour.
+ */
+#define ODA_COOKIE 0xFAFECAD
+
+/*
+ * Verify an ODA cookie - internal usage
+ */
+__always_inline static inline int
+oda_cookie_verify(struct oda_state *state)
+{
+ return (state->cookie == ODA_COOKIE) ? 0 : -EFAULT;
+}
+
+#endif /* !LIBODA_ODAVAR_H */
diff --git a/lib/liboda/include/liboda/types.h b/lib/liboda/include/liboda/types.h
new file mode 100644
index 0000000..ec12330
--- /dev/null
+++ b/lib/liboda/include/liboda/types.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef LIBODA_TYPE_H
+#define LIBODA_TYPE_H
+
+#include <stdint.h>
+
+typedef uint32_t odapos_t; /* X/Y positions */
+typedef uint32_t odapix_t; /* RGB pixel */
+typedef odapix_t odacolor_t; /* RGB color */
+typedef uint32_t odadimm_t; /* Dimensions */
+typedef uint32_t odawid_t; /* Window ID */
+
+#endif /* !LIBODA_TYPE_H */
diff --git a/lib/liboda/src/input.c b/lib/liboda/src/input.c
new file mode 100644
index 0000000..797a9d4
--- /dev/null
+++ b/lib/liboda/src/input.c
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/ascii.h>
+#include <sys/errno.h>
+#include <sys/param.h>
+#include <liboda/oda.h>
+#include <liboda/input.h>
+#include <stdint.h>
+#include <stdio.h>
+
+/*
+ * Convert key scancode/char values to fixed
+ * ODA key constants
+ */
+static inline uint16_t
+oda_map_key(const struct oda_key *key)
+{
+ uint16_t type = ODA_KEY_OTHER;
+
+ switch (key->ch) {
+ case ASCII_ESC:
+ type = ODA_KEY_ESCAPE;
+ break;
+ case ASCII_HT:
+ type = ODA_KEY_TAB;
+ break;
+ case ASCII_BS:
+ type = ODA_KEY_BACKSPACE;
+ break;
+ }
+
+ return type;
+}
+
+/*
+ * Dispatch keyboard events. This is typically
+ * called in an event loop so that keyboard events
+ * are handled per iteration.
+ *
+ * @kbd: Keyboard to monitor
+ */
+int
+oda_kbd_dispatch(struct oda_kbd *kbd)
+{
+ struct oda_key key;
+ int input;
+
+ if (kbd == NULL) {
+ return -EINVAL;
+ }
+
+ /* Attempt to grab the input */
+ if ((input = getchar()) < 0) {
+ return -EAGAIN;
+ }
+
+ key.scancode = ODA_SCANCODE(input);
+ key.ch = ODA_KEYCHAR(input);
+ key.type = oda_map_key(&key);
+ return kbd->handle_keyev(kbd, &key);
+}
diff --git a/lib/liboda/src/oda.c b/lib/liboda/src/oda.c
new file mode 100644
index 0000000..0eef523
--- /dev/null
+++ b/lib/liboda/src/oda.c
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/errno.h>
+#include <stdio.h>
+#include <libgfx/gfx.h>
+#include <liboda/oda.h>
+#include <liboda/odavar.h>
+
+#define oda_log(fmt, ...) printf("oda: " fmt, ##__VA_ARGS__)
+
+/*
+ * Initialize the OSMORA Display Architecture
+ * (ODA) library.
+ *
+ * @res: Initialized ODA state result
+ *
+ * Returns 0 on success, otherwise a less than
+ * zero value.
+ */
+int
+oda_init(struct oda_state *res)
+{
+ int error;
+
+ /* Ensure the argument is valid */
+ if (res == NULL) {
+ return -EINVAL;
+ }
+
+ /*
+ * If this state has already been initialized,
+ * assume programmer error / undefined behaviour
+ * and let them know.
+ */
+ if (oda_cookie_verify(res) == 0) {
+ oda_log("oda_init: 'res' already initialized\n");
+ return -EBUSY;
+ }
+
+ /* Initialize the graphics context */
+ error = gfx_init(&res->gctx);
+ if (error != 0) {
+ oda_log("oda_init: could not init graphics context\n");
+ return error;
+ }
+
+ TAILQ_INIT(&res->winq);
+ res->cookie = ODA_COOKIE;
+ return 0;
+}
diff --git a/lib/liboda/src/window.c b/lib/liboda/src/window.c
new file mode 100644
index 0000000..216b106
--- /dev/null
+++ b/lib/liboda/src/window.c
@@ -0,0 +1,461 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/errno.h>
+#include <sys/queue.h>
+#include <stdlib.h>
+#include <string.h>
+#include <liboda/oda.h>
+#include <liboda/odavar.h>
+#include <liboda/types.h>
+#include <libgfx/gfx.h>
+#include <libgfx/draw.h>
+
+/*
+ * The window cache is used to reduce how many
+ * calls to malloc() and free() are made during
+ * window creation and destruction.
+ */
+static TAILQ_HEAD(, oda_window) wcache;
+static uint32_t wcache_cookie = 0;
+static odawid_t next_wid = 1;
+
+/*
+ * Pop a window from the window cache.
+ * Returns NULL there are no more windows.
+ */
+static struct oda_window *
+oda_window_pop(void)
+{
+ struct oda_window *wdp;
+
+ if (wcache_cookie != ODA_COOKIE) {
+ TAILQ_INIT(&wcache);
+ wcache_cookie = ODA_COOKIE;
+ return NULL;
+ }
+
+ wdp = TAILQ_FIRST(&wcache);
+ TAILQ_REMOVE(&wcache, wdp, link);
+ return wdp;
+}
+
+/*
+ * Place a window into the window cache.
+ */
+static void
+oda_window_cache(struct oda_window *wdp)
+{
+ /* Ensure arg is valid */
+ if (wdp == NULL) {
+ return;
+ }
+
+ if (wcache_cookie != ODA_COOKIE) {
+ TAILQ_INIT(&wcache);
+ wcache_cookie = ODA_COOKIE;
+ }
+
+ TAILQ_INSERT_TAIL(&wcache, wdp, link);
+}
+
+/*
+ * Allocate an ODP window
+ *
+ * Returns NULL on failure
+ */
+static struct oda_window *
+oda_window_alloc(void)
+{
+ struct oda_window *wdp;
+
+ /*
+ * First check if there are any entries
+ * we can grab from the window cache.
+ */
+ wdp = oda_window_pop();
+ if (wdp != NULL) {
+ return wdp;
+ }
+
+ /* Allocate a new window */
+ wdp = malloc(sizeof(*wdp));
+ if (wdp == NULL) {
+ return NULL;
+ }
+
+ memset(wdp, 0, sizeof(*wdp));
+ wdp->wid = next_wid++;
+ return wdp;
+}
+
+/*
+ * Release a given ODA window descriptor
+ *
+ * @wdp: Window to free
+ */
+static void
+oda_window_release(struct oda_state *state, struct oda_window *wdp)
+{
+ if (wdp == NULL) {
+ return;
+ }
+
+ /*
+ * It is probably a good idea to ensure previous
+ * state other than the old window ID is reset
+ * and zeroed.
+ */
+ wdp->session = NULL;
+ memset(&wdp->surface, 0, sizeof(wdp->surface));
+
+ /*
+ * Now we can remove this window from the list
+ * of windows we are tracking and add it to the
+ * cache.
+ */
+ TAILQ_REMOVE(&state->winq, wdp, link);
+ oda_window_cache(wdp);
+}
+
+/*
+ * Check if a point is within the bounds of
+ * a surface.
+ *
+ * @wp: Surface to check with point
+ * @point: Point to check with surface
+ *
+ * Returns 0 if the check has passed,
+ * otherwise a less than zero value.
+ */
+static int
+oda_check_point(struct oda_window *wp, struct oda_point *point)
+{
+ struct gfx_shape *surf;
+ scrpos_t win_startx;
+ scrpos_t win_starty;
+ scrpos_t win_endx;
+ scrpos_t win_endy;
+
+ /* Compute start positions */
+ surf = &wp->surface;
+ win_startx = surf->x;
+ win_starty = surf->y;
+
+ /* Compute end positions */
+ win_endx = surf->x + surf->width;
+ win_endy = surf->y + surf->height;
+
+ /* Check X bounds */
+ if (point->x < win_startx || point->x > win_endx) {
+ return -1;
+ }
+
+ /* Check Y bounds */
+ if (point->y < win_starty || point->y > win_endy) {
+ return -1;
+ }
+
+ /* All good */
+ return 0;
+}
+
+/*
+ * Clean up after ourselves and release
+ * each entry of the wcache.
+ *
+ * Returns 0 on success.
+ */
+static int
+oda_free_wcache(void)
+{
+ struct oda_window *wdp, *next;
+
+ if (wcache_cookie != ODA_COOKIE) {
+ return -1;
+ }
+
+ /*
+ * Go through each entry and call free()
+ * on them.
+ */
+ wdp = TAILQ_FIRST(&wcache);
+ while (wdp != NULL) {
+ next = TAILQ_NEXT(wdp, link);
+ free(wdp);
+ wdp = next;
+ }
+ return 0;
+}
+
+/*
+ * Plot a pixel onto a window
+ *
+ * @state: ODA state pointer
+ * @point: Point to plot
+ *
+ * Returns 0 on success, otherwise a less than
+ * zero value.
+ *
+ * XXX: The x/y params in the 'point' argument must be
+ * relative to the start of the window. In other words,
+ * (0,0) refers to the top left corner of the window.
+ */
+int
+oda_plotwin(struct oda_state *state, const struct oda_point *point)
+{
+ struct gfx_point pixel;
+ struct oda_point point_new;
+ struct oda_window *window;
+ struct gfx_shape *surf;
+ odapos_t plotx, ploty;
+ int error;
+
+ if (state == NULL || point == NULL) {
+ return -EINVAL;
+ }
+
+ /* Validate cookie */
+ if ((error = oda_cookie_verify(state)) != 0) {
+ return error;
+ }
+
+ /* Try to grab the window */
+ if ((window = point->window) == NULL) {
+ return -EINVAL;
+ }
+
+ surf = &window->surface;
+ plotx = surf->x + point->x;
+ ploty = surf->y + point->y;
+
+ /*
+ * We are going to need to transform the coordinates
+ * as they are supposed to be coming in relative to
+ * the window bounds, e.g., (0,0) being the top left
+ * corner of a window.
+ */
+ point_new = *point;
+ point_new.x = plotx;
+ point_new.y = ploty;
+
+ /* Initialize the pixel to plot */
+ pixel.x = plotx;
+ pixel.y = ploty;
+ pixel.rgb = point->rgb;
+
+ /* Is the point within bounds? */
+ error = oda_check_point(window, &point_new);
+ if (error < 0) {
+ return error;
+ }
+
+ gfx_plot_point(&state->gctx, &pixel);
+ return 0;
+}
+
+/*
+ * Request a window from the OSMORA Display
+ * Architecture (ODA).
+ *
+ * @params: Arguments
+ * @res: Resulting pointer for new window
+ *
+ * Returns 0 on success, otherwise a less than
+ * zero value.
+ */
+int
+oda_reqwin(struct oda_wattr *params, struct oda_window **res)
+{
+ struct oda_window *wp;
+ struct gfx_shape *surf;
+ struct oda_state *session;
+ int error;
+
+ if (params == NULL || res == NULL) {
+ return -EINVAL;
+ }
+
+ /* Try to grab the current session */
+ if ((session = params->session) == NULL) {
+ return -EIO;
+ }
+
+ /* Verify that cookie! */
+ if ((error = oda_cookie_verify(session)) != 0) {
+ return error;
+ }
+
+ /* Allocate a new window */
+ wp = oda_window_alloc();
+ if (wp == NULL) {
+ return -ENOMEM;
+ }
+
+ /* Initialize the window */
+ memset(wp, 0, sizeof(*wp));
+ wp->session = session;
+ TAILQ_INSERT_TAIL(&session->winq, wp, link);
+
+ /* Fix up width/height params */
+ if (params->w == 0)
+ params->w = DEFAULT_WIN_WIDTH;
+ if (params->h == 0)
+ params->h = DEFAULT_WIN_HEIGHT;
+
+ /* Initialize the window surface */
+ surf = &wp->surface;
+ surf->color = params->bg;
+ surf->x = params->x;
+ surf->y = params->y;
+ surf->width = params->w;
+ surf->height = params->h;
+ surf->type = SHAPE_SQUARE;
+ *res = wp;
+ return 0;
+}
+
+/*
+ * Move a window to a new position on the
+ * screen.
+ *
+ * @state: ODA state pointer
+ * @params: Arguments to this function
+ */
+int
+oda_movewin(struct oda_state *state, struct oda_movewin *params)
+{
+ struct oda_window *win;
+ struct gfx_shape *wsurf;
+ struct gfx_region r;
+ odadimm_t w, h;
+ odapos_t x, y, x_f, y_f;
+ odapos_t to_x, to_y;
+ uint32_t i = 0;
+ int error;
+
+ /* Make sure arguments are valid */
+ if (state == NULL || params == NULL) {
+ return -EINVAL;
+ }
+
+ /* We need the window */
+ if ((win = params->wp) == NULL) {
+ return -EINVAL;
+ }
+
+ /* Verify state cookie */
+ if ((error = oda_cookie_verify(state)) != 0) {
+ return error;
+ }
+
+ wsurf = &win->surface;
+ to_x = params->to_x;
+ to_y = params->to_y;
+
+ r.x = wsurf->x;
+ r.y = wsurf->y;
+ r.width = wsurf->width;
+ r.height = wsurf->height;
+
+ /*
+ * We will copy the window to the new location and
+ * fill where the old window was with GFX_BLACK.
+ *
+ * TODO: Handle overlapping of windows
+ */
+ gfx_copy_region(&state->gctx, &r, to_x, to_y);
+ wsurf->x = to_x;
+ wsurf->y = to_y;
+ return 0;
+}
+
+/*
+ * Register a window into the current ODA state.
+ * Everytime a compositor requests a window, we
+ * must keep track of it.
+ *
+ * @state: ODA state pointer
+ * @win: Pointer of window to register
+ */
+int
+oda_start_win(struct oda_state *state, struct oda_window *win)
+{
+ int error;
+
+ if (state == NULL || win == NULL) {
+ return -EINVAL;
+ }
+
+ /* Make sure the state is valid */
+ if ((error = oda_cookie_verify(state)) != 0) {
+ return error;
+ }
+
+ gfx_draw_shape(&state->gctx, &win->surface);
+ return 0;
+}
+
+/*
+ * Terminate a running window
+ *
+ * @state: ODA state pointer
+ * @win: Win pointer
+ *
+ * Returns 0 on success, otherwise a less than
+ * zero value.
+ *
+ * TODO: Cleanup screen
+ */
+int
+oda_termwin(struct oda_state *state, struct oda_window *win)
+{
+ int error;
+
+ if (state == NULL || win == NULL) {
+ return -EINVAL;
+ }
+
+ /* Validate the cookie */
+ if ((error = oda_cookie_verify(state)) != 0) {
+ return error;
+ }
+
+ oda_window_release(state, win);
+ return 0;
+}
+
+/*
+ * Shutdown the ODA library
+ */
+int
+oda_shutdown(struct oda_state *state)
+{
+ return oda_free_wcache();
+}
diff --git a/rc/init.rc b/rc/init.rc
new file mode 100644
index 0000000..6979a06
--- /dev/null
+++ b/rc/init.rc
@@ -0,0 +1,9 @@
+@
+@ /usr/rc/init.rc
+@ ---------------
+@ Hyra userspace startup script
+@
+
+/usr/sbin/inject
+beep 500 30
+beep 650 30
diff --git a/share/docs/hw/et131x.txt b/share/docs/hw/et131x.txt
new file mode 100644
index 0000000..43d7c4f
--- /dev/null
+++ b/share/docs/hw/et131x.txt
@@ -0,0 +1,215 @@
+--------------------------------------
+Unofficial ET131X datasheet. If Agere
+doesn't give you shit, OSMORA will
+
+Author: Ian Marco Moffett
+--------------------------------------
+
+-- PCI information
+
+VENDOR ID: 0x11C1 (Agere)
+Device ID: 0xED00
+
+The ET131X exposes its register interface through
+PCI BAR 0.
+
+-- Device register map (register set list)
+|
+++ GLOBAL REGS (GLOBAL):
+ // JAGCore register map (offset BAR[0] + 0x0000)
+ TX_queue_start_addr [dword] (BAR[0] + 0x0000)
+ TX_queue_end_addr [dword] (BAR[0] + 0x0004)
+ RX_queue_start_addr [dword] (BAR[0] + 0x0008)
+ RX_queue_end_addr [dword] (BAR[0] + 0x000C)
+ PM_CSR [dword] (BAR[0] + 0x0010)
+ unused [dword] (BAR[0] + 0x0014)
+ int_status [dword] (BAR[0] + 0x0018)
+ int_mask [dword] (BAR[0] + 0x001C)
+ int_alias_clr_en [dword] (BAR[0] + 0x0020)
+ int_status_alias [dword] (BAR[0] + 0x0024)
+ sw_reset [dword] (BAR[0] + 0x0028)
+ slv_timer [dword] (BAR[0] + 0x002C)
+ msi_config [dword] (BAR[0] + 0x0030)
+ loopback [dword] (BAR[0] + 0x0034)
+ watchdog_timer [dword] (BAR[0] + 0x0038)
+ ----------------------------------------
+ NOTES:
+ [REGISTER INFORMATION]
+ - TX_queue_start_addr:
+ Address of transmit queue start in internal RAM.
+ - TX_queue_end_addr:
+ Address of transmit queue end in internal RAM.
+ - RX_queue_start_addr:
+ Address of receive queue start in internal RAM.
+ - RX_queue_end_addr:
+ Address of receive queue end in internal RAM.
+ - PM_CSR:
+ Power management control/status register.
+ - unused:
+ Not used, leave alone.
+ - int_status:
+ Interrupt status register.
+ - int_mask:
+ Interrupt mask register
+ - int_alias_clr_en:
+ ????
+ - int_status_alias:
+ ????
+ - slv_timer:
+ ???? - for some sort of timeout
+ - loopback:
+ Loopback control register
+ [0x00000001] -> LOOP MAC
+ [0x00000002] -> LOOP DMA
+ - watchdog_timer:
+ Watchdog timer regieter (nanoseconds)
+| [0] -> DISABLED
+++ MAC REGISTERS (MAC_REGS):
+ // JAGCore MAC registers (offset BAR[0] + 0x5000)
+ cfg1 [dword] (BAR[0] + 0x5000)
+ cfg2 [dword] (BAR[0] + 0x5004)
+ ipg [dword] (BAR[0] + 0x5008)
+ hfdp [dword] (BAR[0] + 0x500C)
+ max_fm_len [dword] (BAR[0] + 0x5010)
+ reserved1 [dword] (BAR[0] + 0x5014)
+ reserved2 [dword] (BAR[0] + 0x5018)
+ mac_test [dword] (BAR[0] + 0x501C)
+ mii_mgmt_cfg [dword] (BAR[0] + 0x5020)
+ mii_mgmt_cmd [dword] (BAR[0] + 0x5024)
+ mii_mgmt_addr [dword] (BAR[0] + 0x5028)
+ mii_mgmt_ctrl [dword] (BAR[0] + 0x502C)
+ mii_mgmt_stat [dword] (BAR[0] + 0x5030)
+ mii_mgmt_indicator [dword] (BAR[0] + 0x5034)
+ if_ctrl [dword] (BAR[0] + 0x5038)
+ if_stat [dword] (BAR[0] + 0x503C)
+ station_addr1 [dword] (BAR[0] + 0x5040)
+ station_addr2 [dword] (BAR[0] + 0x5044)
+ NOTES:
+ [REGISTER INFORMATION]
+ - cfg1:
+ First MAC configuration register.
+ - cfg2:
+ Second MAC configuration register.
+ - ipg:
+ MAC Interpacket Gap configuration register.
+ - hfdp:
+ MAC Half Duplex configuration register.
+ - max_fm_len:
+ Max packet length (bytes) sent through MAC without
+ truncation.
+ - mac_test:
+ MAC test registers
+ - mii_mgmt_cfg:
+ MAC MII Management Config register.
+ - mii_mgmt_cmd:
+ MAC MII Management Command register.
+ - mii_mgmt_ctrl:
+ MAC MII Management Control register.
+ - mii_mgmt_stat:
+ MAC MII Management Status register.
+ - mii_mgmt_indicator:
+ MAC MII Management Indicator register.
+ - if_ctrl:
+ MAC interface control register.
+ - station_addr1:
+ First MAC station address register.
+ - station_addr2:
+ Second MAC station address register.
+ [BITS]
+ -------------------------------------
+ @ cfg1:
+ [bits 0]: TX enable
+ [bits 1]: Syncd TX enable
+ [bits 2]: RX enable
+ [bits 3]: Syncd TX enable
+ [bits 4]: TX flow
+ [bits 5]: RX flow
+ [bits 7:6]: Reserved
+ [bits 8]: Loopback
+ [bits 15:9]: Reserved
+ [bits 16]: Reset TX func
+ [bits 17]: Reset RX func
+ [bits 18]: Reset TX MC
+ [bits 19]: Reset RX MC
+ [bits 29:20]: Reserved
+ [bits 30]: Sim reset
+ [bits 31]: Soft reset
+ @ cfg2:
+ [bits 0]: Full-duplex
+ [bits 1]: CRC enable
+ [bits 2]: Pad CRC
+ [bits 3]: Unused (undefined)
+ [bits 4]: Length check
+ [bits 5]: Huge frame
+ [bits 7:6]: Reserved
+ [bits 9:8]: Interface mode
+ [bits 11:10]: Reserved
+ [bits 15:12]: Preamble
+ [bits 31:16]: Reserved
+ @ ipg:
+ [bits 7:0]: B2B IPG
+ [bits 15:8]: Minimum IFG enforce
+ [bits 22:16]: Non B2B IPG 2
+ [bits 23]: Unused (undefined)
+ [bits 30:24]: Non B2B IPG 1
+ [bits 31]: Reserved
+ @ hfdp:
+ [bits 9:0]: Collision window
+ [bits 11:10]: Reserved
+ [bits 15:12]: Re-transmit max
+ [bits 16]: Excess defer
+ [bits 17]: No backoff
+ [bits 18]: BP no backoff
+ [bits 19]: Alt BEB enable [1]
+ [bits 23-20]: Alt BEB trunc [1]
+ [bits 31-24]: Reserved
+ |
+ ++ [1]: BEB refers to Binary Exponential Backoff which is
+ a method to mitigate collisions by doubling the TX
+ backoff window (throttling TX rate) per collision.
+ -------------------------------------
+
+
+------------------------------------------------------------------
+ET131X REGISTER SPACE NOTES:
+------------------------------------------------------------------
+[Padding]:
+ Each register set exists within a 4096 byte region and some
+ register sets do not fully span that full length. Therefore
+ when defining the register space within a struct, one must
+ be sure to account for such gaps by including 'n' bytes of
+ padding after each register set. Where 'n' is how many bytes
+ left to fully span 4096 bytes in other words, '4096 - regset_len'.
+
+------------------------------------------------------------------
+SOFTWARE RESET PROCCESS:
+------------------------------------------------------------------
+#define MAC_CFG1_SOFTRST 0x80000000 /* Soft reset */
+#define MAC_CFG1_SIMRST 0x40000000 /* SIM reset */
+#define MAC_CFG1_RESET_RXMC 0x00080000 /* RX MC reset */
+#define MAC_CFG1_RESET_TXMC 0x00040000 /* TX MC reset */
+#define MAC_CFG1_RESET_RXFUNC 0x00020000 /* RX func reset */
+#define MAC_CFG1_RESET_TXFUNC 0x00010000 /* TX func reset */
+#define GBL_RESET_ALL 0x007F /* Global reset */
+------------------------------------------------------------------
+To perform a software reset, one must write the value of MAC_CFG1_SOFTRST,
+MAC_CFG1_SIMRST, MAC_CFG1_RESET_RXMC, MAC_CFG1_RESET_TXMC, MAC_CFG1_RESET_RXFUNC,
+and MAC_CFG1_RESET_TXMC combined together with a bitwise OR to the 'cfg1' register
+of the 'MAC_REGS' register set.
+
+This results in the MAC core (aka JAGCore) resetting all internal state
+and being brought to halt.
+
+Once the MAC core is reset, you must be sure to also reset the rest of the card,
+(I know, just when you thought you were done). This may be done by writing the
+value of GBL_RESET_ALL to the 'sw_reset' register of the 'GLOBAL' register set.
+This results in the reset of further state such as state machines for TX DMA, RX DMA,
+MAC TX, MAC RX, etc cetera.
+
+To ensure the TX/RX paths of the MAC core are in a known state, one
+must write the value of MAC_CFG1_RESET_RXMC, MAC_CFG1_RESET_TXMC, MAC_CFG1_RESET_RXFUNC,
+and MAC_CFG1_RESET_TXMC combined together with a bitwise OR to the 'cfg1' register
+of the 'MAC_REGS' register set.
+
+And finally, you must also make sure the 'cfg1' register of the 'MAC_REGS' register
+set is also in a known state by clearing it to 0x00000000.
diff --git a/share/docs/kernel/ctlfs.md b/share/docs/kernel/ctlfs.md
new file mode 100644
index 0000000..3087f60
--- /dev/null
+++ b/share/docs/kernel/ctlfs.md
@@ -0,0 +1,125 @@
+# The Hyra control filesystem (ctlfs)
+
+Written by Ian M. Moffett
+
+## Rationale
+
+Historically, Operating Systems of the Unix family typically relied
+on syscalls like ``ioctl()`` or similar to perform operations (e.g., making calls through a driver)
+via some file descriptor. Let's say for example, one wanted to acquire the framebuffer
+dimensions of a given framebuffer device. To start, they'd acquire a file descriptor
+by calling ``open()`` or similar on it. Then they'd make their ``ioctl()`` call.
+
+```c
+int fd = ...;
+
+ioctl(fd, IOCTL_FBINFO, &fb_info);
+...
+```
+
+While this works fine and is relatively simple to use from the user's
+perspective, it is very clunky when you pop the hood and peer into the
+inner-workings of it within the kernel. The number of possible requests
+that can be passed through a file descriptor can grow quite rapidly which
+can require really large switch statements within the drivers that implement
+an ``ioctl()`` interface.
+
+## Replacing ``ioctl()``
+
+Hyra provides ctlfs, an abstract in-memory filesystem designed for
+setting/getting various kernel / driver parameters and state via
+the filesystem API. The control filesystem consists of several
+instances of two fundamentals: "control nodes" and "control entries".
+
+### Control nodes
+
+Control nodes are simply directories within the ``/ctl`` root. For example,
+console specific control files are in the ``/ctl/console`` node.
+
+### Control entries
+
+Control entries are simply files within specific control nodes. For example
+console features may be find in the ``consfeat`` entry of the ``console`` node
+(i.e., ``/ctl/console/consfeat``).
+
+See ``sys/include/sys/console.h`` and ``sys/fs/ctlfs.h`` for more
+information.
+
+## The ctlfs API
+
+The Hyra kernel provides an API for subsystems and drivers
+to create their own ctlfs entries and nodes. This may be found
+in sys/include/fs/ctlfs.h
+
+### Control operations
+
+Each control entry must define their own set of
+"control operations" described by the ``ctlops`` structure:
+
+```c
+struct ctlops {
+ int(*read)(struct ctlfs_dev *cdp, struct sio_txn *sio);
+ int(*write)(struct ctlfs_dev *cdp, struct sio_txn *sio);
+ ...
+};
+```
+
+NOTE: Callbacks defined as ``NULL`` will be
+ignored and unused.
+
+## "Meow World": Creating a ctlfs entry
+
+```c
+#include <sys/types.h>
+#include <sys/sio.h>
+#include <fs/ctlfs.h>
+
+static const struct ctlops meow_ctl;
+
+/*
+ * Ctlfs read callback - this will be called
+ * when "/ctl/meow/hii" is read.
+ */
+static int
+ctl_meow_read(struct ctlfs_dev *cdp, struct sio_txn *sio)
+{
+ char data[] = "Meow World!""
+
+ /* Clamp the input length */
+ if (sio->len > sizeof(data)) {
+ sio->len = sizeof(data)
+ }
+
+ /* End of the data? */
+ if ((sio->offset + sio->len) > sizeof(data)) {
+ return 0;
+ }
+
+ /* Copy the data and return the length */
+ memcpy(sio->buf, &data[sio->offset], sio->len);
+ return sio->len;
+}
+
+static int
+foo_init(void)
+{
+ char ctlname[] = "meow";
+ struct ctlfs_dev ctl;
+
+ /*
+ * Here we create the "/ctl/meow" node.
+ */
+ ctl.mode = 0444;
+ ctl.devname = devname;
+ ctlfs_create_node(devname, &ctl);
+
+ ctl.ops = &fb_size_ctl;
+ ctlfs_create_entry("attr", &ctl);
+ return 0;
+}
+
+static const struct ctlops meow_ctl = {
+ .read = ctl_meow_read,
+ .write = NULL,
+};
+```
diff --git a/share/docs/kernel/disk.txt b/share/docs/kernel/disk.txt
new file mode 100644
index 0000000..4b7f6e5
--- /dev/null
+++ b/share/docs/kernel/disk.txt
@@ -0,0 +1,49 @@
+=======================================
+ Device filesystem (/dev) interface
+=======================================
+
+ USER
+ / \
+ /dev/sd0, /dev/sd1
+ / \
+ namei() namei()
+ / \
+ vop() vop()
+ / \
+ driver driver
+ / \
+ HARD DRIVE 0 HARD DRIVE 1
+
+=======================================
+ Hyra disk engine framework
+=======================================
+ USER
+ |
+ HDEI [ hyra disk-engine interface: like disk_io() ]
+ kernel -- |
+ HDE [ hyra disk engine: drives the core disk logic ]
+ |
+ HDF [ hyra disk framework (core logic) ]
+ / \
+ HARD DRIVE 0 HARD DRIVE 1
+
+
+ [DRIVER] <-> [DISK ENGINE]
+ ^
+ |
+ V
+ [ SLS / FILESYSTEM]
+ ^
+ |
+ V
+ [USER]
+
+
+ NOTES:
+
+ - Unix filesystem-like strucuture with indirection
+ for orthogonally persistent objects
+
+ - Explicit storage lifetime (i.e., persistent or ephemeral)
+ during allocation at a page-level granularity
+
diff --git a/share/docs/lib/liboda.md b/share/docs/lib/liboda.md
new file mode 100644
index 0000000..e5345a4
--- /dev/null
+++ b/share/docs/lib/liboda.md
@@ -0,0 +1,229 @@
+# The OSMORA Display Architecture (ODA)
+
+Written by Ian M. Moffett
+
+## Introduction
+
+The OSMORA Display Architecture (ODA) is a protocol describing how
+a compositor should create and manage graphical windows. A graphical
+session in Hyra consists of a compositor, window management system and
+management of user input.
+
+There are many existing display architectures out there. Take for instace, the X11
+protocol. X11 for example, has the concept of an "X server" in which window managers
+or certain graphical programs would connect to as a means of performing interprocess
+communication (IPC). The idea is that X will service graphics related requests from
+the window manager or compositor.
+
+While this works just fine, the highly centralized nature of X11 or similar protocols
+may complicate the flexibility of the graphics stack. On the other hand with ODA, a
+compositor links with the ODA library and becomes the server for window managers running
+on the system. The idea of ODA is to minimize complexity while preserving flexibility.
+
+Additionally, the compositor should provide its own API for use by window management
+software.
+
+## Scope
+
+This document serves to describe common OSMORA Display Architecture (ODA) concepts
+as well as providing basic example C sources showcasing how compositors and window
+managers may interface with the described APIs for various needs.
+
+## Terminology
+
+### OSMORA Display Architecture (ODA):
+
+OSMORA protocol defining common interfaces for C2W and
+W2C interactions.
+
+### Compositor to window (C2W):
+
+Describes the direction of communication originating from
+a compositor and directed towards a specific window:
+
+``COMPOSITOR -> WINDOW``
+
+### Window to compositor (W2C):
+
+Describes the direction of communication originating from
+a specific window to a compositor running on the system:
+
+``WINDOW -> COMPOSITOR``
+
+## Architecture
+
+```
++-------------+
+| LIBGFX |
++-------------+
+ ^
+ | linked with libgfx
+ V
++-------------+
+| COMPOSITOR |
++-------------+ <---+ signal
+ | | | |
+ | | | | c2w: <winop: e.g., close>
+ | | | | w2c: <winop to accept: e.g., close>
+ WIN WIN WIN <---+
+```
+
+### C2W signal flow:
+
+```
+-- CLOSE SIGNAL EXAMPLE --
+
+WINDOW RECEIVES CLOSE SIGNAL
+ |
+ ECHO BACK TO COMPOSITOR
+ |
+ YES ---+--- NO
+ | |
+ | V
+ | nothing happens
+ |
+ V
+ window is destroyed by compositor
+```
+
+## Libgfx
+
+The Hyra userspace includes ``libgfx`` which is a low-level graphics library aimed
+to facilitate drawing on the screen and performing various graphical operations while
+decoupling from the concept of compositors, windows and the ODA as a whole. In other words,
+libgfx has no knowledge of anything outside of the framebuffer and itself.
+
+The following is an example of how one may draw a yellow square at
+x/y (30,0):
+
+```c
+#include <libgfx/gfx.h> /* Common routines/defs */
+#include <libgfx/draw.h> /* Drawing related routines/defs */
+
+int
+main(void)
+{
+ struct gfx_ctx ctx;
+ struct gfx_shape sh = GFX_SHAPE_DEFAULT;
+ int error;
+
+ /* Set the x/y and color */
+ sh.x = 30;
+ sh.y = 0;
+ sh.color = GFX_YELLOW
+
+ error = gfx_init(&ctx);
+ if (error < 0) {
+ printf("gfx_init returned %d\n", error);
+ return error;
+ }
+
+ /* Draw the square and cleanup */
+ gfx_draw_shape(&ctx, &sh);
+ gfx_cleanup(&ctx);
+ return 0;
+}
+```
+
+## Liboda
+
+The Hyra userspace includes the ``liboda`` library which includes various
+interfaces conforming to the OSMORA Display Architecture (ODA).
+
+### Linking a compositor with liboda
+
+In order for an ODA compliant compositor to reference library
+symbols for ``liboda``, it should use the following linker flags:
+
+``... -loda -logfx``
+
+### ODA Session
+
+For the ODA library to keep track of state, it relies on an ``oda_state``
+structure defined in ``liboda/oda.h``. Additionally, in order for any ODA
+library calls to be made, the compositor must initialize the library with
+``oda_init()`` like in the following example:
+
+```c
+#include <liboda/oda.h>
+
+struct oda_state state;
+int error;
+
+/* Returns 0 on success */
+error = oda_init(&state);
+...
+```
+
+Upon failure of ``oda_init()`` a negative POSIX errno value
+is returned (see ``sys/errno.h``).
+
+### Using liboda to request windows
+
+A compositor may request windows from the ODA by using
+``oda_reqwin()`` like in the following example:
+
+```c
+#include <liboda/oda.h>
+
+...
+struct oda_wattr wattr;
+struct oda_state state;
+
+...
+
+wattr.session = &state;
+wattr.parent = NULL;
+wattr.bg = GFX_YELLOW;
+wattr.x = 200;
+wattr.y = 150;
+wattr.w = 120;
+wattr.h = 300;
+
+/* Returns 0 on success */
+error = oda_reqwin(&wattr, &win);
+```
+
+Arguments passed to ``oda_reqwin()`` are first stored in a ``struct oda_wattr``
+structure to minimize the number of parameters used in the function signature.
+
+Upon failure of ``oda_reqwin()`` a negative POSIX errno value
+is returned (see ``sys/errno.h``).
+
+### Input management
+
+The liboda library additionally provides an input API that facilitates
+management of user input (e.g., keyboard).
+
+(See ``liboda/input.h``)
+
+#### The ``oda_key`` structure
+
+ODA provides a standard description of keys received from a keyboard
+device. Keyboard input is represented by the ``oda_key`` structure shown
+below:
+
+```c
+struct oda_key {
+ uint16_t type;
+ uint8_t scancode;
+ char ch;
+ ...
+};
+```
+
+The ``type`` field describes the key type, valid key types can be
+found within the ``ODA_KEY_*`` definitions in ``liboda/input.h``.
+
+The ``scancode`` field contains the raw keyboard scancode received
+from the device.
+
+The ``ch`` field contains the ASCII representation of the input received
+from the device.
+
+#### The ``oda_kbd`` structure
+
+ODA represents keyboard devices through the ``oda_kbd`` structure found
+in ``liboda/input.h``. This structure contains callbacks that are set up
+by the compositor to be invoked when their respective keyboard related
+event occurs.
diff --git a/share/man/man1/beep.1 b/share/man/man1/beep.1
new file mode 100644
index 0000000..9905952
--- /dev/null
+++ b/share/man/man1/beep.1
@@ -0,0 +1,45 @@
+.\" Copyright (c) 2025 Ian Marco Moffett and the Osmora Team.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright notice,
+.\" this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of Hyra nor the names of its
+.\" contributors may be used to endorse or promote products derived from
+.\" this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+.\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+.\" LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.Dd Jul 17 2025
+.Dt BEEP 1
+.Os HYRA
+.Sh NAME
+.Nm beep - beep the speaker
+.Sh SYNOPSIS
+beep [freq] [duration]
+
+.Sh DESCRIPTION
+
+The
+.Nm
+command beeps the PC speaker at a given frequency ('freq') in hertz for
+a given duration ('duration') in milliseconds. This can be useful scripts
+that notify the user or just for fun! Just beware that it can get very annoying
+very fast!
+
+.Sh AUTHORS
+.An Ian Moffett Aq Mt ian@osmora.org
diff --git a/share/man/man1/cat.1 b/share/man/man1/cat.1
new file mode 100644
index 0000000..1759407
--- /dev/null
+++ b/share/man/man1/cat.1
@@ -0,0 +1,52 @@
+.\" Copyright (c) 2025 Ian Marco Moffett and the Osmora Team.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright notice,
+.\" this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of Hyra nor the names of its
+.\" contributors may be used to endorse or promote products derived from
+.\" this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+.\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+.\" LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.Dd Aug 6 2025
+.Dt CAT 1
+.Os HYRA
+.Sh NAME
+.Nm cat - concatenate files and print to standard output
+.Sh SYNOPSIS
+cat [FILE] ...
+
+.Sh DESCRIPTION
+
+The
+.Nm
+command can be used concatenate files or simply write their contents
+to standard output.
+
+.Bd -literal
+[-n]: Number each line
+[-b]: Number each non-blank line
+.Ed
+
+.Sh SEE ALSO
+
+mex(1)
+
+.Sh AUTHORS
+.An Ian Moffett Aq Mt ian@osmora.org
diff --git a/share/man/man1/echo.1 b/share/man/man1/echo.1
new file mode 100644
index 0000000..6adf192
--- /dev/null
+++ b/share/man/man1/echo.1
@@ -0,0 +1,47 @@
+.\" Copyright (c) 2025 Ian Marco Moffett and the Osmora Team.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright notice,
+.\" this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of Hyra nor the names of its
+.\" contributors may be used to endorse or promote products derived from
+.\" this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+.\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+.\" LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.Dd Jul 17 2025
+.Dt ECHO 1
+.Os HYRA
+.Sh NAME
+.Nm echo - print a line of text
+.Sh SYNOPSIS
+echo [STRING]
+
+.Sh DESCRIPTION
+
+The
+.Nm
+command displays a given string to the console. Used within scripts for logging
+information or debugging. An example usage of this command is:
+
+.Bd -literal
+echo MEOWWWW !!!
+.Ed
+
+.Sh AUTHORS
+.An Ian Moffett Aq Mt ian@osmora.org
diff --git a/share/man/man1/mex.1 b/share/man/man1/mex.1
new file mode 100644
index 0000000..8d19752
--- /dev/null
+++ b/share/man/man1/mex.1
@@ -0,0 +1,48 @@
+.\" Copyright (c) 2025 Ian Marco Moffett and the Osmora Team.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright notice,
+.\" this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of Hyra nor the names of its
+.\" contributors may be used to endorse or promote products derived from
+.\" this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+.\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+.\" LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.Dd Jul 17 2025
+.Dt MEX 1
+.Os HYRA
+.Sh NAME
+.Nm mex - perform a hexdump on a file
+.Sh SYNOPSIS
+mex [ /path/to/file ]
+
+.Sh DESCRIPTION
+
+The
+.Nm
+command dumps the bytes of a given file (specified by a path) in
+base-16 (hexadecimal) representation. It is useful for debugging,
+binary analysis, and satisfying ones nagging curiosity.
+
+.Sh SEE ALSO
+
+cat(1)
+
+.Sh AUTHORS
+.An Ian Moffett Aq Mt ian@osmora.org
diff --git a/share/man/man1/nerve.1 b/share/man/man1/nerve.1
new file mode 100644
index 0000000..8f2d19e
--- /dev/null
+++ b/share/man/man1/nerve.1
@@ -0,0 +1,73 @@
+.\" Copyright (c) 2025 Ian Marco Moffett and the Osmora Team.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright notice,
+.\" this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of Hyra nor the names of its
+.\" contributors may be used to endorse or promote products derived from
+.\" this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+.\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+.\" LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.Dd Jul 18 2025
+.Dt NERVE 1
+.Os HYRA
+.Sh NAME
+.Nm nerve - interact with control files
+.Sh SYNOPSIS
+nerve <verb> [ .. payload for pokes ..]
+
+verb 'poke': Poke a nerve
+
+verb 'peek': Peek at a nerve
+
+nerve ending 'consattr': Console attributes
+
+nerve ending 'consfeat': Console features
+
+.Sh DESCRIPTION
+
+The
+.Nm
+command can be used to modify system attributes and behaviour through various
+nerve endings (e.g., control files, et cetera). To drive a nerve, one must provide
+a verb (describing an operation) as well as a payload. An example of this command
+is moving the cursor position to the HOME (0, 0) position:
+
+.Bd -literal
+nerve poke consattr 0 0
+ / / / /
+ verb nerve [x] [y]
+.Ed
+
+A nerve may also be peeked at by using the 'peek' verb. Here is an example of the usage
+of this verb:
+
+.Bd -literal
+nerve peek consfeat
+ / /
+ verb nerve
+
+output:
+ ansi_esc=1
+ show_curs=0
+ ...
+.Ed
+
+.Sh AUTHORS
+.An Ian Moffett Aq Mt ian@osmora.org
diff --git a/share/man/man1/omar.1 b/share/man/man1/omar.1
new file mode 100644
index 0000000..611b6da
--- /dev/null
+++ b/share/man/man1/omar.1
@@ -0,0 +1,55 @@
+.\" Copyright (c) 2025 Ian Marco Moffett and the Osmora Team.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright notice,
+.\" this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of Hyra nor the names of its
+.\" contributors may be used to endorse or promote products derived from
+.\" this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+.\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+.\" LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.Dd May 2 2025
+.Dt OMAR 1
+.Os HYRA
+.Sh NAME
+.Nm omar - OSMORA Archive Format
+.Sh SYNOPSIS
+omar -i [input] -o [output]
+
+.Sh DESCRIPTION
+Prepare files for use in an initramfs
+
+.Ft -i
+ input path directory
+
+.Ft -o
+ output path
+
+Upon creation of the archive image, OMAR will
+produce pathnames through stdout with the following
+types in square brackets ([])
+
+.Ft [f]
+ Regular file
+
+.Ft [d]
+ Directory
+
+.Sh AUTHORS
+.An Ian Moffett Aq Mt ian@osmora.org
diff --git a/share/man/man1/osh.1 b/share/man/man1/osh.1
new file mode 100644
index 0000000..1b2744c
--- /dev/null
+++ b/share/man/man1/osh.1
@@ -0,0 +1,83 @@
+.\" Copyright (c) 2025 Ian Marco Moffett and the Osmora Team.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright notice,
+.\" this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of Hyra nor the names of its
+.\" contributors may be used to endorse or promote products derived from
+.\" this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+.\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+.\" LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.Dd Aug 2 2025
+.Dt OSH 1
+.Os HYRA
+.Sh NAME
+.Nm osh - OSMORA shell
+.Sh SYNOPSIS
+osh [optional file]
+
+.Sh DESCRIPTION
+
+OSH is a simple shell interpreter that is capable of executing commands
+from the user via stdin or a shell script file passed as an argument.
+
+.Sh COMMENTS
+OSH supports the use of comments (i.e., pieces of text ignored by the shell)
+denoted by '@'. The following is an example of using comments:
+
+.Bd -literal
+echo hello !! @ Echos the text "hello !!"
+.Ed
+
+.Sh DEFINITIONS
+OSH defines "control operators" as any character(s) reserved by OSH for
+representing specific operations (e.g., background jobs, command repetition, etc):
+
+The
+.Ft '&'
+control operator is used after a command or binary path
+in order to run the executable as a background job, allowing
+the shell to continue immediately. Here is an example of running "sleep" in the background:
+.Bd -literal
+--
+@
+@ Usually this will hang the shell for 5
+@ seconds until sleep wakes up. However,
+@ when we postpend '&', the shell executes
+@ 'sleep' as a background job and continues
+@ as usual.
+@
+sleep 5 &
+--
+.Ed
+
+The
+.Ft '!!'
+control operator is used to repeat the most recently used
+command. This is simply written by itself like this:
+
+.Bd -literal
+--
+echo "hewwo" @ Echo "hewwo"
+!! @ Do it again...
+--
+.Ed
+
+.Sh AUTHORS
+.An Ian Moffett Aq Mt ian@osmora.org
diff --git a/share/man/man2/exit.2 b/share/man/man2/exit.2
new file mode 100644
index 0000000..8342f50
--- /dev/null
+++ b/share/man/man2/exit.2
@@ -0,0 +1,55 @@
+.\" Copyright (c) 2025 Ian Marco Moffett and the Osmora Team.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright notice,
+.\" this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of Hyra nor the names of its
+.\" contributors may be used to endorse or promote products derived from
+.\" this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+.\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+.\" LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.Dd Jul 17 2025
+.Dt EXIT 2
+.Os HYRA
+.Sh NAME
+.Nm exit, _Exit
+.Sh SYNOPSIS
+#include <stdlib.h>
+
+[[noreturn]] void exit(int status)
+
+[[noreturn]] void _Exit(int status)
+
+.Sh DESCRIPTION
+
+The exit() function terminates the calling process while calling exit
+handlers and performing libc specific cleanups. On the other hand, the
+_Exit() function terminates the calling process *immediately* bypassing
+internal libc exit routines and cleanup hooks.
+
+The value
+.Ft status[7:0]
+is returned to the parent process to indicate the reason of process termination.
+
+.Sh RETURN VALUE
+
+These functions do not return
+
+.Sh AUTHORS
+.An Ian Moffett Aq Mt ian@osmora.org
diff --git a/share/man/man9/kconf.9 b/share/man/man9/kconf.9
new file mode 100644
index 0000000..0257e9b
--- /dev/null
+++ b/share/man/man9/kconf.9
@@ -0,0 +1,81 @@
+.\" Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright notice,
+.\" this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of Hyra nor the names of its
+.\" contributors may be used to endorse or promote products derived from
+.\" this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+.\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+.\" LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.Dd Jun 7 2025
+.Dt KCONF 9
+.Os Hyra
+.Sh NAME
+.Nm kconf - Hyra kernel configuration
+.Sh SYNOPSIS
+GENERIC configuration:
+.Ft sys/arch/<arch>/conf/GENERIC
+
+Kconf sources:
+.Ft tools/kconf/
+
+.Sh DESCRIPTION
+
+Hyra provides the kconf format for kernel configuration by allowing
+the user to create defines that represent yes/no options and fixed values.
+
+Running kconf on a configuration file results in define flags (-D__KEY=val) to
+be generated so that they may be passed to the compiler of choice.
+
+The
+.Ft option
+keyword allows users to define an option that
+can either be yes or no. For example, the following
+may be used to create an option "FOO" to be set to "yes":
+
+.Ft option FOO yes
+
+This will be given to the kernel as __FOO, for yes/no options the value
+of either 1 (yes) or 0 will be given.
+
+Similarly, a user may create a define that holds a fixed value
+by using the
+.Ft setval
+keyword.
+
+For example, the following may be used to create an option
+"MEOW" set to 0xCA7F00D:
+
+.Ft setval MEOW 0xCA7F00D
+
+These options can be read within the kernel by checking if
+the define exists and potentially falling back to a default
+value if not. This example shows how "MEOW" can be read:
+
+.Bd -literal
+#if defined(__MEOW) /* Option is prefixed with "__" */
+#define MEOW __MEOW
+#else
+#define MEOW 0
+#endif /* __MEOW */
+.Ed
+
+.Sh AUTHORS
+.An Ian Moffett Aq Mt ian@osmora.org
diff --git a/share/man/man9/mmio.9 b/share/man/man9/mmio.9
index 7833cb0..4ede196 100644
--- a/share/man/man9/mmio.9
+++ b/share/man/man9/mmio.9
@@ -59,5 +59,8 @@ argument can be either a physical address or virtual
address; however, it is recommended to use virtual addresses
for the sake of consistency.
+.Sh SEE ALSO
+.Xr vm_map 9,
+
.Sh AUTHORS
.An Ian Moffett Aq Mt ian@osmora.org
diff --git a/share/man/man9/vm.9 b/share/man/man9/vm.9
new file mode 100644
index 0000000..7601a45
--- /dev/null
+++ b/share/man/man9/vm.9
@@ -0,0 +1,46 @@
+.\" Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright notice,
+.\" this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of Hyra nor the names of its
+.\" contributors may be used to endorse or promote products derived from
+.\" this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+.\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+.\" LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.Dd Jun 6 2024
+.Dt VM 9
+.Os Hyra
+.Sh NAME
+.Nm vm - hyra virtual memory subsystem
+.Sh SYNOPSIS
+.In vm/vm.h
+
+.Sh DESCRIPTION
+The Hyra virtual memory subsystem is a crucial subsystem within the
+kernel. This subsystem facilitates machine independent management of
+per-process virtual address spaces and has many frameworks within that
+allow abstractions over various things such as pages, memory mapping, and
+objects.
+
+.Sh AUTHORS
+.An Ian Moffett Aq Mt ian@osmora.org
+
+.Sh SEE ALSO
+.Xr vm_map 9
diff --git a/share/man/man9/vm_map.9 b/share/man/man9/vm_map.9
new file mode 100644
index 0000000..9c5a3f6
--- /dev/null
+++ b/share/man/man9/vm_map.9
@@ -0,0 +1,97 @@
+.\" Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright notice,
+.\" this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of Hyra nor the names of its
+.\" contributors may be used to endorse or promote products derived from
+.\" this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+.\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+.\" LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.Dd Jun 6 2024
+.Dt VM_MAP 9
+.Os Hyra
+.Sh NAME
+.Nm vm_map - create/destory a virtual memory mapping
+.Sh SYNOPSIS
+.In vm/map.h
+
+.Ft int
+.Fn vm_map "struct vas vas" "vaddr_t va" "paddr_t pa" "vm_prot_t prot" "size_t count"
+
+.Ft int
+.Fn vm_unmap "struct vas vas" "vaddr_t va" "size_t count"
+
+.Sh DESCRIPTION
+
+The Hyra virtual memory mapping framework provides a machine independent
+interface for mapping and unmapping pages to respective page frames.
+
+The
+.Fn vm_map
+function creates a virtual to physical memory mapping.
+
+The
+.Fa vas
+argument specifies the virtual address space for the mapping
+to be created within.
+
+The
+.Fa va
+argument specifies the virtual address to be mapped.
+
+The
+.Fa pa
+argument specifies the physical address that
+.Fa va
+is to be mapped to.
+
+The
+.Fa prot
+argument specifies the virtual memory protection flags.
+
+The
+.Fa count
+argument specifies the number of bytes to be mapped which
+is to be aligned to the machine's page size.
+
+The
+.Fn vm_unmap
+function destroys a virtual to physical memory mapping.
+
+The
+.Fa vas
+argument specifies the virtual address space for the mapping
+to be destroyed within.
+
+
+The
+.Fa va
+argument specifies the virtual address to be unmapped.
+
+The
+.Fa count
+argument specifies the number of bytes to be unmapped which
+is to be aligned to the machine's page size.
+
+.Sh AUTHORS
+.An Ian Moffett Aq Mt ian@osmora.org
+
+.Sh SEE ALSO
+.Xr vm_map 9
diff --git a/share/misc/contrib b/share/misc/contrib
index a36c127..39c9c02 100644
--- a/share/misc/contrib
+++ b/share/misc/contrib
@@ -62,6 +62,37 @@ if (!blah) {
--
+When writing switch statements, no indentation is needed
+before the "case" statement, do this:
+
+switch (v) {
+case 0:
+ ...
+ break;
+case 1:
+ ...
+ break;
+case 2:
+ ...
+ break;
+}
+
+Not this:
+
+
+switch (v) {
+case 0:
+ ...
+ break;
+case 1:
+ ...
+ break;
+case 2:
+ ...
+ break;
+}
+
+--
Now, only use predefined integer types in sys/cdefs.h like so:
uint8_t a;
diff --git a/share/misc/tmpfs b/share/misc/tmpfs
new file mode 100644
index 0000000..38eac22
--- /dev/null
+++ b/share/misc/tmpfs
@@ -0,0 +1,15 @@
+----------------------------------------------------
+| Readable + writable in-memory filesystem (tmpfs) |
++--------------------------------------------------+
+| Author: Ian Marco Moffett |
++--------------------------------------------------+
+
+------------------------------------------------------------------------------
+
+ [d] /tmp/ -> [next] -> foo.txt -> [next] -> bar.txt
+ \
+ +> /tmp/noises/ -> [next] -> mrow.txt -> [next] -> squeak.txt
+ \
+ +> /tmp/noises1/ -> [next] -> bark.bin -> [next] -> wrff.txt
+
+------------------------------------------------------------------------------
diff --git a/sys/arch/aarch64/aarch64/exception.c b/sys/arch/aarch64/aarch64/exception.c
new file mode 100644
index 0000000..d6f1f97
--- /dev/null
+++ b/sys/arch/aarch64/aarch64/exception.c
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/syslog.h>
+#include <sys/param.h>
+#include <sys/cdefs.h>
+#include <machine/cdefs.h>
+#include <machine/exception.h>
+
+#define pr_trace(fmt, ...) kprintf("exception: " fmt, ##__VA_ARGS__)
+#define pr_error(...) pr_trace(__VA_ARGS__)
+
+static inline void
+log_esr_class(uint8_t class)
+{
+ switch (class) {
+ case EC_WF:
+ pr_error("trapped WF\n");
+ break;
+ case EC_MCRMRC:
+ pr_error("trapped MCR/MRC\n");
+ break;
+ case EC_MCRRC:
+ pr_trace("trapped MCRR/MRRC\n");
+ break;
+ case EC_LDCSTC:
+ pr_error("trapped LDC/STC\n");
+ break;
+ case EC_SVE:
+ pr_trace("trapped SVE/SIMD/FP operation\n");
+ break;
+ case EC_BRE:
+ pr_error("ibt: bad branch target\n");
+ break;
+ case EC_ILLX:
+ pr_error("illegal execution state\n");
+ break;
+ case EC_SVC64:
+ /* TODO */
+ pr_error("supervisor call (TODO)!!\n");
+ break;
+ case EC_PCALIGN:
+ pr_error("PC alignment fault\n");
+ break;
+ case EC_DABORT:
+ case EC_EDABORT:
+ pr_error("data abort\n");
+ break;
+ case EC_SPALIGN:
+ pr_error("SP alignment fault\n");
+ break;
+ case EC_SERR:
+ pr_error("system error\n");
+ break;
+ default:
+ pr_error("unknown exception\n");
+ }
+}
+
+static void
+regdump(struct trapframe *tf, uint64_t elr)
+{
+ kprintf(OMIT_TIMESTAMP
+ "X0=%p X1=%p X2=%p\n"
+ "X3=%p X4=%p X5=%p\n"
+ "X6=%p X7=%p X8=%p\n"
+ "X9=%p X10=%p X11=%p\n"
+ "X12=%p X13=%p X14=%p\n"
+ "X15=%p X16=%p X17=%p\n"
+ "X18=%p X19=%p X20=%p\n"
+ "X21=%p X22=%p X23=%p\n"
+ "X24=%p X25=%p X26=%p\n"
+ "X27=%p X28=%p X29=%p\n"
+ "X30=%p\n"
+ "ELR=%p\n",
+ tf->x0, tf->x1, tf->x2, tf->x3,
+ tf->x4, tf->x5, tf->x6, tf->x7,
+ tf->x8, tf->x9, tf->x10, tf->x11,
+ tf->x12, tf->x13, tf->x14, tf->x15,
+ tf->x16, tf->x17, tf->x18, tf->x19,
+ tf->x20, tf->x21, tf->x22, tf->x23,
+ tf->x24, tf->x25, tf->x26, tf->x27,
+ tf->x28, tf->x29, tf->x30, elr);
+}
+
+/*
+ * Handle an exception
+ *
+ * @esr: Copy of the Exception Syndrome Register
+ */
+void
+handle_exception(struct trapframe *tf)
+{
+ uint8_t class;
+
+ class = (tf->esr >> 26) & 0x3F;
+ log_esr_class(class);
+ regdump(tf, tf->elr);
+ for (;;) {
+ md_hlt();
+ }
+}
diff --git a/sys/arch/aarch64/aarch64/intr.c b/sys/arch/aarch64/aarch64/intr.c
new file mode 100644
index 0000000..5fd2439
--- /dev/null
+++ b/sys/arch/aarch64/aarch64/intr.c
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <machine/intr.h>
+
+void *
+intr_register(const char *name, const struct intr_hand *ih)
+{
+ /* TODO: Stub */
+ return NULL;
+}
diff --git a/sys/arch/amd64/isa/i8042.S b/sys/arch/aarch64/aarch64/locore.S
index 123d3a5..2155991 100644
--- a/sys/arch/amd64/isa/i8042.S
+++ b/sys/arch/aarch64/aarch64/locore.S
@@ -27,11 +27,10 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
-#include <machine/frameasm.h>
-
.text
- .globl i8042_kb_isr
-INTRENTRY(i8042_kb_isr, handle_kb)
-handle_kb:
- call i8042_kb_event
- retq
+ .globl md_cpu_init
+md_cpu_init:
+ ldr x0, =__vectab
+ msr vbar_el1, x0
+ ret
+
diff --git a/sys/arch/aarch64/aarch64/machdep.c b/sys/arch/aarch64/aarch64/machdep.c
index a29ad7e..9a96cbb 100644
--- a/sys/arch/aarch64/aarch64/machdep.c
+++ b/sys/arch/aarch64/aarch64/machdep.c
@@ -31,11 +31,14 @@
#include <sys/panic.h>
#include <machine/cpu.h>
#include <machine/sync.h>
+#include <machine/board.h>
struct cpu_info g_bsp_ci = {0};
+void md_cpu_init(void);
+
void
-cpu_startup(struct cpu_info *ci)
+cpu_halt_others(void)
{
/* TODO: STUB */
return;
@@ -69,6 +72,13 @@ md_sync_all(void)
return 0;
}
+void
+cpu_halt_all(void)
+{
+ /* TODO: Stub */
+ for (;;);
+}
+
/*
* Get the descriptor for the currently
* running processor.
@@ -78,6 +88,24 @@ this_cpu(void)
{
struct cpu_info *ci;
- __ASMV("mrs %0, tpidr_el0" : "=r" (ci));
+ __ASMV("mrs %0, tpidr_el1" : "=r" (ci));
return ci;
}
+
+void
+cpu_startup(struct cpu_info *ci)
+{
+ ci->self = ci;
+ __ASMV("msr tpidr_el1, %0" :: "r" (ci));
+ md_cpu_init();
+}
+
+void
+md_get_board(struct board_info *res)
+{
+ uint64_t midr_el1;
+
+ __ASMV("mrs %0, midr_el1" : "=r" (midr_el1));
+ res->partno = (midr_el1 >> 4) & 0xFFF;
+ res->implementer = (midr_el1 >> 24) & 0xFF;
+}
diff --git a/sys/arch/aarch64/aarch64/pmap.c b/sys/arch/aarch64/aarch64/pmap.c
index b5ebda9..870ef80 100644
--- a/sys/arch/aarch64/aarch64/pmap.c
+++ b/sys/arch/aarch64/aarch64/pmap.c
@@ -28,35 +28,274 @@
*/
#include <sys/types.h>
+#include <sys/cdefs.h>
+#include <sys/param.h>
+#include <sys/panic.h>
#include <machine/vas.h>
#include <vm/pmap.h>
+#include <vm/physmem.h>
+#include <vm/vm.h>
+
+/* Memory types for MAIR_ELx */
+#define MT_NORMAL 0x00
+#define MT_NORMAL_UC 0x02
+#define MT_DEVICE 0x03
+
+/* Memory attributes */
+#define MEM_DEV_NGNRNE 0x00
+#define MEM_DEV_NVNRE 0x04
+#define MEM_NORMAL_UC 0x44
+#define MEM_NORMAL 0xFF
+
+#define MT_ATTR(idx, attr) ((attr) << (8 * (idx)))
+
+/*
+ * Descriptor bits for page table entries
+ *
+ * @PTE_VALID: Must be set to be valid
+ * @PTE_TABLE: Table (1), block (0)
+ * @PTE_USER: User access allowed
+ * @PTE_READONLY: Read-only
+ * @PTE_ISH: Inner sharable
+ * @PTE_AF: Accessed flag
+ * @PTE_XN: Execute never
+ */
+#define PTE_ADDR_MASK 0x0000FFFFFFFFF000
+#define PTE_VALID BIT(0)
+#define PTE_TABLE BIT(1)
+#define PTE_USER BIT(6)
+#define PTE_READONLY BIT(7)
+#define PTE_ISH (3 << 8)
+#define PTE_AF BIT(10)
+#define PTE_XN BIT(54)
+
+/*
+ * Write the EL1 Memory Attribute Indirection
+ * Register.
+ *
+ * @val: Value to write
+ *
+ * XXX: Refer to the ARMv8 Reference Manual section
+ * D7.2.70
+ */
+static inline void
+mair_el1_write(uint64_t val)
+{
+ __ASMV("msr mair_el1, %0"
+ :
+ : "r" (val)
+ : "memory"
+ );
+}
+
+static inline void
+tlb_flush(vaddr_t va)
+{
+ __ASMV(
+ "tlbi vaae1is, %0\n"
+ "dsb ish\n"
+ "isb\n"
+ :
+ : "r" (va >> 12)
+ : "memory"
+ );
+}
+
+static uint64_t
+pmap_prot_to_pte(vm_prot_t prot)
+{
+ uint64_t pte_flags = 0;
+
+ pte_flags |= (PTE_VALID | PTE_TABLE | PTE_AF);
+ pte_flags |= (PTE_XN | PTE_READONLY | PTE_ISH);
+
+ if (ISSET(prot, PROT_WRITE))
+ pte_flags &= ~PTE_READONLY;
+ if (ISSET(prot, PROT_EXEC))
+ pte_flags &= ~PTE_XN;
+ if (ISSET(prot, PROT_USER))
+ pte_flags |= PTE_USER;
+
+ return pte_flags;
+}
+
+/*
+ * Returns an index for a specific page map
+ * label based on an input address.
+ */
+static size_t
+pmap_level_idx(vaddr_t ia, uint8_t level)
+{
+ switch (level) {
+ case 0: return (ia >> 39) & 0x1FF;
+ case 1: return (ia >> 30) & 0x1FF;
+ case 2: return (ia >> 21) & 0x1FF;
+ case 3: return (ia >> 12) & 0x1FF;
+ default: panic("pmap_level_idx: bad index\n");
+ }
+
+ __builtin_unreachable();
+}
+
+/*
+ * Extract a level from a pagemap
+ *
+ * @level: Current pagemap level
+ * @ia: Input virtual address
+ * @pmap: Current level to extract from
+ * @alloc: Set to true to allocate new entries
+ *
+ * XXX: `level_idx' can be grabbed with pmap_level_idx().
+ */
+static uintptr_t *
+pmap_extract(uint8_t level, vaddr_t ia, vaddr_t *pmap, bool alloc)
+{
+ uintptr_t next, level_alloc;
+ uint8_t idx;
+
+ if (pmap == NULL) {
+ return NULL;
+ }
+
+ idx = pmap_level_idx(ia, level);
+ next = pmap[idx];
+
+ if (ISSET(next, PTE_VALID)) {
+ next = next & PTE_ADDR_MASK;
+ return PHYS_TO_VIRT(next);
+ }
+
+ /*
+ * Nothing to grab at this point, we'll need to
+ * allocate our own entry. However, if we are
+ * told not to allocate anything, just return
+ * NULL.
+ */
+ if (!alloc) {
+ return NULL;
+ }
+
+ level_alloc = vm_alloc_frame(1);
+ if (level_alloc == 0) {
+ return NULL;
+ }
+
+ pmap[idx] = (level_alloc | PTE_VALID | PTE_USER | PTE_TABLE);
+ return PHYS_TO_VIRT(level_alloc);
+}
+
+/*
+ * Get the lowest pagemap table referring to a 4 KiB
+ * frame.
+ *
+ * @ttrb: Translation table base to use
+ * @ia: Input virtual address
+ * @alloc: If true, allocate new pagemap entries as needed
+ * @res: Result goes here
+ */
+static int
+pmap_get_tbl(paddr_t ttbrn, vaddr_t ia, bool alloc, uintptr_t **res)
+{
+ vaddr_t *root;
+ uintptr_t *l1, *l2, *l3;
+
+ root = PHYS_TO_VIRT(ttbrn);
+
+ l1 = pmap_extract(0, ia, root, alloc);
+ if (l1 == NULL) {
+ return -1;
+ }
+
+ l2 = pmap_extract(1, ia, l1, alloc);
+ if (l2 == NULL) {
+ return -1;
+ }
+
+ l3 = pmap_extract(2, ia, l2, alloc);
+ if (l3 == NULL) {
+ return -1;
+ }
+
+ *res = l3;
+ return 0;
+}
struct vas
pmap_read_vas(void)
{
- /* TODO: STUB */
struct vas vas = {0};
+
+ __ASMV(
+ "mrs %0, ttbr0_el1\n"
+ "mrs %1, ttbr1_el1\n"
+ : "=r" (vas.ttbr0_el1),
+ "=r" (vas.ttbr1_el1)
+ :
+ : "memory"
+ );
+
return vas;
}
void
pmap_switch_vas(struct vas vas)
{
- /* TODO: STUB */
+ __ASMV(
+ "msr ttbr0_el1, %0\n"
+ "msr ttbr1_el1, %1\n"
+ :
+ : "r" (vas.ttbr0_el1),
+ "r" (vas.ttbr1_el1)
+ : "memory"
+ );
return;
}
int
pmap_map(struct vas vas, vaddr_t va, paddr_t pa, vm_prot_t prot)
{
- /* TODO: STUB */
+ paddr_t ttbrn = vas.ttbr0_el1;
+ uint64_t pte_flags;
+ uintptr_t *tbl;
+ int error;
+
+ if (va >= VM_HIGHER_HALF) {
+ ttbrn = vas.ttbr1_el1;
+ }
+
+ if ((error = pmap_get_tbl(ttbrn, va, true, &tbl)) < 0) {
+ return error;
+ }
+ if (__unlikely(tbl == NULL)) {
+ return -1;
+ }
+
+ pte_flags = pmap_prot_to_pte(prot);
+ tbl[pmap_level_idx(va, 3)] = pa | pte_flags;
+ tlb_flush(va);
return 0;
}
int
pmap_unmap(struct vas vas, vaddr_t va)
{
- /* TODO: STUB */
+ paddr_t ttbrn = vas.ttbr0_el1;
+ uintptr_t *tbl;
+ int error;
+
+ if (va >= VM_HIGHER_HALF) {
+ ttbrn = vas.ttbr1_el1;
+ }
+
+ if ((error = pmap_get_tbl(ttbrn, va, true, &tbl)) < 0) {
+ return error;
+ }
+ if (__unlikely(tbl == NULL)) {
+ return -1;
+ }
+
+ tbl[pmap_level_idx(va, 3)] = 0;
+ tlb_flush(va);
return 0;
}
@@ -66,3 +305,36 @@ pmap_destroy_vas(struct vas vas)
/* TODO: STUB */
return;
}
+
+bool
+pmap_is_clean(struct vas vas, vaddr_t va)
+{
+ /* TODO: STUB */
+ return false;
+}
+
+void
+pmap_mark_clean(struct vas vas, vaddr_t va)
+{
+ /* TODO: STUB */
+ return;
+}
+
+int
+pmap_set_cache(struct vas vas, vaddr_t va, int type)
+{
+ /* TODO: STUB */
+ return 0;
+}
+
+int
+pmap_init(void)
+{
+ uint64_t mair;
+
+ mair = MT_ATTR(MT_NORMAL, MEM_NORMAL) |
+ MT_ATTR(MT_NORMAL_UC, MEM_NORMAL_UC) |
+ MT_ATTR(MT_DEVICE, MEM_DEV_NGNRNE);
+ mair_el1_write(mair);
+ return 0;
+}
diff --git a/sys/arch/aarch64/aarch64/proc_machdep.c b/sys/arch/aarch64/aarch64/proc_machdep.c
index 97902e5..cc58af9 100644
--- a/sys/arch/aarch64/aarch64/proc_machdep.c
+++ b/sys/arch/aarch64/aarch64/proc_machdep.c
@@ -37,17 +37,17 @@
* @ip: Instruction pointer.
*/
int
-md_fork(struct proc *p, struct proc *parent, uintptr_t ip)
+md_spawn(struct proc *p, struct proc *parent, uintptr_t ip)
{
/* TODO: STUB */
return 0;
}
-void
+uintptr_t
md_td_stackinit(struct proc *td, void *stack_top, struct exec_prog *prog)
{
/* TODO: STUB */
- return;
+ return 0;
}
void
diff --git a/sys/arch/aarch64/aarch64/reboot.c b/sys/arch/aarch64/aarch64/reboot.c
index 6d82133..372012a 100644
--- a/sys/arch/aarch64/aarch64/reboot.c
+++ b/sys/arch/aarch64/aarch64/reboot.c
@@ -30,9 +30,25 @@
#include <sys/reboot.h>
#include <sys/param.h>
+/*
+ * Typically the reset vector is at address 0 but this can
+ * be remapped if the vendor is feeling silly.
+ */
+void(*g_cpu_reboot)(void) = NULL;
+
void
cpu_reboot(int method)
{
- /* TODO: STUB */
+ g_cpu_reboot();
for (;;);
}
+
+/*
+ * arg0: Method bits
+ */
+scret_t
+sys_reboot(struct syscall_args *scargs)
+{
+ cpu_reboot(scargs->arg0);
+ __builtin_unreachable();
+}
diff --git a/sys/arch/aarch64/aarch64/vector.S b/sys/arch/aarch64/aarch64/vector.S
new file mode 100644
index 0000000..c8f77ca
--- /dev/null
+++ b/sys/arch/aarch64/aarch64/vector.S
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <machine/frameasm.h>
+
+// Vector table entries are aligned at 128 bytes
+// giving us 32 exception entries
+.macro ventry label
+ .align 7
+ b \label
+.endm
+
+ .text
+x_sync_elx:
+ PUSH_XFRAME(TRAPNO_XSYNC) // Synchronous: sp+top @ X0
+ bl handle_exception // Handle the exception
+ POP_XFRAME() // Pop the trapframe
+1: hlt #0 // TODO
+ b 1b
+
+x_irq_elx:
+ PUSH_XFRAME(TRAPNO_XIRQ) // IRQ: sp+top @ X0
+ bl handle_exception // Handle the exception
+ POP_XFRAME() // Pop the trapframe
+1: hlt #0 // TODO
+ b 1b
+
+x_fiq_elx:
+ PUSH_XFRAME(TRAPNO_XFIQ) // FIQ: sp+top @ X0
+ bl handle_exception // Handle the exception
+ POP_XFRAME() // Pop the trapframe
+1: hlt #0
+ b 1b
+
+x_serr_elx:
+ PUSH_XFRAME(TRAPNO_XSERR) // SERR: sp+top @ X0
+ bl handle_exception // Handle the exception
+ POP_XFRAME() // Pop the trapframe
+1: hlt #0 // TODO
+ b 1b
+
+x_unimpl:
+1: hlt #0
+ b 1b
+
+ .align 11 // Table aligned @ 2 KiB
+ .globl __vectab
+__vectab:
+ // From current EL (w/ SP_EL0)
+ ventry x_sync_elx
+ ventry x_irq_elx
+ ventry x_fiq_elx
+ ventry x_serr_elx
+
+ // From current EL (w/ SP_ELx > 0)
+ ventry x_sync_elx
+ ventry x_irq_elx
+ ventry x_fiq_elx
+ ventry x_serr_elx
+
+ // Lower EL with faulting code in AARCH64
+ ventry x_sync_elx
+ ventry x_irq_elx
+ ventry x_fiq_elx
+ ventry x_serr_elx
+
+ ventry x_unimpl
+ ventry x_unimpl
+ ventry x_unimpl
+ ventry x_unimpl
diff --git a/sys/arch/aarch64/conf/GENERIC b/sys/arch/aarch64/conf/GENERIC
index 5691685..702a248 100644
--- a/sys/arch/aarch64/conf/GENERIC
+++ b/sys/arch/aarch64/conf/GENERIC
@@ -1,5 +1,3 @@
// Kernel options
-option SERIAL_DEBUG yes
-
-// Kernel constants
-setval SCHED_NQUEUE 4
+option SERIAL_DEBUG yes // Enable kmsg serial logging
+option USER_KMSG yes // Show kmsg in user consoles
diff --git a/sys/arch/aarch64/conf/link.ld b/sys/arch/aarch64/conf/link.ld
index c64cec3..2aa8c93 100644
--- a/sys/arch/aarch64/conf/link.ld
+++ b/sys/arch/aarch64/conf/link.ld
@@ -40,6 +40,12 @@ SECTIONS
__drivers_init_end = .;
} :rodata
+ .drivers.defer : {
+ __driversd_init_start = .;
+ *(.drivers.defer .drivers.defer)
+ __driversd_init_end = .;
+ } :rodata
+
/* Move to the next memory page for .data */
. += CONSTANT(MAXPAGESIZE);
diff --git a/sys/arch/aarch64/pci/pci_machdep.c b/sys/arch/aarch64/pci/pci_machdep.c
index fa92165..8de6cc9 100644
--- a/sys/arch/aarch64/pci/pci_machdep.c
+++ b/sys/arch/aarch64/pci/pci_machdep.c
@@ -30,20 +30,6 @@
#include <sys/types.h>
#include <dev/pci/pci.h>
-pcireg_t
-pci_readl(struct pci_device *dev, uint32_t offset)
-{
- /* TODO: STUB */
- return 0;
-}
-
-void
-pci_writel(struct pci_device *dev, uint32_t offset, pcireg_t val)
-{
- /* TODO: STUB */
- return;
-}
-
/*
* Map a BAR into kernel memory.
*
diff --git a/sys/arch/amd64/amd64/acpi_machdep.c b/sys/arch/amd64/amd64/acpi_machdep.c
index 7533621..0f80607 100644
--- a/sys/arch/amd64/amd64/acpi_machdep.c
+++ b/sys/arch/amd64/amd64/acpi_machdep.c
@@ -34,6 +34,7 @@
#include <dev/acpi/tables.h>
#include <machine/ioapic.h>
#include <machine/lapic.h>
+#include <vm/vm.h>
#define pr_trace(fmt, ...) kprintf("acpi: " fmt, ##__VA_ARGS__)
@@ -51,7 +52,7 @@ acpi_init_madt(void)
cur = (uint8_t *)(madt + 1);
end = (uint8_t *)madt + madt->hdr.length;
- g_lapic_base = madt->lapic_addr;
+ g_lapic_base = PHYS_TO_VIRT(madt->lapic_addr);
while (cur < end) {
apichdr = (void *)cur;
diff --git a/sys/arch/amd64/amd64/gdt.c b/sys/arch/amd64/amd64/gdt.c
index a8fe54d..40d8f48 100644
--- a/sys/arch/amd64/amd64/gdt.c
+++ b/sys/arch/amd64/amd64/gdt.c
@@ -29,50 +29,70 @@
#include <machine/gdt.h>
-struct gdt_entry g_gdt_data[256] = {
+/*
+ * The GDT should be cache line aligned, since it is accessed every time a
+ * segment selector is reloaded
+ */
+__cacheline_aligned struct gdt_entry g_gdt_data[GDT_ENTRY_COUNT] = {
/* Null */
{0},
- /* Kernel code (0x8) */
+ /* Kernel code (0x08) */
{
- .limit = 0x0000,
- .base_low = 0x0000,
- .base_mid = 0x00,
- .access = 0x9A,
- .granularity = 0x20,
- .base_hi = 0x00
+ .limit = 0x0000,
+ .base_low = 0x0000,
+ .base_mid = 0x00,
+ .attributes = GDT_ATTRIBUTE_64BIT_CODE | GDT_ATTRIBUTE_PRESENT |
+ GDT_ATTRIBUTE_DPL0 | GDT_ATTRIBUTE_NONSYSTEM |
+ GDT_ATTRIBUTE_EXECUTABLE | GDT_ATTRIBUTE_READABLE,
+ .base_hi = 0x00
},
/* Kernel data (0x10) */
{
- .limit = 0x0000,
- .base_low = 0x0000,
- .base_mid = 0x00,
- .access = 0x92,
- .granularity = 0x00,
- .base_hi = 0x00
+ .limit = 0x0000,
+ .base_low = 0x0000,
+ .base_mid = 0x00,
+ .attributes = GDT_ATTRIBUTE_PRESENT | GDT_ATTRIBUTE_DPL0 |
+ GDT_ATTRIBUTE_NONSYSTEM | GDT_ATTRIBUTE_WRITABLE,
+ .base_hi = 0x00
},
/* User code (0x18) */
{
- .limit = 0x0000,
- .base_low = 0x0000,
- .base_mid = 0x00,
- .access = 0xFA,
- .granularity = 0xAF,
- .base_hi = 0x00
+ .limit = 0x0000,
+ .base_low = 0x0000,
+ .base_mid = 0x00,
+ .attributes = GDT_ATTRIBUTE_64BIT_CODE | GDT_ATTRIBUTE_PRESENT |
+ GDT_ATTRIBUTE_DPL3 | GDT_ATTRIBUTE_NONSYSTEM |
+ GDT_ATTRIBUTE_EXECUTABLE | GDT_ATTRIBUTE_READABLE,
+ .base_hi = 0x00
},
/* User data (0x20) */
{
- .limit = 0x0000,
- .base_low = 0x0000,
- .base_mid = 0x00,
- .access = 0xF2,
- .granularity = 0x00,
- .base_hi = 0x00
+ .limit = 0x0000,
+ .base_low = 0x0000,
+ .base_mid = 0x00,
+ .attributes = GDT_ATTRIBUTE_PRESENT | GDT_ATTRIBUTE_DPL3 |
+ GDT_ATTRIBUTE_NONSYSTEM | GDT_ATTRIBUTE_WRITABLE,
+ .base_hi = 0x00
},
- /* TSS segment (0x28) */
- {0}
+ /*
+ * TSS segment (0x28)
+ *
+ * NOTE: 64-bit TSS descriptors are 16 bytes, equivalent to the size of two
+ * regular descriptor entries.
+ * See Intel SPG 3/25 Section 9.2.3 - TSS Descriptor in 64-bit mode.
+ */
+ {0}, {0}
+};
+
+/* Verify that the GDT is of the correct size */
+__static_assert(sizeof(g_gdt_data) == (8 * GDT_ENTRY_COUNT));
+
+const struct gdtr g_gdtr = {
+ .limit = sizeof(g_gdt_data) - 1,
+ .offset = (uintptr_t)&g_gdt_data[0]
};
diff --git a/sys/arch/amd64/amd64/hpet.c b/sys/arch/amd64/amd64/hpet.c
index 1670546..9191bee 100644
--- a/sys/arch/amd64/amd64/hpet.c
+++ b/sys/arch/amd64/amd64/hpet.c
@@ -47,6 +47,7 @@
#define CAP_CLK_PERIOD(caps) (caps >> 32)
#define FSEC_PER_SECOND 1000000000000000ULL
+#define NSEC_PER_SECOND 1000000000ULL
#define USEC_PER_SECOND 1000000ULL
static void *hpet_base = NULL;
@@ -135,6 +136,20 @@ hpet_time_usec(void)
}
static size_t
+hpet_time_nsec(void)
+{
+ uint64_t period, freq, caps;
+ uint64_t counter;
+
+ caps = hpet_read(HPET_REG_CAPS);
+ period = CAP_CLK_PERIOD(caps);
+ freq = FSEC_PER_SECOND / period;
+
+ counter = hpet_read(HPET_REG_MAIN_COUNTER);
+ return (counter * NSEC_PER_SECOND) / freq;
+}
+
+static size_t
hpet_time_sec(void)
{
return hpet_time_usec() / USEC_PER_SECOND;
@@ -180,7 +195,9 @@ hpet_init(void)
timer.usleep = hpet_usleep;
timer.nsleep = hpet_nsleep;
timer.get_time_usec = hpet_time_usec;
+ timer.get_time_nsec = hpet_time_nsec;
timer.get_time_sec = hpet_time_sec;
+ timer.flags = TIMER_MONOTONIC;
register_timer(TIMER_GP, &timer);
return 0;
}
diff --git a/sys/arch/amd64/amd64/intr.c b/sys/arch/amd64/amd64/intr.c
index c31ee3c..c44c88e 100644
--- a/sys/arch/amd64/amd64/intr.c
+++ b/sys/arch/amd64/amd64/intr.c
@@ -31,12 +31,19 @@
#include <sys/param.h>
#include <sys/errno.h>
#include <sys/panic.h>
+#include <sys/cdefs.h>
+#include <sys/syslog.h>
#include <machine/intr.h>
#include <machine/cpu.h>
#include <machine/asm.h>
+#include <machine/ioapic.h>
#include <vm/dynalloc.h>
+#include <string.h>
-static struct intr_entry *intrs[256] = {0};
+#define pr_trace(fmt, ...) kprintf("intr: " fmt, ##__VA_ARGS__)
+#define pr_error(...) pr_trace(__VA_ARGS__)
+
+struct intr_hand *g_intrs[256] = {0};
int
splraise(uint8_t s)
@@ -67,35 +74,70 @@ splx(uint8_t s)
ci->ipl = s;
}
-int
-intr_alloc_vector(const char *name, uint8_t priority)
+void *
+intr_register(const char *name, const struct intr_hand *ih)
{
- size_t vec = MAX(priority << IPL_SHIFT, 0x20);
- struct intr_entry *intr;
+ uint32_t vec = MAX(ih->priority << IPL_SHIFT, 0x20);
+ struct intr_hand *ih_new;
+ struct intr_data *idp_new;
+ const struct intr_data *idp;
+ size_t name_len;
/* Sanity check */
- if (vec > NELEM(intrs)) {
- return -1;
+ if (vec > NELEM(g_intrs) || name == NULL) {
+ return NULL;
+ }
+
+ ih_new = dynalloc(sizeof(*ih_new));
+ if (ih_new == NULL) {
+ pr_error("could not allocate new interrupt handler\n");
+ return NULL;
}
/*
* Try to allocate an interrupt vector. An IPL is made up
* of 4 bits so there can be 16 vectors per IPL.
+ *
+ * XXX: Vector 0x20 is reserved for the Hyra scheduler and
+ * vectors 0x21 to 0x21 + N_IPIVEC are reserved for
+ * inter-processor interrupts.
*/
for (int i = vec; i < vec + 16; ++i) {
- if (intrs[i] != NULL) {
+ if (g_intrs[i] != NULL || i < 0x24) {
continue;
}
- intr = dynalloc(sizeof(*intr));
- if (intr == NULL) {
- return -ENOMEM;
+ /* Allocate memory for the name */
+ name_len = strlen(name) + 1;
+ ih_new->name = dynalloc(name_len);
+ if (ih_new->name == NULL) {
+ dynfree(ih_new);
+ pr_trace("could not allocate interrupt name\n");
+ return NULL;
}
- intr->priority = priority;
- intrs[i] = intr;
- return i;
+ memcpy(ih_new->name, name, name_len);
+ idp_new = &ih_new->data;
+ idp = &ih->data;
+
+ /* Pass the interrupt data */
+ idp_new->ihp = ih_new;
+ idp_new->data_u64 = idp->data_u64;
+
+ /* Setup the new intr_hand */
+ ih_new->func = ih->func;
+ ih_new->priority = ih->priority;
+ ih_new->irq = ih->irq;
+ ih_new->vector = i;
+ ih_new->nintr = 0;
+ g_intrs[i] = ih_new;
+
+ if (ih->irq >= 0) {
+ ioapic_set_vec(ih->irq, i);
+ ioapic_irq_unmask(ih->irq);
+ }
+ return ih_new;
}
- return -1;
+ return NULL;
}
diff --git a/sys/arch/amd64/amd64/ipi.c b/sys/arch/amd64/amd64/ipi.c
new file mode 100644
index 0000000..bf263d3
--- /dev/null
+++ b/sys/arch/amd64/amd64/ipi.c
@@ -0,0 +1,191 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/syslog.h>
+#include <sys/param.h>
+#include <sys/panic.h>
+#include <sys/spinlock.h>
+#include <machine/cpu.h>
+#include <machine/idt.h>
+#include <machine/ipi.h>
+#include <machine/lapic.h>
+#include <vm/dynalloc.h>
+#include <string.h>
+
+void ipi_isr(void);
+void halt_isr(void);
+
+void __ipi_handle_common(void);
+
+#define pr_trace(fmt, ...) kprintf("ipi: " fmt, ##__VA_ARGS__)
+#define pr_error(...) pr_trace(__VA_ARGS__)
+
+#define COOKIE 0x7E0A
+#define MAX_IPI 32
+
+/* For the global state of the subsystem */
+static uint32_t cookie = 0;
+
+static struct cpu_ipi ipi_list[MAX_IPI];
+static uint8_t ipi_count = 0;
+static struct spinlock lock;
+
+/*
+ * Allocate an IPI that can be sent to other
+ * cores on the CPU. This is the core logic
+ * and contains *no* locks. One should be
+ * using the md_ipi_alloc() function instead.
+ *
+ * Returns the allocated IPI identifier on succes,
+ * otherwise a less than zero value is returned.
+ */
+static int
+__ipi_alloc(struct cpu_ipi **res)
+{
+ struct cpu_ipi *ipip;
+
+ if (ipi_count >= MAX_IPI) {
+ return -EAGAIN;
+ }
+
+ ipip = &ipi_list[ipi_count];
+ ipip->cookie = COOKIE;
+ ipip->id = ipi_count++;
+ ipip->handler = NULL;
+ *res = ipip;
+ return ipip->id;
+}
+
+/*
+ * Common IPI routine, called from vector.S
+ *
+ * XXX: Internal usage only
+ */
+void
+__ipi_handle_common(void)
+{
+ struct cpu_ipi *ipip;
+ struct cpu_info *ci = this_cpu();
+ ipi_pend_t pending = 0;
+
+ if (cookie != COOKIE) {
+ pr_trace("[warn]: got spurious ipi\n");
+ return;
+ }
+
+ if (ci == NULL) {
+ pr_error("could not get current CPU\n");
+ return;
+ }
+
+ if (ipi_count == 0) {
+ pr_error("no registered IPIs\n");
+ return;
+ }
+
+ /* Attempt to find a handler */
+ pending = ci->ipi_pending;
+ for (int i = 0; i < ipi_count; ++i) {
+ ipip = &ipi_list[i];
+ if (ISSET(pending, BIT(i))) {
+ ipip->handler(ipip);
+ ci->ipi_pending &= ~BIT(i);
+ }
+ }
+
+ /* We are done dispatching IPIs */
+ ci->ipi_dispatch = 0;
+}
+
+/*
+ * Send one or more IPIs to a specific
+ * processor after caller sets bits in
+ * the `ci->ipi_pending' field
+ *
+ * @ci: Processor to send IPI(s) to
+ * @ipi: IPIs to send
+ */
+int
+md_ipi_send(struct cpu_info *ci, ipi_pend_t ipi)
+{
+ uint32_t apic_id = 0;
+
+ if (ci != NULL) {
+ /*
+ * We are already dispatching IPIs, we don't
+ * want to find ourselves in interrupt hell.
+ */
+ if (ci->ipi_dispatch) {
+ return -EAGAIN;
+ }
+
+ apic_id = ci->apicid;
+ }
+
+ ci->ipi_dispatch = 1;
+ ci->ipi_pending |= BIT(ipi);
+
+ /* Send it through on the bus */
+ lapic_send_ipi(
+ apic_id,
+ IPI_SHORTHAND_NONE,
+ IPI_VECTOR
+ );
+ return 0;
+}
+
+
+/*
+ * IPI allocation interface with
+ * locking.
+ */
+int
+md_ipi_alloc(struct cpu_ipi **res)
+{
+ int retval;
+
+ spinlock_acquire(&lock);
+ retval = __ipi_alloc(res);
+ spinlock_release(&lock);
+ return retval;
+}
+
+/*
+ * Initialize the IPI thunks
+ */
+void
+md_ipi_init(void)
+{
+ /* Initialize the IPI vectors */
+ idt_set_desc(IPI_VECTOR, IDT_INT_GATE, ISR(ipi_isr), 0);
+ idt_set_desc(HALT_VECTOR, IDT_INT_GATE, ISR(halt_isr), 0);
+ cookie = COOKIE;
+}
diff --git a/sys/arch/amd64/amd64/lapic.c b/sys/arch/amd64/amd64/lapic.c
index 70d36a5..b44b769 100644
--- a/sys/arch/amd64/amd64/lapic.c
+++ b/sys/arch/amd64/amd64/lapic.c
@@ -60,7 +60,7 @@
static struct timer lapic_timer;
static uint8_t lapic_timer_vec = 0;
-uintptr_t g_lapic_base = 0;
+void *g_lapic_base = 0;
void lapic_tmr_isr(void);
@@ -104,7 +104,7 @@ lapic_readl(uint32_t reg)
const struct cpu_info *ci = this_cpu();
if (!ci->has_x2apic) {
- addr = (void *)(g_lapic_base + reg);
+ addr = PTR_OFFSET(g_lapic_base, reg);
return mmio_read32(addr);
} else {
reg >>= 4;
@@ -125,7 +125,7 @@ lapic_writel(uint32_t reg, uint64_t val)
const struct cpu_info *ci = this_cpu();
if (!ci->has_x2apic) {
- addr = (void *)(g_lapic_base + reg);
+ addr = PTR_OFFSET(g_lapic_base, reg);
mmio_write32(addr, val);
} else {
reg >>= 4;
@@ -340,7 +340,7 @@ lapic_init(void)
/* Allocate a vector if needed */
if (lapic_timer_vec == 0) {
- lapic_timer_vec = intr_alloc_vector("lapictmr", IPL_CLOCK);
+ lapic_timer_vec = (IPL_CLOCK << IPL_SHIFT) | 0x20;
idt_set_desc(lapic_timer_vec, IDT_INT_GATE, ISR(lapic_tmr_isr),
IST_SCHED);
}
@@ -364,5 +364,6 @@ lapic_init(void)
lapic_timer.name = "LAPIC_INTEGRATED_TIMER";
lapic_timer.stop = lapic_timer_stop;
lapic_timer.oneshot_us = lapic_timer_oneshot_us;
+ lapic_timer.flags = 0;
register_timer(TIMER_SCHED, &lapic_timer);
}
diff --git a/sys/arch/amd64/amd64/lapic_intr.S b/sys/arch/amd64/amd64/lapic_intr.S
index e22cbca..1413660 100644
--- a/sys/arch/amd64/amd64/lapic_intr.S
+++ b/sys/arch/amd64/amd64/lapic_intr.S
@@ -33,7 +33,6 @@
.globl lapic_tmr_isr
INTRENTRY(lapic_tmr_isr, handle_lapic_tmr)
handle_lapic_tmr:
- call sched_switch // Context switch per every timer IRQ
- call i8042_sync // Sometimes needed depending on i8042 quirks
+ call md_sched_switch // Context switch per every timer IRQ
call lapic_eoi // Done! Signal that we finished to the Local APIC
retq
diff --git a/sys/arch/amd64/amd64/machdep.c b/sys/arch/amd64/amd64/machdep.c
index 07d6cdd..60c37bf 100644
--- a/sys/arch/amd64/amd64/machdep.c
+++ b/sys/arch/amd64/amd64/machdep.c
@@ -42,7 +42,25 @@
#include <machine/uart.h>
#include <machine/sync.h>
#include <machine/intr.h>
+#include <machine/ipi.h>
+#include <machine/cdefs.h>
#include <machine/isa/i8042var.h>
+#include <dev/cons/cons.h>
+#include <string.h>
+
+/*
+ * This defines the max number of frames
+ * we will pass while walking the callstack
+ * in md_backtrace()
+ */
+#define MAX_FRAME_DEPTH 16
+
+#define pr_trace(fmt, ...) kprintf("cpu: " fmt, ##__VA_ARGS__)
+#define pr_error(...) pr_trace(__VA_ARGS__)
+#define pr_trace_bsp(...) \
+ if (!bsp_init) { \
+ pr_trace(__VA_ARGS__); \
+ }
#if defined(__SPECTRE_IBRS)
#define SPECTRE_IBRS __SPECTRE_IBRS
@@ -50,46 +68,86 @@
#define SPECTRE_IBRS 0
#endif
-static uint8_t halt_vector = 0;
+#if defined(__CPU_SMEP)
+#define CPU_SMEP __CPU_SMEP
+#else
+#define CPU_SMEP 0
+#endif
+
+#if defined(__CPU_UMIP)
+#define CPU_UMIP __CPU_UMIP
+#else
+#define CPU_UMIP 0
+#endif
int ibrs_enable(void);
+int simd_init(void);
void syscall_isr(void);
+void pin_isr_load(void);
struct cpu_info g_bsp_ci = {0};
-static struct gdtr bsp_gdtr = {
- .limit = sizeof(struct gdt_entry) * 256 - 1,
- .offset = (uintptr_t)&g_gdt_data[0]
-};
+static struct cpu_ipi *tlb_ipi;
+static struct spinlock ipi_lock = {0};
+static bool bsp_init = false;
-__attribute__((__interrupt__))
-static void
-cpu_halt_isr(void *p)
+static int
+tlb_shootdown_handler(struct cpu_ipi *ipi)
{
- __ASMV("cli; hlt");
- __builtin_unreachable();
+ struct cpu_info *ci;
+ int ipl;
+
+ /*
+ * Get the current CPU and check if we even
+ * need a shootdown. If `tlb_shootdown' is
+ * unset, this is not for us.
+ */
+ ci = this_cpu();
+ if (!ci->tlb_shootdown) {
+ return -1;
+ }
+
+ ipl = splraise(IPL_HIGH);
+ __invlpg(ci->shootdown_va);
+
+ ci->shootdown_va = 0;
+ ci->tlb_shootdown = 0;
+ splx(ipl);
+ return 0;
}
static void
-setup_vectors(void)
+setup_vectors(struct cpu_info *ci)
{
- if (halt_vector == 0) {
- halt_vector = intr_alloc_vector("cpu-halt", IPL_HIGH);
+ union tss_stack scstack;
+ union tss_stack dfstack;
+
+ /* Try to allocate a syscall stack */
+ if (tss_alloc_stack(&scstack, DEFAULT_PAGESIZE) != 0) {
+ panic("failed to allocate syscall stack\n");
+ }
+
+ /* Try to allocate a double fault stack */
+ if (tss_alloc_stack(&dfstack, DEFAULT_PAGESIZE) != 0) {
+ panic("failed to allocate double fault stack\n");
}
+ tss_update_ist(ci, scstack, IST_SYSCALL);
+ tss_update_ist(ci, dfstack, IST_DBFLT);
+
idt_set_desc(0x0, IDT_TRAP_GATE, ISR(arith_err), 0);
idt_set_desc(0x2, IDT_TRAP_GATE, ISR(nmi), 0);
idt_set_desc(0x3, IDT_TRAP_GATE, ISR(breakpoint_handler), 0);
idt_set_desc(0x4, IDT_TRAP_GATE, ISR(overflow), 0);
idt_set_desc(0x5, IDT_TRAP_GATE, ISR(bound_range), 0);
idt_set_desc(0x6, IDT_TRAP_GATE, ISR(invl_op), 0);
- idt_set_desc(0x8, IDT_TRAP_GATE, ISR(double_fault), 0);
+ idt_set_desc(0x8, IDT_TRAP_GATE, ISR(double_fault), IST_DBFLT);
idt_set_desc(0xA, IDT_TRAP_GATE, ISR(invl_tss), 0);
idt_set_desc(0xB, IDT_TRAP_GATE, ISR(segnp), 0);
idt_set_desc(0xC, IDT_TRAP_GATE, ISR(ss_fault), 0);
idt_set_desc(0xD, IDT_TRAP_GATE, ISR(general_prot), 0);
idt_set_desc(0xE, IDT_TRAP_GATE, ISR(page_fault), 0);
- idt_set_desc(0x80, IDT_USER_INT_GATE, ISR(syscall_isr), 0);
- idt_set_desc(halt_vector, IDT_INT_GATE, ISR(cpu_halt_isr), 0);
+ idt_set_desc(0x80, IDT_USER_INT_GATE, ISR(syscall_isr), IST_SYSCALL);
+ pin_isr_load();
}
static inline void
@@ -97,7 +155,7 @@ init_tss(struct cpu_info *ci)
{
struct tss_desc *desc;
- desc = (struct tss_desc *)&g_gdt_data[GDT_TSS];
+ desc = (struct tss_desc *)&g_gdt_data[GDT_TSS_INDEX];
write_tss(ci, desc);
tss_load();
}
@@ -133,45 +191,281 @@ backtrace_addr_to_name(uintptr_t addr, off_t *off)
return NULL;
}
+static void
+enable_simd(void)
+{
+ int retval;
+
+ if ((retval = simd_init()) < 0) {
+ pr_trace_bsp("SIMD not supported\n");
+ }
+
+ if (retval == 1) {
+ pr_trace_bsp("SSE enabled but not AVX\n");
+ }
+}
+
+static void
+init_ipis(void)
+{
+ int error;
+
+ if (bsp_init) {
+ return;
+ }
+
+ spinlock_acquire(&ipi_lock);
+ error = md_ipi_alloc(&tlb_ipi);
+ if (error < 0) {
+ pr_error("md_ipi_alloc: returned %d\n", error);
+ panic("failed to init TLB IPI\n");
+ }
+
+ tlb_ipi->handler = tlb_shootdown_handler;
+
+ /*
+ * Some IPIs must have very specific IDs
+ * so that they are standard and usable
+ * throughout the rest of the sytem.
+ */
+ if (tlb_ipi->id != IPI_TLB)
+ panic("expected IPI_TLB for TLB IPI\n");
+
+ spinlock_release(&ipi_lock);
+}
+
+static void
+cpu_get_vendor(struct cpu_info *ci)
+{
+ uint32_t unused, ebx, ecx, edx;
+ char vendor_str[13];
+
+ /*
+ * This CPUID returns a 12 byte CPU vendor string
+ * that we'll put together and use to detect the vendor.
+ */
+ CPUID(0, unused, ebx, ecx, edx);
+
+ /* Dword 0 */
+ vendor_str[0] = ebx & 0xFF;
+ vendor_str[1] = (ebx >> 8) & 0xFF;
+ vendor_str[2] = (ebx >> 16) & 0xFF;
+ vendor_str[3] = (ebx >> 24) & 0xFF;
+
+ /* Dword 1 */
+ vendor_str[4] = edx & 0xFF;
+ vendor_str[5] = (edx >> 8) & 0xFF;
+ vendor_str[6] = (edx >> 16) & 0xFF;
+ vendor_str[7] = (edx >> 24) & 0xFF;
+
+ /* Dword 2 */
+ vendor_str[8] = ecx & 0xFF;
+ vendor_str[9] = (ecx >> 8) & 0xFF;
+ vendor_str[10] = (ecx >> 16) & 0xFF;
+ vendor_str[11] = (ecx >> 24) & 0xFF;
+ vendor_str[12] = '\0';
+
+ /* Is this an AMD CPU? */
+ if (strcmp(vendor_str, "AuthenticAMD") == 0) {
+ ci->vendor = CPU_VENDOR_AMD;
+ return;
+ }
+
+ /* Is this an Intel CPU? */
+ if (strcmp(vendor_str, "GenuineIntel") == 0) {
+ ci->vendor = CPU_VENDOR_INTEL;
+ return;
+ }
+
+ /*
+ * Some buggy Intel CPUs report the string "GenuineIotel"
+ * instead of "GenuineIntel". This is rare but we should
+ * still handle it as it can happen. Probably a good idea
+ * to log it so the user can know about their rare CPU
+ * quirk and brag to their friends :~)
+ */
+ if (strcmp(vendor_str, "GenuineIotel") == 0) {
+ pr_trace_bsp("vendor_str=%s\n", vendor_str);
+ pr_trace_bsp("detected vendor string quirk\n");
+ ci->vendor = CPU_VENDOR_INTEL;
+ return;
+ }
+
+ ci->vendor = CPU_VENDOR_OTHER;
+}
+
+static void
+cpu_get_info(struct cpu_info *ci)
+{
+ uint32_t unused, eax, ebx, ecx, edx;
+ uint8_t ext_model, ext_family;
+
+ /* Get the vendor information */
+ cpu_get_vendor(ci);
+
+ /* Extended features */
+ CPUID(0x07, unused, ebx, ecx, unused);
+ if (ISSET(ebx, BIT(7)))
+ ci->feat |= CPU_FEAT_SMEP;
+ if (ISSET(ebx, BIT(20)))
+ ci->feat |= CPU_FEAT_SMAP;
+ if (ISSET(ecx, BIT(2)))
+ ci->feat |= CPU_FEAT_UMIP;
+
+ /*
+ * Processor power management information bits as well
+ * as bits describing RAS capabilities
+ */
+ CPUID(0x80000007, unused, unused, unused, edx);
+ if (ISSET(edx, BIT(8)))
+ ci->feat |= CPU_FEAT_TSCINV;
+
+ /*
+ * Processor info and feature bits
+ */
+ CPUID(0x01, eax, unused, unused, unused);
+ ci->model = (eax >> 4) & 0xF;
+ ci->family = (eax >> 8) & 0xF;
+
+ /*
+ * If the family ID is 15 then the actual family
+ * ID is the sum of the extended family and the
+ * family ID fields.
+ */
+ if (ci->family == 0xF) {
+ ext_family = (eax >> 20) & 0xFF;
+ ci->family += ext_family;
+ }
+
+ /*
+ * If the family has the value of either 6 or 15,
+ * then the extended model number would be used.
+ * Slap them together if this is the case.
+ */
+ if (ci->family == 6 || ci->family == 15) {
+ ext_model = (eax >> 16) & 0xF;
+ ci->model |= (ext_model << 4);
+ }
+}
+
+/*
+ * The CR4.UMIP bit prevents user programs from
+ * executing instructions related to accessing
+ * system memory structures. This should be enabled
+ * by default if supported.
+ */
+static void
+cpu_enable_umip(void)
+{
+ struct cpu_info *ci = this_cpu();
+ uint64_t cr4;
+
+ if (!CPU_UMIP) {
+ pr_trace_bsp("UMIP not configured\n");
+ return;
+ }
+
+ if (ISSET(ci->feat, CPU_FEAT_UMIP)) {
+ cr4 = amd64_read_cr4();
+ cr4 |= CR4_UMIP;
+ amd64_write_cr4(cr4);
+ }
+}
+
+void
+cpu_shootdown_tlb(vaddr_t va)
+{
+ uint32_t ncpu = cpu_count();
+ struct cpu_info *cip;
+
+ for (uint32_t i = 0; i < ncpu; ++i) {
+ cip = cpu_get(i);
+ if (cip == NULL) {
+ break;
+ }
+
+ spinlock_acquire(&cip->lock);
+ cip->shootdown_va = va;
+ cip->tlb_shootdown = 1;
+ md_ipi_send(cip, IPI_TLB);
+ spinlock_release(&cip->lock);
+ }
+}
+
void
md_backtrace(void)
{
- uintptr_t *rbp;
- uintptr_t rip;
+ uintptr_t *rbp = NULL;
+ uintptr_t rip, tmp;
off_t off;
const char *name;
+ char line[256];
+ uint8_t n = 0;
__ASMV("mov %%rbp, %0" : "=r" (rbp) :: "memory");
while (1) {
+ if (n >= MAX_FRAME_DEPTH) {
+ break;
+ }
+
+ /* End of callstack */
+ if (rbp == NULL) {
+ break;
+ }
+
rip = rbp[1];
rbp = (uintptr_t *)rbp[0];
- name = backtrace_addr_to_name(rip, &off);
- if (rbp == NULL)
+ /*
+ * RBP should be aligned on an 8-byte
+ * boundary... Don't trust this state
+ * anymore if it is not.
+ */
+ tmp = (uintptr_t)rbp;
+ if ((tmp & (8 - 1)) != 0) {
break;
- if (name == NULL)
- name = "???";
+ }
- kprintf(OMIT_TIMESTAMP "%p @ <%s+0x%x>\n", rip, name, off);
+ /*
+ * This is not a valid value, get out
+ * of this loop!!
+ */
+ if (rip == 0) {
+ break;
+ }
+
+ name = backtrace_addr_to_name(rip, &off);
+ snprintf(line, sizeof(line), "%p @ <%s+0x%x>\n", rip, name, off);
+ cons_putstr(&g_root_scr, line, strlen(line));
+ ++n;
}
}
void
cpu_halt_all(void)
{
- /*
- * If we have no current 'cpu_info' structure set,
- * we can't send IPIs, so just assume only the current
- * processor is the only one active, clear interrupts
- * then halt it.
- */
- if (rdmsr(IA32_GS_BASE) == 0) {
- __ASMV("cli; hlt");
- }
+ lapic_send_ipi(
+ 0,
+ IPI_SHORTHAND_ALL,
+ HALT_VECTOR
+ );
- /* Send IPI to all cores */
- lapic_send_ipi(0, IPI_SHORTHAND_ALL, halt_vector);
- for (;;);
+ __ASMV("cli; hlt");
+ __builtin_unreachable();
+}
+
+/*
+ * Same as cpu_halt_all() but for all other
+ * cores but ourselves.
+ */
+void
+cpu_halt_others(void)
+{
+ lapic_send_ipi(
+ 0,
+ IPI_SHORTHAND_OTHERS,
+ HALT_VECTOR
+ );
}
void
@@ -195,6 +489,10 @@ this_cpu(void)
{
struct cpu_info *ci;
+ if (rdmsr(IA32_GS_BASE) == 0) {
+ return NULL;
+ }
+
/*
* This might look crazy but we are just leveraging the "m"
* constraint to add the offset of the self field within
@@ -221,17 +519,74 @@ md_sync_all(void)
}
void
+cpu_enable_smep(void)
+{
+ struct cpu_info *ci;
+ uint64_t cr4;
+
+ /* Don't bother if not enabled */
+ if (!CPU_SMEP) {
+ return;
+ }
+
+ ci = this_cpu();
+ if (!ISSET(ci->feat, CPU_FEAT_SMEP)) {
+ pr_trace_bsp("SMEP not supported\n");
+ return;
+ }
+
+ cr4 = amd64_read_cr4();
+ cr4 |= BIT(20); /* CR4.SMEP */
+ amd64_write_cr4(cr4);
+}
+
+void
+cpu_disable_smep(void)
+{
+ struct cpu_info *ci;
+ uint64_t cr4;
+
+ if (!CPU_SMEP) {
+ return;
+ }
+
+ ci = this_cpu();
+ if (!ISSET(ci->feat, CPU_FEAT_SMEP)) {
+ return;
+ }
+
+ cr4 = amd64_read_cr4();
+ cr4 &= ~BIT(20); /* CR4.SMEP */
+ amd64_write_cr4(cr4);
+}
+
+void
cpu_startup(struct cpu_info *ci)
{
ci->self = ci;
- gdt_load(&bsp_gdtr);
+ ci->feat = 0;
+ gdt_load();
idt_load();
- setup_vectors();
wrmsr(IA32_GS_BASE, (uintptr_t)ci);
-
init_tss(ci);
+
+ setup_vectors(ci);
+ md_ipi_init();
+ init_ipis();
+
try_mitigate_spectre();
+ ci->online = 1;
+ ci->preempt = 1;
+
+ cpu_get_info(ci);
+ cpu_enable_smep();
+ cpu_enable_umip();
+ enable_simd();
lapic_init();
+
+ if (!bsp_init) {
+ bsp_init = true;
+ }
}
diff --git a/sys/arch/amd64/amd64/mp.c b/sys/arch/amd64/amd64/mp.c
index a8a36c7..43830ba 100644
--- a/sys/arch/amd64/amd64/mp.c
+++ b/sys/arch/amd64/amd64/mp.c
@@ -29,9 +29,13 @@
#include <sys/types.h>
#include <sys/limine.h>
+#include <sys/limits.h>
+#include <sys/systm.h>
#include <sys/syslog.h>
+#include <sys/proc.h>
#include <sys/spinlock.h>
#include <sys/sched.h>
+#include <sys/atomic.h>
#include <machine/cpu.h>
#include <vm/dynalloc.h>
#include <assert.h>
@@ -39,42 +43,95 @@
#define pr_trace(fmt, ...) kprintf("cpu_mp: " fmt, ##__VA_ARGS__)
+extern struct proc g_proc0;
static volatile struct limine_smp_request g_smp_req = {
.id = LIMINE_SMP_REQUEST,
.revision = 0
};
+static volatile uint32_t ncpu_up = 1;
+static struct cpu_info *ci_list[CPU_MAX];
+static struct spinlock ci_list_lock = {0};
+
static void
ap_trampoline(struct limine_smp_info *si)
{
- static struct spinlock lock = {0};
struct cpu_info *ci;
+ struct proc *idle;
ci = dynalloc(sizeof(*ci));
__assert(ci != NULL);
memset(ci, 0, sizeof(*ci));
- spinlock_acquire(&lock);
cpu_startup(ci);
+ spinlock_acquire(&ci_list_lock);
+ ci_list[ncpu_up] = ci;
- spinlock_release(&lock);
- sched_enter();
+ ci->id = ncpu_up;
+ spawn(&g_proc0, sched_enter, NULL, 0, &idle);
+ proc_pin(idle, ci->id);
+
+ spinlock_release(&ci_list_lock);
+ atomic_inc_int(&ncpu_up);
+ sched_enter();
while (1);
}
+struct cpu_info *
+cpu_get(uint32_t index)
+{
+ if (index >= ncpu_up) {
+ return NULL;
+ }
+
+ return ci_list[index];
+}
+
+/*
+ * Grab the CPU stat structured of a specified
+ * processor
+ *
+ * @cpu_index: CPU index number
+ */
+struct sched_cpu *
+cpu_get_stat(uint32_t cpu_index)
+{
+ struct cpu_info *ci;
+
+ if ((ci = cpu_get(cpu_index)) == NULL) {
+ return NULL;
+ }
+
+ return &ci->stat;
+}
+
+uint32_t
+cpu_count(void)
+{
+ return ncpu_up;
+}
+
void
mp_bootstrap_aps(struct cpu_info *ci)
{
struct limine_smp_response *resp = g_smp_req.response;
struct limine_smp_info **cpus;
+ struct proc *idle;
size_t cpu_init_counter;
+ uint32_t ncpu;
/* Should not happen */
__assert(resp != NULL);
cpus = resp->cpus;
- cpu_init_counter = resp->cpu_count - 1;
+ ncpu = resp->cpu_count;
+ cpu_init_counter = ncpu - 1;
+ ci_list[0] = ci;
+
+ /* Pin an idle thread to the BSP */
+ spawn(&g_proc0, sched_enter, NULL, 0, &idle);
+ proc_pin(idle, 0);
if (resp->cpu_count == 1) {
pr_trace("CPU has 1 core, no APs to bootstrap...\n");
@@ -90,4 +147,8 @@ mp_bootstrap_aps(struct cpu_info *ci)
cpus[i]->goto_address = ap_trampoline;
}
+
+ /* Wait for all cores to be ready */
+ while ((ncpu_up - 1) < cpu_init_counter);
+ cpu_report_count(ncpu_up);
}
diff --git a/sys/arch/amd64/amd64/pmap.c b/sys/arch/amd64/amd64/pmap.c
index 2e62a4b..6c6bfcd 100644
--- a/sys/arch/amd64/amd64/pmap.c
+++ b/sys/arch/amd64/amd64/pmap.c
@@ -33,6 +33,8 @@
#include <sys/errno.h>
#include <machine/tlb.h>
#include <machine/vas.h>
+#include <machine/cpu.h>
+#include <machine/cdefs.h>
#include <vm/pmap.h>
#include <vm/physmem.h>
#include <vm/vm.h>
@@ -52,7 +54,7 @@
#define PTE_PCD BIT(4) /* Page-level cache disable */
#define PTE_ACC BIT(5) /* Accessed */
#define PTE_DIRTY BIT(6) /* Dirty (written-to page) */
-#define PTE_PAT BIT(7)
+#define PTE_PS BIT(7) /* Page size */
#define PTE_GLOBAL BIT(8)
#define PTE_NX BIT(63) /* Execute-disable */
@@ -112,6 +114,16 @@ pmap_extract(uint8_t level, vaddr_t va, vaddr_t *pmap, bool alloc)
return NULL;
}
+ /*
+ * TODO: Support huge pages... For now, don't let the
+ * bootloader fuck us up with their pre-kernel
+ * mappings and tell huge pages to get the fuck.
+ *
+ */
+ if (ISSET(pmap[idx], PTE_PS)) {
+ pmap[idx] = 0;
+ }
+
if (ISSET(pmap[idx], PTE_P)) {
next = (pmap[idx] & PTE_ADDR_MASK);
return PHYS_TO_VIRT(next);
@@ -176,14 +188,15 @@ done:
* @vas: Virtual address space.
* @va: Target virtual address.
* @val: Value to write.
+ * @alloc: True to alloc new paging entries.
*/
static int
-pmap_update_tbl(struct vas vas, vaddr_t va, uint64_t val)
+pmap_update_tbl(struct vas vas, vaddr_t va, uint64_t val, bool alloc)
{
uintptr_t *tbl;
int status;
- if ((status = pmap_get_tbl(vas, va, true, &tbl)) != 0) {
+ if ((status = pmap_get_tbl(vas, va, alloc, &tbl)) != 0) {
return status;
}
@@ -266,19 +279,21 @@ pmap_map(struct vas vas, vaddr_t va, paddr_t pa, vm_prot_t prot)
{
uint32_t flags = pmap_prot_to_pte(prot);
- return pmap_update_tbl(vas, va, (pa | flags));
+ return pmap_update_tbl(vas, va, (pa | flags), true);
}
int
pmap_unmap(struct vas vas, vaddr_t va)
{
- return pmap_update_tbl(vas, va, 0);
+ return pmap_update_tbl(vas, va, 0, false);
}
int
pmap_set_cache(struct vas vas, vaddr_t va, int type)
{
uintptr_t *tbl;
+ uint32_t flags;
+ paddr_t pa;
int status;
size_t idx;
@@ -286,20 +301,62 @@ pmap_set_cache(struct vas vas, vaddr_t va, int type)
return status;
idx = pmap_get_level_index(1, va);
+ pa = tbl[idx] & PTE_ADDR_MASK;
+ flags = tbl[idx] & ~PTE_ADDR_MASK;
/* Set the caching policy */
switch (type) {
case VM_CACHE_UC:
- tbl[idx] |= PTE_PCD;
- tbl[idx] &= ~PTE_PWT;
+ flags |= PTE_PCD;
+ flags &= ~PTE_PWT;
break;
case VM_CACHE_WT:
- tbl[idx] &= ~PTE_PCD;
- tbl[idx] |= PTE_PWT;
+ flags &= ~PTE_PCD;
+ flags |= PTE_PWT;
break;
default:
return -EINVAL;
}
+ return pmap_update_tbl(vas, va, (pa | flags), false);
+}
+
+bool
+pmap_is_clean(struct vas vas, vaddr_t va)
+{
+ uintptr_t *tbl;
+ int status;
+ size_t idx;
+
+ if ((status = pmap_get_tbl(vas, va, false, &tbl)) != 0)
+ return status;
+
+ idx = pmap_get_level_index(1, va);
+ return ISSET(tbl[idx], PTE_DIRTY) == 0;
+}
+
+void
+pmap_mark_clean(struct vas vas, vaddr_t va)
+{
+ uintptr_t *tbl;
+ int status;
+ size_t idx;
+
+ if ((status = pmap_get_tbl(vas, va, false, &tbl)) != 0)
+ return;
+
+ idx = pmap_get_level_index(1, va);
+ tbl[idx] &= ~PTE_DIRTY;
+
+ if (cpu_count() > 1) {
+ cpu_shootdown_tlb(va);
+ } else {
+ __invlpg(va);
+ }
+}
+
+int
+pmap_init(void)
+{
return 0;
}
diff --git a/sys/arch/amd64/amd64/proc_machdep.c b/sys/arch/amd64/amd64/proc_machdep.c
index 0be85fd..82b4e4f 100644
--- a/sys/arch/amd64/amd64/proc_machdep.c
+++ b/sys/arch/amd64/amd64/proc_machdep.c
@@ -32,6 +32,8 @@
#include <sys/param.h>
#include <sys/errno.h>
#include <sys/exec.h>
+#include <sys/sched.h>
+#include <sys/schedvar.h>
#include <machine/frame.h>
#include <machine/gdt.h>
#include <machine/cpu.h>
@@ -40,7 +42,7 @@
#include <vm/map.h>
#include <string.h>
-void
+uintptr_t
md_td_stackinit(struct proc *td, void *stack_top, struct exec_prog *prog)
{
uintptr_t *sp = stack_top;
@@ -97,6 +99,7 @@ md_td_stackinit(struct proc *td, void *stack_top, struct exec_prog *prog)
STACK_PUSH(sp, argc);
tfp = &td->tf;
tfp->rsp = (uintptr_t)sp - VM_HIGHER_HALF;
+ return tfp->rsp;
}
void
@@ -123,24 +126,31 @@ md_td_kick(struct proc *td)
{
struct trapframe *tfp;
struct cpu_info *ci;
+ uint16_t ds = USER_DS | 3;
tfp = &td->tf;
ci = this_cpu();
ci->curtd = td;
+ td->flags &= ~PROC_KTD;
__ASMV(
- "push %0\n"
+ "mov %0, %%rax\n"
"push %1\n"
- "pushf\n"
"push %2\n"
"push %3\n"
+ "push %%rax\n"
+ "push %4\n"
+ "test $3, %%ax\n"
+ "jz 1f\n"
"lfence\n"
"swapgs\n"
- "iretq"
+ "1:\n"
+ " iretq"
:
- : "i" (USER_DS | 3),
+ : "r" (tfp->cs),
+ "r" (ds),
"r" (tfp->rsp),
- "i" (USER_CS | 3),
+ "m" (tfp->rflags),
"r" (tfp->rip)
);
@@ -155,13 +165,14 @@ md_td_kick(struct proc *td)
* @ip: Instruction pointer.
*/
int
-md_fork(struct proc *p, struct proc *parent, uintptr_t ip)
+md_spawn(struct proc *p, struct proc *parent, uintptr_t ip)
{
uintptr_t stack_base;
struct trapframe *tfp;
struct pcb *pcbp;
uint8_t rpl = 0;
int error;
+ vm_prot_t prot = PROT_READ | PROT_WRITE;
tfp = &p->tf;
@@ -201,12 +212,117 @@ md_fork(struct proc *p, struct proc *parent, uintptr_t ip)
*/
if (rpl == 0) {
stack_base += VM_HIGHER_HALF;
+ p->flags |= PROC_KTD;
} else {
- vm_map(pcbp->addrsp, stack_base, stack_base,
- PROT_READ | PROT_WRITE | PROT_USER, PROC_STACK_PAGES);
+ prot |= PROT_USER;
+ vm_map(pcbp->addrsp, stack_base, stack_base, prot, PROC_STACK_PAGES);
}
p->stack_base = stack_base;
tfp->rsp = ALIGN_DOWN((stack_base + PROC_STACK_SIZE) - 1, 16);
return 0;
}
+
+/*
+ * Save thread state and enqueue it back into one
+ * of the ready queues.
+ */
+static void
+sched_save_td(struct proc *td, struct trapframe *tf)
+{
+ /*
+ * Save trapframe to process structure only
+ * if PROC_EXEC is not set.
+ */
+ if (!ISSET(td->flags, PROC_EXEC)) {
+ memcpy(&td->tf, tf, sizeof(td->tf));
+ }
+
+ sched_enqueue_td(td);
+}
+
+static void
+sched_switch_to(struct trapframe *tf, struct proc *td)
+{
+ struct cpu_info *ci;
+ struct sched_cpu *cpustat;
+ struct pcb *pcbp;
+
+ ci = this_cpu();
+
+ if (tf != NULL) {
+ memcpy(tf, &td->tf, sizeof(*tf));
+ }
+
+ /* Update stats */
+ cpustat = &ci->stat;
+ atomic_inc_64(&cpustat->nswitch);
+
+ ci->curtd = td;
+ pcbp = &td->pcb;
+ pmap_switch_vas(pcbp->addrsp);
+}
+
+/*
+ * Enable or disable preemption on the current
+ * processor
+ *
+ * @enable: Enable preemption if true
+ */
+void
+sched_preempt_set(bool enable)
+{
+ struct cpu_info *ci = this_cpu();
+
+ if (ci == NULL) {
+ return;
+ }
+
+ ci->preempt = enable;
+}
+
+bool
+sched_preemptable(void)
+{
+ struct cpu_info *ci = this_cpu();
+
+ if (ci == NULL) {
+ return false;
+ }
+
+ return ci->preempt;
+}
+
+/*
+ * Perform a context switch.
+ */
+void
+md_sched_switch(struct trapframe *tf)
+{
+ struct proc *next_td, *td;
+ struct cpu_info *ci;
+
+ ci = this_cpu();
+ if (!ci->preempt) {
+ sched_oneshot(false);
+ return;
+ }
+
+ td = ci->curtd;
+ mi_sched_switch(td);
+
+ if (td != NULL) {
+ if (td->pid == 0)
+ return;
+
+ sched_save_td(td, tf);
+ }
+
+ if ((next_td = sched_dequeue_td()) == NULL) {
+ sched_oneshot(false);
+ return;
+ }
+
+ sched_switch_to(tf, next_td);
+ sched_oneshot(false);
+}
diff --git a/sys/arch/amd64/amd64/reboot.c b/sys/arch/amd64/amd64/reboot.c
index b9df1c0..8ebe15e 100644
--- a/sys/arch/amd64/amd64/reboot.c
+++ b/sys/arch/amd64/amd64/reboot.c
@@ -32,16 +32,70 @@
#include <sys/cdefs.h>
#include <machine/pio.h>
#include <machine/cpu.h>
+#include <dev/acpi/acpi.h>
+
+static void
+cpu_reset_intel(struct cpu_info *ci)
+{
+ /*
+ * Ivy bridge processors and their panther point chipsets
+ * (family 6) can be reset through special PCH reset control
+ * registers
+ */
+ if (ci->family == 6) {
+ outb(0xCF9, 3 << 1);
+ }
+}
+
+/*
+ * Attempt to reboot the system, we do this in many
+ * stages of escalation. If a reset via the i8042
+ * controller fails and we are on an Intel processor,
+ * attempt a chipset specific reset. If that somehow fails
+ * as well, just smack the cpu with a NULL IDTR as well
+ * as an INT $0x0
+ */
+static void
+__cpu_reset(struct cpu_info *ci)
+{
+ /* Try via the i8042 */
+ outb(0x64, 0xFE);
+
+ /* Something went wrong if we are here */
+ if (ci == NULL) {
+ return;
+ }
+
+ if (ci->vendor == CPU_VENDOR_INTEL) {
+ cpu_reset_intel(ci);
+ }
+}
void
cpu_reboot(int method)
{
+ struct cpu_info *ci = this_cpu();
+ uint32_t *__dmmy = NULL;
+
+ if (ISSET(method, REBOOT_POWEROFF)) {
+ acpi_sleep(ACPI_SLEEP_S5);
+ }
+
if (ISSET(method, REBOOT_HALT)) {
cpu_halt_all();
}
- /* Pulse the reset line until the machine goes down */
- for (;;) {
- outb(0x64, 0xFE);
- }
+ __cpu_reset(ci);
+ asm volatile("lgdt %0; int $0x0" :: "m" (__dmmy));
+ __builtin_unreachable();
+}
+
+/*
+ * arg0: Method bits
+ */
+scret_t
+sys_reboot(struct syscall_args *scargs)
+{
+ cpu_reboot(scargs->arg0);
+ __builtin_unreachable();
}
diff --git a/sys/arch/amd64/amd64/simd.S b/sys/arch/amd64/amd64/simd.S
new file mode 100644
index 0000000..23fe461
--- /dev/null
+++ b/sys/arch/amd64/amd64/simd.S
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+ .text
+ .globl simd_init
+simd_init:
+ /*
+ * Enable SIMD, if SSE and AVX is supported,
+ * a value of zero is returned. If SSE is
+ * supported yet AVX is not, a value of one
+ * is returned. However, if none are supported,
+ * this routine returns -1.
+ */
+
+ // Do we support SSE?
+ mov $1, %eax
+ cpuid
+ bt $25, %edx
+ jnc .sse_not_sup
+
+ mov %cr0, %rax // Old CR0 -> EAX
+ and $0xFFFB, %ax // Disable co-processor emulation
+ or $0x02, %ax // Enable co-processor monitoring
+ mov %rax, %cr0 // Update CR0 with new flags
+
+ mov %cr4, %rax // Old CR4 -> EAX
+ or $0x200, %ax // Enable FXSAVE/FXRSTOR
+ or $0x400, %ax // Enable SIMD FP exceptions
+ mov %rax, %cr4 // Update CR4 with new flags
+
+ mov $1, %eax // LEAF 1
+ cpuid // Bit 28 of ECX indicates AVX support
+ mov $3, %eax // We need to check two bits
+ shl $27, %eax // Which are ECX.OSXSAVE and ECX.AVX
+ test %eax, %ecx // Are XSAVE and AVX supported?
+ jnc .avx_not_sup // Nope, just continue
+
+ // Enable AVX
+ xor %rcx, %rcx // Select XCR0
+ xgetbv // Load extended control register
+ or $0x07, %eax // Set AVX + SSE bits
+ xsetbv // Store new flags
+ xor %rax, %rax // Everything is good
+ retq // Return back to caller (RETURN)
+.sse_not_sup:
+ mov $-1, %rax
+ retq
+.avx_not_sup:
+ mov $1, %rax
+ retq
diff --git a/sys/arch/amd64/amd64/trap.c b/sys/arch/amd64/amd64/trap.c
index 9a3a7ba..68d7f89 100644
--- a/sys/arch/amd64/amd64/trap.c
+++ b/sys/arch/amd64/amd64/trap.c
@@ -60,6 +60,17 @@ static const char *trap_type[] = {
[TRAP_SS] = "stack-segment fault"
};
+/* Page-fault flags */
+static const char pf_flags[] = {
+ 'p', /* Present */
+ 'w', /* Write */
+ 'u', /* User */
+ 'r', /* Reserved write */
+ 'x', /* Instruction fetch */
+ 'k', /* Protection key violation */
+ 's' /* Shadow stack access */
+};
+
static inline uintptr_t
pf_faultaddr(void)
{
@@ -69,7 +80,24 @@ pf_faultaddr(void)
}
static void
-regdump(struct trapframe *tf)
+pf_code(uint64_t error_code)
+{
+ char tab[8] = {
+ '-', '-', '-',
+ '-', '-', '-',
+ '-', '\0'
+ };
+
+ for (int i = 0; i < 7; ++i) {
+ if (ISSET(error_code, BIT(i))) {
+ tab[i] = pf_flags[i];
+ }
+ }
+ kprintf("code=[%s]\n", tab);
+}
+
+__dead static void
+trap_fatal(struct trapframe *tf)
{
uintptr_t cr3, cr2 = pf_faultaddr();
@@ -79,11 +107,17 @@ regdump(struct trapframe *tf)
: "memory"
);
- kprintf(OMIT_TIMESTAMP
+ if (tf->trapno == TRAP_PAGEFLT) {
+ pf_code(tf->error_code);
+ }
+
+ panic("got fatal trap (%s)\n\n"
+ "-- DUMPING PROCESSOR STATE --\n"
"RAX=%p RCX=%p RDX=%p\n"
"RBX=%p RSI=%p RDI=%p\n"
"RFL=%p CR2=%p CR3=%p\n"
- "RBP=%p RSP=%p RIP=%p\n",
+ "RBP=%p RSP=%p RIP=%p\n\n",
+ trap_type[tf->trapno],
tf->rax, tf->rcx, tf->rdx,
tf->rbx, tf->rsi, tf->rdi,
tf->rflags, cr2, cr3,
@@ -94,6 +128,7 @@ static void
trap_user(struct trapframe *tf)
{
struct proc *td = this_td();
+ uintptr_t fault_addr;
sigset_t sigset;
sigemptyset(&sigset);
@@ -101,6 +136,9 @@ trap_user(struct trapframe *tf)
switch (tf->trapno) {
case TRAP_PROTFLT:
case TRAP_PAGEFLT:
+ if (tf->trapno == TRAP_PAGEFLT) {
+ pf_code(tf->error_code);
+ }
sigaddset(&sigset, SIGSEGV);
break;
case TRAP_ARITH_ERR:
@@ -112,6 +150,9 @@ trap_user(struct trapframe *tf)
break;
}
+ fault_addr = pf_faultaddr();
+ proc_coredump(td, fault_addr);
+
/*
* Send the signal then flush the signal queue right
* away as these types of events are critical.
@@ -120,20 +161,6 @@ trap_user(struct trapframe *tf)
dispatch_signals(td);
}
-static void
-trap_quirks(struct cpu_info *ci)
-{
- static uint8_t count;
-
- if (ISSET(ci->irq_mask, CPU_IRQ(1)) && count < 1) {
- ++count;
- pr_error("detected buggy i8042\n");
- pr_error("applying I8042_HOSTILE quirk\n");
- i8042_quirk(I8042_HOSTILE);
- return;
- }
-}
-
void
trap_syscall(struct trapframe *tf)
{
@@ -155,17 +182,11 @@ trap_syscall(struct trapframe *tf)
void
trap_handler(struct trapframe *tf)
{
- struct cpu_info *ci;
-
- splraise(IPL_HIGH);
-
if (tf->trapno >= NELEM(trap_type)) {
panic("got unknown trap %d\n", tf->trapno);
}
pr_error("got %s\n", trap_type[tf->trapno]);
- ci = this_cpu();
- trap_quirks(ci);
/* Handle traps from userland */
if (ISSET(tf->cs, 3)) {
@@ -173,6 +194,6 @@ trap_handler(struct trapframe *tf)
return;
}
- regdump(tf);
- panic("fatal trap - halting\n");
+ trap_fatal(tf);
+ __builtin_unreachable();
}
diff --git a/sys/arch/amd64/amd64/tsc.c b/sys/arch/amd64/amd64/tsc.c
new file mode 100644
index 0000000..2111cd0
--- /dev/null
+++ b/sys/arch/amd64/amd64/tsc.c
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/errno.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/cdefs.h>
+#include <sys/driver.h>
+#include <sys/syslog.h>
+#include <machine/tsc.h>
+#include <machine/asm.h>
+#include <machine/cpuid.h>
+
+/* See kconf(9) */
+#if defined(__USER_TSC)
+#define USER_TSC __USER_TSC
+#else
+#define USER_TSC 0
+#endif /* __USER_TSC */
+
+#define pr_trace(fmt, ...) kprintf("tsc: " fmt, ##__VA_ARGS__)
+#define pr_error(...) pr_trace(__VA_ARGS__)
+
+static uint64_t tsc_i = 0;
+
+uint64_t
+rdtsc_rel(void)
+{
+ return rdtsc() - tsc_i;
+}
+
+/*
+ * Check if the TSC and RDTSC instruction is
+ * supported on the current CPU.
+ *
+ * Returns zero if supported, otherwise a less
+ * than zero value is returned.
+ */
+static int
+tsc_check(void)
+{
+ uint32_t edx, unused;
+
+ CPUID(1, unused, unused, unused, edx);
+ if (ISSET(edx, BIT(4))) {
+ return 0;
+ }
+
+ return -ENOTSUP;
+}
+
+static int
+tsc_init(void)
+{
+ uint64_t cr4;
+ int error;
+
+ /* Is the TSC even supported? */
+ if ((error = tsc_check()) != 0) {
+ pr_error("TSC not supported by machine\n");
+ return error;
+ }
+
+ cr4 = amd64_read_cr4();
+ tsc_i = rdtsc();
+ pr_trace("initial count @ %d\n", rdtsc_rel());
+
+ /*
+ * If we USER_TSC is configured to "yes" then
+ * we'll need to enable the 'rdtsc' instruction
+ * in user mode.
+ */
+ if (!USER_TSC) {
+ cr4 &= ~CR4_TSD;
+ } else {
+ cr4 |= CR4_TSD;
+ }
+
+ amd64_write_cr4(cr4);
+ return 0;
+}
+
+DRIVER_EXPORT(tsc_init, "x86-tsc");
diff --git a/sys/arch/amd64/amd64/vector.S b/sys/arch/amd64/amd64/vector.S
new file mode 100644
index 0000000..62bed1b
--- /dev/null
+++ b/sys/arch/amd64/amd64/vector.S
@@ -0,0 +1,221 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <machine/frameasm.h>
+
+#define IDT_INT_GATE 0x8E
+
+.macro IDT_SET_VEC vec, sym
+ mov $\vec, %rdi
+ mov $IDT_INT_GATE, %rsi
+ lea \sym(%rip), %rdx
+ xor %rcx, %rcx
+ call idt_set_desc
+.endm
+
+ .text
+ ALIGN_TEXT
+ioapic_common_func:
+ xor %rcx, %rcx // Clear counter
+.walk: // Walk the handlers
+ lea g_intrs(%rip), %rbx // Grab table to RBX
+ lea (%rbx, %rcx, 8), %rbx // g_intrs + (8 * rcx)
+ mov (%rbx), %rdx // Grab the intr_hand
+ or %rdx, %rdx // No more?
+ jz 1f // Nope, return
+
+ mov (%rdx), %rbx // intr_hand.func
+ add $16, %rdx // Get interrupt data
+ mov %rdx, %rdi // Pass the interrupt data
+ push %rcx // Save our counter
+ push %rdx
+ call *%rbx // Call the handler
+ pop %rdx
+ pop %rcx // Restore our counter
+ or %rax, %rax // Was it theirs? (RET >= 1)
+ jnz handled // Yes, we are done.
+1: inc %rcx // Next
+ cmp $256, %rcx // Did we reach the end?
+ jl .walk // Nope, keep going
+ jmp done // Out of entries
+handled:
+ sub $8, %rdx
+ addq $1, (%rdx)
+done:
+ call lapic_eoi
+ retq
+
+ .globl pin_isr_load
+pin_isr_load:
+ IDT_SET_VEC 37, ioapic_edge_0
+ IDT_SET_VEC 38, ioapic_edge_1
+ IDT_SET_VEC 39, ioapic_edge_2
+ IDT_SET_VEC 40, ioapic_edge_3
+ IDT_SET_VEC 41, ioapic_edge_4
+ IDT_SET_VEC 42, ioapic_edge_5
+ IDT_SET_VEC 43, ioapic_edge_6
+ IDT_SET_VEC 44, ioapic_edge_7
+ IDT_SET_VEC 45, ioapic_edge_8
+ IDT_SET_VEC 46, ioapic_edge_9
+ IDT_SET_VEC 47, ioapic_edge_10
+ IDT_SET_VEC 48, ioapic_edge_11
+ IDT_SET_VEC 49, ioapic_edge_12
+ IDT_SET_VEC 50, ioapic_edge_13
+ IDT_SET_VEC 51, ioapic_edge_14
+ IDT_SET_VEC 52, ioapic_edge_15
+ IDT_SET_VEC 53, ioapic_edge_16
+ IDT_SET_VEC 54, ioapic_edge_17
+ IDT_SET_VEC 55, ioapic_edge_18
+ IDT_SET_VEC 56, ioapic_edge_19
+ IDT_SET_VEC 57, ioapic_edge_20
+ IDT_SET_VEC 58, ioapic_edge_21
+ IDT_SET_VEC 59, ioapic_edge_22
+ IDT_SET_VEC 60, ioapic_edge_23
+ IDT_SET_VEC 61, ioapic_edge_24
+ IDT_SET_VEC 62, ioapic_edge_25
+ IDT_SET_VEC 63, ioapic_edge_26
+ IDT_SET_VEC 64, ioapic_edge_27
+ IDT_SET_VEC 65, ioapic_edge_28
+ IDT_SET_VEC 66, ioapic_edge_29
+ IDT_SET_VEC 67, ioapic_edge_30
+ IDT_SET_VEC 68, ioapic_edge_31
+ IDT_SET_VEC 69, ioapic_edge_32
+ IDT_SET_VEC 70, ioapic_edge_33
+ IDT_SET_VEC 71, ioapic_edge_34
+ IDT_SET_VEC 72, ioapic_edge_35
+ IDT_SET_VEC 73, ioapic_edge_36
+ IDT_SET_VEC 74, ioapic_edge_37
+ IDT_SET_VEC 75, ioapic_edge_38
+ IDT_SET_VEC 76, ioapic_edge_39
+ IDT_SET_VEC 77, ioapic_edge_40
+ IDT_SET_VEC 78, ioapic_edge_41
+ IDT_SET_VEC 79, ioapic_edge_42
+ IDT_SET_VEC 80, ioapic_edge_43
+ IDT_SET_VEC 81, ioapic_edge_44
+ IDT_SET_VEC 82, ioapic_edge_45
+ IDT_SET_VEC 83, ioapic_edge_46
+ IDT_SET_VEC 84, ioapic_edge_47
+ IDT_SET_VEC 85, ioapic_edge_48
+ IDT_SET_VEC 86, ioapic_edge_49
+ IDT_SET_VEC 87, ioapic_edge_50
+ IDT_SET_VEC 88, ioapic_edge_51
+ IDT_SET_VEC 89, ioapic_edge_52
+ IDT_SET_VEC 90, ioapic_edge_53
+ IDT_SET_VEC 91, ioapic_edge_54
+ IDT_SET_VEC 92, ioapic_edge_55
+ IDT_SET_VEC 93, ioapic_edge_56
+ IDT_SET_VEC 94, ioapic_edge_57
+ IDT_SET_VEC 95, ioapic_edge_58
+ IDT_SET_VEC 96, ioapic_edge_59
+ IDT_SET_VEC 97, ioapic_edge_60
+ IDT_SET_VEC 98, ioapic_edge_61
+ IDT_SET_VEC 99, ioapic_edge_62
+ IDT_SET_VEC 100, ioapic_edge_63
+ ret
+
+ .globl ipi_isr
+INTRENTRY(ipi_isr, ipi_trampoline)
+ call ipi_trampoline
+ retq
+
+ .globl halt_isr
+INTRENTRY(halt_isr, halt_trampoline)
+halt_trampoline:
+ cli
+ hlt
+
+ipi_trampoline:
+ call __ipi_handle_common
+ retq
+
+/* I/O APIC edge ISRs */
+INTRENTRY(ioapic_edge_0, ioapic_common_func)
+INTRENTRY(ioapic_edge_1, ioapic_common_func)
+INTRENTRY(ioapic_edge_2, ioapic_common_func)
+INTRENTRY(ioapic_edge_3, ioapic_common_func)
+INTRENTRY(ioapic_edge_4, ioapic_common_func)
+INTRENTRY(ioapic_edge_5, ioapic_common_func)
+INTRENTRY(ioapic_edge_6, ioapic_common_func)
+INTRENTRY(ioapic_edge_7, ioapic_common_func)
+INTRENTRY(ioapic_edge_8, ioapic_common_func)
+INTRENTRY(ioapic_edge_9, ioapic_common_func)
+INTRENTRY(ioapic_edge_10, ioapic_common_func)
+INTRENTRY(ioapic_edge_11, ioapic_common_func)
+INTRENTRY(ioapic_edge_12, ioapic_common_func)
+INTRENTRY(ioapic_edge_13, ioapic_common_func)
+INTRENTRY(ioapic_edge_14, ioapic_common_func)
+INTRENTRY(ioapic_edge_15, ioapic_common_func)
+INTRENTRY(ioapic_edge_16, ioapic_common_func)
+INTRENTRY(ioapic_edge_17, ioapic_common_func)
+INTRENTRY(ioapic_edge_18, ioapic_common_func)
+INTRENTRY(ioapic_edge_19, ioapic_common_func)
+INTRENTRY(ioapic_edge_20, ioapic_common_func)
+INTRENTRY(ioapic_edge_21, ioapic_common_func)
+INTRENTRY(ioapic_edge_22, ioapic_common_func)
+INTRENTRY(ioapic_edge_23, ioapic_common_func)
+INTRENTRY(ioapic_edge_24, ioapic_common_func)
+INTRENTRY(ioapic_edge_25, ioapic_common_func)
+INTRENTRY(ioapic_edge_26, ioapic_common_func)
+INTRENTRY(ioapic_edge_27, ioapic_common_func)
+INTRENTRY(ioapic_edge_28, ioapic_common_func)
+INTRENTRY(ioapic_edge_29, ioapic_common_func)
+INTRENTRY(ioapic_edge_30, ioapic_common_func)
+INTRENTRY(ioapic_edge_31, ioapic_common_func)
+INTRENTRY(ioapic_edge_32, ioapic_common_func)
+INTRENTRY(ioapic_edge_33, ioapic_common_func)
+INTRENTRY(ioapic_edge_34, ioapic_common_func)
+INTRENTRY(ioapic_edge_35, ioapic_common_func)
+INTRENTRY(ioapic_edge_36, ioapic_common_func)
+INTRENTRY(ioapic_edge_37, ioapic_common_func)
+INTRENTRY(ioapic_edge_38, ioapic_common_func)
+INTRENTRY(ioapic_edge_39, ioapic_common_func)
+INTRENTRY(ioapic_edge_40, ioapic_common_func)
+INTRENTRY(ioapic_edge_41, ioapic_common_func)
+INTRENTRY(ioapic_edge_42, ioapic_common_func)
+INTRENTRY(ioapic_edge_43, ioapic_common_func)
+INTRENTRY(ioapic_edge_44, ioapic_common_func)
+INTRENTRY(ioapic_edge_45, ioapic_common_func)
+INTRENTRY(ioapic_edge_46, ioapic_common_func)
+INTRENTRY(ioapic_edge_47, ioapic_common_func)
+INTRENTRY(ioapic_edge_48, ioapic_common_func)
+INTRENTRY(ioapic_edge_49, ioapic_common_func)
+INTRENTRY(ioapic_edge_50, ioapic_common_func)
+INTRENTRY(ioapic_edge_51, ioapic_common_func)
+INTRENTRY(ioapic_edge_52, ioapic_common_func)
+INTRENTRY(ioapic_edge_53, ioapic_common_func)
+INTRENTRY(ioapic_edge_54, ioapic_common_func)
+INTRENTRY(ioapic_edge_55, ioapic_common_func)
+INTRENTRY(ioapic_edge_56, ioapic_common_func)
+INTRENTRY(ioapic_edge_57, ioapic_common_func)
+INTRENTRY(ioapic_edge_58, ioapic_common_func)
+INTRENTRY(ioapic_edge_59, ioapic_common_func)
+INTRENTRY(ioapic_edge_60, ioapic_common_func)
+INTRENTRY(ioapic_edge_61, ioapic_common_func)
+INTRENTRY(ioapic_edge_62, ioapic_common_func)
+INTRENTRY(ioapic_edge_63, ioapic_common_func)
diff --git a/sys/arch/amd64/conf/GENERIC b/sys/arch/amd64/conf/GENERIC
index d3a4368..6bf3af5 100644
--- a/sys/arch/amd64/conf/GENERIC
+++ b/sys/arch/amd64/conf/GENERIC
@@ -1,10 +1,14 @@
+//
// Kernel options
-option SPECTRE_IBRS no
-option SERIAL_DEBUG yes
-
-// Kernel constants
-setval SCHED_NQUEUE 4
-
-// Console attributes
-setval CONSOLE_BG 0x000000
-setval CONSOLE_FG 0XB57614
+//
+// XXX: Indirect branch restricted speculation (SPECTRE_IBRS)
+// is disabled by default as it can lead to significant
+// performance degradation.
+//
+option SPECTRE_IBRS no // Enable the IBRS CPU feature
+option SERIAL_DEBUG yes // Enable kmsg serial logging
+option CPU_UMIP yes // Enable User-mode Instruction Prevention
+option USER_KMSG no // Show kmsg in user consoles
+option USER_TSC no // Enable 'rdtsc' in user mode
+option CPU_SMEP yes // Supervisor Memory Exec Protection
+option I8042_POLL yes // Use polling for the i8042
diff --git a/sys/arch/amd64/conf/link.ld b/sys/arch/amd64/conf/link.ld
index 9c47a81..a43824f 100644
--- a/sys/arch/amd64/conf/link.ld
+++ b/sys/arch/amd64/conf/link.ld
@@ -29,6 +29,12 @@ SECTIONS
__drivers_init_end = .;
} :rodata
+ .drivers.defer : {
+ __driversd_init_start = .;
+ *(.drivers.defer .drivers.defer)
+ __driversd_init_end = .;
+ } :rodata
+
. += CONSTANT(MAXPAGESIZE);
.data : {
diff --git a/sys/arch/amd64/isa/i8042.c b/sys/arch/amd64/isa/i8042.c
index 2582d8f..095f1f4 100644
--- a/sys/arch/amd64/isa/i8042.c
+++ b/sys/arch/amd64/isa/i8042.c
@@ -33,12 +33,14 @@
#include <sys/syslog.h>
#include <sys/spinlock.h>
#include <sys/param.h>
+#include <sys/ascii.h>
#include <sys/proc.h>
#include <sys/reboot.h>
#include <sys/queue.h>
#include <dev/acpi/acpi.h>
#include <dev/timer.h>
#include <dev/cons/cons.h>
+#include <dev/dmi/dmi.h>
#include <machine/cpu.h>
#include <machine/pio.h>
#include <machine/isa/i8042var.h>
@@ -51,6 +53,13 @@
#include <string.h>
#include <assert.h>
+/* From kconf(9) */
+#if !defined(__I8042_POLL)
+#define I8042_POLL 0
+#else
+#define I8042_POLL __I8042_POLL
+#endif
+
#define KEY_REP_MAX 2
#define pr_trace(fmt, ...) kprintf("i8042: " fmt, ##__VA_ARGS__)
@@ -58,7 +67,28 @@
#define IO_NOP() inb(0x80)
-static struct spinlock data_lock;
+struct i8042_databuf {
+ uint8_t data[8];
+ size_t len;
+};
+
+/*
+ * This table allows the lookup of extended
+ * scancode bytes.
+ *
+ * XXX: Excludes the 0xE0 byte
+ */
+static struct i8042_databuf i8042_etab[] = {
+ [ I8042_XSC_ENDPR] = {
+ .data = { 0x4F },
+ .len = 1
+ },
+ [I8042_XSC_ENDRL] = {
+ .data = { 0xCF },
+ .len = 1
+ }
+};
+
static struct spinlock isr_lock;
static bool shift_key = false;
static bool capslock = false;
@@ -68,12 +98,13 @@ static struct proc polltd;
static struct timer tmr;
static bool is_init = false;
+static void i8042_ibuf_wait(void);
static int dev_send(bool aux, uint8_t data);
static int i8042_kb_getc(uint8_t sc, char *chr);
-static void i8042_drain(void);
+static void i8042_drain(struct i8042_databuf *res);
static char keytab[] = {
- '\0', '\0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
+ '\0', '\x1B', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
'-', '=', '\b', '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
'o', 'p', '[', ']', '\n', '\0', 'a', 's', 'd', 'f', 'g', 'h',
'j', 'k', 'l', ';', '\'', '`', '\0', '\\', 'z', 'x', 'c', 'v',
@@ -103,54 +134,56 @@ kbd_set_leds(uint8_t mask)
dev_send(false, mask);
}
-/*
- * Poll the i8042 status register
- *
- * @bits: Status bits.
- * @pollset: True to poll if set
- */
-static int
-i8042_statpoll(uint8_t bits, bool pollset)
+static void
+i8042_obuf_wait(void)
{
- size_t usec_start, usec;
- size_t elapsed_msec;
- uint8_t val;
- bool tmp;
+ uint8_t status;
- usec_start = tmr.get_time_usec();
for (;;) {
- val = inb(I8042_STATUS);
- tmp = (pollset) ? ISSET(val, bits) : !ISSET(val, bits);
- usec = tmr.get_time_usec();
- elapsed_msec = (usec - usec_start) / 1000;
-
- IO_NOP();
-
- /* If tmp is set, the register updated in time */
- if (tmp) {
- break;
+ status = inb(I8042_STATUS);
+ if (ISSET(status, I8042_OBUFF)) {
+ return;
}
+ }
+}
- /* Exit with an error if we timeout */
- if (elapsed_msec > I8042_DELAY) {
- return -ETIME;
+static void
+i8042_ibuf_wait(void)
+{
+ uint8_t status;
+
+ for (;;) {
+ status = inb(I8042_STATUS);
+ if (!ISSET(status, I8042_IBUFF)) {
+ return;
}
}
-
- return val;
}
/*
* Drain i8042 internal data registers.
+ *
+ * @res: Pointer for read data to be buffered to
+ *
+ * XXX: The 'res' argument is NULLable
*/
static void
-i8042_drain(void)
+i8042_drain(struct i8042_databuf *res)
{
- spinlock_acquire(&data_lock);
while (ISSET(inb(I8042_STATUS), I8042_OBUFF)) {
- inb(I8042_DATA);
+ if (res == NULL) {
+ inb(I8042_DATA);
+ continue;
+ }
+
+ if (res->len >= sizeof(res->data)) {
+ pr_error("data recieved from i8042 is too big\n");
+ break;
+ }
+
+ res->data[res->len++] = inb(I8042_DATA);
+ tmr.msleep(10);
}
- spinlock_release(&data_lock);
}
/*
@@ -162,34 +195,47 @@ i8042_drain(void)
static void
i8042_write(uint16_t port, uint8_t val)
{
- i8042_statpoll(I8042_IBUFF, false);
+ i8042_ibuf_wait();
outb(port, val);
}
/*
- * Read the i8042 config register
+ * Read from an i8042 register.
+ *
+ * @port: I/O port
+ */
+static uint8_t
+i8042_read(uint16_t port)
+{
+ i8042_obuf_wait();
+ return inb(port);
+}
+
+/*
+ * Read the i8042 controller configuration
+ * byte.
*/
static uint8_t
i8042_read_conf(void)
{
- i8042_drain();
+ uint8_t conf;
+
i8042_write(I8042_CMD, I8042_GET_CONFB);
- i8042_statpoll(I8042_OBUFF, true);
- return inb(I8042_DATA);
+ i8042_obuf_wait();
+ conf = i8042_read(I8042_DATA);
+ return conf;
}
/*
- * Write the i8042 config register
+ * Write a new value to the i8042 controller
+ * configuration byte.
*/
static void
-i8042_write_conf(uint8_t value)
+i8042_write_conf(uint8_t conf)
{
- i8042_drain();
- i8042_statpoll(I8042_IBUFF, false);
i8042_write(I8042_CMD, I8042_SET_CONFB);
- i8042_statpoll(I8042_IBUFF, false);
- i8042_write(I8042_DATA, value);
- i8042_drain();
+ i8042_ibuf_wait();
+ i8042_write(I8042_DATA, conf);
}
/*
@@ -205,14 +251,13 @@ dev_send(bool aux, uint8_t data)
i8042_write(I8042_CMD, I8042_PORT1_SEND);
}
- i8042_statpoll(I8042_IBUFF, false);
i8042_write(I8042_DATA, data);
- i8042_statpoll(I8042_OBUFF, true);
+ i8042_obuf_wait();
return inb(I8042_DATA);
}
-void
-i8042_kb_event(void)
+static int
+i8042_kb_event(void *sp)
{
struct cpu_info *ci;
struct cons_input input;
@@ -232,50 +277,103 @@ i8042_kb_event(void)
input.chr = c;
cons_ibuf_push(&g_root_scr, input);
done:
- ci->irq_mask &= CPU_IRQ(1);
+ ci->irq_mask &= ~CPU_IRQ(1);
spinlock_release(&isr_lock);
- lapic_eoi();
+ return 1; /* handled */
}
static void
i8042_en_intr(void)
{
+ struct intr_hand ih;
uint8_t conf;
- int vec;
-
- pr_trace("ENTER -> i8042_en_intr\n");
- i8042_write(I8042_CMD, I8042_DISABLE_PORT0);
- pr_trace("port 0 disabled\n");
- vec = intr_alloc_vector("i8042-kb", IPL_BIO);
- idt_set_desc(vec, IDT_INT_GATE, ISR(i8042_kb_isr), IST_HW_IRQ);
- ioapic_set_vec(KB_IRQ, vec);
- ioapic_irq_unmask(KB_IRQ);
- pr_trace("irq 1 -> vec[%x]\n", vec);
+ ih.func = i8042_kb_event;
+ ih.priority = IPL_BIO;
+ ih.irq = KB_IRQ;
+ intr_register("i8042-kb", &ih);
- /* Setup config bits */
+ /*
+ * Enable the clock of PS/2 port 0 and tell
+ * the controller that we are accepting
+ * interrupts.
+ */
conf = i8042_read_conf();
+ conf &= ~I8042_PORT0_CLK;
conf |= I8042_PORT0_INTR;
- conf &= ~I8042_PORT1_INTR;
i8042_write_conf(conf);
- pr_trace("conf written\n");
+}
- i8042_write(I8042_CMD, I8042_ENABLE_PORT0);
- pr_trace("port 0 enabled\n");
+/*
+ * Toggle the capslock and LED
+ */
+static void
+capslock_toggle(void)
+{
+ /*
+ * In case we are holding the caps lock button down,
+ * we don't want it to be spam toggled as that would
+ * be pretty strange looking and probably annoying.
+ */
+ if (!capslock_released) {
+ return;
+ }
+
+ capslock_released = false;
+ capslock = !capslock;
+
+ if (!capslock) {
+ kbd_set_leds(0);
+ } else {
+ kbd_set_leds(I8042_LED_CAPS);
+ }
}
+/*
+ * Dump extended data buffer
+ *
+ * @buf: Data
+ */
static void
-esckey_reboot(void)
+i8042_ext_dump(struct i8042_databuf *buf)
{
- syslock();
- kprintf("** Machine going down for a reboot");
+ if (buf == NULL) {
+ return;
+ }
+
+ for (int i = 0; i < buf->len; ++i) {
+ kprintf(OMIT_TIMESTAMP "%x", buf->data[i]);
+ }
- for (size_t i = 0; i < 3; ++i) {
- kprintf(OMIT_TIMESTAMP ".");
- tmr.msleep(1000);
+ kprintf(OMIT_TIMESTAMP "\n");
+}
+
+/*
+ * Used internally by i8042_kb_getc() to acquire
+ * a key from an extended scancode
+ *
+ * @buf: Scancode buf
+ * @chr: Char res
+ *
+ * Returns the extended scancode type on success,
+ * otherwise a less than zero value (see I8042_XSC_*)
+ */
+static int
+i8042_kb_getxc(struct i8042_databuf *buf, char *chr)
+{
+ size_t nelem = NELEM(i8042_etab);
+ struct i8042_databuf *buf_tmp;
+ size_t len;
+
+ for (int i = 0; i < nelem; ++i) {
+ buf_tmp = &i8042_etab[i];
+ len = buf_tmp->len;
+ if (memcmp(buf->data, buf_tmp->data, len) == 0) {
+ return i;
+ }
}
- cpu_reboot(0);
+ return -1;
}
/*
@@ -290,31 +388,16 @@ static int
i8042_kb_getc(uint8_t sc, char *chr)
{
bool release = ISSET(sc, BIT(7));
+ struct i8042_databuf buf = {0};
+ int x_type;
switch (sc) {
- /* Left alt [press] */
- case 0x38:
- esckey_reboot();
- break;
+ case 0x76:
+ *chr = ASCII_ESC;
+ return 0;
/* Caps lock [press] */
case 0x3A:
- /*
- * In case we are holding the caps lock button down,
- * we don't want it to be spam toggled as that would
- * be pretty strange looking and probably annoying.
- */
- if (!capslock_released) {
- return -EAGAIN;
- }
-
- capslock_released = false;
- capslock = !capslock;
-
- if (!capslock) {
- kbd_set_leds(0);
- } else {
- kbd_set_leds(I8042_LED_CAPS);
- }
+ capslock_toggle();
return -EAGAIN;
/* Caps lock [release] */
case 0xBA:
@@ -331,6 +414,26 @@ i8042_kb_getc(uint8_t sc, char *chr)
shift_key = false;
}
return -EAGAIN;
+ /* Extended byte */
+ case 0xE0:
+ /*
+ * Most keyboards have extended scancodes which
+ * consist of multiple bytes to represent certain
+ * special keys. We'll need to give the controller
+ * about 10 ms to refill its buffer.
+ */
+ tmr.msleep(10);
+ i8042_drain(&buf);
+ x_type = i8042_kb_getxc(&buf, chr);
+
+ /* Did we implement it? */
+ if (x_type < 0) {
+ pr_error("unknown xsc: ");
+ i8042_ext_dump(&buf);
+ return -EAGAIN;
+ }
+
+ return -1;
}
if (release) {
@@ -351,43 +454,30 @@ i8042_kb_getc(uint8_t sc, char *chr)
return 0;
}
-static void
-i8042_sync_loop(void)
-{
- /* Wake up the bus */
- outb(I8042_DATA, 0x00);
- i8042_drain();
-
- for (;;) {
- i8042_sync();
- md_pause();
- }
-}
-
/*
* Grabs a key from the keyboard, used typically
* for syncing the machine however can be used
- * to bypass IRQs in case of buggy EC.
+ * to bypass IRQs to prevent lost bytes.
*/
void
i8042_sync(void)
{
static struct spinlock lock;
struct cons_input input;
- uint8_t data;
+ uint8_t data, status;
char c;
if (spinlock_try_acquire(&lock)) {
return;
}
- if (ISSET(quirks, I8042_HOSTILE) && is_init) {
- if (i8042_statpoll(I8042_OBUFF, true) < 0) {
- /* No data ready */
+ if (is_init) {
+ status = inb(I8042_STATUS);
+ if (!ISSET(status, I8042_OBUFF)) {
goto done;
}
- data = inb(I8042_DATA);
+ data = inb(I8042_DATA);
if (i8042_kb_getc(data, &c) == 0) {
input.scancode = data;
input.chr = c;
@@ -404,9 +494,20 @@ i8042_quirk(int mask)
quirks |= mask;
}
+static void
+i8042_sync_loop(void)
+{
+ for (;;) {
+ i8042_obuf_wait();
+ i8042_sync();
+ }
+}
+
static int
i8042_init(void)
{
+ const char *prodver = NULL;
+
/* Try to request a general purpose timer */
if (req_timer(TIMER_GP, &tmr) != TMRR_SUCCESS) {
pr_error("failed to fetch general purpose timer\n");
@@ -425,6 +526,9 @@ i8042_init(void)
return -ENODEV;
}
+ i8042_write(I8042_CMD, I8042_DISABLE_PORT0);
+ i8042_write(I8042_CMD, I8042_DISABLE_PORT1);
+
/*
* On some thinkpads, e.g., the T420s, the EC implementing
* the i8042 logic likes to play cop and throw NMIs at us
@@ -432,26 +536,33 @@ i8042_init(void)
* etc... As of now, treat the i8042 like a fucking bomb
* if this bit is set.
*/
- if (strcmp(acpi_oemid(), "LENOVO") == 0) {
+ if ((prodver = dmi_prodver()) == NULL) {
+ prodver = "None";
+ }
+ if (strcmp(prodver, "ThinkPad T420s") == 0) {
quirks |= I8042_HOSTILE;
- pr_trace("lenovo device, assuming hostile\n");
+ pr_trace("ThinkPad T420s detected, assuming hostile\n");
pr_trace("disabling irq 1, polling as fallback\n");
- fork1(&polltd, 0, i8042_sync_loop, NULL);
}
- if (!ISSET(quirks, I8042_HOSTILE)) {
+ /*
+ * If the i8042 has the hostile quirk or we are
+ * configured to poll for events, spawn the polling
+ * thread.
+ */
+ if (!ISSET(quirks, I8042_HOSTILE) && !I8042_POLL) {
/* Enable interrupts */
- i8042_drain();
+ i8042_drain(NULL);
i8042_en_intr();
+ } else if (ISSET(quirks, I8042_HOSTILE) || I8042_POLL) {
+ spawn(&polltd, i8042_sync_loop, NULL, 0, NULL);
+ pr_trace("polling events\n");
}
- if (dev_send(false, 0xFF) == 0xFC) {
- pr_error("kbd self test failure\n");
- return -EIO;
- }
-
+ i8042_write(I8042_CMD, I8042_ENABLE_PORT0);
+ i8042_drain(NULL);
is_init = true;
return 0;
}
-DRIVER_EXPORT(i8042_init);
+DRIVER_EXPORT(i8042_init, "i8042");
diff --git a/sys/arch/amd64/isa/mc1468.c b/sys/arch/amd64/isa/mc1468.c
new file mode 100644
index 0000000..1f3ae1d
--- /dev/null
+++ b/sys/arch/amd64/isa/mc1468.c
@@ -0,0 +1,281 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/driver.h>
+#include <sys/device.h>
+#include <sys/syslog.h>
+#include <fs/devfs.h>
+#include <machine/pio.h>
+#include <machine/cdefs.h>
+#include <string.h>
+
+#define MC1468_REGSEL 0x70
+#define MC1468_DATA 0x71
+
+/* Register A flags */
+#define MC1468_UPDATING BIT(7)
+
+/* Register B flags */
+#define MC1468_DAYSAVE BIT(1)
+#define MC1468_CLOCK24 BIT(2)
+
+static struct cdevsw mc1468_cdevsw;
+
+static uint8_t
+bin_dabble(uint8_t bin)
+{
+ uint8_t retval = 0;
+ uint8_t nibble;
+
+ for (int i = 7; i >= 0; --i) {
+ retval <<= 1;
+ if (bin & (1 << i)) {
+ retval |= 1;
+ }
+
+ for (int j = 0; j < 2; ++j) {
+ nibble = retval & (retval >> (4 * nibble)) & 0x0F;
+ if (nibble >= 5) {
+ retval += 0x03 << (4 * nibble);
+ }
+ }
+ }
+
+ return retval;
+}
+
+/*
+ * Read a byte from an MC1468XX register.
+ */
+static uint8_t
+mc1468_read(uint8_t reg)
+{
+ outb(MC1468_REGSEL, reg);
+ return inb(MC1468_DATA);
+}
+
+/*
+ * Write a byte to the MC1468XX register.
+ */
+static void
+mc1468_write(uint8_t reg, uint8_t val)
+{
+ outb(MC1468_REGSEL, reg);
+ outb(MC1468_DATA, val);
+}
+
+/*
+ * Returns true if the MC1468XX is updating
+ * its time registers.
+ */
+static bool
+mc1468_updating(void)
+{
+ uint8_t reg_b;
+
+ reg_b = mc1468_read(0xB);
+ return ISSET(reg_b, MC1468_UPDATING) != 0;
+}
+
+/*
+ * Check if date `a' and date `b' are synced.
+ * Used to make sure a bogus date caused by a
+ * read right before an MC1468XX register
+ * update doesn't occur.
+ */
+static bool
+mc1468_date_synced(struct date *a, struct date *b)
+{
+ if (a->year != b->year)
+ return false;
+ if (a->month != b->month)
+ return false;
+ if (a->day != b->day)
+ return false;
+ if (a->sec != b->sec)
+ return false;
+ if (a->min != b->min)
+ return false;
+ if (a->hour != b->hour)
+ return false;
+
+ return true;
+}
+
+/*
+ * Sometimes the clock chip may encode the
+ * date in binary-coded-decimal. This function
+ * converts a date in BCD format to plain binary.
+ */
+static void
+mc1468_bcd_conv(struct date *dp)
+{
+ dp->year = (dp->year & 0x0F) + ((dp->year / 16) * 10);
+ dp->month = (dp->month & 0x0F) + ((dp->month / 16) * 10);
+ dp->day = (dp->day & 0x0F) + ((dp->day / 16) * 10);
+ dp->sec = (dp->sec & 0x0F) + ((dp->sec / 16) * 10);
+ dp->min = (dp->min & 0x0F) + ((dp->min / 16) * 10);
+ dp->hour = (dp->hour & 0x0F) + (((dp->hour & 0x70) / 16) * 10);
+ dp->hour |= dp->hour & 0x80;
+}
+
+/*
+ * Read the time for the clock without syncing
+ * it up.
+ *
+ * XXX: Please use mc1468_get_date() instead as
+ * this function may return inconsistent
+ * values if not used correctly.
+ */
+static void
+__mc1468_get_time(struct date *dp)
+{
+ dp->year = mc1468_read(0x09);
+ dp->month = mc1468_read(0x08);
+ dp->day = mc1468_read(0x07);
+ dp->sec = mc1468_read(0x00);
+ dp->min = mc1468_read(0x02);
+ dp->hour = mc1468_read(0x04);
+}
+
+/*
+ * Write a new time/date to the chip.
+ */
+static void
+mc1468_set_date(const struct date *dp)
+{
+ while (mc1468_updating()) {
+ md_pause();
+ }
+
+ mc1468_write(0x08, bin_dabble(dp->month));
+ mc1468_write(0x07, bin_dabble(dp->day));
+ mc1468_write(0x04, bin_dabble(dp->hour));
+ mc1468_write(0x02, bin_dabble(dp->min));
+ mc1468_write(0x00, bin_dabble(dp->sec));
+}
+
+static int
+mc1468_get_date(struct date *dp)
+{
+ struct date date_cur, date_last;
+ uint8_t reg_b = mc1468_read(0x0B);
+
+ while (mc1468_updating()) {
+ __mc1468_get_time(&date_last);
+ }
+
+ /*
+ * Get the current date and time.
+ *
+ * XXX: The date and time returned by __mc1468_get_time()
+ * may at times be out of sync, read it twice to
+ * make sure everything is synced up.
+ */
+ do {
+ while (mc1468_updating()) {
+ md_pause();
+ }
+ __mc1468_get_time(&date_last);
+ date_cur.year = date_last.year;
+ date_cur.month = date_last.month;
+ date_cur.day = date_last.day;
+ date_cur.sec = date_last.sec;
+ date_cur.min = date_last.min;
+ date_cur.hour = date_last.hour;
+ } while (!mc1468_date_synced(&date_cur, &date_last));
+
+ /* Is this in BCD? */
+ if (!ISSET(reg_b, 0x04)) {
+ mc1468_bcd_conv(&date_cur);
+ }
+
+ /* 24-hour mode? */
+ if (ISSET(reg_b, MC1468_CLOCK24)) {
+ date_cur.hour = ((date_cur.hour & 0x7F) + 12) % 24;
+ }
+
+ date_cur.year += 2000;
+ *dp = date_cur;
+ return 0;
+}
+
+static int
+mc1468_dev_read(dev_t dev, struct sio_txn *sio, int flags)
+{
+ struct date d;
+ size_t len = sizeof(d);
+
+ if (sio->len > len) {
+ sio->len = len;
+ }
+
+ mc1468_get_date(&d);
+ memcpy(sio->buf, &d, sio->len);
+ return sio->len;
+}
+
+static int
+mc1468_dev_write(dev_t dev, struct sio_txn *sio, int flags)
+{
+ struct date d;
+ size_t len = sizeof(d);
+
+ if (sio->len > len) {
+ sio->len = len;
+ }
+
+ memcpy(&d, sio->buf, sio->len);
+ mc1468_set_date(&d);
+ return sio->len;
+}
+
+static int
+mc1468_init(void)
+{
+ char devname[] = "rtc";
+ devmajor_t major;
+ dev_t dev;
+
+ major = dev_alloc_major();
+ dev = dev_alloc(major);
+ dev_register(major, dev, &mc1468_cdevsw);
+ devfs_create_entry(devname, major, dev, 0444);
+ return 0;
+}
+
+static struct cdevsw mc1468_cdevsw = {
+ .read = mc1468_dev_read,
+ .write = mc1468_dev_write,
+};
+
+DRIVER_EXPORT(mc1468_init, "mc1468");
diff --git a/sys/arch/amd64/isa/spkr.c b/sys/arch/amd64/isa/spkr.c
index b1bd2a2..c96e5f9 100644
--- a/sys/arch/amd64/isa/spkr.c
+++ b/sys/arch/amd64/isa/spkr.c
@@ -30,14 +30,60 @@
#include <sys/cdefs.h>
#include <sys/errno.h>
#include <sys/param.h>
+#include <sys/device.h>
+#include <sys/driver.h>
+#include <fs/devfs.h>
#include <dev/timer.h>
#include <machine/isa/spkr.h>
#include <machine/isa/i8254.h>
#include <machine/pio.h>
+#include <string.h>
#define DIVIDEND 1193180
#define CTRL_PORT 0x61
+static struct cdevsw beep_cdevsw;
+
+/*
+ * Write to the pcspkr
+ *
+ * Bits 15:0 - frequency (hz)
+ * Bits 31:16 - duration (msec)
+ */
+static int
+dev_write(dev_t dev, struct sio_txn *sio, int flags)
+{
+ uint32_t payload = 0;
+ uint16_t hz;
+ uint16_t duration;
+ size_t len = sizeof(payload);
+
+ if (sio->len < len) {
+ return -EINVAL;
+ }
+
+ memcpy(&payload, sio->buf, len);
+ hz = payload & 0xFFFF;
+ duration = (payload >> 16) & 0xFFFF;
+ pcspkr_tone(hz, duration);
+ return sio->len;
+}
+
+static int
+beep_init(void)
+{
+ char devname[] = "beep";
+ devmajor_t major;
+ dev_t dev;
+
+ /* Register the device here */
+ major = dev_alloc_major();
+ dev = dev_alloc(major);
+ dev_register(major, dev, &beep_cdevsw);
+ devfs_create_entry(devname, major, dev, 0666);
+ return 0;
+}
+
int
pcspkr_tone(uint16_t freq, uint32_t msec)
{
@@ -67,3 +113,10 @@ pcspkr_tone(uint16_t freq, uint32_t msec)
outb(CTRL_PORT, tmp & ~3);
return 0;
}
+
+static struct cdevsw beep_cdevsw = {
+ .read = noread,
+ .write = dev_write
+};
+
+DRIVER_EXPORT(beep_init, "pcspkr");
diff --git a/sys/arch/amd64/pci/pci_machdep.c b/sys/arch/amd64/pci/pci_machdep.c
index 43065b0..5b49a78 100644
--- a/sys/arch/amd64/pci/pci_machdep.c
+++ b/sys/arch/amd64/pci/pci_machdep.c
@@ -33,6 +33,7 @@
#include <sys/mmio.h>
#include <dev/pci/pci.h>
#include <dev/pci/pciregs.h>
+#include <machine/pci/pci.h>
#include <machine/pio.h>
#include <machine/bus.h>
#include <machine/cpu.h>
@@ -73,8 +74,8 @@ pci_get_barreg(struct pci_device *dev, uint8_t bar)
}
}
-pcireg_t
-pci_readl(struct pci_device *dev, uint32_t offset)
+__weak pcireg_t
+md_pci_readl(struct pci_device *dev, uint32_t offset)
{
uint32_t address;
@@ -83,8 +84,8 @@ pci_readl(struct pci_device *dev, uint32_t offset)
return inl(0xCFC) >> ((offset & 3) * 8);
}
-void
-pci_writel(struct pci_device *dev, uint32_t offset, pcireg_t val)
+__weak void
+md_pci_writel(struct pci_device *dev, uint32_t offset, pcireg_t val)
{
uint32_t address;
@@ -163,6 +164,7 @@ pci_enable_msix(struct pci_device *dev, const struct msi_intr *intr)
{
volatile uint64_t *tbl;
struct cpu_info *ci;
+ struct intr_hand ih, *ih_res;
uint32_t data, msg_ctl;
uint64_t msg_addr, tmp;
uint16_t tbl_off;
@@ -184,9 +186,14 @@ pci_enable_msix(struct pci_device *dev, const struct msi_intr *intr)
tbl = (void *)((dev->bar[bir] & PCI_BAR_MEMMASK) + MMIO_OFFSET);
tbl = (void *)((char *)tbl + tbl_off);
- /* Get the vector and setup handler */
- vector = intr_alloc_vector(intr->name, IPL_BIO);
- idt_set_desc(vector, IDT_INT_GATE, ISR(intr->handler), 0);
+ ih.func = intr->handler;
+ ih.priority = IPL_BIO;
+ ih.irq = -1;
+ ih_res = intr_register(intr->name, &ih);
+ if (ih_res == NULL) {
+ return -EIO;
+ }
+ vector = ih_res->vector;
/*
* Setup the message data at bits 95:64 of the message
diff --git a/sys/conf/GENERIC b/sys/conf/GENERIC
new file mode 100644
index 0000000..a8e4620
--- /dev/null
+++ b/sys/conf/GENERIC
@@ -0,0 +1,10 @@
+// Kernel options
+option PANIC_SCR no // Clear screen on panic
+
+// Kernel constants
+setval SCHED_NQUEUE 4 // Number of scheduler queues (for MLFQ)
+setval DISK_MAX 16 // Maximum disks to be registered
+
+// Console attributes
+setval CONSOLE_BG 0x000000
+setval CONSOLE_FG 0xB57614
diff --git a/sys/crypto/chacha20.c b/sys/crypto/chacha20.c
new file mode 100644
index 0000000..5c979a2
--- /dev/null
+++ b/sys/crypto/chacha20.c
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <crypto/chacha20.h>
+
+static const char sigma[16] = "expand 32-byte k";
+
+void chacha20_init(uint32_t state[16], const uint8_t key[32],
+ const uint8_t nonce[12], uint32_t counter)
+{
+ state[0] = ((uint32_t *)sigma)[0];
+ state[1] = ((uint32_t *)sigma)[1];
+ state[2] = ((uint32_t *)sigma)[2];
+ state[3] = ((uint32_t *)sigma)[3];
+
+ for (int i = 0; i < 8; ++i) {
+ state[4 + i] = ((uint32_t *)key)[i];
+ }
+
+ state[12] = counter;
+ state[13] = ((uint32_t *)nonce)[0];
+ state[14] = ((uint32_t *)nonce)[1];
+ state[15] = ((uint32_t *)nonce)[2];
+}
+
+void
+chacha20_block(uint32_t state[16], uint8_t out[64])
+{
+ uint32_t x[16];
+ memcpy(x, state, sizeof(x));
+
+ for (int i = 0; i < 10; i++) {
+
+ QR(x[0], x[4], x[8], x[12]);
+ QR(x[1], x[5], x[9], x[13]);
+ QR(x[2], x[6], x[10], x[14]);
+ QR(x[3], x[7], x[11], x[15]);
+
+ QR(x[0], x[5], x[10], x[15]);
+ QR(x[1], x[6], x[11], x[12]);
+ QR(x[2], x[7], x[8], x[13]);
+ QR(x[3], x[4], x[9], x[14]);
+ }
+
+ for (int i = 0; i < 16; ++i) {
+ x[i] += state[i];
+ ((uint32_t *)out)[i] = x[i];
+ }
+
+ state[12]++;
+}
+
+void
+chacha20_encrypt(uint32_t state[16], uint8_t *in,
+ uint8_t *out, size_t len)
+{
+ uint8_t block[64];
+ size_t offset = 0;
+
+ while (len > 0) {
+ chacha20_block(state, block);
+ size_t n = len > 64 ? 64 : len;
+
+ for (size_t i = 0; i < n; ++i) {
+ out[offset + i] = in ? in[offset + i] ^ block[i] : block[i];
+ }
+
+ offset += n;
+ len -= n;
+ }
+}
diff --git a/sys/crypto/siphash.c b/sys/crypto/siphash.c
new file mode 100644
index 0000000..e0cad44
--- /dev/null
+++ b/sys/crypto/siphash.c
@@ -0,0 +1,116 @@
+/* <MIT License>
+ Copyright (c) 2013 Marek Majkowski <marek@popcount.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+ </MIT License>
+
+ Original location:
+ https://github.com/majek/csiphash/
+
+ Solution inspired by code from:
+ Samuel Neves (supercop/crypto_auth/siphash24/little)
+ djb (supercop/crypto_auth/siphash24/little2)
+ Jean-Philippe Aumasson (https://131002.net/siphash/siphash24.c)
+*/
+
+#include <crypto/siphash.h>
+#include <stdint.h>
+
+#if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \
+ __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+# define _le64toh(x) ((uint64_t)(x))
+#elif defined(_WIN32)
+/* Windows is always little endian, unless you're on xbox360
+ http://msdn.microsoft.com/en-us/library/b0084kay(v=vs.80).aspx */
+# define _le64toh(x) ((uint64_t)(x))
+#elif defined(__APPLE__)
+# include <libkern/OSByteOrder.h>
+# define _le64toh(x) OSSwapLittleToHostInt64(x)
+#else
+
+/* See: http://sourceforge.net/p/predef/wiki/Endianness/ */
+# if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
+# include <sys/endian.h>
+# else
+# include <endian.h>
+# endif
+# if defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && \
+ __BYTE_ORDER == __LITTLE_ENDIAN
+# define _le64toh(x) ((uint64_t)(x))
+# else
+# define _le64toh(x) le64toh(x)
+# endif
+
+#endif
+
+
+#define ROTATE(x, b) (uint64_t)( ((x) << (b)) | ( (x) >> (64 - (b))) )
+
+#define HALF_ROUND(a,b,c,d,s,t) \
+ a += b; c += d; \
+ b = ROTATE(b, s) ^ a; \
+ d = ROTATE(d, t) ^ c; \
+ a = ROTATE(a, 32);
+
+#define DOUBLE_ROUND(v0,v1,v2,v3) \
+ HALF_ROUND(v0,v1,v2,v3,13,16); \
+ HALF_ROUND(v2,v1,v0,v3,17,21); \
+ HALF_ROUND(v0,v1,v2,v3,13,16); \
+ HALF_ROUND(v2,v1,v0,v3,17,21);
+
+
+uint64_t siphash24(const void *src, unsigned long src_sz, const char key[16]) {
+ const uint64_t *_key = (uint64_t *)key;
+ uint64_t k0 = _le64toh(_key[0]);
+ uint64_t k1 = _le64toh(_key[1]);
+ uint64_t b = (uint64_t)src_sz << 56;
+ const uint64_t *in = (uint64_t*)src;
+
+ uint64_t v0 = k0 ^ 0x736f6d6570736575ULL;
+ uint64_t v1 = k1 ^ 0x646f72616e646f6dULL;
+ uint64_t v2 = k0 ^ 0x6c7967656e657261ULL;
+ uint64_t v3 = k1 ^ 0x7465646279746573ULL;
+
+ while (src_sz >= 8) {
+ uint64_t mi = _le64toh(*in);
+ in += 1; src_sz -= 8;
+ v3 ^= mi;
+ DOUBLE_ROUND(v0,v1,v2,v3);
+ v0 ^= mi;
+ }
+
+ uint64_t t = 0; uint8_t *pt = (uint8_t *)&t; uint8_t *m = (uint8_t *)in;
+ switch (src_sz) {
+ case 7: pt[6] = m[6];
+ case 6: pt[5] = m[5];
+ case 5: pt[4] = m[4];
+ case 4: *((uint32_t*)&pt[0]) = *((uint32_t*)&m[0]); break;
+ case 3: pt[2] = m[2];
+ case 2: pt[1] = m[1];
+ case 1: pt[0] = m[0];
+ }
+ b |= _le64toh(t);
+
+ v3 ^= b;
+ DOUBLE_ROUND(v0,v1,v2,v3);
+ v0 ^= b; v2 ^= 0xff;
+ DOUBLE_ROUND(v0,v1,v2,v3);
+ DOUBLE_ROUND(v0,v1,v2,v3);
+ return (v0 ^ v1) ^ (v2 ^ v3);
+}
diff --git a/sys/dev/acpi/acpi_init.c b/sys/dev/acpi/acpi_init.c
index ecfb129..67eed29 100644
--- a/sys/dev/acpi/acpi_init.c
+++ b/sys/dev/acpi/acpi_init.c
@@ -46,6 +46,7 @@
static char oemid[OEMID_SIZE];
static struct acpi_root_sdt *root_sdt = NULL;
static size_t root_sdt_entries = 0;
+static paddr_t rsdp_pa = 0;
static volatile struct limine_rsdp_request rsdp_req = {
.id = LIMINE_RSDP_REQUEST,
.revision = 0
@@ -99,6 +100,12 @@ acpi_oemid(void)
return oemid;
}
+paddr_t
+acpi_rsdp(void)
+{
+ return rsdp_pa;
+}
+
void
acpi_init(void)
{
@@ -112,6 +119,7 @@ acpi_init(void)
rsdp = rsdp_req.response->address;
acpi_print_oemid("RSDP", rsdp->oemid);
memcpy(oemid, rsdp->oemid, OEMID_SIZE);
+ rsdp_pa = VIRT_TO_PHYS(rsdp);
/* Fetch the root SDT */
if (rsdp->revision >= 2) {
diff --git a/sys/dev/acpi/acpi_sleep.c b/sys/dev/acpi/acpi_sleep.c
new file mode 100644
index 0000000..5c72031
--- /dev/null
+++ b/sys/dev/acpi/acpi_sleep.c
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/errno.h>
+#include <sys/syslog.h>
+#include <machine/cdefs.h>
+#include <machine/cpu.h>
+#include <dev/acpi/acpi.h>
+#include <uacpi/sleep.h>
+
+#define pr_trace(fmt, ...) kprintf("acpi: " fmt, ##__VA_ARGS__)
+#define pr_error(...) pr_trace(__VA_ARGS__)
+
+int
+acpi_sleep(int type)
+{
+ uacpi_status error;
+ uacpi_sleep_state state;
+ const uacpi_char *error_str;
+
+ switch (type) {
+ case ACPI_SLEEP_S5:
+ state = UACPI_SLEEP_STATE_S5;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ error = uacpi_prepare_for_sleep_state(state);
+ if (uacpi_unlikely_error(error)) {
+ error_str = uacpi_status_to_string(error);
+ pr_error("failed to prep sleep: %s\n", error_str);
+ return -EIO;
+ }
+
+ /*
+ * If we are entering the S5 sleep state, bring
+ * everything down first.
+ */
+ if (type == ACPI_SLEEP_S5) {
+ pr_trace("powering off, halting all cores...\n");
+ cpu_halt_others();
+ md_intoff();
+ }
+
+ error = uacpi_enter_sleep_state(UACPI_SLEEP_STATE_S5);
+ if (uacpi_unlikely_error(error)) {
+ error_str = uacpi_status_to_string(error);
+ pr_error("could not enter S5 state: %s\n", error_str);
+ return -EIO;
+ }
+
+ return 0;
+}
diff --git a/sys/dev/acpi/uacpi.c b/sys/dev/acpi/uacpi.c
new file mode 100644
index 0000000..6c2bf50
--- /dev/null
+++ b/sys/dev/acpi/uacpi.c
@@ -0,0 +1,677 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/spinlock.h>
+#include <sys/proc.h>
+#include <sys/param.h>
+#include <sys/syslog.h>
+#include <sys/panic.h>
+#include <sys/proc.h>
+#include <sys/queue.h>
+#include <dev/timer.h>
+#include <uacpi/kernel_api.h>
+#include <uacpi/platform/arch_helpers.h>
+#include <uacpi/types.h>
+#include <uacpi/event.h>
+#include <uacpi/sleep.h>
+#include <machine/cdefs.h>
+#include <machine/pio.h>
+#include <machine/cpu.h>
+#include <machine/intr.h>
+#if defined(__x86_64__)
+#include <machine/idt.h>
+#include <machine/ioapic.h>
+#endif /* __x86_64__ */
+#include <dev/acpi/uacpi.h>
+#include <dev/acpi/acpi.h>
+#include <dev/pci/pci.h>
+#include <vm/dynalloc.h>
+#include <vm/vm.h>
+#include <string.h>
+
+#define pr_trace(fmt, ...) kprintf("acpi: " fmt, ##__VA_ARGS__)
+#define pr_error(...) pr_trace(__VA_ARGS__)
+
+typedef struct {
+ uacpi_io_addr base;
+ uacpi_size length;
+} io_range_t;
+
+struct uacpi_work {
+ uacpi_work_handler hand;
+ uacpi_handle ctx;
+ TAILQ_ENTRY(uacpi_work) link;
+};
+
+uacpi_status
+uacpi_kernel_schedule_work(uacpi_work_type type, uacpi_work_handler h, uacpi_handle ctx);
+
+extern struct proc g_proc0;
+
+static struct proc *event_td;
+static TAILQ_HEAD(, uacpi_work) acpi_gpe_eventq;
+static TAILQ_HEAD(, uacpi_work) acpi_notify_eventq;
+
+/*
+ * Dispatch ACPI general purpose events from
+ * hardware.
+ */
+static void
+uacpi_gpe_dispatch(void)
+{
+ struct uacpi_work *work;
+
+ work = TAILQ_FIRST(&acpi_gpe_eventq);
+ if (work == NULL) {
+ return;
+ }
+
+ work->hand(work->ctx);
+ TAILQ_REMOVE(&acpi_gpe_eventq, work, link);
+ dynfree(work);
+}
+
+/*
+ * Dispatch ACPI general notify events.
+ */
+static void
+uacpi_notify_dispatch(void)
+{
+ struct uacpi_work *work;
+
+ work = TAILQ_FIRST(&acpi_notify_eventq);
+ if (work == NULL) {
+ return;
+ }
+
+ work->hand(work->ctx);
+ TAILQ_REMOVE(&acpi_gpe_eventq, work, link);
+ dynfree(work);
+}
+
+static void
+uacpi_event_td(void)
+{
+ for (;;) {
+ uacpi_gpe_dispatch();
+ uacpi_notify_dispatch();
+ sched_yield();
+ }
+}
+
+static void
+shutdown(uacpi_handle ctx)
+{
+ kprintf("power button pressed\n");
+ kprintf("halting machine...\n");
+ cpu_halt_all();
+}
+
+static uacpi_interrupt_ret
+power_button_handler(uacpi_handle ctx)
+{
+ md_intoff();
+ uacpi_kernel_schedule_work(UACPI_WORK_GPE_EXECUTION, shutdown, NULL);
+ md_inton();
+
+ for (;;) {
+ md_hlt();
+ }
+
+ __builtin_unreachable();
+}
+
+void *
+uacpi_kernel_alloc(uacpi_size size)
+{
+ return dynalloc(size);
+}
+
+void
+uacpi_kernel_free(void *mem)
+{
+ dynfree(mem);
+}
+
+uacpi_status
+uacpi_kernel_get_rsdp(uacpi_phys_addr *out_rsdp_address)
+{
+ paddr_t pa;
+
+ pa = acpi_rsdp();
+ if (pa == 0) {
+ return UACPI_STATUS_NOT_FOUND;
+ }
+
+ *out_rsdp_address = pa;
+ return UACPI_STATUS_OK;
+}
+
+/* TODO: Actual mutex */
+uacpi_handle
+uacpi_kernel_create_mutex(void)
+{
+ struct spinlock *lp;
+
+ lp = dynalloc(sizeof(*lp));
+ if (lp == NULL) {
+ return NULL;
+ }
+ memset(lp, 0, sizeof(*lp));
+ return lp;
+}
+
+void
+uacpi_kernel_free_mutex(uacpi_handle handle)
+{
+ dynfree(handle);
+}
+
+uacpi_status
+uacpi_kernel_acquire_mutex(uacpi_handle handle, [[maybe_unused]] uacpi_u16 timeout)
+{
+ spinlock_acquire((struct spinlock *)handle);
+ return UACPI_STATUS_OK;
+}
+
+void
+uacpi_kernel_release_mutex(uacpi_handle handle)
+{
+ spinlock_release((struct spinlock *)handle);
+}
+
+uacpi_thread_id
+uacpi_kernel_get_thread_id(void)
+{
+ struct proc *td = this_td();
+
+ if (td == NULL) {
+ return 0; /* PID 0 */
+ }
+
+ return &td->pid;
+}
+
+uacpi_status
+uacpi_kernel_handle_firmware_request(uacpi_firmware_request *request)
+{
+ switch (request->type) {
+ case UACPI_FIRMWARE_REQUEST_TYPE_FATAL:
+ panic("uacpi: fatal firmware request\n");
+ break;
+ }
+
+ return UACPI_STATUS_OK;
+}
+
+uacpi_handle
+uacpi_kernel_create_spinlock(void)
+{
+ struct spinlock *lp;
+
+ lp = dynalloc(sizeof(*lp));
+ if (lp == NULL) {
+ return NULL;
+ }
+ memset(lp, 0, sizeof(*lp));
+ return lp;
+}
+
+void
+uacpi_kernel_free_spinlock(uacpi_handle lock)
+{
+ dynfree(lock);
+}
+
+uacpi_cpu_flags
+uacpi_kernel_lock_spinlock(uacpi_handle lock)
+{
+ struct spinlock *lp = lock;
+
+ return __atomic_test_and_set(&lp->lock, __ATOMIC_ACQUIRE);
+}
+
+void
+uacpi_kernel_unlock_spinlock(uacpi_handle lock, uacpi_cpu_flags interrupt_state)
+{
+ spinlock_release((struct spinlock *)lock);
+}
+
+uacpi_handle
+uacpi_kernel_create_event(void)
+{
+ size_t *counter;
+
+ counter = dynalloc(sizeof(*counter));
+ if (counter == NULL) {
+ return NULL;
+ }
+
+ *counter = 0;
+ return counter;
+}
+
+void
+uacpi_kernel_free_event(uacpi_handle handle)
+{
+ dynfree(handle);
+}
+
+uacpi_bool
+uacpi_kernel_wait_for_event(uacpi_handle handle, uacpi_u16 timeout)
+{
+ size_t *counter = (size_t *)handle;
+ struct timer tmr;
+ size_t usec_start, usec;
+ size_t elapsed_msec;
+
+ if (timeout == 0xFFFF) {
+ while (*counter != 0) {
+ md_pause();
+ }
+ return UACPI_TRUE;
+ }
+
+ req_timer(TIMER_GP, &tmr);
+ usec_start = tmr.get_time_usec();
+
+ for (;;) {
+ if (*counter == 0) {
+ return UACPI_TRUE;
+ }
+
+ usec = tmr.get_time_usec();
+ elapsed_msec = (usec - usec_start) / 1000;
+ if (elapsed_msec >= timeout) {
+ break;
+ }
+
+ md_pause();
+ }
+
+ __atomic_fetch_sub((size_t *)handle, 1, __ATOMIC_SEQ_CST);
+ return UACPI_FALSE;
+}
+
+void
+uacpi_kernel_signal_event(uacpi_handle handle)
+{
+ __atomic_fetch_add((size_t *)handle, 1, __ATOMIC_SEQ_CST);
+}
+
+void
+uacpi_kernel_reset_event(uacpi_handle handle)
+{
+ __atomic_store_n((size_t *)handle, 0, __ATOMIC_SEQ_CST);
+}
+
+uacpi_status
+uacpi_kernel_install_interrupt_handler(uacpi_u32 irq, uacpi_interrupt_handler fn,
+ uacpi_handle ctx, uacpi_handle *out_irq_handle)
+{
+ struct intr_hand ih;
+
+ ih.func = (void *)fn;
+ ih.priority = IPL_HIGH;
+ ih.irq = irq;
+ if (intr_register("acpi", &ih) == NULL) {
+ return UACPI_STATUS_INTERNAL_ERROR;
+ }
+
+ return UACPI_STATUS_OK;
+}
+
+uacpi_status
+uacpi_kernel_uninstall_interrupt_handler([[maybe_unused]] uacpi_interrupt_handler fn, uacpi_handle irq_handle)
+{
+ return UACPI_STATUS_UNIMPLEMENTED;
+}
+
+uacpi_status
+uacpi_kernel_schedule_work(uacpi_work_type type, uacpi_work_handler h, uacpi_handle ctx)
+{
+ struct uacpi_work *work;
+
+ work = dynalloc(sizeof(*work));
+ if (work == NULL) {
+ return UACPI_STATUS_OUT_OF_MEMORY;
+ }
+
+ work->hand = h;
+ work->ctx = ctx;
+
+ switch (type) {
+ case UACPI_WORK_GPE_EXECUTION:
+ TAILQ_INSERT_TAIL(&acpi_gpe_eventq, work, link);
+ break;
+ case UACPI_WORK_NOTIFICATION:
+ TAILQ_INSERT_TAIL(&acpi_notify_eventq, work, link);
+ break;
+ }
+
+ return 0;
+}
+
+uacpi_status
+uacpi_kernel_wait_for_work_completion(void)
+{
+ return UACPI_STATUS_UNIMPLEMENTED;
+}
+
+void uacpi_kernel_stall(uacpi_u8 usec)
+{
+ /* XXX: STUB */
+ (void)usec;
+}
+
+void
+uacpi_kernel_sleep(uacpi_u64 msec)
+{
+ struct timer tmr;
+
+ req_timer(TIMER_GP, &tmr);
+ tmr.msleep(msec);
+}
+
+void *
+uacpi_kernel_map(uacpi_phys_addr addr, [[maybe_unused]] uacpi_size len)
+{
+ return PHYS_TO_VIRT(addr);
+}
+
+void
+uacpi_kernel_unmap([[maybe_unused]] void *addr, [[maybe_unused]] uacpi_size len)
+{
+ /* XXX: no-op */
+ (void)addr;
+ (void)len;
+}
+
+uacpi_status
+uacpi_kernel_io_read8(uacpi_handle handle, uacpi_size offset, uacpi_u8 *out_value)
+{
+ io_range_t *rp = (io_range_t *)handle;
+
+ if (offset >= rp->length) {
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+
+ *out_value = inb(rp->base + offset);
+ return UACPI_STATUS_OK;
+}
+
+uacpi_status
+uacpi_kernel_io_read16(uacpi_handle handle, uacpi_size offset, uacpi_u16 *out_value)
+{
+ io_range_t *rp = (io_range_t *)handle;
+
+ if (offset >= rp->length) {
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+
+ *out_value = inw(rp->base + offset);
+ return UACPI_STATUS_OK;
+}
+
+uacpi_status
+uacpi_kernel_io_read32(uacpi_handle handle, uacpi_size offset, uacpi_u32 *out_value)
+{
+ io_range_t *rp = (io_range_t *)handle;
+
+ if (offset >= rp->length) {
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+
+ *out_value = inl(rp->base + offset);
+ return UACPI_STATUS_OK;
+}
+
+uacpi_status
+uacpi_kernel_io_write8(uacpi_handle handle, uacpi_size offset, uacpi_u8 in_value)
+{
+ io_range_t *rp = (io_range_t *)handle;
+
+ if (offset >= rp->length) {
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+
+ outb(rp->base + offset, in_value);
+ return UACPI_STATUS_OK;
+}
+
+uacpi_status
+uacpi_kernel_io_write16(uacpi_handle handle, uacpi_size offset, uacpi_u16 in_value)
+{
+ io_range_t *rp = (io_range_t *)handle;
+
+ if (offset >= rp->length) {
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+
+ outw(rp->base + offset, in_value);
+ return UACPI_STATUS_OK;
+}
+
+uacpi_status
+uacpi_kernel_io_write32(uacpi_handle handle, uacpi_size offset, uacpi_u32 in_value)
+{
+ io_range_t *rp = (io_range_t *)handle;
+
+ if (offset >= rp->length) {
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+
+ outl(rp->base + offset, in_value);
+ return UACPI_STATUS_OK;
+}
+
+uacpi_status
+uacpi_kernel_io_map(uacpi_io_addr base, uacpi_size len, uacpi_handle *out_handle)
+{
+ io_range_t *rp;
+
+ rp = dynalloc(sizeof(*rp));
+ if (rp == NULL) {
+ return UACPI_STATUS_OUT_OF_MEMORY;
+ }
+
+ rp->base = base;
+ rp->length = len;
+ *out_handle = rp;
+ return UACPI_STATUS_OK;
+}
+
+void
+uacpi_kernel_io_unmap(uacpi_handle handle)
+{
+ dynfree(handle);
+}
+
+void
+uacpi_kernel_pci_device_close([[maybe_unused]] uacpi_handle handle)
+{
+ /* XXX: no-op */
+ (void)handle;
+}
+
+uacpi_status
+uacpi_kernel_pci_device_open(uacpi_pci_address address, uacpi_handle *out_handle)
+{
+ struct pci_device *devp;
+
+ devp = dynalloc(sizeof(*devp));
+ if (devp == NULL) {
+ return UACPI_STATUS_OUT_OF_MEMORY;
+ }
+
+ devp->segment = address.segment;
+ devp->bus = address.bus;
+ devp->slot = address.device;
+ devp->func = address.function;
+ pci_add_device(devp);
+
+ *out_handle = devp;
+ return UACPI_STATUS_OK;
+}
+
+uacpi_status
+uacpi_kernel_pci_read8(uacpi_handle handle, uacpi_size offset, uacpi_u8 *out_value)
+{
+ struct pci_device *devp = handle;
+ uint32_t v;
+
+ v = pci_readl(devp, offset);
+ *out_value = (v >> ((offset & 3) * 8)) & MASK(8);
+ return UACPI_STATUS_OK;
+}
+
+uacpi_status
+uacpi_kernel_pci_read16(uacpi_handle handle, uacpi_size offset, uacpi_u16 *out_value)
+{
+ struct pci_device *devp = handle;
+ uint32_t v;
+
+ v = pci_readl(devp, offset);
+ *out_value = (v >> ((offset & 2) * 8)) & MASK(16);
+ return UACPI_STATUS_OK;
+}
+
+uacpi_status uacpi_kernel_pci_read32(uacpi_handle handle, uacpi_size offset, uacpi_u32 *out_value)
+{
+ struct pci_device *devp = handle;
+ *out_value = pci_readl(devp, offset);
+ return UACPI_STATUS_OK;
+}
+
+uacpi_status
+uacpi_kernel_pci_write8(uacpi_handle handle, uacpi_size offset, uacpi_u8 in_value)
+{
+ struct pci_device *devp = handle;
+ uint32_t v;
+
+ uacpi_kernel_pci_read8(handle, offset, (void *)&v);
+ v &= ~(0xFFFF >> ((offset & 3) * 8));
+ v |= (in_value >> ((offset & 3) * 8));
+ pci_writel(devp, offset, v);
+ return UACPI_STATUS_OK;
+}
+
+uacpi_status
+uacpi_kernel_pci_write16(uacpi_handle handle, uacpi_size offset, uacpi_u16 in_value)
+{
+ struct pci_device *devp = handle;
+ uint32_t v;
+
+ uacpi_kernel_pci_read8(handle, offset, (void *)&v);
+ v &= ~(0xFFFF >> ((offset & 2) * 8));
+ v |= (in_value >> ((offset & 2) * 8));
+ pci_writel(devp, offset, v);
+ return UACPI_STATUS_OK;
+}
+
+uacpi_status
+uacpi_kernel_pci_write32(uacpi_handle handle, uacpi_size offset, uacpi_u32 in_value)
+{
+ struct pci_device *devp = handle;
+
+ pci_writel(devp, offset, in_value);
+ return UACPI_STATUS_OK;
+}
+
+uacpi_u64
+uacpi_kernel_get_nanoseconds_since_boot(void)
+{
+ static uacpi_u64 time = 0;
+ static struct timer tmr = {0};
+ tmrr_status_t tmr_error;
+
+ if (time == 0) {
+ tmr_error = req_timer(TIMER_GP, &tmr);
+ if (tmr_error != TMRR_SUCCESS) {
+ time += 1000000;
+ return time;
+ }
+ }
+
+ time = tmr.get_time_nsec();
+ return time;
+}
+
+void
+uacpi_kernel_log(uacpi_log_level level, const uacpi_char *p)
+{
+ kprintf(p);
+}
+
+int
+uacpi_init(void)
+{
+ uacpi_status ret;
+
+ ret = uacpi_initialize(0);
+ if (uacpi_unlikely_error(ret)) {
+ pr_error("uacpi init error: %s\n", uacpi_status_to_string(ret));
+ return -1;
+ }
+
+ ret = uacpi_namespace_load();
+ if (uacpi_unlikely_error(ret)) {
+ pr_error("uacpi namespace load error: %s\n", uacpi_status_to_string(ret));
+ return -1;
+ }
+
+ ret = uacpi_namespace_initialize();
+ if (uacpi_unlikely_error(ret)) {
+ pr_error("uacpi namespace init error: %s\n", uacpi_status_to_string(ret));
+ return -1;
+ }
+
+ ret = uacpi_finalize_gpe_initialization();
+ if (uacpi_unlikely_error(ret)) {
+ pr_error("uacpi GPE init error: %s\n", uacpi_status_to_string(ret));
+ return -1;
+ }
+
+ ret = uacpi_install_fixed_event_handler(
+ UACPI_FIXED_EVENT_POWER_BUTTON,
+ power_button_handler, UACPI_NULL
+ );
+
+ if (uacpi_unlikely_error(ret)) {
+ pr_error("failed to install power button event: %s\n",
+ uacpi_status_to_string(ret)
+ );
+ return -1;
+ }
+
+ TAILQ_INIT(&acpi_gpe_eventq);
+ TAILQ_INIT(&acpi_notify_eventq);
+ spawn(&g_proc0, uacpi_event_td, NULL, 0, &event_td);
+ return 0;
+}
diff --git a/sys/dev/acpi/uacpi/default_handlers.c b/sys/dev/acpi/uacpi/default_handlers.c
new file mode 100644
index 0000000..32259d6
--- /dev/null
+++ b/sys/dev/acpi/uacpi/default_handlers.c
@@ -0,0 +1,336 @@
+#include <uacpi/internal/opregion.h>
+#include <uacpi/internal/namespace.h>
+#include <uacpi/internal/utilities.h>
+#include <uacpi/internal/helpers.h>
+#include <uacpi/internal/log.h>
+#include <uacpi/internal/io.h>
+#include <uacpi/kernel_api.h>
+#include <uacpi/uacpi.h>
+
+#ifndef UACPI_BAREBONES_MODE
+
+#define PCI_ROOT_PNP_ID "PNP0A03"
+#define PCI_EXPRESS_ROOT_PNP_ID "PNP0A08"
+
+static uacpi_namespace_node *find_pci_root(uacpi_namespace_node *node)
+{
+ static const uacpi_char *pci_root_ids[] = {
+ PCI_ROOT_PNP_ID,
+ PCI_EXPRESS_ROOT_PNP_ID,
+ UACPI_NULL
+ };
+ uacpi_namespace_node *parent = node->parent;
+
+ while (parent != uacpi_namespace_root()) {
+ if (uacpi_device_matches_pnp_id(parent, pci_root_ids)) {
+ uacpi_trace(
+ "found a PCI root node %.4s controlling region %.4s\n",
+ parent->name.text, node->name.text
+ );
+ return parent;
+ }
+
+ parent = parent->parent;
+ }
+
+ uacpi_trace_region_error(
+ node, "unable to find PCI root controlling",
+ UACPI_STATUS_NOT_FOUND
+ );
+ return node;
+}
+
+static uacpi_status pci_region_attach(uacpi_region_attach_data *data)
+{
+ uacpi_namespace_node *node, *pci_root, *device;
+ uacpi_pci_address address = { 0 };
+ uacpi_u64 value;
+ uacpi_status ret;
+
+ node = data->region_node;
+ pci_root = find_pci_root(node);
+
+ /*
+ * Find the actual device object that is supposed to be controlling
+ * this operation region.
+ */
+ device = node;
+ while (device) {
+ uacpi_object_type type;
+
+ ret = uacpi_namespace_node_type(device, &type);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ if (type == UACPI_OBJECT_DEVICE)
+ break;
+
+ device = device->parent;
+ }
+
+ if (uacpi_unlikely(device == UACPI_NULL)) {
+ ret = UACPI_STATUS_NOT_FOUND;
+ uacpi_trace_region_error(
+ node, "unable to find device responsible for", ret
+ );
+ return ret;
+ }
+
+ ret = uacpi_eval_simple_integer(device, "_ADR", &value);
+ if (ret == UACPI_STATUS_OK) {
+ address.function = (value >> 0) & 0xFF;
+ address.device = (value >> 16) & 0xFF;
+ }
+
+ ret = uacpi_eval_simple_integer(pci_root, "_SEG", &value);
+ if (ret == UACPI_STATUS_OK)
+ address.segment = value;
+
+ ret = uacpi_eval_simple_integer(pci_root, "_BBN", &value);
+ if (ret == UACPI_STATUS_OK)
+ address.bus = value;
+
+ uacpi_trace(
+ "detected PCI device %.4s@%04X:%02X:%02X:%01X\n",
+ device->name.text, address.segment, address.bus,
+ address.device, address.function
+ );
+
+ return uacpi_kernel_pci_device_open(address, &data->out_region_context);
+}
+
+static uacpi_status pci_region_detach(uacpi_region_detach_data *data)
+{
+ uacpi_kernel_pci_device_close(data->region_context);
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status pci_region_do_rw(
+ uacpi_region_op op, uacpi_region_rw_data *data
+)
+{
+ uacpi_handle dev = data->region_context;
+ uacpi_u8 width;
+ uacpi_size offset;
+
+ offset = data->offset;
+ width = data->byte_width;
+
+ return op == UACPI_REGION_OP_READ ?
+ uacpi_pci_read(dev, offset, width, &data->value) :
+ uacpi_pci_write(dev, offset, width, data->value);
+}
+
+static uacpi_status handle_pci_region(uacpi_region_op op, uacpi_handle op_data)
+{
+ switch (op) {
+ case UACPI_REGION_OP_ATTACH:
+ return pci_region_attach(op_data);
+ case UACPI_REGION_OP_DETACH:
+ return pci_region_detach(op_data);
+ case UACPI_REGION_OP_READ:
+ case UACPI_REGION_OP_WRITE:
+ return pci_region_do_rw(op, op_data);
+ default:
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+}
+
+struct memory_region_ctx {
+ uacpi_phys_addr phys;
+ uacpi_u8 *virt;
+ uacpi_size size;
+};
+
+static uacpi_status memory_region_attach(uacpi_region_attach_data *data)
+{
+ struct memory_region_ctx *ctx;
+ uacpi_status ret = UACPI_STATUS_OK;
+
+ ctx = uacpi_kernel_alloc(sizeof(*ctx));
+ if (ctx == UACPI_NULL)
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ ctx->size = data->generic_info.length;
+
+ // FIXME: this really shouldn't try to map everything at once
+ ctx->phys = data->generic_info.base;
+ ctx->virt = uacpi_kernel_map(ctx->phys, ctx->size);
+
+ if (uacpi_unlikely(ctx->virt == UACPI_NULL)) {
+ ret = UACPI_STATUS_MAPPING_FAILED;
+ uacpi_trace_region_error(data->region_node, "unable to map", ret);
+ uacpi_free(ctx, sizeof(*ctx));
+ goto out;
+ }
+
+ data->out_region_context = ctx;
+out:
+ return ret;
+}
+
+static uacpi_status memory_region_detach(uacpi_region_detach_data *data)
+{
+ struct memory_region_ctx *ctx = data->region_context;
+
+ uacpi_kernel_unmap(ctx->virt, ctx->size);
+ uacpi_free(ctx, sizeof(*ctx));
+ return UACPI_STATUS_OK;
+}
+
+struct io_region_ctx {
+ uacpi_io_addr base;
+ uacpi_handle handle;
+};
+
+static uacpi_status io_region_attach(uacpi_region_attach_data *data)
+{
+ struct io_region_ctx *ctx;
+ uacpi_generic_region_info *info = &data->generic_info;
+ uacpi_status ret;
+
+ ctx = uacpi_kernel_alloc(sizeof(*ctx));
+ if (ctx == UACPI_NULL)
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ ctx->base = info->base;
+
+ ret = uacpi_kernel_io_map(ctx->base, info->length, &ctx->handle);
+ if (uacpi_unlikely_error(ret)) {
+ uacpi_trace_region_error(
+ data->region_node, "unable to map an IO", ret
+ );
+ uacpi_free(ctx, sizeof(*ctx));
+ return ret;
+ }
+
+ data->out_region_context = ctx;
+ return ret;
+}
+
+static uacpi_status io_region_detach(uacpi_region_detach_data *data)
+{
+ struct io_region_ctx *ctx = data->region_context;
+
+ uacpi_kernel_io_unmap(ctx->handle);
+ uacpi_free(ctx, sizeof(*ctx));
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status memory_region_do_rw(
+ uacpi_region_op op, uacpi_region_rw_data *data
+)
+{
+ struct memory_region_ctx *ctx = data->region_context;
+ uacpi_size offset;
+
+ offset = data->address - ctx->phys;
+
+ return op == UACPI_REGION_OP_READ ?
+ uacpi_system_memory_read(ctx->virt, offset, data->byte_width, &data->value) :
+ uacpi_system_memory_write(ctx->virt, offset, data->byte_width, data->value);
+}
+
+static uacpi_status handle_memory_region(uacpi_region_op op, uacpi_handle op_data)
+{
+ switch (op) {
+ case UACPI_REGION_OP_ATTACH:
+ return memory_region_attach(op_data);
+ case UACPI_REGION_OP_DETACH:
+ return memory_region_detach(op_data);
+ case UACPI_REGION_OP_READ:
+ case UACPI_REGION_OP_WRITE:
+ return memory_region_do_rw(op, op_data);
+ default:
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+}
+
+static uacpi_status table_data_region_do_rw(
+ uacpi_region_op op, uacpi_region_rw_data *data
+)
+{
+ void *addr = UACPI_VIRT_ADDR_TO_PTR((uacpi_virt_addr)data->offset);
+
+ return op == UACPI_REGION_OP_READ ?
+ uacpi_system_memory_read(addr, 0, data->byte_width, &data->value) :
+ uacpi_system_memory_write(addr, 0, data->byte_width, data->value);
+}
+
+static uacpi_status handle_table_data_region(uacpi_region_op op, uacpi_handle op_data)
+{
+ switch (op) {
+ case UACPI_REGION_OP_ATTACH:
+ case UACPI_REGION_OP_DETACH:
+ return UACPI_STATUS_OK;
+ case UACPI_REGION_OP_READ:
+ case UACPI_REGION_OP_WRITE:
+ return table_data_region_do_rw(op, op_data);
+ default:
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+}
+
+static uacpi_status io_region_do_rw(
+ uacpi_region_op op, uacpi_region_rw_data *data
+)
+{
+ struct io_region_ctx *ctx = data->region_context;
+ uacpi_u8 width;
+ uacpi_size offset;
+
+ offset = data->offset - ctx->base;
+ width = data->byte_width;
+
+ return op == UACPI_REGION_OP_READ ?
+ uacpi_system_io_read(ctx->handle, offset, width, &data->value) :
+ uacpi_system_io_write(ctx->handle, offset, width, data->value);
+}
+
+static uacpi_status handle_io_region(uacpi_region_op op, uacpi_handle op_data)
+{
+ switch (op) {
+ case UACPI_REGION_OP_ATTACH:
+ return io_region_attach(op_data);
+ case UACPI_REGION_OP_DETACH:
+ return io_region_detach(op_data);
+ case UACPI_REGION_OP_READ:
+ case UACPI_REGION_OP_WRITE:
+ return io_region_do_rw(op, op_data);
+ default:
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+}
+
+void uacpi_install_default_address_space_handlers(void)
+{
+ uacpi_namespace_node *root;
+
+ root = uacpi_namespace_root();
+
+ uacpi_install_address_space_handler_with_flags(
+ root, UACPI_ADDRESS_SPACE_SYSTEM_MEMORY,
+ handle_memory_region, UACPI_NULL,
+ UACPI_ADDRESS_SPACE_HANDLER_DEFAULT
+ );
+
+ uacpi_install_address_space_handler_with_flags(
+ root, UACPI_ADDRESS_SPACE_SYSTEM_IO,
+ handle_io_region, UACPI_NULL,
+ UACPI_ADDRESS_SPACE_HANDLER_DEFAULT
+ );
+
+ uacpi_install_address_space_handler_with_flags(
+ root, UACPI_ADDRESS_SPACE_PCI_CONFIG,
+ handle_pci_region, UACPI_NULL,
+ UACPI_ADDRESS_SPACE_HANDLER_DEFAULT
+ );
+
+ uacpi_install_address_space_handler_with_flags(
+ root, UACPI_ADDRESS_SPACE_TABLE_DATA,
+ handle_table_data_region, UACPI_NULL,
+ UACPI_ADDRESS_SPACE_HANDLER_DEFAULT
+ );
+}
+
+#endif // !UACPI_BAREBONES_MODE
diff --git a/sys/dev/acpi/uacpi/event.c b/sys/dev/acpi/uacpi/event.c
new file mode 100644
index 0000000..0c58372
--- /dev/null
+++ b/sys/dev/acpi/uacpi/event.c
@@ -0,0 +1,2449 @@
+#include <uacpi/internal/event.h>
+#include <uacpi/internal/registers.h>
+#include <uacpi/internal/context.h>
+#include <uacpi/internal/io.h>
+#include <uacpi/internal/log.h>
+#include <uacpi/internal/namespace.h>
+#include <uacpi/internal/interpreter.h>
+#include <uacpi/internal/notify.h>
+#include <uacpi/internal/utilities.h>
+#include <uacpi/internal/mutex.h>
+#include <uacpi/internal/stdlib.h>
+#include <uacpi/acpi.h>
+
+#define UACPI_EVENT_DISABLED 0
+#define UACPI_EVENT_ENABLED 1
+
+#if !defined(UACPI_REDUCED_HARDWARE) && !defined(UACPI_BAREBONES_MODE)
+
+static uacpi_handle g_gpe_state_slock;
+static struct uacpi_recursive_lock g_event_lock;
+static uacpi_bool g_gpes_finalized;
+
+struct fixed_event {
+ uacpi_u8 enable_field;
+ uacpi_u8 status_field;
+ uacpi_u16 enable_mask;
+ uacpi_u16 status_mask;
+};
+
+struct fixed_event_handler {
+ uacpi_interrupt_handler handler;
+ uacpi_handle ctx;
+};
+
+static const struct fixed_event fixed_events[UACPI_FIXED_EVENT_MAX + 1] = {
+ [UACPI_FIXED_EVENT_GLOBAL_LOCK] = {
+ .status_field = UACPI_REGISTER_FIELD_GBL_STS,
+ .enable_field = UACPI_REGISTER_FIELD_GBL_EN,
+ .enable_mask = ACPI_PM1_EN_GBL_EN_MASK,
+ .status_mask = ACPI_PM1_STS_GBL_STS_MASK,
+ },
+ [UACPI_FIXED_EVENT_TIMER_STATUS] = {
+ .status_field = UACPI_REGISTER_FIELD_TMR_STS,
+ .enable_field = UACPI_REGISTER_FIELD_TMR_EN,
+ .enable_mask = ACPI_PM1_EN_TMR_EN_MASK,
+ .status_mask = ACPI_PM1_STS_TMR_STS_MASK,
+ },
+ [UACPI_FIXED_EVENT_POWER_BUTTON] = {
+ .status_field = UACPI_REGISTER_FIELD_PWRBTN_STS,
+ .enable_field = UACPI_REGISTER_FIELD_PWRBTN_EN,
+ .enable_mask = ACPI_PM1_EN_PWRBTN_EN_MASK,
+ .status_mask = ACPI_PM1_STS_PWRBTN_STS_MASK,
+ },
+ [UACPI_FIXED_EVENT_SLEEP_BUTTON] = {
+ .status_field = UACPI_REGISTER_FIELD_SLPBTN_STS,
+ .enable_field = UACPI_REGISTER_FIELD_SLPBTN_EN,
+ .enable_mask = ACPI_PM1_EN_SLPBTN_EN_MASK,
+ .status_mask = ACPI_PM1_STS_SLPBTN_STS_MASK,
+ },
+ [UACPI_FIXED_EVENT_RTC] = {
+ .status_field = UACPI_REGISTER_FIELD_RTC_STS,
+ .enable_field = UACPI_REGISTER_FIELD_RTC_EN,
+ .enable_mask = ACPI_PM1_EN_RTC_EN_MASK,
+ .status_mask = ACPI_PM1_STS_RTC_STS_MASK,
+ },
+};
+
+static struct fixed_event_handler
+fixed_event_handlers[UACPI_FIXED_EVENT_MAX + 1];
+
+static uacpi_status initialize_fixed_events(void)
+{
+ uacpi_size i;
+
+ for (i = 0; i < UACPI_FIXED_EVENT_MAX; ++i) {
+ uacpi_write_register_field(
+ fixed_events[i].enable_field, UACPI_EVENT_DISABLED
+ );
+ }
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status set_event(uacpi_u8 event, uacpi_u8 value)
+{
+ uacpi_status ret;
+ uacpi_u64 raw_value;
+ const struct fixed_event *ev = &fixed_events[event];
+
+ ret = uacpi_write_register_field(ev->enable_field, value);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ ret = uacpi_read_register_field(ev->enable_field, &raw_value);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ if (raw_value != value) {
+ uacpi_error("failed to %sable fixed event %d\n",
+ value ? "en" : "dis", event);
+ return UACPI_STATUS_HARDWARE_TIMEOUT;
+ }
+
+ uacpi_trace("fixed event %d %sabled successfully\n",
+ event, value ? "en" : "dis");
+ return UACPI_STATUS_OK;
+}
+
+uacpi_status uacpi_enable_fixed_event(uacpi_fixed_event event)
+{
+ uacpi_status ret;
+
+ UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_SUBSYSTEM_INITIALIZED);
+
+ if (uacpi_unlikely(event < 0 || event > UACPI_FIXED_EVENT_MAX))
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ if (uacpi_is_hardware_reduced())
+ return UACPI_STATUS_OK;
+
+ ret = uacpi_recursive_lock_acquire(&g_event_lock);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ /*
+ * Attempting to enable an event that doesn't have a handler is most likely
+ * an error, don't allow it.
+ */
+ if (uacpi_unlikely(fixed_event_handlers[event].handler == UACPI_NULL)) {
+ ret = UACPI_STATUS_NO_HANDLER;
+ goto out;
+ }
+
+ ret = set_event(event, UACPI_EVENT_ENABLED);
+
+out:
+ uacpi_recursive_lock_release(&g_event_lock);
+ return ret;
+}
+
+uacpi_status uacpi_disable_fixed_event(uacpi_fixed_event event)
+{
+ uacpi_status ret;
+
+ UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_SUBSYSTEM_INITIALIZED);
+
+ if (uacpi_unlikely(event < 0 || event > UACPI_FIXED_EVENT_MAX))
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ if (uacpi_is_hardware_reduced())
+ return UACPI_STATUS_OK;
+
+ ret = uacpi_recursive_lock_acquire(&g_event_lock);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ ret = set_event(event, UACPI_EVENT_DISABLED);
+
+ uacpi_recursive_lock_release(&g_event_lock);
+ return ret;
+}
+
+uacpi_status uacpi_clear_fixed_event(uacpi_fixed_event event)
+{
+ UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_SUBSYSTEM_INITIALIZED);
+
+ if (uacpi_unlikely(event < 0 || event > UACPI_FIXED_EVENT_MAX))
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ if (uacpi_is_hardware_reduced())
+ return UACPI_STATUS_OK;
+
+ return uacpi_write_register_field(
+ fixed_events[event].status_field, ACPI_PM1_STS_CLEAR
+ );
+}
+
+static uacpi_interrupt_ret dispatch_fixed_event(
+ const struct fixed_event *ev, uacpi_fixed_event event
+)
+{
+ uacpi_status ret;
+ struct fixed_event_handler *evh = &fixed_event_handlers[event];
+
+ ret = uacpi_write_register_field(ev->status_field, ACPI_PM1_STS_CLEAR);
+ if (uacpi_unlikely_error(ret))
+ return UACPI_INTERRUPT_NOT_HANDLED;
+
+ if (uacpi_unlikely(evh->handler == UACPI_NULL)) {
+ uacpi_warn(
+ "fixed event %d fired but no handler installed, disabling...\n",
+ event
+ );
+ uacpi_write_register_field(ev->enable_field, UACPI_EVENT_DISABLED);
+ return UACPI_INTERRUPT_NOT_HANDLED;
+ }
+
+ return evh->handler(evh->ctx);
+}
+
+static uacpi_interrupt_ret handle_fixed_events(void)
+{
+ uacpi_interrupt_ret int_ret = UACPI_INTERRUPT_NOT_HANDLED;
+ uacpi_status ret;
+ uacpi_u64 enable_mask, status_mask;
+ uacpi_size i;
+
+ ret = uacpi_read_register(UACPI_REGISTER_PM1_STS, &status_mask);
+ if (uacpi_unlikely_error(ret))
+ return int_ret;
+
+ ret = uacpi_read_register(UACPI_REGISTER_PM1_EN, &enable_mask);
+ if (uacpi_unlikely_error(ret))
+ return int_ret;
+
+ for (i = 0; i < UACPI_FIXED_EVENT_MAX; ++i)
+ {
+ const struct fixed_event *ev = &fixed_events[i];
+
+ if (!(status_mask & ev->status_mask) ||
+ !(enable_mask & ev->enable_mask))
+ continue;
+
+ int_ret |= dispatch_fixed_event(ev, i);
+ }
+
+ return int_ret;
+}
+
+struct gpe_native_handler {
+ uacpi_gpe_handler cb;
+ uacpi_handle ctx;
+
+ /*
+ * Preserved values to be used for state restoration if this handler is
+ * removed at any point.
+ */
+ uacpi_handle previous_handler;
+ uacpi_u8 previous_triggering : 1;
+ uacpi_u8 previous_handler_type : 3;
+ uacpi_u8 previously_enabled : 1;
+};
+
+struct gpe_implicit_notify_handler {
+ struct gpe_implicit_notify_handler *next;
+ uacpi_namespace_node *device;
+};
+
+#define EVENTS_PER_GPE_REGISTER 8
+
+/*
+ * NOTE:
+ * This API and handler types are inspired by ACPICA, let's not reinvent the
+ * wheel and follow a similar path that people ended up finding useful after
+ * years of dealing with ACPI. Obviously credit goes to them for inventing
+ * "implicit notify" and other neat API.
+ */
+enum gpe_handler_type {
+ GPE_HANDLER_TYPE_NONE = 0,
+ GPE_HANDLER_TYPE_AML_HANDLER = 1,
+ GPE_HANDLER_TYPE_NATIVE_HANDLER = 2,
+ GPE_HANDLER_TYPE_NATIVE_HANDLER_RAW = 3,
+ GPE_HANDLER_TYPE_IMPLICIT_NOTIFY = 4,
+};
+
+struct gp_event {
+ union {
+ struct gpe_native_handler *native_handler;
+ struct gpe_implicit_notify_handler *implicit_handler;
+ uacpi_namespace_node *aml_handler;
+ uacpi_handle *any_handler;
+ };
+
+ struct gpe_register *reg;
+ uacpi_u16 idx;
+
+ // "reference count" of the number of times this event has been enabled
+ uacpi_u8 num_users;
+
+ uacpi_u8 handler_type : 3;
+ uacpi_u8 triggering : 1;
+ uacpi_u8 wake : 1;
+ uacpi_u8 block_interrupts : 1;
+};
+
+struct gpe_register {
+ uacpi_mapped_gas status;
+ uacpi_mapped_gas enable;
+
+ uacpi_u8 runtime_mask;
+ uacpi_u8 wake_mask;
+ uacpi_u8 masked_mask;
+ uacpi_u8 current_mask;
+
+ uacpi_u16 base_idx;
+};
+
+struct gpe_block {
+ struct gpe_block *prev, *next;
+
+ /*
+ * Technically this can only refer to \_GPE, but there's also apparently a
+ * "GPE Block Device" with id "ACPI0006", which is not used by anyone. We
+ * still keep it as a possibility that someone might eventually use it, so
+ * it is supported here.
+ */
+ uacpi_namespace_node *device_node;
+
+ struct gpe_register *registers;
+ struct gp_event *events;
+ struct gpe_interrupt_ctx *irq_ctx;
+
+ uacpi_u16 num_registers;
+ uacpi_u16 num_events;
+ uacpi_u16 base_idx;
+};
+
+struct gpe_interrupt_ctx {
+ struct gpe_interrupt_ctx *prev, *next;
+
+ struct gpe_block *gpe_head;
+ uacpi_handle irq_handle;
+ uacpi_u32 irq;
+};
+static struct gpe_interrupt_ctx *g_gpe_interrupt_head;
+
+static uacpi_u8 gpe_get_mask(struct gp_event *event)
+{
+ return 1 << (event->idx - event->reg->base_idx);
+}
+
+enum gpe_state {
+ GPE_STATE_ENABLED,
+ GPE_STATE_ENABLED_CONDITIONALLY,
+ GPE_STATE_DISABLED,
+};
+
+static uacpi_status set_gpe_state(struct gp_event *event, enum gpe_state state)
+{
+ uacpi_status ret;
+ struct gpe_register *reg = event->reg;
+ uacpi_u64 enable_mask;
+ uacpi_u8 event_bit;
+ uacpi_cpu_flags flags;
+
+ event_bit = gpe_get_mask(event);
+ if (state != GPE_STATE_DISABLED && (reg->masked_mask & event_bit))
+ return UACPI_STATUS_OK;
+
+ if (state == GPE_STATE_ENABLED_CONDITIONALLY) {
+ if (!(reg->current_mask & event_bit))
+ return UACPI_STATUS_OK;
+
+ state = GPE_STATE_ENABLED;
+ }
+
+ flags = uacpi_kernel_lock_spinlock(g_gpe_state_slock);
+
+ ret = uacpi_gas_read_mapped(&reg->enable, &enable_mask);
+ if (uacpi_unlikely_error(ret))
+ goto out;
+
+ switch (state) {
+ case GPE_STATE_ENABLED:
+ enable_mask |= event_bit;
+ break;
+ case GPE_STATE_DISABLED:
+ enable_mask &= ~event_bit;
+ break;
+ default:
+ ret = UACPI_STATUS_INVALID_ARGUMENT;
+ goto out;
+ }
+
+ ret = uacpi_gas_write_mapped(&reg->enable, enable_mask);
+out:
+ uacpi_kernel_unlock_spinlock(g_gpe_state_slock, flags);
+ return ret;
+}
+
+static uacpi_status clear_gpe(struct gp_event *event)
+{
+ struct gpe_register *reg = event->reg;
+
+ return uacpi_gas_write_mapped(&reg->status, gpe_get_mask(event));
+}
+
+static uacpi_status restore_gpe(struct gp_event *event)
+{
+ uacpi_status ret;
+
+ if (event->triggering == UACPI_GPE_TRIGGERING_LEVEL) {
+ ret = clear_gpe(event);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+ }
+
+ ret = set_gpe_state(event, GPE_STATE_ENABLED_CONDITIONALLY);
+ event->block_interrupts = UACPI_FALSE;
+
+ return ret;
+}
+
+static void async_restore_gpe(uacpi_handle opaque)
+{
+ uacpi_status ret;
+ struct gp_event *event = opaque;
+
+ ret = restore_gpe(event);
+ if (uacpi_unlikely_error(ret)) {
+ uacpi_error("unable to restore GPE(%02X): %s\n",
+ event->idx, uacpi_status_to_string(ret));
+ }
+}
+
+static void async_run_gpe_handler(uacpi_handle opaque)
+{
+ uacpi_status ret;
+ struct gp_event *event = opaque;
+
+ ret = uacpi_namespace_write_lock();
+ if (uacpi_unlikely_error(ret))
+ goto out_no_unlock;
+
+ switch (event->handler_type) {
+ case GPE_HANDLER_TYPE_AML_HANDLER: {
+ uacpi_object *method_obj;
+ uacpi_object_name name;
+
+ method_obj = uacpi_namespace_node_get_object_typed(
+ event->aml_handler, UACPI_OBJECT_METHOD_BIT
+ );
+ if (uacpi_unlikely(method_obj == UACPI_NULL)) {
+ uacpi_error("GPE(%02X) AML handler gone\n", event->idx);
+ break;
+ }
+
+ name = uacpi_namespace_node_name(event->aml_handler);
+ uacpi_trace(
+ "executing GPE(%02X) handler %.4s\n",
+ event->idx, name.text
+ );
+
+ ret = uacpi_execute_control_method(
+ event->aml_handler, method_obj->method, UACPI_NULL, UACPI_NULL
+ );
+ if (uacpi_unlikely_error(ret)) {
+ uacpi_error(
+ "error while executing GPE(%02X) handler %.4s: %s\n",
+ event->idx, event->aml_handler->name.text,
+ uacpi_status_to_string(ret)
+ );
+ }
+ break;
+ }
+
+ case GPE_HANDLER_TYPE_IMPLICIT_NOTIFY: {
+ struct gpe_implicit_notify_handler *handler;
+
+ handler = event->implicit_handler;
+ while (handler) {
+ /*
+ * 2 - Device Wake. Used to notify OSPM that the device has signaled
+ * its wake event, and that OSPM needs to notify OSPM native device
+ * driver for the device.
+ */
+ uacpi_notify_all(handler->device, 2);
+ handler = handler->next;
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ uacpi_namespace_write_unlock();
+
+out_no_unlock:
+ /*
+ * We schedule the work as NOTIFICATION to make sure all other notifications
+ * finish before this GPE is re-enabled.
+ */
+ ret = uacpi_kernel_schedule_work(
+ UACPI_WORK_NOTIFICATION, async_restore_gpe, event
+ );
+ if (uacpi_unlikely_error(ret)) {
+ uacpi_error("unable to schedule GPE(%02X) restore: %s\n",
+ event->idx, uacpi_status_to_string(ret));
+ async_restore_gpe(event);
+ }
+}
+
+static uacpi_interrupt_ret dispatch_gpe(
+ uacpi_namespace_node *device_node, struct gp_event *event
+)
+{
+ uacpi_status ret;
+ uacpi_interrupt_ret int_ret = UACPI_INTERRUPT_NOT_HANDLED;
+
+ /*
+ * For raw handlers we don't do any management whatsoever, we just let the
+ * handler know a GPE has triggered and let it handle disable/enable as
+ * well as clearing.
+ */
+ if (event->handler_type == GPE_HANDLER_TYPE_NATIVE_HANDLER_RAW) {
+ return event->native_handler->cb(
+ event->native_handler->ctx, device_node, event->idx
+ );
+ }
+
+ ret = set_gpe_state(event, GPE_STATE_DISABLED);
+ if (uacpi_unlikely_error(ret)) {
+ uacpi_error("failed to disable GPE(%02X): %s\n",
+ event->idx, uacpi_status_to_string(ret));
+ return int_ret;
+ }
+
+ event->block_interrupts = UACPI_TRUE;
+
+ if (event->triggering == UACPI_GPE_TRIGGERING_EDGE) {
+ ret = clear_gpe(event);
+ if (uacpi_unlikely_error(ret)) {
+ uacpi_error("unable to clear GPE(%02X): %s\n",
+ event->idx, uacpi_status_to_string(ret));
+ set_gpe_state(event, GPE_STATE_ENABLED_CONDITIONALLY);
+ return int_ret;
+ }
+ }
+
+ switch (event->handler_type) {
+ case GPE_HANDLER_TYPE_NATIVE_HANDLER:
+ int_ret = event->native_handler->cb(
+ event->native_handler->ctx, device_node, event->idx
+ );
+ if (!(int_ret & UACPI_GPE_REENABLE))
+ break;
+
+ ret = restore_gpe(event);
+ if (uacpi_unlikely_error(ret)) {
+ uacpi_error("unable to restore GPE(%02X): %s\n",
+ event->idx, uacpi_status_to_string(ret));
+ }
+ break;
+
+ case GPE_HANDLER_TYPE_AML_HANDLER:
+ case GPE_HANDLER_TYPE_IMPLICIT_NOTIFY:
+ ret = uacpi_kernel_schedule_work(
+ UACPI_WORK_GPE_EXECUTION, async_run_gpe_handler, event
+ );
+ if (uacpi_unlikely_error(ret)) {
+ uacpi_warn(
+ "unable to schedule GPE(%02X) for execution: %s\n",
+ event->idx, uacpi_status_to_string(ret)
+ );
+ }
+ break;
+
+ default:
+ uacpi_warn("GPE(%02X) fired but no handler, keeping disabled\n",
+ event->idx);
+ break;
+ }
+
+ return UACPI_INTERRUPT_HANDLED;
+}
+
+static uacpi_interrupt_ret detect_gpes(struct gpe_block *block)
+{
+ uacpi_status ret;
+ uacpi_interrupt_ret int_ret = UACPI_INTERRUPT_NOT_HANDLED;
+ struct gpe_register *reg;
+ struct gp_event *event;
+ uacpi_u64 status, enable;
+ uacpi_size i, j;
+
+ while (block) {
+ for (i = 0; i < block->num_registers; ++i) {
+ reg = &block->registers[i];
+
+ if (!reg->runtime_mask && !reg->wake_mask)
+ continue;
+
+ ret = uacpi_gas_read_mapped(&reg->status, &status);
+ if (uacpi_unlikely_error(ret))
+ return int_ret;
+
+ ret = uacpi_gas_read_mapped(&reg->enable, &enable);
+ if (uacpi_unlikely_error(ret))
+ return int_ret;
+
+ if (status == 0)
+ continue;
+
+ for (j = 0; j < EVENTS_PER_GPE_REGISTER; ++j) {
+ if (!((status & enable) & (1ull << j)))
+ continue;
+
+ event = &block->events[j + i * EVENTS_PER_GPE_REGISTER];
+ int_ret |= dispatch_gpe(block->device_node, event);
+ }
+ }
+
+ block = block->next;
+ }
+
+ return int_ret;
+}
+
+static uacpi_status maybe_dispatch_gpe(
+ uacpi_namespace_node *gpe_device, struct gp_event *event
+)
+{
+ uacpi_status ret;
+ struct gpe_register *reg = event->reg;
+ uacpi_u64 status;
+
+ ret = uacpi_gas_read_mapped(&reg->status, &status);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ if (!(status & gpe_get_mask(event)))
+ return ret;
+
+ dispatch_gpe(gpe_device, event);
+ return ret;
+}
+
+static uacpi_interrupt_ret handle_gpes(uacpi_handle opaque)
+{
+ struct gpe_interrupt_ctx *ctx = opaque;
+
+ if (uacpi_unlikely(ctx == UACPI_NULL))
+ return UACPI_INTERRUPT_NOT_HANDLED;
+
+ return detect_gpes(ctx->gpe_head);
+}
+
+static uacpi_status find_or_create_gpe_interrupt_ctx(
+ uacpi_u32 irq, struct gpe_interrupt_ctx **out_ctx
+)
+{
+ uacpi_status ret;
+ struct gpe_interrupt_ctx *entry = g_gpe_interrupt_head;
+
+ while (entry) {
+ if (entry->irq == irq) {
+ *out_ctx = entry;
+ return UACPI_STATUS_OK;
+ }
+
+ entry = entry->next;
+ }
+
+ entry = uacpi_kernel_alloc_zeroed(sizeof(*entry));
+ if (uacpi_unlikely(entry == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ /*
+ * SCI interrupt is installed by other code and is responsible for more
+ * things than just the GPE handling. Don't install it here.
+ */
+ if (irq != g_uacpi_rt_ctx.fadt.sci_int) {
+ ret = uacpi_kernel_install_interrupt_handler(
+ irq, handle_gpes, entry, &entry->irq_handle
+ );
+ if (uacpi_unlikely_error(ret)) {
+ uacpi_free(entry, sizeof(*entry));
+ return ret;
+ }
+ }
+
+ entry->irq = irq;
+ entry->next = g_gpe_interrupt_head;
+ g_gpe_interrupt_head = entry;
+
+ *out_ctx = entry;
+ return UACPI_STATUS_OK;
+}
+
+static void gpe_release_implicit_notify_handlers(struct gp_event *event)
+{
+ struct gpe_implicit_notify_handler *handler, *next_handler;
+
+ handler = event->implicit_handler;
+ while (handler) {
+ next_handler = handler->next;
+ uacpi_free(handler, sizeof(*handler));
+ handler = next_handler;
+ }
+
+ event->implicit_handler = UACPI_NULL;
+}
+
+enum gpe_block_action
+{
+ GPE_BLOCK_ACTION_DISABLE_ALL,
+ GPE_BLOCK_ACTION_ENABLE_ALL_FOR_RUNTIME,
+ GPE_BLOCK_ACTION_ENABLE_ALL_FOR_WAKE,
+ GPE_BLOCK_ACTION_CLEAR_ALL,
+};
+
+static uacpi_status gpe_block_apply_action(
+ struct gpe_block *block, enum gpe_block_action action
+)
+{
+ uacpi_status ret;
+ uacpi_size i;
+ uacpi_u8 value;
+ struct gpe_register *reg;
+
+ for (i = 0; i < block->num_registers; ++i) {
+ reg = &block->registers[i];
+
+ switch (action) {
+ case GPE_BLOCK_ACTION_DISABLE_ALL:
+ value = 0;
+ break;
+ case GPE_BLOCK_ACTION_ENABLE_ALL_FOR_RUNTIME:
+ value = reg->runtime_mask & ~reg->masked_mask;
+ break;
+ case GPE_BLOCK_ACTION_ENABLE_ALL_FOR_WAKE:
+ value = reg->wake_mask;
+ break;
+ case GPE_BLOCK_ACTION_CLEAR_ALL:
+ ret = uacpi_gas_write_mapped(&reg->status, 0xFF);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+ continue;
+ default:
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+
+ reg->current_mask = value;
+ ret = uacpi_gas_write_mapped(&reg->enable, value);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+ }
+
+ return UACPI_STATUS_OK;
+}
+
+static void gpe_block_mask_safe(struct gpe_block *block)
+{
+ uacpi_size i;
+ struct gpe_register *reg;
+
+ for (i = 0; i < block->num_registers; ++i) {
+ reg = &block->registers[i];
+
+ // No need to flush or do anything if it's not currently enabled
+ if (!reg->current_mask)
+ continue;
+
+ // 1. Mask the GPEs, this makes sure their state is no longer modifyable
+ reg->masked_mask = 0xFF;
+
+ /*
+ * 2. Wait for in-flight work & IRQs to finish, these might already
+ * be past the respective "if (masked)" check and therefore may
+ * try to re-enable a masked GPE.
+ */
+ uacpi_kernel_wait_for_work_completion();
+
+ /*
+ * 3. Now that this GPE's state is unmodifyable and we know that
+ * currently in-flight IRQs will see the masked state, we can
+ * safely disable all events knowing they won't be re-enabled by
+ * a racing IRQ.
+ */
+ uacpi_gas_write_mapped(&reg->enable, 0x00);
+
+ /*
+ * 4. Wait for the last possible IRQ to finish, now that this event is
+ * disabled.
+ */
+ uacpi_kernel_wait_for_work_completion();
+ }
+}
+
+static void uninstall_gpe_block(struct gpe_block *block)
+{
+ if (block->registers != UACPI_NULL) {
+ struct gpe_register *reg;
+ uacpi_size i;
+
+ gpe_block_mask_safe(block);
+
+ for (i = 0; i < block->num_registers; ++i) {
+ reg = &block->registers[i];
+
+ if (reg->enable.total_bit_width)
+ uacpi_unmap_gas_nofree(&reg->enable);
+ if (reg->status.total_bit_width)
+ uacpi_unmap_gas_nofree(&reg->status);
+ }
+ }
+
+ if (block->prev)
+ block->prev->next = block->next;
+
+ if (block->irq_ctx) {
+ struct gpe_interrupt_ctx *ctx = block->irq_ctx;
+
+ // Are we the first GPE block?
+ if (block == ctx->gpe_head) {
+ ctx->gpe_head = ctx->gpe_head->next;
+ } else {
+ struct gpe_block *prev_block = ctx->gpe_head;
+
+ // We're not, do a search
+ while (prev_block) {
+ if (prev_block->next == block) {
+ prev_block->next = block->next;
+ break;
+ }
+
+ prev_block = prev_block->next;
+ }
+ }
+
+ // This GPE block was the last user of this interrupt context, remove it
+ if (ctx->gpe_head == UACPI_NULL) {
+ if (ctx->prev)
+ ctx->prev->next = ctx->next;
+
+ if (ctx->irq != g_uacpi_rt_ctx.fadt.sci_int) {
+ uacpi_kernel_uninstall_interrupt_handler(
+ handle_gpes, ctx->irq_handle
+ );
+ }
+
+ uacpi_free(block->irq_ctx, sizeof(*block->irq_ctx));
+ }
+ }
+
+ if (block->events != UACPI_NULL) {
+ uacpi_size i;
+ struct gp_event *event;
+
+ for (i = 0; i < block->num_events; ++i) {
+ event = &block->events[i];
+
+ switch (event->handler_type) {
+ case GPE_HANDLER_TYPE_NONE:
+ case GPE_HANDLER_TYPE_AML_HANDLER:
+ break;
+
+ case GPE_HANDLER_TYPE_NATIVE_HANDLER:
+ case GPE_HANDLER_TYPE_NATIVE_HANDLER_RAW:
+ uacpi_free(event->native_handler,
+ sizeof(*event->native_handler));
+ break;
+
+ case GPE_HANDLER_TYPE_IMPLICIT_NOTIFY: {
+ gpe_release_implicit_notify_handlers(event);
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+
+ }
+
+ uacpi_free(block->registers,
+ sizeof(*block->registers) * block->num_registers);
+ uacpi_free(block->events,
+ sizeof(*block->events) * block->num_events);
+ uacpi_free(block, sizeof(*block));
+}
+
+static struct gp_event *gpe_from_block(struct gpe_block *block, uacpi_u16 idx)
+{
+ uacpi_u16 offset;
+
+ if (idx < block->base_idx)
+ return UACPI_NULL;
+
+ offset = idx - block->base_idx;
+ if (offset > block->num_events)
+ return UACPI_NULL;
+
+ return &block->events[offset];
+}
+
+struct gpe_match_ctx {
+ struct gpe_block *block;
+ uacpi_u32 matched_count;
+ uacpi_bool post_dynamic_table_load;
+};
+
+static uacpi_iteration_decision do_match_gpe_methods(
+ uacpi_handle opaque, uacpi_namespace_node *node, uacpi_u32 depth
+)
+{
+ uacpi_status ret;
+ struct gpe_match_ctx *ctx = opaque;
+ struct gp_event *event;
+ uacpi_u8 triggering;
+ uacpi_u64 idx;
+
+ UACPI_UNUSED(depth);
+
+ if (node->name.text[0] != '_')
+ return UACPI_ITERATION_DECISION_CONTINUE;
+
+ switch (node->name.text[1]) {
+ case 'L':
+ triggering = UACPI_GPE_TRIGGERING_LEVEL;
+ break;
+ case 'E':
+ triggering = UACPI_GPE_TRIGGERING_EDGE;
+ break;
+ default:
+ return UACPI_ITERATION_DECISION_CONTINUE;
+ }
+
+ ret = uacpi_string_to_integer(&node->name.text[2], 2, UACPI_BASE_HEX, &idx);
+ if (uacpi_unlikely_error(ret)) {
+ uacpi_trace("invalid GPE method name %.4s, ignored\n", node->name.text);
+ return UACPI_ITERATION_DECISION_CONTINUE;
+ }
+
+ event = gpe_from_block(ctx->block, idx);
+ if (event == UACPI_NULL)
+ return UACPI_ITERATION_DECISION_CONTINUE;
+
+ switch (event->handler_type) {
+ /*
+ * This had implicit notify configured but this is no longer needed as we
+ * now have an actual AML handler. Free the implicit notify list and switch
+ * this handler to AML mode.
+ */
+ case GPE_HANDLER_TYPE_IMPLICIT_NOTIFY:
+ gpe_release_implicit_notify_handlers(event);
+ UACPI_FALLTHROUGH;
+ case GPE_HANDLER_TYPE_NONE:
+ event->aml_handler = node;
+ event->handler_type = GPE_HANDLER_TYPE_AML_HANDLER;
+ break;
+
+ case GPE_HANDLER_TYPE_AML_HANDLER:
+ // This is okay, since we're re-running the detection code
+ if (!ctx->post_dynamic_table_load) {
+ uacpi_warn(
+ "GPE(%02X) already matched %.4s, skipping %.4s\n",
+ (uacpi_u32)idx, event->aml_handler->name.text, node->name.text
+ );
+ }
+ return UACPI_ITERATION_DECISION_CONTINUE;
+
+ case GPE_HANDLER_TYPE_NATIVE_HANDLER:
+ case GPE_HANDLER_TYPE_NATIVE_HANDLER_RAW:
+ uacpi_trace(
+ "not assigning GPE(%02X) to %.4s, override "
+ "installed by user\n", (uacpi_u32)idx, node->name.text
+ );
+ UACPI_FALLTHROUGH;
+ default:
+ return UACPI_ITERATION_DECISION_CONTINUE;
+ }
+
+ uacpi_trace("assigned GPE(%02X) -> %.4s\n",
+ (uacpi_u32)idx, node->name.text);
+ event->triggering = triggering;
+ ctx->matched_count++;
+
+ return UACPI_ITERATION_DECISION_CONTINUE;
+}
+
+void uacpi_events_match_post_dynamic_table_load(void)
+{
+ struct gpe_match_ctx match_ctx = {
+ .post_dynamic_table_load = UACPI_TRUE,
+ };
+ struct gpe_interrupt_ctx *irq_ctx;
+
+ uacpi_namespace_write_unlock();
+
+ if (uacpi_unlikely_error(uacpi_recursive_lock_acquire(&g_event_lock)))
+ goto out;
+
+ irq_ctx = g_gpe_interrupt_head;
+
+ while (irq_ctx) {
+ match_ctx.block = irq_ctx->gpe_head;
+
+ while (match_ctx.block) {
+ uacpi_namespace_do_for_each_child(
+ match_ctx.block->device_node, do_match_gpe_methods, UACPI_NULL,
+ UACPI_OBJECT_METHOD_BIT, UACPI_MAX_DEPTH_ANY,
+ UACPI_SHOULD_LOCK_YES, UACPI_PERMANENT_ONLY_YES, &match_ctx
+ );
+ match_ctx.block = match_ctx.block->next;
+ }
+
+ irq_ctx = irq_ctx->next;
+ }
+
+ if (match_ctx.matched_count) {
+ uacpi_info("matched %u additional GPEs post dynamic table load\n",
+ match_ctx.matched_count);
+ }
+
+out:
+ uacpi_recursive_lock_release(&g_event_lock);
+ uacpi_namespace_write_lock();
+}
+
+static uacpi_status create_gpe_block(
+ uacpi_namespace_node *device_node, uacpi_u32 irq, uacpi_u16 base_idx,
+ uacpi_u64 address, uacpi_u8 address_space_id, uacpi_u16 num_registers
+)
+{
+ uacpi_status ret = UACPI_STATUS_OUT_OF_MEMORY;
+ struct gpe_match_ctx match_ctx = { 0 };
+ struct gpe_block *block;
+ struct gpe_register *reg;
+ struct gp_event *event;
+ struct acpi_gas tmp_gas = { 0 };
+ uacpi_size i, j;
+
+ tmp_gas.address_space_id = address_space_id;
+ tmp_gas.register_bit_width = 8;
+
+ block = uacpi_kernel_alloc_zeroed(sizeof(*block));
+ if (uacpi_unlikely(block == UACPI_NULL))
+ return ret;
+
+ block->device_node = device_node;
+ block->base_idx = base_idx;
+
+ block->num_registers = num_registers;
+ block->registers = uacpi_kernel_alloc_zeroed(
+ num_registers * sizeof(*block->registers)
+ );
+ if (uacpi_unlikely(block->registers == UACPI_NULL))
+ goto error_out;
+
+ block->num_events = num_registers * EVENTS_PER_GPE_REGISTER;
+ block->events = uacpi_kernel_alloc_zeroed(
+ block->num_events * sizeof(*block->events)
+ );
+ if (uacpi_unlikely(block->events == UACPI_NULL))
+ goto error_out;
+
+ for (reg = block->registers, event = block->events, i = 0;
+ i < num_registers; ++i, ++reg) {
+
+ /*
+ * Initialize this register pair as well as all the events within it.
+ *
+ * Each register has two sub registers: status & enable, 8 bits each.
+ * Each bit corresponds to one event that we initialize below.
+ */
+ reg->base_idx = base_idx + (i * EVENTS_PER_GPE_REGISTER);
+
+
+ tmp_gas.address = address + i;
+ ret = uacpi_map_gas_noalloc(&tmp_gas, &reg->status);
+ if (uacpi_unlikely_error(ret))
+ goto error_out;
+
+ tmp_gas.address += num_registers;
+ ret = uacpi_map_gas_noalloc(&tmp_gas, &reg->enable);
+ if (uacpi_unlikely_error(ret))
+ goto error_out;
+
+ for (j = 0; j < EVENTS_PER_GPE_REGISTER; ++j, ++event) {
+ event->idx = reg->base_idx + j;
+ event->reg = reg;
+ }
+
+ /*
+ * Disable all GPEs in this register & clear anything that might be
+ * pending from earlier.
+ */
+ ret = uacpi_gas_write_mapped(&reg->enable, 0x00);
+ if (uacpi_unlikely_error(ret))
+ goto error_out;
+
+ ret = uacpi_gas_write_mapped(&reg->status, 0xFF);
+ if (uacpi_unlikely_error(ret))
+ goto error_out;
+ }
+
+ ret = find_or_create_gpe_interrupt_ctx(irq, &block->irq_ctx);
+ if (uacpi_unlikely_error(ret))
+ goto error_out;
+
+ block->next = block->irq_ctx->gpe_head;
+ block->irq_ctx->gpe_head = block;
+ match_ctx.block = block;
+
+ uacpi_namespace_do_for_each_child(
+ device_node, do_match_gpe_methods, UACPI_NULL,
+ UACPI_OBJECT_METHOD_BIT, UACPI_MAX_DEPTH_ANY,
+ UACPI_SHOULD_LOCK_YES, UACPI_PERMANENT_ONLY_YES, &match_ctx
+ );
+
+ uacpi_trace("initialized GPE block %.4s[%d->%d], %d AML handlers (IRQ %d)\n",
+ device_node->name.text, base_idx, base_idx + block->num_events,
+ match_ctx.matched_count, irq);
+ return UACPI_STATUS_OK;
+
+error_out:
+ uninstall_gpe_block(block);
+ return ret;
+}
+
+typedef uacpi_iteration_decision (*gpe_block_iteration_callback)
+ (struct gpe_block*, uacpi_handle);
+
+static void for_each_gpe_block(
+ gpe_block_iteration_callback cb, uacpi_handle handle
+)
+{
+ uacpi_iteration_decision decision;
+ struct gpe_interrupt_ctx *irq_ctx = g_gpe_interrupt_head;
+ struct gpe_block *block;
+
+ while (irq_ctx) {
+ block = irq_ctx->gpe_head;
+
+ while (block) {
+ decision = cb(block, handle);
+ if (decision == UACPI_ITERATION_DECISION_BREAK)
+ return;
+
+ block = block->next;
+ }
+
+ irq_ctx = irq_ctx->next;
+ }
+}
+
+struct gpe_search_ctx {
+ uacpi_namespace_node *gpe_device;
+ uacpi_u16 idx;
+ struct gpe_block *out_block;
+ struct gp_event *out_event;
+};
+
+static uacpi_iteration_decision do_find_gpe(
+ struct gpe_block *block, uacpi_handle opaque
+)
+{
+ struct gpe_search_ctx *ctx = opaque;
+
+ if (block->device_node != ctx->gpe_device)
+ return UACPI_ITERATION_DECISION_CONTINUE;
+
+ ctx->out_block = block;
+ ctx->out_event = gpe_from_block(block, ctx->idx);
+ if (ctx->out_event == UACPI_NULL)
+ return UACPI_ITERATION_DECISION_CONTINUE;
+
+ return UACPI_ITERATION_DECISION_BREAK;
+}
+
+static struct gp_event *get_gpe(
+ uacpi_namespace_node *gpe_device, uacpi_u16 idx
+)
+{
+ struct gpe_search_ctx ctx = { 0 };
+
+ ctx.gpe_device = gpe_device;
+ ctx.idx = idx;
+
+ for_each_gpe_block(do_find_gpe, &ctx);
+ return ctx.out_event;
+}
+
+static void gp_event_toggle_masks(struct gp_event *event, uacpi_bool set_on)
+{
+ uacpi_u8 this_mask;
+ struct gpe_register *reg = event->reg;
+
+ this_mask = gpe_get_mask(event);
+
+ if (set_on) {
+ reg->runtime_mask |= this_mask;
+ reg->current_mask = reg->runtime_mask;
+ return;
+ }
+
+ reg->runtime_mask &= ~this_mask;
+ reg->current_mask = reg->runtime_mask;
+}
+
+static uacpi_status gpe_remove_user(struct gp_event *event)
+{
+ uacpi_status ret = UACPI_STATUS_OK;
+
+ if (uacpi_unlikely(event->num_users == 0))
+ return UACPI_STATUS_INVALID_ARGUMENT;
+
+ if (--event->num_users == 0) {
+ gp_event_toggle_masks(event, UACPI_FALSE);
+
+ ret = set_gpe_state(event, GPE_STATE_DISABLED);
+ if (uacpi_unlikely_error(ret)) {
+ gp_event_toggle_masks(event, UACPI_TRUE);
+ event->num_users++;
+ }
+ }
+
+ return ret;
+}
+
+enum event_clear_if_first {
+ EVENT_CLEAR_IF_FIRST_YES,
+ EVENT_CLEAR_IF_FIRST_NO,
+};
+
+static uacpi_status gpe_add_user(
+ struct gp_event *event, enum event_clear_if_first clear_if_first
+)
+{
+ uacpi_status ret = UACPI_STATUS_OK;
+
+ if (uacpi_unlikely(event->num_users == 0xFF))
+ return UACPI_STATUS_INVALID_ARGUMENT;
+
+ if (++event->num_users == 1) {
+ if (clear_if_first == EVENT_CLEAR_IF_FIRST_YES)
+ clear_gpe(event);
+
+ gp_event_toggle_masks(event, UACPI_TRUE);
+
+ ret = set_gpe_state(event, GPE_STATE_ENABLED);
+ if (uacpi_unlikely_error(ret)) {
+ gp_event_toggle_masks(event, UACPI_FALSE);
+ event->num_users--;
+ }
+ }
+
+ return ret;
+}
+
+const uacpi_char *uacpi_gpe_triggering_to_string(
+ uacpi_gpe_triggering triggering
+)
+{
+ switch (triggering) {
+ case UACPI_GPE_TRIGGERING_EDGE:
+ return "edge";
+ case UACPI_GPE_TRIGGERING_LEVEL:
+ return "level";
+ default:
+ return "invalid";
+ }
+}
+
+static uacpi_bool gpe_needs_polling(struct gp_event *event)
+{
+ return event->num_users && event->triggering == UACPI_GPE_TRIGGERING_EDGE;
+}
+
+static uacpi_status gpe_mask_unmask(
+ struct gp_event *event, uacpi_bool should_mask
+)
+{
+ struct gpe_register *reg;
+ uacpi_u8 mask;
+
+ reg = event->reg;
+ mask = gpe_get_mask(event);
+
+ if (should_mask) {
+ if (reg->masked_mask & mask)
+ return UACPI_STATUS_INVALID_ARGUMENT;
+
+ // 1. Mask the GPE, this makes sure its state is no longer modifyable
+ reg->masked_mask |= mask;
+
+ /*
+ * 2. Wait for in-flight work & IRQs to finish, these might already
+ * be past the respective "if (masked)" check and therefore may
+ * try to re-enable a masked GPE.
+ */
+ uacpi_kernel_wait_for_work_completion();
+
+ /*
+ * 3. Now that this GPE's state is unmodifyable and we know that currently
+ * in-flight IRQs will see the masked state, we can safely disable this
+ * event knowing it won't be re-enabled by a racing IRQ.
+ */
+ set_gpe_state(event, GPE_STATE_DISABLED);
+
+ /*
+ * 4. Wait for the last possible IRQ to finish, now that this event is
+ * disabled.
+ */
+ uacpi_kernel_wait_for_work_completion();
+
+ return UACPI_STATUS_OK;
+ }
+
+ if (!(reg->masked_mask & mask))
+ return UACPI_STATUS_INVALID_ARGUMENT;
+
+ reg->masked_mask &= ~mask;
+ if (!event->block_interrupts && event->num_users)
+ set_gpe_state(event, GPE_STATE_ENABLED_CONDITIONALLY);
+
+ return UACPI_STATUS_OK;
+}
+
+/*
+ * Safely mask the event before we modify its handlers.
+ *
+ * This makes sure we can't get an IRQ in the middle of modifying this
+ * event's structures.
+ */
+static uacpi_bool gpe_mask_safe(struct gp_event *event)
+{
+ // No need to flush or do anything if it's not currently enabled
+ if (!(event->reg->current_mask & gpe_get_mask(event)))
+ return UACPI_FALSE;
+
+ gpe_mask_unmask(event, UACPI_TRUE);
+ return UACPI_TRUE;
+}
+
+static uacpi_iteration_decision do_initialize_gpe_block(
+ struct gpe_block *block, uacpi_handle opaque
+)
+{
+ uacpi_status ret;
+ uacpi_bool *poll_blocks = opaque;
+ uacpi_size i, j, count_enabled = 0;
+ struct gp_event *event;
+
+ for (i = 0; i < block->num_registers; ++i) {
+ for (j = 0; j < EVENTS_PER_GPE_REGISTER; ++j) {
+ event = &block->events[j + i * EVENTS_PER_GPE_REGISTER];
+
+ if (event->wake ||
+ event->handler_type != GPE_HANDLER_TYPE_AML_HANDLER)
+ continue;
+
+ ret = gpe_add_user(event, EVENT_CLEAR_IF_FIRST_NO);
+ if (uacpi_unlikely_error(ret)) {
+ uacpi_warn("failed to enable GPE(%02X): %s\n",
+ event->idx, uacpi_status_to_string(ret));
+ continue;
+ }
+
+ *poll_blocks |= gpe_needs_polling(event);
+ count_enabled++;
+ }
+ }
+
+ if (count_enabled) {
+ uacpi_info(
+ "enabled %zu GPEs in block %.4s@[%d->%d]\n",
+ count_enabled, block->device_node->name.text,
+ block->base_idx, block->base_idx + block->num_events
+ );
+ }
+ return UACPI_ITERATION_DECISION_CONTINUE;
+}
+
+uacpi_status uacpi_finalize_gpe_initialization(void)
+{
+ uacpi_status ret;
+ uacpi_bool poll_blocks = UACPI_FALSE;
+
+ UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_NAMESPACE_LOADED);
+
+ ret = uacpi_recursive_lock_acquire(&g_event_lock);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ if (g_gpes_finalized)
+ goto out;
+
+ g_gpes_finalized = UACPI_TRUE;
+
+ for_each_gpe_block(do_initialize_gpe_block, &poll_blocks);
+ if (poll_blocks)
+ detect_gpes(g_gpe_interrupt_head->gpe_head);
+
+out:
+ uacpi_recursive_lock_release(&g_event_lock);
+ return ret;
+}
+
+static uacpi_status sanitize_device_and_find_gpe(
+ uacpi_namespace_node **gpe_device, uacpi_u16 idx,
+ struct gp_event **out_event
+)
+{
+ if (*gpe_device == UACPI_NULL) {
+ *gpe_device = uacpi_namespace_get_predefined(
+ UACPI_PREDEFINED_NAMESPACE_GPE
+ );
+ }
+
+ *out_event = get_gpe(*gpe_device, idx);
+ if (*out_event == UACPI_NULL)
+ return UACPI_STATUS_NOT_FOUND;
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status do_install_gpe_handler(
+ uacpi_namespace_node *gpe_device, uacpi_u16 idx,
+ uacpi_gpe_triggering triggering, enum gpe_handler_type type,
+ uacpi_gpe_handler handler, uacpi_handle ctx
+)
+{
+ uacpi_status ret;
+ struct gp_event *event;
+ struct gpe_native_handler *native_handler;
+ uacpi_bool did_mask;
+
+ UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_NAMESPACE_LOADED);
+
+ if (uacpi_unlikely(triggering > UACPI_GPE_TRIGGERING_MAX))
+ return UACPI_STATUS_INVALID_ARGUMENT;
+
+ ret = uacpi_recursive_lock_acquire(&g_event_lock);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ ret = sanitize_device_and_find_gpe(&gpe_device, idx, &event);
+ if (uacpi_unlikely_error(ret))
+ goto out;
+
+ if (event->handler_type == GPE_HANDLER_TYPE_NATIVE_HANDLER ||
+ event->handler_type == GPE_HANDLER_TYPE_NATIVE_HANDLER_RAW) {
+ ret = UACPI_STATUS_ALREADY_EXISTS;
+ goto out;
+ }
+
+ native_handler = uacpi_kernel_alloc(sizeof(*native_handler));
+ if (uacpi_unlikely(native_handler == UACPI_NULL)) {
+ ret = UACPI_STATUS_OUT_OF_MEMORY;
+ goto out;
+ }
+
+ native_handler->cb = handler;
+ native_handler->ctx = ctx;
+ native_handler->previous_handler = event->any_handler;
+ native_handler->previous_handler_type = event->handler_type;
+ native_handler->previous_triggering = event->triggering;
+ native_handler->previously_enabled = UACPI_FALSE;
+
+ did_mask = gpe_mask_safe(event);
+
+ if ((event->handler_type == GPE_HANDLER_TYPE_AML_HANDLER ||
+ event->handler_type == GPE_HANDLER_TYPE_IMPLICIT_NOTIFY) &&
+ event->num_users != 0) {
+ native_handler->previously_enabled = UACPI_TRUE;
+ gpe_remove_user(event);
+
+ if (uacpi_unlikely(event->triggering != triggering)) {
+ uacpi_warn(
+ "GPE(%02X) user handler claims %s triggering, originally "
+ "configured as %s\n", idx,
+ uacpi_gpe_triggering_to_string(triggering),
+ uacpi_gpe_triggering_to_string(event->triggering)
+ );
+ }
+ }
+
+ event->native_handler = native_handler;
+ event->handler_type = type;
+ event->triggering = triggering;
+
+ if (did_mask)
+ gpe_mask_unmask(event, UACPI_FALSE);
+out:
+ uacpi_recursive_lock_release(&g_event_lock);
+ return ret;
+}
+
+uacpi_status uacpi_install_gpe_handler(
+ uacpi_namespace_node *gpe_device, uacpi_u16 idx,
+ uacpi_gpe_triggering triggering, uacpi_gpe_handler handler,
+ uacpi_handle ctx
+)
+{
+ return do_install_gpe_handler(
+ gpe_device, idx, triggering, GPE_HANDLER_TYPE_NATIVE_HANDLER,
+ handler, ctx
+ );
+}
+
+uacpi_status uacpi_install_gpe_handler_raw(
+ uacpi_namespace_node *gpe_device, uacpi_u16 idx,
+ uacpi_gpe_triggering triggering, uacpi_gpe_handler handler,
+ uacpi_handle ctx
+)
+{
+ return do_install_gpe_handler(
+ gpe_device, idx, triggering, GPE_HANDLER_TYPE_NATIVE_HANDLER_RAW,
+ handler, ctx
+ );
+}
+
+uacpi_status uacpi_uninstall_gpe_handler(
+ uacpi_namespace_node *gpe_device, uacpi_u16 idx,
+ uacpi_gpe_handler handler
+)
+{
+ uacpi_status ret;
+ struct gp_event *event;
+ struct gpe_native_handler *native_handler;
+ uacpi_bool did_mask;
+
+ UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_NAMESPACE_LOADED);
+
+ ret = uacpi_recursive_lock_acquire(&g_event_lock);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ ret = sanitize_device_and_find_gpe(&gpe_device, idx, &event);
+ if (uacpi_unlikely_error(ret))
+ goto out;
+
+ if (event->handler_type != GPE_HANDLER_TYPE_NATIVE_HANDLER &&
+ event->handler_type != GPE_HANDLER_TYPE_NATIVE_HANDLER_RAW) {
+ ret = UACPI_STATUS_NOT_FOUND;
+ goto out;
+ }
+
+ native_handler = event->native_handler;
+ if (uacpi_unlikely(native_handler->cb != handler)) {
+ ret = UACPI_STATUS_INVALID_ARGUMENT;
+ goto out;
+ }
+
+ did_mask = gpe_mask_safe(event);
+
+ event->aml_handler = native_handler->previous_handler;
+ event->triggering = native_handler->previous_triggering;
+ event->handler_type = native_handler->previous_handler_type;
+
+ if ((event->handler_type == GPE_HANDLER_TYPE_AML_HANDLER ||
+ event->handler_type == GPE_HANDLER_TYPE_IMPLICIT_NOTIFY) &&
+ native_handler->previously_enabled) {
+ gpe_add_user(event, EVENT_CLEAR_IF_FIRST_NO);
+ }
+
+ uacpi_free(native_handler, sizeof(*native_handler));
+
+ if (did_mask)
+ gpe_mask_unmask(event, UACPI_FALSE);
+
+ if (gpe_needs_polling(event))
+ maybe_dispatch_gpe(gpe_device, event);
+out:
+ uacpi_recursive_lock_release(&g_event_lock);
+ return ret;
+}
+
+uacpi_status uacpi_enable_gpe(
+ uacpi_namespace_node *gpe_device, uacpi_u16 idx
+)
+{
+ uacpi_status ret;
+ struct gp_event *event;
+
+ UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_NAMESPACE_LOADED);
+
+ ret = uacpi_recursive_lock_acquire(&g_event_lock);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ ret = sanitize_device_and_find_gpe(&gpe_device, idx, &event);
+ if (uacpi_unlikely_error(ret))
+ goto out;
+
+ if (uacpi_unlikely(event->handler_type == GPE_HANDLER_TYPE_NONE)) {
+ ret = UACPI_STATUS_NO_HANDLER;
+ goto out;
+ }
+
+ ret = gpe_add_user(event, EVENT_CLEAR_IF_FIRST_YES);
+ if (uacpi_unlikely_error(ret))
+ goto out;
+
+ if (gpe_needs_polling(event))
+ maybe_dispatch_gpe(gpe_device, event);
+
+out:
+ uacpi_recursive_lock_release(&g_event_lock);
+ return ret;
+}
+
+uacpi_status uacpi_disable_gpe(
+ uacpi_namespace_node *gpe_device, uacpi_u16 idx
+)
+{
+ uacpi_status ret;
+ struct gp_event *event;
+
+ UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_NAMESPACE_LOADED);
+
+ ret = uacpi_recursive_lock_acquire(&g_event_lock);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ ret = sanitize_device_and_find_gpe(&gpe_device, idx, &event);
+ if (uacpi_unlikely_error(ret))
+ goto out;
+
+ ret = gpe_remove_user(event);
+out:
+ uacpi_recursive_lock_release(&g_event_lock);
+ return ret;
+}
+
+uacpi_status uacpi_clear_gpe(
+ uacpi_namespace_node *gpe_device, uacpi_u16 idx
+)
+{
+ uacpi_status ret;
+ struct gp_event *event;
+
+ UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_NAMESPACE_LOADED);
+
+ ret = uacpi_recursive_lock_acquire(&g_event_lock);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ ret = sanitize_device_and_find_gpe(&gpe_device, idx, &event);
+ if (uacpi_unlikely_error(ret))
+ goto out;
+
+ ret = clear_gpe(event);
+out:
+ uacpi_recursive_lock_release(&g_event_lock);
+ return ret;
+}
+
+static uacpi_status gpe_suspend_resume(
+ uacpi_namespace_node *gpe_device, uacpi_u16 idx, enum gpe_state state
+)
+{
+ uacpi_status ret;
+ struct gp_event *event;
+
+ UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_NAMESPACE_LOADED);
+
+ ret = uacpi_recursive_lock_acquire(&g_event_lock);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ ret = sanitize_device_and_find_gpe(&gpe_device, idx, &event);
+ if (uacpi_unlikely_error(ret))
+ goto out;
+
+ event->block_interrupts = state == GPE_STATE_DISABLED;
+ ret = set_gpe_state(event, state);
+out:
+ uacpi_recursive_lock_release(&g_event_lock);
+ return ret;
+}
+
+uacpi_status uacpi_suspend_gpe(
+ uacpi_namespace_node *gpe_device, uacpi_u16 idx
+)
+{
+ return gpe_suspend_resume(gpe_device, idx, GPE_STATE_DISABLED);
+}
+
+uacpi_status uacpi_resume_gpe(
+ uacpi_namespace_node *gpe_device, uacpi_u16 idx
+)
+{
+ return gpe_suspend_resume(gpe_device, idx, GPE_STATE_ENABLED);
+}
+
+uacpi_status uacpi_finish_handling_gpe(
+ uacpi_namespace_node *gpe_device, uacpi_u16 idx
+)
+{
+ uacpi_status ret;
+ struct gp_event *event;
+
+ UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_NAMESPACE_LOADED);
+
+ ret = uacpi_recursive_lock_acquire(&g_event_lock);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ ret = sanitize_device_and_find_gpe(&gpe_device, idx, &event);
+ if (uacpi_unlikely_error(ret))
+ goto out;
+
+ event = get_gpe(gpe_device, idx);
+ if (uacpi_unlikely(event == UACPI_NULL)) {
+ ret = UACPI_STATUS_NOT_FOUND;
+ goto out;
+ }
+
+ ret = restore_gpe(event);
+out:
+ uacpi_recursive_lock_release(&g_event_lock);
+ return ret;
+
+}
+
+static uacpi_status gpe_get_mask_unmask(
+ uacpi_namespace_node *gpe_device, uacpi_u16 idx, uacpi_bool should_mask
+)
+{
+ uacpi_status ret;
+ struct gp_event *event;
+
+ UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_NAMESPACE_LOADED);
+
+ ret = uacpi_recursive_lock_acquire(&g_event_lock);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ ret = sanitize_device_and_find_gpe(&gpe_device, idx, &event);
+ if (uacpi_unlikely_error(ret))
+ goto out;
+
+ ret = gpe_mask_unmask(event, should_mask);
+
+out:
+ uacpi_recursive_lock_release(&g_event_lock);
+ return ret;
+}
+
+uacpi_status uacpi_mask_gpe(
+ uacpi_namespace_node *gpe_device, uacpi_u16 idx
+)
+{
+ return gpe_get_mask_unmask(gpe_device, idx, UACPI_TRUE);
+}
+
+uacpi_status uacpi_unmask_gpe(
+ uacpi_namespace_node *gpe_device, uacpi_u16 idx
+)
+{
+ return gpe_get_mask_unmask(gpe_device, idx, UACPI_FALSE);
+}
+
+uacpi_status uacpi_setup_gpe_for_wake(
+ uacpi_namespace_node *gpe_device, uacpi_u16 idx,
+ uacpi_namespace_node *wake_device
+)
+{
+ uacpi_status ret;
+ struct gp_event *event;
+ uacpi_bool did_mask;
+
+ UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_NAMESPACE_LOADED);
+
+ if (wake_device != UACPI_NULL) {
+ uacpi_bool is_dev = wake_device == uacpi_namespace_root();
+
+ if (!is_dev) {
+ ret = uacpi_namespace_node_is(wake_device, UACPI_OBJECT_DEVICE, &is_dev);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+ }
+
+ if (!is_dev)
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+
+ ret = uacpi_recursive_lock_acquire(&g_event_lock);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ ret = sanitize_device_and_find_gpe(&gpe_device, idx, &event);
+ if (uacpi_unlikely_error(ret))
+ goto out;
+
+ did_mask = gpe_mask_safe(event);
+
+ if (wake_device != UACPI_NULL) {
+ switch (event->handler_type) {
+ case GPE_HANDLER_TYPE_NONE:
+ event->handler_type = GPE_HANDLER_TYPE_IMPLICIT_NOTIFY;
+ event->triggering = UACPI_GPE_TRIGGERING_LEVEL;
+ break;
+
+ case GPE_HANDLER_TYPE_AML_HANDLER:
+ /*
+ * An AML handler already exists, we expect it to call Notify() as
+ * it sees fit. For now just make sure this event is disabled if it
+ * had been enabled automatically previously during initialization.
+ */
+ gpe_remove_user(event);
+ break;
+
+ case GPE_HANDLER_TYPE_NATIVE_HANDLER_RAW:
+ case GPE_HANDLER_TYPE_NATIVE_HANDLER:
+ uacpi_warn(
+ "not configuring implicit notify for GPE(%02X) -> %.4s: "
+ " a user handler already installed\n", event->idx,
+ wake_device->name.text
+ );
+ break;
+
+ // We will re-check this below
+ case GPE_HANDLER_TYPE_IMPLICIT_NOTIFY:
+ break;
+
+ default:
+ uacpi_warn("invalid GPE(%02X) handler type: %d\n",
+ event->idx, event->handler_type);
+ ret = UACPI_STATUS_INTERNAL_ERROR;
+ goto out_unmask;
+ }
+
+ /*
+ * This GPE has no known AML handler, so we configure it to receive
+ * implicit notifications for wake devices when we get a corresponding
+ * GPE triggered. Usually it's the job of a matching AML handler, but
+ * we didn't find any.
+ */
+ if (event->handler_type == GPE_HANDLER_TYPE_IMPLICIT_NOTIFY) {
+ struct gpe_implicit_notify_handler *implicit_handler;
+
+ implicit_handler = event->implicit_handler;
+ while (implicit_handler) {
+ if (implicit_handler->device == wake_device) {
+ ret = UACPI_STATUS_ALREADY_EXISTS;
+ goto out_unmask;
+ }
+
+ implicit_handler = implicit_handler->next;
+ }
+
+ implicit_handler = uacpi_kernel_alloc(sizeof(*implicit_handler));
+ if (uacpi_likely(implicit_handler != UACPI_NULL)) {
+ implicit_handler->device = wake_device;
+ implicit_handler->next = event->implicit_handler;
+ event->implicit_handler = implicit_handler;
+ } else {
+ uacpi_warn(
+ "unable to configure implicit wake for GPE(%02X) -> %.4s: "
+ "out of memory\n", event->idx, wake_device->name.text
+ );
+ }
+ }
+ }
+
+ event->wake = UACPI_TRUE;
+
+out_unmask:
+ if (did_mask)
+ gpe_mask_unmask(event, UACPI_FALSE);
+out:
+ uacpi_recursive_lock_release(&g_event_lock);
+ return ret;
+}
+
+static uacpi_status gpe_enable_disable_for_wake(
+ uacpi_namespace_node *gpe_device, uacpi_u16 idx, uacpi_bool enabled
+)
+{
+ uacpi_status ret;
+ struct gp_event *event;
+ struct gpe_register *reg;
+ uacpi_u8 mask;
+
+ UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_NAMESPACE_LOADED);
+
+ ret = uacpi_recursive_lock_acquire(&g_event_lock);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ ret = sanitize_device_and_find_gpe(&gpe_device, idx, &event);
+ if (uacpi_unlikely_error(ret))
+ goto out;
+
+ if (!event->wake) {
+ ret = UACPI_STATUS_INVALID_ARGUMENT;
+ goto out;
+ }
+
+ reg = event->reg;
+ mask = gpe_get_mask(event);
+
+ if (enabled)
+ reg->wake_mask |= mask;
+ else
+ reg->wake_mask &= mask;
+
+out:
+ uacpi_recursive_lock_release(&g_event_lock);
+ return ret;
+}
+
+uacpi_status uacpi_enable_gpe_for_wake(
+ uacpi_namespace_node *gpe_device, uacpi_u16 idx
+)
+{
+ return gpe_enable_disable_for_wake(gpe_device, idx, UACPI_TRUE);
+}
+
+uacpi_status uacpi_disable_gpe_for_wake(
+ uacpi_namespace_node *gpe_device, uacpi_u16 idx
+)
+{
+ return gpe_enable_disable_for_wake(gpe_device, idx, UACPI_FALSE);
+}
+
+struct do_for_all_gpes_ctx {
+ enum gpe_block_action action;
+ uacpi_status ret;
+};
+
+static uacpi_iteration_decision do_for_all_gpes(
+ struct gpe_block *block, uacpi_handle opaque
+)
+{
+ struct do_for_all_gpes_ctx *ctx = opaque;
+
+ ctx->ret = gpe_block_apply_action(block, ctx->action);
+ if (uacpi_unlikely_error(ctx->ret))
+ return UACPI_ITERATION_DECISION_BREAK;
+
+ return UACPI_ITERATION_DECISION_CONTINUE;
+}
+
+static uacpi_status for_all_gpes_locked(struct do_for_all_gpes_ctx *ctx)
+{
+ uacpi_status ret;
+
+ UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_NAMESPACE_LOADED);
+
+ ret = uacpi_recursive_lock_acquire(&g_event_lock);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ for_each_gpe_block(do_for_all_gpes, ctx);
+
+ uacpi_recursive_lock_release(&g_event_lock);
+ return ctx->ret;
+}
+
+uacpi_status uacpi_disable_all_gpes(void)
+{
+ struct do_for_all_gpes_ctx ctx = {
+ .action = GPE_BLOCK_ACTION_DISABLE_ALL,
+ };
+ return for_all_gpes_locked(&ctx);
+}
+
+uacpi_status uacpi_enable_all_runtime_gpes(void)
+{
+ struct do_for_all_gpes_ctx ctx = {
+ .action = GPE_BLOCK_ACTION_ENABLE_ALL_FOR_RUNTIME,
+ };
+ return for_all_gpes_locked(&ctx);
+}
+
+uacpi_status uacpi_enable_all_wake_gpes(void)
+{
+ struct do_for_all_gpes_ctx ctx = {
+ .action = GPE_BLOCK_ACTION_ENABLE_ALL_FOR_WAKE,
+ };
+ return for_all_gpes_locked(&ctx);
+}
+
+static uacpi_status initialize_gpes(void)
+{
+ uacpi_status ret;
+ uacpi_namespace_node *gpe_node;
+ struct acpi_fadt *fadt = &g_uacpi_rt_ctx.fadt;
+ uacpi_u8 gpe0_regs = 0, gpe1_regs = 0;
+
+ gpe_node = uacpi_namespace_get_predefined(UACPI_PREDEFINED_NAMESPACE_GPE);
+
+ if (fadt->x_gpe0_blk.address && fadt->gpe0_blk_len) {
+ gpe0_regs = fadt->gpe0_blk_len / 2;
+
+ ret = create_gpe_block(
+ gpe_node, fadt->sci_int, 0, fadt->x_gpe0_blk.address,
+ fadt->x_gpe0_blk.address_space_id, gpe0_regs
+ );
+ if (uacpi_unlikely_error(ret)) {
+ uacpi_error("unable to create FADT GPE block 0: %s\n",
+ uacpi_status_to_string(ret));
+ }
+ }
+
+ if (fadt->x_gpe1_blk.address && fadt->gpe1_blk_len) {
+ gpe1_regs = fadt->gpe1_blk_len / 2;
+
+ if (uacpi_unlikely((gpe0_regs * EVENTS_PER_GPE_REGISTER) >
+ fadt->gpe1_base)) {
+ uacpi_error(
+ "FADT GPE block 1 [%d->%d] collides with GPE block 0 "
+ "[%d->%d], ignoring\n",
+ 0, gpe0_regs * EVENTS_PER_GPE_REGISTER, fadt->gpe1_base,
+ gpe1_regs * EVENTS_PER_GPE_REGISTER
+ );
+ gpe1_regs = 0;
+ goto out;
+ }
+
+ ret = create_gpe_block(
+ gpe_node, fadt->sci_int, fadt->gpe1_base, fadt->x_gpe1_blk.address,
+ fadt->x_gpe1_blk.address_space_id, gpe1_regs
+ );
+ if (uacpi_unlikely_error(ret)) {
+ uacpi_error("unable to create FADT GPE block 1: %s\n",
+ uacpi_status_to_string(ret));
+ }
+ }
+
+ if (gpe0_regs == 0 && gpe1_regs == 0)
+ uacpi_trace("platform has no FADT GPE events\n");
+
+out:
+ return UACPI_STATUS_OK;
+}
+
+uacpi_status uacpi_install_gpe_block(
+ uacpi_namespace_node *gpe_device, uacpi_u64 address,
+ uacpi_address_space address_space, uacpi_u16 num_registers, uacpi_u32 irq
+)
+{
+ uacpi_status ret;
+ uacpi_bool is_dev;
+
+ UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_NAMESPACE_LOADED);
+
+ ret = uacpi_namespace_node_is(gpe_device, UACPI_OBJECT_DEVICE, &is_dev);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+ if (!is_dev)
+ return UACPI_STATUS_INVALID_ARGUMENT;
+
+ ret = uacpi_recursive_lock_acquire(&g_event_lock);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ if (uacpi_unlikely(get_gpe(gpe_device, 0) != UACPI_NULL)) {
+ ret = UACPI_STATUS_ALREADY_EXISTS;
+ goto out;
+ }
+
+ ret = create_gpe_block(
+ gpe_device, irq, 0, address, address_space, num_registers
+ );
+
+out:
+ uacpi_recursive_lock_release(&g_event_lock);
+ return ret;
+}
+
+uacpi_status uacpi_uninstall_gpe_block(
+ uacpi_namespace_node *gpe_device
+)
+{
+ uacpi_status ret;
+ uacpi_bool is_dev;
+ struct gpe_search_ctx search_ctx = { 0 };
+
+ search_ctx.idx = 0;
+ search_ctx.gpe_device = gpe_device;
+
+ UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_NAMESPACE_LOADED);
+
+ ret = uacpi_namespace_node_is(gpe_device, UACPI_OBJECT_DEVICE, &is_dev);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+ if (!is_dev)
+ return UACPI_STATUS_INVALID_ARGUMENT;
+
+ ret = uacpi_recursive_lock_acquire(&g_event_lock);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ for_each_gpe_block(do_find_gpe, &search_ctx);
+ if (search_ctx.out_block == UACPI_NULL) {
+ ret = UACPI_STATUS_NOT_FOUND;
+ goto out;
+ }
+
+ uninstall_gpe_block(search_ctx.out_block);
+
+out:
+ uacpi_recursive_lock_release(&g_event_lock);
+ return ret;
+}
+
+static uacpi_interrupt_ret handle_global_lock(uacpi_handle ctx)
+{
+ uacpi_cpu_flags flags;
+ UACPI_UNUSED(ctx);
+
+ if (uacpi_unlikely(!g_uacpi_rt_ctx.has_global_lock)) {
+ uacpi_warn("platform has no global lock but a release event "
+ "was fired anyway?\n");
+ return UACPI_INTERRUPT_HANDLED;
+ }
+
+ flags = uacpi_kernel_lock_spinlock(g_uacpi_rt_ctx.global_lock_spinlock);
+ if (!g_uacpi_rt_ctx.global_lock_pending) {
+ uacpi_trace("spurious firmware global lock release notification\n");
+ goto out;
+ }
+
+ uacpi_trace("received a firmware global lock release notification\n");
+
+ uacpi_kernel_signal_event(g_uacpi_rt_ctx.global_lock_event);
+ g_uacpi_rt_ctx.global_lock_pending = UACPI_FALSE;
+
+out:
+ uacpi_kernel_unlock_spinlock(g_uacpi_rt_ctx.global_lock_spinlock, flags);
+ return UACPI_INTERRUPT_HANDLED;
+}
+
+static uacpi_interrupt_ret handle_sci(uacpi_handle ctx)
+{
+ uacpi_interrupt_ret int_ret = UACPI_INTERRUPT_NOT_HANDLED;
+
+ int_ret |= handle_fixed_events();
+ int_ret |= handle_gpes(ctx);
+
+ return int_ret;
+}
+
+uacpi_status uacpi_initialize_events_early(void)
+{
+ uacpi_status ret;
+
+ if (uacpi_is_hardware_reduced())
+ return UACPI_STATUS_OK;
+
+ g_gpe_state_slock = uacpi_kernel_create_spinlock();
+ if (uacpi_unlikely(g_gpe_state_slock == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ ret = uacpi_recursive_lock_init(&g_event_lock);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ ret = initialize_fixed_events();
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ return UACPI_STATUS_OK;
+}
+
+uacpi_status uacpi_initialize_events(void)
+{
+ uacpi_status ret;
+
+ if (uacpi_is_hardware_reduced())
+ return UACPI_STATUS_OK;
+
+ ret = initialize_gpes();
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ ret = uacpi_kernel_install_interrupt_handler(
+ g_uacpi_rt_ctx.fadt.sci_int, handle_sci, g_gpe_interrupt_head,
+ &g_uacpi_rt_ctx.sci_handle
+ );
+ if (uacpi_unlikely_error(ret)) {
+ uacpi_error(
+ "unable to install SCI interrupt handler: %s\n",
+ uacpi_status_to_string(ret)
+ );
+ return ret;
+ }
+ g_uacpi_rt_ctx.sci_handle_valid = UACPI_TRUE;
+
+ g_uacpi_rt_ctx.global_lock_event = uacpi_kernel_create_event();
+ if (uacpi_unlikely(g_uacpi_rt_ctx.global_lock_event == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ g_uacpi_rt_ctx.global_lock_spinlock = uacpi_kernel_create_spinlock();
+ if (uacpi_unlikely(g_uacpi_rt_ctx.global_lock_spinlock == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ ret = uacpi_install_fixed_event_handler(
+ UACPI_FIXED_EVENT_GLOBAL_LOCK, handle_global_lock, UACPI_NULL
+ );
+ if (uacpi_likely_success(ret)) {
+ if (uacpi_unlikely(g_uacpi_rt_ctx.facs == UACPI_NULL)) {
+ uacpi_uninstall_fixed_event_handler(UACPI_FIXED_EVENT_GLOBAL_LOCK);
+ uacpi_warn("platform has global lock but no FACS was provided\n");
+ return ret;
+ }
+ g_uacpi_rt_ctx.has_global_lock = UACPI_TRUE;
+ } else if (ret == UACPI_STATUS_HARDWARE_TIMEOUT) {
+ // has_global_lock remains set to false
+ uacpi_trace("platform has no global lock\n");
+ ret = UACPI_STATUS_OK;
+ }
+
+ return ret;
+}
+
+void uacpi_deinitialize_events(void)
+{
+ struct gpe_interrupt_ctx *ctx, *next_ctx = g_gpe_interrupt_head;
+ uacpi_size i;
+
+ g_gpes_finalized = UACPI_FALSE;
+
+ if (g_uacpi_rt_ctx.sci_handle_valid) {
+ uacpi_kernel_uninstall_interrupt_handler(
+ handle_sci, g_uacpi_rt_ctx.sci_handle
+ );
+ g_uacpi_rt_ctx.sci_handle_valid = UACPI_FALSE;
+ }
+
+ while (next_ctx) {
+ struct gpe_block *block, *next_block;
+
+ ctx = next_ctx;
+ next_ctx = ctx->next;
+
+ next_block = ctx->gpe_head;
+ while (next_block) {
+ block = next_block;
+ next_block = block->next;
+ uninstall_gpe_block(block);
+ }
+ }
+
+ for (i = 0; i < UACPI_FIXED_EVENT_MAX; ++i) {
+ if (fixed_event_handlers[i].handler)
+ uacpi_uninstall_fixed_event_handler(i);
+ }
+
+ if (g_gpe_state_slock != UACPI_NULL) {
+ uacpi_kernel_free_spinlock(g_gpe_state_slock);
+ g_gpe_state_slock = UACPI_NULL;
+ }
+
+ uacpi_recursive_lock_deinit(&g_event_lock);
+
+ g_gpe_interrupt_head = UACPI_NULL;
+}
+
+uacpi_status uacpi_install_fixed_event_handler(
+ uacpi_fixed_event event, uacpi_interrupt_handler handler,
+ uacpi_handle user
+)
+{
+ uacpi_status ret;
+ struct fixed_event_handler *ev;
+
+ UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_SUBSYSTEM_INITIALIZED);
+
+ if (uacpi_unlikely(event < 0 || event > UACPI_FIXED_EVENT_MAX))
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ if (uacpi_is_hardware_reduced())
+ return UACPI_STATUS_OK;
+
+ ret = uacpi_recursive_lock_acquire(&g_event_lock);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ ev = &fixed_event_handlers[event];
+
+ if (ev->handler != UACPI_NULL) {
+ ret = UACPI_STATUS_ALREADY_EXISTS;
+ goto out;
+ }
+
+ ev->handler = handler;
+ ev->ctx = user;
+
+ ret = set_event(event, UACPI_EVENT_ENABLED);
+ if (uacpi_unlikely_error(ret)) {
+ ev->handler = UACPI_NULL;
+ ev->ctx = UACPI_NULL;
+ }
+
+out:
+ uacpi_recursive_lock_release(&g_event_lock);
+ return ret;
+}
+
+uacpi_status uacpi_uninstall_fixed_event_handler(
+ uacpi_fixed_event event
+)
+{
+ uacpi_status ret;
+ struct fixed_event_handler *ev;
+
+ UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_SUBSYSTEM_INITIALIZED);
+
+ if (uacpi_unlikely(event < 0 || event > UACPI_FIXED_EVENT_MAX))
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ if (uacpi_is_hardware_reduced())
+ return UACPI_STATUS_OK;
+
+ ret = uacpi_recursive_lock_acquire(&g_event_lock);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ ev = &fixed_event_handlers[event];
+
+ ret = set_event(event, UACPI_EVENT_DISABLED);
+ if (uacpi_unlikely_error(ret))
+ goto out;
+
+ uacpi_kernel_wait_for_work_completion();
+
+ ev->handler = UACPI_NULL;
+ ev->ctx = UACPI_NULL;
+
+out:
+ uacpi_recursive_lock_release(&g_event_lock);
+ return ret;
+}
+
+uacpi_status uacpi_fixed_event_info(
+ uacpi_fixed_event event, uacpi_event_info *out_info
+)
+{
+ uacpi_status ret;
+ const struct fixed_event *ev;
+ uacpi_u64 raw_value;
+ uacpi_event_info info = 0;
+
+ UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_SUBSYSTEM_INITIALIZED);
+
+ if (uacpi_unlikely(event < 0 || event > UACPI_FIXED_EVENT_MAX))
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ if (uacpi_is_hardware_reduced())
+ return UACPI_STATUS_NOT_FOUND;
+
+ ret = uacpi_recursive_lock_acquire(&g_event_lock);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ if (fixed_event_handlers[event].handler != UACPI_NULL)
+ info |= UACPI_EVENT_INFO_HAS_HANDLER;
+
+ ev = &fixed_events[event];
+
+ ret = uacpi_read_register_field(ev->enable_field, &raw_value);
+ if (uacpi_unlikely_error(ret))
+ goto out;
+ if (raw_value)
+ info |= UACPI_EVENT_INFO_ENABLED | UACPI_EVENT_INFO_HW_ENABLED;
+
+ ret = uacpi_read_register_field(ev->status_field, &raw_value);
+ if (uacpi_unlikely_error(ret))
+ goto out;
+ if (raw_value)
+ info |= UACPI_EVENT_INFO_HW_STATUS;
+
+ *out_info = info;
+out:
+ uacpi_recursive_lock_release(&g_event_lock);
+ return ret;
+}
+
+uacpi_status uacpi_gpe_info(
+ uacpi_namespace_node *gpe_device, uacpi_u16 idx, uacpi_event_info *out_info
+)
+{
+ uacpi_status ret;
+ struct gp_event *event;
+ struct gpe_register *reg;
+ uacpi_u8 mask;
+ uacpi_u64 raw_value;
+ uacpi_event_info info = 0;
+
+ UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_NAMESPACE_LOADED);
+
+ ret = uacpi_recursive_lock_acquire(&g_event_lock);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ ret = sanitize_device_and_find_gpe(&gpe_device, idx, &event);
+ if (uacpi_unlikely_error(ret))
+ goto out;
+
+ if (event->handler_type != GPE_HANDLER_TYPE_NONE)
+ info |= UACPI_EVENT_INFO_HAS_HANDLER;
+
+ mask = gpe_get_mask(event);
+ reg = event->reg;
+
+ if (reg->runtime_mask & mask)
+ info |= UACPI_EVENT_INFO_ENABLED;
+ if (reg->masked_mask & mask)
+ info |= UACPI_EVENT_INFO_MASKED;
+ if (reg->wake_mask & mask)
+ info |= UACPI_EVENT_INFO_ENABLED_FOR_WAKE;
+
+ ret = uacpi_gas_read_mapped(&reg->enable, &raw_value);
+ if (uacpi_unlikely_error(ret))
+ goto out;
+ if (raw_value & mask)
+ info |= UACPI_EVENT_INFO_HW_ENABLED;
+
+ ret = uacpi_gas_read_mapped(&reg->status, &raw_value);
+ if (uacpi_unlikely_error(ret))
+ goto out;
+ if (raw_value & mask)
+ info |= UACPI_EVENT_INFO_HW_STATUS;
+
+ *out_info = info;
+out:
+ uacpi_recursive_lock_release(&g_event_lock);
+ return ret;
+}
+
+#define PM1_STATUS_BITS ( \
+ ACPI_PM1_STS_TMR_STS_MASK | \
+ ACPI_PM1_STS_BM_STS_MASK | \
+ ACPI_PM1_STS_GBL_STS_MASK | \
+ ACPI_PM1_STS_PWRBTN_STS_MASK | \
+ ACPI_PM1_STS_SLPBTN_STS_MASK | \
+ ACPI_PM1_STS_RTC_STS_MASK | \
+ ACPI_PM1_STS_PCIEXP_WAKE_STS_MASK | \
+ ACPI_PM1_STS_WAKE_STS_MASK \
+)
+
+uacpi_status uacpi_clear_all_events(void)
+{
+ uacpi_status ret;
+ struct do_for_all_gpes_ctx ctx = {
+ .action = GPE_BLOCK_ACTION_CLEAR_ALL,
+ };
+
+ UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_NAMESPACE_LOADED);
+
+ ret = uacpi_recursive_lock_acquire(&g_event_lock);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ ret = uacpi_write_register(UACPI_REGISTER_PM1_STS, PM1_STATUS_BITS);
+ if (uacpi_unlikely_error(ret))
+ goto out;
+
+ for_each_gpe_block(do_for_all_gpes, &ctx);
+ ret = ctx.ret;
+
+out:
+ uacpi_recursive_lock_release(&g_event_lock);
+ return ret;
+}
+
+#endif // !UACPI_REDUCED_HARDWARE && !UACPI_BAREBONES_MODE
diff --git a/sys/dev/acpi/uacpi/interpreter.c b/sys/dev/acpi/uacpi/interpreter.c
new file mode 100644
index 0000000..8ffb8d5
--- /dev/null
+++ b/sys/dev/acpi/uacpi/interpreter.c
@@ -0,0 +1,6053 @@
+#include <uacpi/internal/types.h>
+#include <uacpi/internal/interpreter.h>
+#include <uacpi/internal/dynamic_array.h>
+#include <uacpi/internal/opcodes.h>
+#include <uacpi/internal/namespace.h>
+#include <uacpi/internal/stdlib.h>
+#include <uacpi/internal/context.h>
+#include <uacpi/internal/shareable.h>
+#include <uacpi/internal/tables.h>
+#include <uacpi/internal/helpers.h>
+#include <uacpi/kernel_api.h>
+#include <uacpi/internal/utilities.h>
+#include <uacpi/internal/opregion.h>
+#include <uacpi/internal/io.h>
+#include <uacpi/internal/notify.h>
+#include <uacpi/internal/resources.h>
+#include <uacpi/internal/event.h>
+#include <uacpi/internal/mutex.h>
+#include <uacpi/internal/osi.h>
+
+#ifndef UACPI_BAREBONES_MODE
+
+enum item_type {
+ ITEM_NONE = 0,
+ ITEM_NAMESPACE_NODE,
+ ITEM_OBJECT,
+ ITEM_EMPTY_OBJECT,
+ ITEM_PACKAGE_LENGTH,
+ ITEM_IMMEDIATE,
+};
+
+struct package_length {
+ uacpi_u32 begin;
+ uacpi_u32 end;
+};
+
+struct item {
+ uacpi_u8 type;
+ union {
+ uacpi_handle handle;
+ uacpi_object *obj;
+ struct uacpi_namespace_node *node;
+ struct package_length pkg;
+ uacpi_u64 immediate;
+ uacpi_u8 immediate_bytes[8];
+ };
+};
+
+DYNAMIC_ARRAY_WITH_INLINE_STORAGE(item_array, struct item, 8)
+DYNAMIC_ARRAY_WITH_INLINE_STORAGE_IMPL(item_array, struct item, static)
+
+struct op_context {
+ uacpi_u8 pc;
+ uacpi_bool preempted;
+
+ /*
+ * == 0 -> none
+ * >= 1 -> item[idx - 1]
+ */
+ uacpi_u8 tracked_pkg_idx;
+
+ uacpi_aml_op switched_from;
+
+ const struct uacpi_op_spec *op;
+ struct item_array items;
+};
+
+DYNAMIC_ARRAY_WITH_INLINE_STORAGE(op_context_array, struct op_context, 8)
+DYNAMIC_ARRAY_WITH_INLINE_STORAGE_IMPL(
+ op_context_array, struct op_context, static
+)
+
+static struct op_context *op_context_array_one_before_last(
+ struct op_context_array *arr
+)
+{
+ uacpi_size size;
+
+ size = op_context_array_size(arr);
+
+ if (size < 2)
+ return UACPI_NULL;
+
+ return op_context_array_at(arr, size - 2);
+}
+
+enum code_block_type {
+ CODE_BLOCK_IF = 1,
+ CODE_BLOCK_ELSE = 2,
+ CODE_BLOCK_WHILE = 3,
+ CODE_BLOCK_SCOPE = 4,
+};
+
+struct code_block {
+ enum code_block_type type;
+ uacpi_u32 begin, end;
+ union {
+ struct uacpi_namespace_node *node;
+ uacpi_u64 expiration_point;
+ };
+};
+
+DYNAMIC_ARRAY_WITH_INLINE_STORAGE(code_block_array, struct code_block, 8)
+DYNAMIC_ARRAY_WITH_INLINE_STORAGE_IMPL(
+ code_block_array, struct code_block, static
+)
+
+DYNAMIC_ARRAY_WITH_INLINE_STORAGE(held_mutexes_array, uacpi_mutex*, 8)
+DYNAMIC_ARRAY_WITH_INLINE_STORAGE_IMPL(
+ held_mutexes_array, uacpi_mutex*, static
+)
+
+static uacpi_status held_mutexes_array_push(
+ struct held_mutexes_array *arr, uacpi_mutex *mutex
+)
+{
+ uacpi_mutex **slot;
+
+ slot = held_mutexes_array_alloc(arr);
+ if (uacpi_unlikely(slot == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ *slot = mutex;
+ uacpi_shareable_ref(mutex);
+ return UACPI_STATUS_OK;
+}
+
+static void held_mutexes_array_remove_idx(
+ struct held_mutexes_array *arr, uacpi_size i
+)
+{
+ uacpi_size size;
+
+ size = held_mutexes_array_inline_capacity(arr);
+
+ // Only the dynamic array part is affected
+ if (i >= size) {
+ i -= size;
+ size = arr->size_including_inline - size;
+ size -= i + 1;
+
+ uacpi_memmove(
+ &arr->dynamic_storage[i], &arr->dynamic_storage[i + 1],
+ size * sizeof(arr->inline_storage[0])
+ );
+
+ held_mutexes_array_pop(arr);
+ return;
+ }
+
+ size = UACPI_MIN(held_mutexes_array_inline_capacity(arr),
+ arr->size_including_inline);
+ size -= i + 1;
+ uacpi_memmove(
+ &arr->inline_storage[i], &arr->inline_storage[i + 1],
+ size * sizeof(arr->inline_storage[0])
+ );
+
+ size = held_mutexes_array_size(arr);
+ i = held_mutexes_array_inline_capacity(arr);
+
+ /*
+ * This array has dynamic storage as well, now we have to take the first
+ * dynamic item, move it to the top of inline storage, and then shift all
+ * dynamic items backward by 1 as well.
+ */
+ if (size > i) {
+ arr->inline_storage[i - 1] = arr->dynamic_storage[0];
+ size -= i + 1;
+
+ uacpi_memmove(
+ &arr->dynamic_storage[0], &arr->dynamic_storage[1],
+ size * sizeof(arr->inline_storage[0])
+ );
+ }
+
+ held_mutexes_array_pop(arr);
+}
+
+enum force_release {
+ FORCE_RELEASE_NO,
+ FORCE_RELEASE_YES,
+};
+
+static uacpi_status held_mutexes_array_remove_and_release(
+ struct held_mutexes_array *arr, uacpi_mutex *mutex,
+ enum force_release force
+)
+{
+ uacpi_mutex *item;
+ uacpi_size i;
+
+ if (uacpi_unlikely(held_mutexes_array_size(arr) == 0))
+ return UACPI_STATUS_INVALID_ARGUMENT;
+
+ item = *held_mutexes_array_last(arr);
+
+ if (uacpi_unlikely(item->sync_level != mutex->sync_level &&
+ force != FORCE_RELEASE_YES)) {
+ uacpi_warn(
+ "ignoring mutex @%p release due to sync level mismatch: %d vs %d\n",
+ mutex, mutex->sync_level, item->sync_level
+ );
+
+ // We still return OK because we don't want to abort because of this
+ return UACPI_STATUS_OK;
+ }
+
+ if (mutex->depth > 1 && force == FORCE_RELEASE_NO) {
+ uacpi_release_aml_mutex(mutex);
+ return UACPI_STATUS_OK;
+ }
+
+ // Fast path for well-behaved AML that releases mutexes in descending order
+ if (uacpi_likely(item == mutex)) {
+ held_mutexes_array_pop(arr);
+ goto do_release;
+ }
+
+ /*
+ * The mutex being released is not the last one acquired, although we did
+ * verify that at least it has the same sync level. Anyway, now we have
+ * to search for it and then remove it from the array while shifting
+ * everything backwards.
+ */
+ i = held_mutexes_array_size(arr);
+ for (;;) {
+ item = *held_mutexes_array_at(arr, --i);
+ if (item == mutex)
+ break;
+
+ if (uacpi_unlikely(i == 0))
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+
+ held_mutexes_array_remove_idx(arr, i);
+
+do_release:
+ // This is either a force release, or depth was already 1 to begin with
+ mutex->depth = 1;
+ uacpi_release_aml_mutex(mutex);
+
+ uacpi_mutex_unref(mutex);
+ return UACPI_STATUS_OK;
+}
+
+DYNAMIC_ARRAY_WITH_INLINE_STORAGE(
+ temp_namespace_node_array, uacpi_namespace_node*, 8)
+DYNAMIC_ARRAY_WITH_INLINE_STORAGE_IMPL(
+ temp_namespace_node_array, uacpi_namespace_node*, static
+)
+
+static uacpi_status temp_namespace_node_array_push(
+ struct temp_namespace_node_array *arr, uacpi_namespace_node *node
+)
+{
+ uacpi_namespace_node **slot;
+
+ slot = temp_namespace_node_array_alloc(arr);
+ if (uacpi_unlikely(slot == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ *slot = node;
+ return UACPI_STATUS_OK;
+}
+
+struct call_frame {
+ struct uacpi_control_method *method;
+
+ uacpi_object *args[7];
+ uacpi_object *locals[8];
+
+ struct op_context_array pending_ops;
+ struct code_block_array code_blocks;
+ struct temp_namespace_node_array temp_nodes;
+ struct code_block *last_while;
+ uacpi_u64 prev_while_expiration;
+ uacpi_u32 prev_while_code_offset;
+
+ uacpi_u32 code_offset;
+
+ struct uacpi_namespace_node *cur_scope;
+
+ // Only used if the method is serialized
+ uacpi_u8 prev_sync_level;
+};
+
+static void *call_frame_cursor(struct call_frame *frame)
+{
+ return frame->method->code + frame->code_offset;
+}
+
+static uacpi_size call_frame_code_bytes_left(struct call_frame *frame)
+{
+ return frame->method->size - frame->code_offset;
+}
+
+static uacpi_bool call_frame_has_code(struct call_frame *frame)
+{
+ return call_frame_code_bytes_left(frame) > 0;
+}
+
+DYNAMIC_ARRAY_WITH_INLINE_STORAGE(call_frame_array, struct call_frame, 4)
+DYNAMIC_ARRAY_WITH_INLINE_STORAGE_IMPL(
+ call_frame_array, struct call_frame, static
+)
+
+static struct call_frame *call_frame_array_one_before_last(
+ struct call_frame_array *arr
+)
+{
+ uacpi_size size;
+
+ size = call_frame_array_size(arr);
+
+ if (size < 2)
+ return UACPI_NULL;
+
+ return call_frame_array_at(arr, size - 2);
+}
+
+// NOTE: Try to keep size under 2 pages
+struct execution_context {
+ uacpi_object *ret;
+ struct call_frame_array call_stack;
+ struct held_mutexes_array held_mutexes;
+
+ struct call_frame *cur_frame;
+ struct code_block *cur_block;
+ const struct uacpi_op_spec *cur_op;
+ struct op_context *prev_op_ctx;
+ struct op_context *cur_op_ctx;
+
+ uacpi_u8 sync_level;
+};
+
+#define AML_READ(ptr, offset) (*(((uacpi_u8*)(ptr)) + offset))
+
+static uacpi_status parse_nameseg(uacpi_u8 *cursor,
+ uacpi_object_name *out_name)
+{
+ if (uacpi_unlikely(!uacpi_is_valid_nameseg(cursor)))
+ return UACPI_STATUS_AML_INVALID_NAMESTRING;
+
+ uacpi_memcpy(&out_name->id, cursor, 4);
+ return UACPI_STATUS_OK;
+}
+
+/*
+ * -------------------------------------------------------------
+ * RootChar := ‘\’
+ * ParentPrefixChar := ‘^’
+ * ‘\’ := 0x5C
+ * ‘^’ := 0x5E
+ * ------------------------------------------------------------
+ * NameSeg := <leadnamechar namechar namechar namechar>
+ * NameString := <rootchar namepath> | <prefixpath namepath>
+ * PrefixPath := Nothing | <’^’ prefixpath>
+ * NamePath := NameSeg | DualNamePath | MultiNamePath | NullName
+ * DualNamePath := DualNamePrefix NameSeg NameSeg
+ * MultiNamePath := MultiNamePrefix SegCount NameSeg(SegCount)
+ */
+
+static uacpi_status name_string_to_path(
+ struct call_frame *frame, uacpi_size offset,
+ uacpi_char **out_string, uacpi_size *out_size
+)
+{
+ uacpi_size bytes_left, prefix_bytes, nameseg_bytes = 0, namesegs;
+ uacpi_char *base_cursor, *cursor;
+ uacpi_char prev_char;
+
+ bytes_left = frame->method->size - offset;
+ cursor = (uacpi_char*)frame->method->code + offset;
+ base_cursor = cursor;
+ namesegs = 0;
+
+ prefix_bytes = 0;
+ for (;;) {
+ if (uacpi_unlikely(bytes_left == 0))
+ return UACPI_STATUS_AML_INVALID_NAMESTRING;
+
+ prev_char = *cursor;
+
+ switch (prev_char) {
+ case '^':
+ case '\\':
+ prefix_bytes++;
+ cursor++;
+ bytes_left--;
+ break;
+ default:
+ break;
+ }
+
+ if (prev_char != '^')
+ break;
+ }
+
+ // At least a NullName byte is expected here
+ if (uacpi_unlikely(bytes_left == 0))
+ return UACPI_STATUS_AML_INVALID_NAMESTRING;
+
+ namesegs = 0;
+ bytes_left--;
+ switch (*cursor++)
+ {
+ case UACPI_DUAL_NAME_PREFIX:
+ namesegs = 2;
+ break;
+ case UACPI_MULTI_NAME_PREFIX:
+ if (uacpi_unlikely(bytes_left == 0))
+ return UACPI_STATUS_AML_INVALID_NAMESTRING;
+
+ namesegs = *(uacpi_u8*)cursor;
+ if (uacpi_unlikely(namesegs == 0)) {
+ uacpi_error("MultiNamePrefix but SegCount is 0\n");
+ return UACPI_STATUS_AML_INVALID_NAMESTRING;
+ }
+
+ cursor++;
+ bytes_left--;
+ break;
+ case UACPI_NULL_NAME:
+ break;
+ default:
+ /*
+ * Might be an invalid byte, but assume single nameseg for now,
+ * the code below will validate it for us.
+ */
+ cursor--;
+ bytes_left++;
+ namesegs = 1;
+ break;
+ }
+
+ if (uacpi_unlikely((namesegs * 4) > bytes_left))
+ return UACPI_STATUS_AML_INVALID_NAMESTRING;
+
+ if (namesegs) {
+ // 4 chars per nameseg
+ nameseg_bytes = namesegs * 4;
+
+ // dot separator for every nameseg
+ nameseg_bytes += namesegs - 1;
+ }
+
+ *out_size = nameseg_bytes + prefix_bytes + 1;
+
+ *out_string = uacpi_kernel_alloc(*out_size);
+ if (*out_string == UACPI_NULL)
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ uacpi_memcpy(*out_string, base_cursor, prefix_bytes);
+
+ base_cursor = *out_string;
+ base_cursor += prefix_bytes;
+
+ while (namesegs-- > 0) {
+ uacpi_memcpy(base_cursor, cursor, 4);
+ cursor += 4;
+ base_cursor += 4;
+
+ if (namesegs)
+ *base_cursor++ = '.';
+ }
+
+ *base_cursor = '\0';
+ return UACPI_STATUS_OK;
+}
+
+enum resolve_behavior {
+ RESOLVE_CREATE_LAST_NAMESEG_FAIL_IF_EXISTS,
+ RESOLVE_FAIL_IF_DOESNT_EXIST,
+};
+
+static uacpi_status resolve_name_string(
+ struct call_frame *frame,
+ enum resolve_behavior behavior,
+ struct uacpi_namespace_node **out_node
+)
+{
+ uacpi_status ret = UACPI_STATUS_OK;
+ uacpi_u8 *cursor;
+ uacpi_size bytes_left, namesegs = 0;
+ struct uacpi_namespace_node *parent, *cur_node = frame->cur_scope;
+ uacpi_char prev_char = 0;
+ uacpi_bool just_one_nameseg = UACPI_TRUE;
+
+ bytes_left = call_frame_code_bytes_left(frame);
+ cursor = call_frame_cursor(frame);
+
+ for (;;) {
+ if (uacpi_unlikely(bytes_left == 0))
+ return UACPI_STATUS_AML_INVALID_NAMESTRING;
+
+ switch (*cursor) {
+ case '\\':
+ if (prev_char == '^')
+ return UACPI_STATUS_AML_INVALID_NAMESTRING;
+
+ cur_node = uacpi_namespace_root();
+ break;
+ case '^':
+ // Tried to go behind root
+ if (uacpi_unlikely(cur_node == uacpi_namespace_root()))
+ return UACPI_STATUS_AML_INVALID_NAMESTRING;
+
+ cur_node = cur_node->parent;
+ break;
+ default:
+ break;
+ }
+
+ prev_char = *cursor;
+
+ switch (prev_char) {
+ case '^':
+ case '\\':
+ just_one_nameseg = UACPI_FALSE;
+ cursor++;
+ bytes_left--;
+ break;
+ default:
+ break;
+ }
+
+ if (prev_char != '^')
+ break;
+ }
+
+ // At least a NullName byte is expected here
+ if (uacpi_unlikely(bytes_left == 0))
+ return UACPI_STATUS_AML_INVALID_NAMESTRING;
+
+ bytes_left--;
+ switch (*cursor++)
+ {
+ case UACPI_DUAL_NAME_PREFIX:
+ namesegs = 2;
+ just_one_nameseg = UACPI_FALSE;
+ break;
+ case UACPI_MULTI_NAME_PREFIX:
+ if (uacpi_unlikely(bytes_left == 0))
+ return UACPI_STATUS_AML_INVALID_NAMESTRING;
+
+ namesegs = *cursor;
+ if (uacpi_unlikely(namesegs == 0)) {
+ uacpi_error("MultiNamePrefix but SegCount is 0\n");
+ return UACPI_STATUS_AML_INVALID_NAMESTRING;
+ }
+
+ cursor++;
+ bytes_left--;
+ just_one_nameseg = UACPI_FALSE;
+ break;
+ case UACPI_NULL_NAME:
+ if (behavior == RESOLVE_CREATE_LAST_NAMESEG_FAIL_IF_EXISTS ||
+ just_one_nameseg)
+ return UACPI_STATUS_AML_INVALID_NAMESTRING;
+
+ goto out;
+ default:
+ /*
+ * Might be an invalid byte, but assume single nameseg for now,
+ * the code below will validate it for us.
+ */
+ cursor--;
+ bytes_left++;
+ namesegs = 1;
+ break;
+ }
+
+ if (uacpi_unlikely((namesegs * 4) > bytes_left))
+ return UACPI_STATUS_AML_INVALID_NAMESTRING;
+
+ for (; namesegs; cursor += 4, namesegs--) {
+ uacpi_object_name name;
+
+ ret = parse_nameseg(cursor, &name);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ parent = cur_node;
+ cur_node = uacpi_namespace_node_find_sub_node(parent, name);
+
+ switch (behavior) {
+ case RESOLVE_CREATE_LAST_NAMESEG_FAIL_IF_EXISTS:
+ if (namesegs == 1) {
+ if (cur_node) {
+ cur_node = UACPI_NULL;
+ ret = UACPI_STATUS_AML_OBJECT_ALREADY_EXISTS;
+ goto out;
+ }
+
+ // Create the node and link to parent but don't install YET
+ cur_node = uacpi_namespace_node_alloc(name);
+ if (uacpi_unlikely(cur_node == UACPI_NULL)) {
+ ret = UACPI_STATUS_OUT_OF_MEMORY;
+ goto out;
+ }
+
+ cur_node->parent = parent;
+ }
+ break;
+ case RESOLVE_FAIL_IF_DOESNT_EXIST:
+ if (just_one_nameseg) {
+ while (!cur_node && parent != uacpi_namespace_root()) {
+ cur_node = parent;
+ parent = cur_node->parent;
+
+ cur_node = uacpi_namespace_node_find_sub_node(parent, name);
+ }
+ }
+ break;
+ default:
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+
+ if (cur_node == UACPI_NULL) {
+ ret = UACPI_STATUS_NOT_FOUND;
+ break;
+ }
+ }
+
+out:
+ cursor += namesegs * 4;
+ frame->code_offset = cursor - frame->method->code;
+
+ if (uacpi_likely_success(ret) && behavior == RESOLVE_FAIL_IF_DOESNT_EXIST)
+ uacpi_shareable_ref(cur_node);
+
+ *out_node = cur_node;
+ return ret;
+}
+
+static uacpi_status do_install_node_item(struct call_frame *frame,
+ struct item *item)
+{
+ uacpi_status ret;
+
+ ret = uacpi_namespace_node_install(item->node->parent, item->node);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ if (!frame->method->named_objects_persist)
+ ret = temp_namespace_node_array_push(&frame->temp_nodes, item->node);
+
+ if (uacpi_likely_success(ret))
+ item->node = UACPI_NULL;
+
+ return ret;
+}
+
+static uacpi_u8 peek_next_op(struct call_frame *frame, uacpi_aml_op *out_op)
+{
+ uacpi_aml_op op;
+ uacpi_size bytes_left;
+ uacpi_u8 length = 0;
+ uacpi_u8 *cursor;
+ struct code_block *block;
+
+ block = code_block_array_last(&frame->code_blocks);
+ bytes_left = block->end - frame->code_offset;
+ if (bytes_left == 0)
+ return 0;
+
+ cursor = call_frame_cursor(frame);
+
+ op = AML_READ(cursor, length++);
+ if (op == UACPI_EXT_PREFIX) {
+ if (uacpi_unlikely(bytes_left < 2))
+ return 0;
+
+ op <<= 8;
+ op |= AML_READ(cursor, length++);
+ }
+
+ *out_op = op;
+ return length;
+}
+
+static uacpi_status get_op(struct execution_context *ctx)
+{
+ uacpi_aml_op op;
+ uacpi_u8 length;
+
+ length = peek_next_op(ctx->cur_frame, &op);
+ if (uacpi_unlikely(length == 0))
+ return UACPI_STATUS_AML_BAD_ENCODING;
+
+ ctx->cur_frame->code_offset += length;
+ g_uacpi_rt_ctx.opcodes_executed++;
+
+ ctx->cur_op = uacpi_get_op_spec(op);
+ if (uacpi_unlikely(ctx->cur_op->properties & UACPI_OP_PROPERTY_RESERVED)) {
+ uacpi_error(
+ "invalid opcode '%s' encountered in bytestream\n",
+ ctx->cur_op->name
+ );
+ return UACPI_STATUS_AML_INVALID_OPCODE;
+ }
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status handle_buffer(struct execution_context *ctx)
+{
+ struct package_length *pkg;
+ uacpi_u8 *src;
+ uacpi_object *dst, *declared_size;
+ uacpi_u32 buffer_size, init_size, aml_offset;
+ struct op_context *op_ctx = ctx->cur_op_ctx;
+
+ aml_offset = item_array_at(&op_ctx->items, 2)->immediate;
+ src = ctx->cur_frame->method->code;
+ src += aml_offset;
+
+ pkg = &item_array_at(&op_ctx->items, 0)->pkg;
+ init_size = pkg->end - aml_offset;
+
+ // TODO: do package bounds checking at parse time
+ if (uacpi_unlikely(pkg->end > ctx->cur_frame->method->size))
+ return UACPI_STATUS_AML_BAD_ENCODING;
+
+ declared_size = item_array_at(&op_ctx->items, 1)->obj;
+
+ if (uacpi_unlikely(declared_size->integer > 0xE0000000)) {
+ uacpi_error(
+ "buffer is too large (%"UACPI_PRIu64"), assuming corrupted "
+ "bytestream\n", UACPI_FMT64(declared_size->integer)
+ );
+ return UACPI_STATUS_AML_BAD_ENCODING;
+ }
+
+ if (uacpi_unlikely(declared_size->integer == 0)) {
+ uacpi_error("attempted to create an empty buffer\n");
+ return UACPI_STATUS_AML_BAD_ENCODING;
+ }
+
+ buffer_size = declared_size->integer;
+ if (uacpi_unlikely(init_size > buffer_size)) {
+ uacpi_error(
+ "too many buffer initializers: %u (size is %u)\n",
+ init_size, buffer_size
+ );
+ return UACPI_STATUS_AML_BAD_ENCODING;
+ }
+
+ dst = item_array_at(&op_ctx->items, 3)->obj;
+ dst->buffer->data = uacpi_kernel_alloc(buffer_size);
+ if (uacpi_unlikely(dst->buffer->data == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+ dst->buffer->size = buffer_size;
+
+ uacpi_memcpy_zerout(dst->buffer->data, src, buffer_size, init_size);
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status handle_string(struct execution_context *ctx)
+{
+ struct call_frame *frame = ctx->cur_frame;
+ uacpi_object *obj;
+
+ uacpi_char *string;
+ uacpi_size length, max_bytes;
+
+ obj = item_array_last(&ctx->cur_op_ctx->items)->obj;
+ string = call_frame_cursor(frame);
+
+ // TODO: sanitize string for valid UTF-8
+ max_bytes = call_frame_code_bytes_left(frame);
+ length = uacpi_strnlen(string, max_bytes);
+
+ if (uacpi_unlikely((length == max_bytes) || (string[length++] != 0x00)))
+ return UACPI_STATUS_AML_BAD_ENCODING;
+
+ obj->buffer->text = uacpi_kernel_alloc(length);
+ if (uacpi_unlikely(obj->buffer->text == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ uacpi_memcpy(obj->buffer->text, string, length);
+ obj->buffer->size = length;
+ frame->code_offset += length;
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status handle_package(struct execution_context *ctx)
+{
+ struct op_context *op_ctx = ctx->cur_op_ctx;
+ uacpi_package *package;
+ uacpi_u32 num_elements, num_defined_elements, i;
+
+ /*
+ * Layout of items here:
+ * [0] -> Package length, not interesting
+ * [1] -> Immediate or integer object, depending on PackageOp/VarPackageOp
+ * [2..N-2] -> AML pc+Package element pairs
+ * [N-1] -> The resulting package object that we're constructing
+ */
+ package = item_array_last(&op_ctx->items)->obj->package;
+
+ // 1. Detect how many elements we have, do sanity checking
+ if (op_ctx->op->code == UACPI_AML_OP_VarPackageOp) {
+ uacpi_object *var_num_elements;
+
+ var_num_elements = item_array_at(&op_ctx->items, 1)->obj;
+ if (uacpi_unlikely(var_num_elements->integer > 0xE0000000)) {
+ uacpi_error(
+ "package is too large (%"UACPI_PRIu64"), assuming "
+ "corrupted bytestream\n", UACPI_FMT64(var_num_elements->integer)
+ );
+ return UACPI_STATUS_AML_BAD_ENCODING;
+ }
+ num_elements = var_num_elements->integer;
+ } else {
+ num_elements = item_array_at(&op_ctx->items, 1)->immediate;
+ }
+
+ num_defined_elements = (item_array_size(&op_ctx->items) - 3) / 2;
+ if (uacpi_unlikely(num_defined_elements > num_elements)) {
+ uacpi_warn(
+ "too many package initializers: %u, truncating to %u\n",
+ num_defined_elements, num_elements
+ );
+
+ num_defined_elements = num_elements;
+ }
+
+ // 2. Create every object in the package, start as uninitialized
+ if (uacpi_unlikely(!uacpi_package_fill(package, num_elements,
+ UACPI_PREALLOC_OBJECTS_YES)))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ // 3. Go through every defined object and copy it into the package
+ for (i = 0; i < num_defined_elements; ++i) {
+ uacpi_size base_pkg_index;
+ uacpi_status ret;
+ struct item *item;
+ uacpi_object *obj;
+
+ base_pkg_index = (i * 2) + 2;
+ item = item_array_at(&op_ctx->items, base_pkg_index + 1);
+ obj = item->obj;
+
+ if (obj != UACPI_NULL && obj->type == UACPI_OBJECT_REFERENCE) {
+ /*
+ * For named objects we don't actually need the object itself, but
+ * simply the path to it. Often times objects referenced by the
+ * package are not defined until later so it's not possible to
+ * resolve them. For uniformity and to follow the behavior of NT,
+ * simply convert the name string to a path string object to be
+ * resolved later when actually needed.
+ */
+ if (obj->flags == UACPI_REFERENCE_KIND_NAMED) {
+ uacpi_object_unref(obj);
+ item->obj = UACPI_NULL;
+ obj = UACPI_NULL;
+ } else {
+ obj = uacpi_unwrap_internal_reference(obj);
+ }
+ }
+
+ if (obj == UACPI_NULL) {
+ uacpi_size length;
+ uacpi_char *path;
+
+ obj = uacpi_create_object(UACPI_OBJECT_STRING);
+ if (uacpi_unlikely(obj == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ ret = name_string_to_path(
+ ctx->cur_frame,
+ item_array_at(&op_ctx->items, base_pkg_index)->immediate,
+ &path, &length
+ );
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ obj->flags = UACPI_STRING_KIND_PATH;
+ obj->buffer->text = path;
+ obj->buffer->size = length;
+
+ item->obj = obj;
+ item->type = ITEM_OBJECT;
+ }
+
+ ret = uacpi_object_assign(package->objects[i], obj,
+ UACPI_ASSIGN_BEHAVIOR_DEEP_COPY);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+ }
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_size sizeof_int(void)
+{
+ return g_uacpi_rt_ctx.is_rev1 ? 4 : 8;
+}
+
+static uacpi_status get_object_storage(
+ uacpi_object *obj, uacpi_data_view *out_buf, uacpi_bool include_null
+)
+{
+ switch (obj->type) {
+ case UACPI_OBJECT_INTEGER:
+ out_buf->length = sizeof_int();
+ out_buf->data = &obj->integer;
+ break;
+ case UACPI_OBJECT_STRING:
+ out_buf->length = obj->buffer->size;
+ if (out_buf->length && !include_null)
+ out_buf->length--;
+
+ out_buf->text = obj->buffer->text;
+ break;
+ case UACPI_OBJECT_BUFFER:
+ if (obj->buffer->size == 0) {
+ out_buf->bytes = UACPI_NULL;
+ out_buf->length = 0;
+ break;
+ }
+
+ out_buf->length = obj->buffer->size;
+ out_buf->bytes = obj->buffer->data;
+ break;
+ case UACPI_OBJECT_REFERENCE:
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ default:
+ return UACPI_STATUS_AML_INCOMPATIBLE_OBJECT_TYPE;
+ }
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_u8 *buffer_index_cursor(uacpi_buffer_index *buf_idx)
+{
+ uacpi_u8 *out_cursor;
+
+ out_cursor = buf_idx->buffer->data;
+ out_cursor += buf_idx->idx;
+
+ return out_cursor;
+}
+
+static void write_buffer_index(uacpi_buffer_index *buf_idx,
+ uacpi_data_view *src_buf)
+{
+ uacpi_memcpy_zerout(buffer_index_cursor(buf_idx), src_buf->bytes,
+ 1, src_buf->length);
+}
+
+/*
+ * The word "implicit cast" here is only because it's called that in
+ * the specification. In reality, we just copy one buffer to another
+ * because that's what NT does.
+ */
+static uacpi_status object_assign_with_implicit_cast(
+ uacpi_object *dst, uacpi_object *src, uacpi_data_view *wtr_response
+)
+{
+ uacpi_status ret;
+ uacpi_data_view src_buf;
+
+ ret = get_object_storage(src, &src_buf, UACPI_FALSE);
+ if (uacpi_unlikely_error(ret))
+ goto out_bad_cast;
+
+ switch (dst->type) {
+ case UACPI_OBJECT_INTEGER:
+ case UACPI_OBJECT_STRING:
+ case UACPI_OBJECT_BUFFER: {
+ uacpi_data_view dst_buf;
+
+ ret = get_object_storage(dst, &dst_buf, UACPI_FALSE);
+ if (uacpi_unlikely_error(ret))
+ goto out_bad_cast;
+
+ uacpi_memcpy_zerout(
+ dst_buf.bytes, src_buf.bytes, dst_buf.length, src_buf.length
+ );
+ break;
+ }
+
+ case UACPI_OBJECT_BUFFER_FIELD:
+ uacpi_write_buffer_field(
+ &dst->buffer_field, src_buf.bytes, src_buf.length
+ );
+ break;
+
+ case UACPI_OBJECT_FIELD_UNIT:
+ return uacpi_write_field_unit(
+ dst->field_unit, src_buf.bytes, src_buf.length,
+ wtr_response
+ );
+
+ case UACPI_OBJECT_BUFFER_INDEX:
+ write_buffer_index(&dst->buffer_index, &src_buf);
+ break;
+
+ default:
+ ret = UACPI_STATUS_AML_INCOMPATIBLE_OBJECT_TYPE;
+ goto out_bad_cast;
+ }
+
+ return ret;
+
+out_bad_cast:
+ uacpi_error(
+ "attempted to perform an invalid implicit cast (%s -> %s)\n",
+ uacpi_object_type_to_string(src->type),
+ uacpi_object_type_to_string(dst->type)
+ );
+ return ret;
+}
+
+enum argx_or_localx {
+ ARGX,
+ LOCALX,
+};
+
+static uacpi_status handle_arg_or_local(
+ struct execution_context *ctx,
+ uacpi_size idx, enum argx_or_localx type
+)
+{
+ uacpi_object **src;
+ struct item *dst;
+ enum uacpi_reference_kind kind;
+
+ if (type == ARGX) {
+ src = &ctx->cur_frame->args[idx];
+ kind = UACPI_REFERENCE_KIND_ARG;
+ } else {
+ src = &ctx->cur_frame->locals[idx];
+ kind = UACPI_REFERENCE_KIND_LOCAL;
+ }
+
+ if (*src == UACPI_NULL) {
+ uacpi_object *default_value;
+
+ default_value = uacpi_create_object(UACPI_OBJECT_UNINITIALIZED);
+ if (uacpi_unlikely(default_value == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ *src = uacpi_create_internal_reference(kind, default_value);
+ if (uacpi_unlikely(*src == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ uacpi_object_unref(default_value);
+ }
+
+ dst = item_array_last(&ctx->cur_op_ctx->items);
+ dst->obj = *src;
+ dst->type = ITEM_OBJECT;
+ uacpi_object_ref(dst->obj);
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status handle_local(struct execution_context *ctx)
+{
+ uacpi_size idx;
+ struct op_context *op_ctx = ctx->cur_op_ctx;
+
+ idx = op_ctx->op->code - UACPI_AML_OP_Local0Op;
+ return handle_arg_or_local(ctx, idx, LOCALX);
+}
+
+static uacpi_status handle_arg(struct execution_context *ctx)
+{
+ uacpi_size idx;
+ struct op_context *op_ctx = ctx->cur_op_ctx;
+
+ idx = op_ctx->op->code - UACPI_AML_OP_Arg0Op;
+ return handle_arg_or_local(ctx, idx, ARGX);
+}
+
+static uacpi_status handle_named_object(struct execution_context *ctx)
+{
+ struct uacpi_namespace_node *src;
+ struct item *dst;
+
+ src = item_array_at(&ctx->cur_op_ctx->items, 0)->node;
+ dst = item_array_at(&ctx->cur_op_ctx->items, 1);
+
+ dst->obj = src->object;
+ dst->type = ITEM_OBJECT;
+ uacpi_object_ref(dst->obj);
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status handle_create_alias(struct execution_context *ctx)
+{
+ uacpi_namespace_node *src, *dst;
+
+ src = item_array_at(&ctx->cur_op_ctx->items, 0)->node;
+ dst = item_array_at(&ctx->cur_op_ctx->items, 1)->node;
+
+ dst->object = src->object;
+ dst->flags = UACPI_NAMESPACE_NODE_FLAG_ALIAS;
+ uacpi_object_ref(dst->object);
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status handle_create_op_region(struct execution_context *ctx)
+{
+ uacpi_namespace_node *node;
+ uacpi_object *obj;
+ uacpi_operation_region *op_region;
+ uacpi_u64 region_end;
+
+ node = item_array_at(&ctx->cur_op_ctx->items, 0)->node;
+ obj = item_array_at(&ctx->cur_op_ctx->items, 4)->obj;
+ op_region = obj->op_region;
+
+ op_region->space = item_array_at(&ctx->cur_op_ctx->items, 1)->immediate;
+ op_region->offset = item_array_at(&ctx->cur_op_ctx->items, 2)->obj->integer;
+ op_region->length = item_array_at(&ctx->cur_op_ctx->items, 3)->obj->integer;
+ region_end = op_region->offset + op_region->length;
+
+ if (uacpi_unlikely(op_region->length == 0)) {
+ // Don't abort here, as long as it's never accessed we don't care
+ uacpi_warn("unusable/empty operation region %.4s\n", node->name.text);
+ } else if (uacpi_unlikely(op_region->offset > region_end)) {
+ uacpi_error(
+ "invalid operation region %.4s bounds: offset=0x%"UACPI_PRIX64
+ " length=0x%"UACPI_PRIX64"\n", node->name.text,
+ UACPI_FMT64(op_region->offset), UACPI_FMT64(op_region->length)
+ );
+ return UACPI_STATUS_AML_BAD_ENCODING;
+ }
+
+ if (op_region->space == UACPI_ADDRESS_SPACE_PCC && op_region->offset > 255) {
+ uacpi_warn(
+ "invalid PCC operation region %.4s subspace %"UACPI_PRIX64"\n",
+ node->name.text, UACPI_FMT64(op_region->offset)
+ );
+ }
+
+ node->object = uacpi_create_internal_reference(
+ UACPI_REFERENCE_KIND_NAMED, obj
+ );
+ if (uacpi_unlikely(node->object == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ uacpi_initialize_opregion_node(node);
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status table_id_error(
+ const uacpi_char *opcode, const uacpi_char *arg,
+ uacpi_buffer *str
+)
+{
+ uacpi_error("%s: invalid %s '%s'\n", opcode, arg, str->text);
+ return UACPI_STATUS_AML_BAD_ENCODING;
+}
+
+static void report_table_id_find_error(
+ const uacpi_char *opcode, struct uacpi_table_identifiers *id,
+ uacpi_status ret
+)
+{
+ uacpi_error(
+ "%s: unable to find table '%.4s' (OEM ID '%.6s', "
+ "OEM Table ID '%.8s'): %s\n",
+ opcode, id->signature.text, id->oemid, id->oem_table_id,
+ uacpi_status_to_string(ret)
+ );
+}
+
+static uacpi_status build_table_id(
+ const uacpi_char *opcode,
+ struct uacpi_table_identifiers *out_id,
+ uacpi_buffer *signature, uacpi_buffer *oem_id,
+ uacpi_buffer *oem_table_id
+)
+{
+ if (uacpi_unlikely(signature->size != (sizeof(uacpi_object_name) + 1)))
+ return table_id_error(opcode, "SignatureString", signature);
+
+ uacpi_memcpy(out_id->signature.text, signature->text,
+ sizeof(uacpi_object_name));
+
+ if (uacpi_unlikely(oem_id->size > (sizeof(out_id->oemid) + 1)))
+ return table_id_error(opcode, "OemIDString", oem_id);
+
+ uacpi_memcpy_zerout(
+ out_id->oemid, oem_id->text,
+ sizeof(out_id->oemid), oem_id->size ? oem_id->size - 1 : 0
+ );
+
+ if (uacpi_unlikely(oem_table_id->size > (sizeof(out_id->oem_table_id) + 1)))
+ return table_id_error(opcode, "OemTableIDString", oem_table_id);
+
+ uacpi_memcpy_zerout(
+ out_id->oem_table_id, oem_table_id->text,
+ sizeof(out_id->oem_table_id),
+ oem_table_id->size ? oem_table_id->size - 1 : 0
+ );
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status handle_create_data_region(struct execution_context *ctx)
+{
+ uacpi_status ret;
+ struct item_array *items = &ctx->cur_op_ctx->items;
+ struct uacpi_table_identifiers table_id;
+ uacpi_table table;
+ uacpi_namespace_node *node;
+ uacpi_object *obj;
+ uacpi_operation_region *op_region;
+
+ node = item_array_at(items, 0)->node;
+
+ ret = build_table_id(
+ "DataTableRegion", &table_id,
+ item_array_at(items, 1)->obj->buffer,
+ item_array_at(items, 2)->obj->buffer,
+ item_array_at(items, 3)->obj->buffer
+ );
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ ret = uacpi_table_find(&table_id, &table);
+ if (uacpi_unlikely_error(ret)) {
+ report_table_id_find_error("DataTableRegion", &table_id, ret);
+ return ret;
+ }
+
+ obj = item_array_at(items, 4)->obj;
+ op_region = obj->op_region;
+ op_region->space = UACPI_ADDRESS_SPACE_TABLE_DATA;
+ op_region->offset = table.virt_addr;
+ op_region->length = table.hdr->length;
+ op_region->table_idx = table.index;
+
+ node->object = uacpi_create_internal_reference(
+ UACPI_REFERENCE_KIND_NAMED, obj
+ );
+ if (uacpi_unlikely(node->object == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ uacpi_initialize_opregion_node(node);
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_bool is_dynamic_table_load(enum uacpi_table_load_cause cause)
+{
+ return cause != UACPI_TABLE_LOAD_CAUSE_INIT;
+}
+
+static void prepare_table_load(
+ void *ptr, enum uacpi_table_load_cause cause, uacpi_control_method *in_method
+)
+{
+ struct acpi_dsdt *dsdt = ptr;
+ enum uacpi_log_level log_level = UACPI_LOG_TRACE;
+ const uacpi_char *log_prefix = "load of";
+
+ if (is_dynamic_table_load(cause)) {
+ log_prefix = cause == UACPI_TABLE_LOAD_CAUSE_HOST ?
+ "host-invoked load of" : "dynamic load of";
+ log_level = UACPI_LOG_INFO;
+ }
+
+ uacpi_log_lvl(
+ log_level, "%s "UACPI_PRI_TBL_HDR"\n",
+ log_prefix, UACPI_FMT_TBL_HDR(&dsdt->hdr)
+ );
+
+ in_method->code = dsdt->definition_block;
+ in_method->size = dsdt->hdr.length - sizeof(dsdt->hdr);
+ in_method->named_objects_persist = UACPI_TRUE;
+}
+
+static uacpi_status do_load_table(
+ uacpi_namespace_node *parent, struct acpi_sdt_hdr *tbl,
+ enum uacpi_table_load_cause cause
+)
+{
+ struct uacpi_control_method method = { 0 };
+ uacpi_status ret;
+
+ prepare_table_load(tbl, cause, &method);
+
+ ret = uacpi_execute_control_method(parent, &method, UACPI_NULL, UACPI_NULL);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ if (is_dynamic_table_load(cause))
+ uacpi_events_match_post_dynamic_table_load();
+
+ return ret;
+}
+
+static uacpi_status handle_load_table(struct execution_context *ctx)
+{
+ uacpi_status ret;
+ struct item_array *items = &ctx->cur_op_ctx->items;
+ struct item *root_node_item;
+ struct uacpi_table_identifiers table_id;
+ uacpi_table table;
+ uacpi_buffer *root_path, *param_path;
+ uacpi_control_method *method;
+ uacpi_namespace_node *root_node, *param_node = UACPI_NULL;
+
+ /*
+ * If we already have the last true/false object loaded, this is a second
+ * invocation of this handler. For the second invocation we want to detect
+ * new AML GPE handlers that might've been loaded, as well as potentially
+ * remove the target.
+ */
+ if (item_array_size(items) == 12) {
+ uacpi_size idx;
+ struct uacpi_table tmp_table = { 0 };
+
+ idx = item_array_at(items, 2)->immediate;
+ tmp_table.index = idx;
+ uacpi_table_unref(&tmp_table);
+
+ /*
+ * If this load failed, remove the target that was provided via
+ * ParameterPathString so that it doesn't get stored to.
+ */
+ if (uacpi_unlikely(item_array_at(items, 11)->obj->integer == 0)) {
+ uacpi_object *target;
+
+ target = item_array_at(items, 3)->obj;
+ if (target != UACPI_NULL) {
+ uacpi_object_unref(target);
+ item_array_at(items, 3)->obj = UACPI_NULL;
+ }
+
+ return UACPI_STATUS_OK;
+ }
+
+ uacpi_events_match_post_dynamic_table_load();
+ return UACPI_STATUS_OK;
+ }
+
+ ret = build_table_id(
+ "LoadTable", &table_id,
+ item_array_at(items, 5)->obj->buffer,
+ item_array_at(items, 6)->obj->buffer,
+ item_array_at(items, 7)->obj->buffer
+ );
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ root_path = item_array_at(items, 8)->obj->buffer;
+ param_path = item_array_at(items, 9)->obj->buffer;
+ root_node_item = item_array_at(items, 0);
+
+ if (root_path->size > 1) {
+ ret = uacpi_namespace_node_resolve(
+ ctx->cur_frame->cur_scope, root_path->text, UACPI_SHOULD_LOCK_NO,
+ UACPI_MAY_SEARCH_ABOVE_PARENT_YES, UACPI_PERMANENT_ONLY_NO,
+ &root_node
+ );
+ if (uacpi_unlikely_error(ret)) {
+ table_id_error("LoadTable", "RootPathString", root_path);
+ if (ret == UACPI_STATUS_NOT_FOUND)
+ ret = UACPI_STATUS_AML_UNDEFINED_REFERENCE;
+ return ret;
+ }
+ } else {
+ root_node = uacpi_namespace_root();
+ }
+
+ root_node_item->node = root_node;
+ root_node_item->type = ITEM_NAMESPACE_NODE;
+ uacpi_shareable_ref(root_node);
+
+ if (param_path->size > 1) {
+ struct item *param_item;
+
+ ret = uacpi_namespace_node_resolve(
+ root_node, param_path->text, UACPI_SHOULD_LOCK_NO,
+ UACPI_MAY_SEARCH_ABOVE_PARENT_YES, UACPI_PERMANENT_ONLY_NO,
+ &param_node
+ );
+ if (uacpi_unlikely_error(ret)) {
+ table_id_error("LoadTable", "ParameterPathString", root_path);
+ if (ret == UACPI_STATUS_NOT_FOUND)
+ ret = UACPI_STATUS_AML_UNDEFINED_REFERENCE;
+ return ret;
+ }
+
+ param_item = item_array_at(items, 3);
+ param_item->obj = param_node->object;
+ uacpi_object_ref(param_item->obj);
+ param_item->type = ITEM_OBJECT;
+ }
+
+ ret = uacpi_table_find(&table_id, &table);
+ if (uacpi_unlikely_error(ret)) {
+ report_table_id_find_error("LoadTable", &table_id, ret);
+ return ret;
+ }
+ uacpi_table_mark_as_loaded(table.index);
+
+ item_array_at(items, 2)->immediate = table.index;
+ method = item_array_at(items, 1)->obj->method;
+ prepare_table_load(table.hdr, UACPI_TABLE_LOAD_CAUSE_LOAD_TABLE_OP, method);
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status handle_load(struct execution_context *ctx)
+{
+ uacpi_status ret;
+ struct item_array *items = &ctx->cur_op_ctx->items;
+ uacpi_table table;
+ uacpi_control_method *method;
+ uacpi_object *src;
+ struct acpi_sdt_hdr *src_table = UACPI_NULL;
+ void *table_buffer;
+ uacpi_size declared_size;
+ uacpi_bool unmap_src = UACPI_FALSE;
+
+ /*
+ * If we already have the last true/false object loaded, this is a second
+ * invocation of this handler. For the second invocation we simply want to
+ * detect new AML GPE handlers that might've been loaded.
+ * We do this only if table load was successful though.
+ */
+ if (item_array_size(items) == 5) {
+ if (item_array_at(items, 4)->obj->integer != 0)
+ uacpi_events_match_post_dynamic_table_load();
+ return UACPI_STATUS_OK;
+ }
+
+ src = item_array_at(items, 2)->obj;
+
+ switch (src->type) {
+ case UACPI_OBJECT_OPERATION_REGION: {
+ uacpi_operation_region *op_region;
+
+ op_region = src->op_region;
+ if (uacpi_unlikely(
+ op_region->space != UACPI_ADDRESS_SPACE_SYSTEM_MEMORY
+ )) {
+ uacpi_error("Load: operation region is not SystemMemory\n");
+ goto error_out;
+ }
+
+ if (uacpi_unlikely(op_region->length < sizeof(struct acpi_sdt_hdr))) {
+ uacpi_error(
+ "Load: operation region is too small: %"UACPI_PRIu64"\n",
+ UACPI_FMT64(op_region->length)
+ );
+ goto error_out;
+ }
+
+ src_table = uacpi_kernel_map(op_region->offset, op_region->length);
+ if (uacpi_unlikely(src_table == UACPI_NULL)) {
+ uacpi_error(
+ "Load: failed to map operation region "
+ "0x%016"UACPI_PRIX64" -> 0x%016"UACPI_PRIX64"\n",
+ UACPI_FMT64(op_region->offset),
+ UACPI_FMT64(op_region->offset + op_region->length)
+ );
+ goto error_out;
+ }
+
+ unmap_src = UACPI_TRUE;
+ declared_size = op_region->length;
+ break;
+ }
+
+ case UACPI_OBJECT_BUFFER: {
+ uacpi_buffer *buffer;
+
+ buffer = src->buffer;
+ if (buffer->size < sizeof(struct acpi_sdt_hdr)) {
+ uacpi_error(
+ "Load: buffer is too small: %zu\n",
+ buffer->size
+ );
+ goto error_out;
+ }
+
+ src_table = buffer->data;
+ declared_size = buffer->size;
+ break;
+ }
+
+ default:
+ uacpi_error(
+ "Load: invalid argument '%s', expected "
+ "Buffer/Field/OperationRegion\n",
+ uacpi_object_type_to_string(src->type)
+ );
+ goto error_out;
+ }
+
+ if (uacpi_unlikely(src_table->length > declared_size)) {
+ uacpi_error(
+ "Load: table size %u is larger than the declared size %zu\n",
+ src_table->length, declared_size
+ );
+ goto error_out;
+ }
+
+ if (uacpi_unlikely(src_table->length < sizeof(struct acpi_sdt_hdr))) {
+ uacpi_error("Load: table size %u is too small\n", src_table->length);
+ goto error_out;
+ }
+
+ table_buffer = uacpi_kernel_alloc(src_table->length);
+ if (uacpi_unlikely(table_buffer == UACPI_NULL))
+ goto error_out;
+
+ uacpi_memcpy(table_buffer, src_table, src_table->length);
+
+ if (unmap_src) {
+ uacpi_kernel_unmap(src_table, declared_size);
+ unmap_src = UACPI_FALSE;
+ }
+
+ ret = uacpi_table_install_with_origin(
+ table_buffer, UACPI_TABLE_ORIGIN_FIRMWARE_VIRTUAL, &table
+ );
+ if (uacpi_unlikely_error(ret)) {
+ uacpi_free(table_buffer, src_table->length);
+
+ if (ret != UACPI_STATUS_OVERRIDDEN)
+ goto error_out;
+ }
+ uacpi_table_mark_as_loaded(table.index);
+
+ item_array_at(items, 0)->node = uacpi_namespace_root();
+
+ method = item_array_at(items, 1)->obj->method;
+ prepare_table_load(table.ptr, UACPI_TABLE_LOAD_CAUSE_LOAD_OP, method);
+
+ return UACPI_STATUS_OK;
+
+error_out:
+ if (unmap_src && src_table)
+ uacpi_kernel_unmap(src_table, declared_size);
+ return UACPI_STATUS_OK;
+}
+
+uacpi_status uacpi_execute_table(void *tbl, enum uacpi_table_load_cause cause)
+{
+ uacpi_status ret;
+
+ ret = uacpi_namespace_write_lock();
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ ret = do_load_table(uacpi_namespace_root(), tbl, cause);
+
+ uacpi_namespace_write_unlock();
+ return ret;
+}
+
+static uacpi_u32 get_field_length(struct item *item)
+{
+ struct package_length *pkg = &item->pkg;
+ return pkg->end - pkg->begin;
+}
+
+struct field_specific_data {
+ uacpi_namespace_node *region;
+ struct uacpi_field_unit *field0;
+ struct uacpi_field_unit *field1;
+ uacpi_u64 value;
+};
+
+static uacpi_status ensure_is_a_field_unit(uacpi_namespace_node *node,
+ uacpi_field_unit **out_field)
+{
+ uacpi_object *obj;
+
+ obj = uacpi_namespace_node_get_object(node);
+ if (obj->type != UACPI_OBJECT_FIELD_UNIT) {
+ uacpi_error(
+ "invalid argument: '%.4s' is not a field unit (%s)\n",
+ node->name.text, uacpi_object_type_to_string(obj->type)
+ );
+ return UACPI_STATUS_AML_INCOMPATIBLE_OBJECT_TYPE;
+ }
+
+ *out_field = obj->field_unit;
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status ensure_is_an_op_region(uacpi_namespace_node *node,
+ uacpi_namespace_node **out_node)
+{
+ uacpi_object *obj;
+
+ obj = uacpi_namespace_node_get_object(node);
+ if (obj->type != UACPI_OBJECT_OPERATION_REGION) {
+ uacpi_error(
+ "invalid argument: '%.4s' is not an operation region (%s)\n",
+ node->name.text, uacpi_object_type_to_string(obj->type)
+ );
+ return UACPI_STATUS_AML_INCOMPATIBLE_OBJECT_TYPE;
+ }
+
+ *out_node = node;
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status handle_create_field(struct execution_context *ctx)
+{
+ uacpi_status ret;
+ struct op_context *op_ctx = ctx->cur_op_ctx;
+ uacpi_namespace_node *node;
+ uacpi_object *obj, *connection_obj = UACPI_NULL;
+ struct field_specific_data field_data = { 0 };
+ uacpi_size i = 1, bit_offset = 0;
+ uacpi_u32 length, pin_offset = 0;
+
+ uacpi_u8 raw_value, access_type, lock_rule, update_rule;
+ uacpi_u8 access_attrib = 0, access_length = 0;
+
+ switch (op_ctx->op->code) {
+ case UACPI_AML_OP_FieldOp:
+ node = item_array_at(&op_ctx->items, i++)->node;
+ ret = ensure_is_an_op_region(node, &field_data.region);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+ break;
+
+ case UACPI_AML_OP_BankFieldOp:
+ node = item_array_at(&op_ctx->items, i++)->node;
+ ret = ensure_is_an_op_region(node, &field_data.region);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ node = item_array_at(&op_ctx->items, i++)->node;
+ ret = ensure_is_a_field_unit(node, &field_data.field0);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ field_data.value = item_array_at(&op_ctx->items, i++)->obj->integer;
+ break;
+
+ case UACPI_AML_OP_IndexFieldOp:
+ node = item_array_at(&op_ctx->items, i++)->node;
+ ret = ensure_is_a_field_unit(node, &field_data.field0);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ node = item_array_at(&op_ctx->items, i++)->node;
+ ret = ensure_is_a_field_unit(node, &field_data.field1);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+ break;
+
+ default:
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+
+ /*
+ * ByteData
+ * bit 0-3: AccessType
+ * 0 AnyAcc
+ * 1 ByteAcc
+ * 2 WordAcc
+ * 3 DWordAcc
+ * 4 QWordAcc
+ * 5 BufferAcc
+ * 6 Reserved
+ * 7-15 Reserved
+ * bit 4: LockRule
+ * 0 NoLock
+ * 1 Lock
+ * bit 5-6: UpdateRule
+ * 0 Preserve
+ * 1 WriteAsOnes
+ * 2 WriteAsZeros
+ * bit 7: Reserved (must be 0)
+ */
+ raw_value = item_array_at(&op_ctx->items, i++)->immediate;
+ access_type = (raw_value >> 0) & 0xF;
+ lock_rule = (raw_value >> 4) & 0x1;
+ update_rule = (raw_value >> 5) & 0x3;
+
+ while (i < item_array_size(&op_ctx->items)) {
+ struct item *item;
+ item = item_array_at(&op_ctx->items, i++);
+
+ // An actual field object
+ if (item->type == ITEM_NAMESPACE_NODE) {
+ uacpi_field_unit *field;
+
+ length = get_field_length(item_array_at(&op_ctx->items, i++));
+ node = item->node;
+
+ obj = item_array_at(&op_ctx->items, i++)->obj;
+ field = obj->field_unit;
+
+ field->update_rule = update_rule;
+ field->lock_rule = lock_rule;
+ field->attributes = access_attrib;
+ field->access_length = access_length;
+
+ /*
+ * 0 AnyAcc
+ * 1 ByteAcc
+ * 2 WordAcc
+ * 3 DWordAcc
+ * 4 QWordAcc
+ * 5 BufferAcc
+ * 6 Reserved
+ * 7-15 Reserved
+ */
+ switch (access_type) {
+ case 0:
+ // TODO: optimize to calculate best access strategy
+ UACPI_FALLTHROUGH;
+ case 1:
+ case 5:
+ field->access_width_bytes = 1;
+ break;
+ case 2:
+ field->access_width_bytes = 2;
+ break;
+ case 3:
+ field->access_width_bytes = 4;
+ break;
+ case 4:
+ field->access_width_bytes = 8;
+ break;
+ default:
+ uacpi_error("invalid field '%.4s' access type %d\n",
+ node->name.text, access_type);
+ return UACPI_STATUS_AML_BAD_ENCODING;
+ }
+
+ field->bit_length = length;
+ field->pin_offset = pin_offset;
+
+ // FIXME: overflow, OOB, etc checks
+ field->byte_offset = UACPI_ALIGN_DOWN(
+ bit_offset / 8,
+ field->access_width_bytes,
+ uacpi_u32
+ );
+
+ field->bit_offset_within_first_byte = bit_offset;
+ field->bit_offset_within_first_byte =
+ bit_offset & ((field->access_width_bytes * 8) - 1);
+
+ switch (op_ctx->op->code) {
+ case UACPI_AML_OP_FieldOp:
+ field->region = field_data.region;
+ uacpi_shareable_ref(field->region);
+
+ field->kind = UACPI_FIELD_UNIT_KIND_NORMAL;
+ break;
+
+ case UACPI_AML_OP_BankFieldOp:
+ field->bank_region = field_data.region;
+ uacpi_shareable_ref(field->bank_region);
+
+ field->bank_selection = field_data.field0;
+ uacpi_shareable_ref(field->bank_selection);
+
+ field->bank_value = field_data.value;
+ field->kind = UACPI_FIELD_UNIT_KIND_BANK;
+ break;
+
+ case UACPI_AML_OP_IndexFieldOp:
+ field->index = field_data.field0;
+ uacpi_shareable_ref(field->index);
+
+ field->data = field_data.field1;
+ uacpi_shareable_ref(field->data);
+
+ field->kind = UACPI_FIELD_UNIT_KIND_INDEX;
+ break;
+
+ default:
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+
+ field->connection = connection_obj;
+ if (field->connection)
+ uacpi_object_ref(field->connection);
+
+ node->object = uacpi_create_internal_reference(
+ UACPI_REFERENCE_KIND_NAMED, obj
+ );
+ if (uacpi_unlikely(node->object == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ ret = do_install_node_item(ctx->cur_frame, item);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ bit_offset += length;
+ pin_offset += length;
+ continue;
+ }
+
+ // All other stuff
+ switch ((int)item->immediate) {
+ // ReservedField := 0x00 PkgLength
+ case 0x00:
+ length = get_field_length(item_array_at(&op_ctx->items, i++));
+ bit_offset += length;
+ pin_offset += length;
+ break;
+
+ // AccessField := 0x01 AccessType AccessAttrib
+ // ExtendedAccessField := 0x03 AccessType ExtendedAccessAttrib AccessLength
+ case 0x01:
+ case 0x03:
+ raw_value = item_array_at(&op_ctx->items, i++)->immediate;
+
+ access_type = raw_value & 0xF;
+ access_attrib = (raw_value >> 6) & 0x3;
+
+ raw_value = item_array_at(&op_ctx->items, i++)->immediate;
+
+ /*
+ * Bits 7:6
+ * 0 = AccessAttrib = Normal Access Attributes
+ * 1 = AccessAttrib = AttribBytes (x)
+ * 2 = AccessAttrib = AttribRawBytes (x)
+ * 3 = AccessAttrib = AttribRawProcessBytes (x)
+ * x is encoded as bits 0:7 of the AccessAttrib byte.
+ */
+ if (access_attrib) {
+ switch (access_attrib) {
+ case 1:
+ access_attrib = UACPI_ACCESS_ATTRIBUTE_BYTES;
+ break;
+ case 2:
+ access_attrib = UACPI_ACCESS_ATTRIBUTE_RAW_BYTES;
+ break;
+ case 3:
+ access_attrib = UACPI_ACCESS_ATTRIBUTE_RAW_PROCESS_BYTES;
+ break;
+ }
+
+ access_length = raw_value;
+ } else { // Normal access attributes
+ access_attrib = raw_value;
+ }
+
+ if (item->immediate == 3)
+ access_length = item_array_at(&op_ctx->items, i++)->immediate;
+ break;
+
+ // ConnectField := <0x02 NameString> | <0x02 BufferData>
+ case 0x02:
+ connection_obj = item_array_at(&op_ctx->items, i++)->obj;
+ pin_offset = 0;
+ break;
+
+ default:
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+ }
+
+ return UACPI_STATUS_OK;
+}
+
+static void truncate_number_if_needed(uacpi_object *obj)
+{
+ if (!g_uacpi_rt_ctx.is_rev1)
+ return;
+
+ obj->integer &= 0xFFFFFFFF;
+}
+
+static uacpi_u64 ones(void)
+{
+ return g_uacpi_rt_ctx.is_rev1 ? 0xFFFFFFFF : 0xFFFFFFFFFFFFFFFF;
+}
+
+static uacpi_status method_get_ret_target(struct execution_context *ctx,
+ uacpi_object **out_operand)
+{
+ uacpi_size depth;
+
+ // Check if we're targeting the previous call frame
+ depth = call_frame_array_size(&ctx->call_stack);
+ if (depth > 1) {
+ struct op_context *op_ctx;
+ struct call_frame *frame;
+
+ frame = call_frame_array_at(&ctx->call_stack, depth - 2);
+ depth = op_context_array_size(&frame->pending_ops);
+
+ // Ok, no one wants the return value at call site. Discard it.
+ if (!depth) {
+ *out_operand = UACPI_NULL;
+ return UACPI_STATUS_OK;
+ }
+
+ op_ctx = op_context_array_at(&frame->pending_ops, depth - 1);
+
+ /*
+ * Prevent the table being dynamically loaded from attempting to return
+ * a value to the caller. This is unlikely to be ever encountered in the
+ * wild, but we should still guard against the possibility.
+ */
+ if (uacpi_unlikely(op_ctx->op->code == UACPI_AML_OP_LoadOp ||
+ op_ctx->op->code == UACPI_AML_OP_LoadTableOp)) {
+ *out_operand = UACPI_NULL;
+ return UACPI_STATUS_OK;
+ }
+
+ *out_operand = item_array_last(&op_ctx->items)->obj;
+ return UACPI_STATUS_OK;
+ }
+
+ return UACPI_STATUS_NOT_FOUND;
+}
+
+static uacpi_status method_get_ret_object(struct execution_context *ctx,
+ uacpi_object **out_obj)
+{
+ uacpi_status ret;
+
+ ret = method_get_ret_target(ctx, out_obj);
+ if (ret == UACPI_STATUS_NOT_FOUND) {
+ *out_obj = ctx->ret;
+ return UACPI_STATUS_OK;
+ }
+ if (ret != UACPI_STATUS_OK || *out_obj == UACPI_NULL)
+ return ret;
+
+ *out_obj = uacpi_unwrap_internal_reference(*out_obj);
+ return UACPI_STATUS_OK;
+}
+
+static struct code_block *find_last_block(struct code_block_array *blocks,
+ enum code_block_type type)
+{
+ uacpi_size i;
+
+ i = code_block_array_size(blocks);
+ while (i-- > 0) {
+ struct code_block *block;
+
+ block = code_block_array_at(blocks, i);
+ if (block->type == type)
+ return block;
+ }
+
+ return UACPI_NULL;
+}
+
+static void update_scope(struct call_frame *frame)
+{
+ struct code_block *block;
+
+ block = find_last_block(&frame->code_blocks, CODE_BLOCK_SCOPE);
+ if (block == UACPI_NULL) {
+ frame->cur_scope = uacpi_namespace_root();
+ return;
+ }
+
+ frame->cur_scope = block->node;
+}
+
+static uacpi_status begin_block_execution(struct execution_context *ctx)
+{
+ struct call_frame *cur_frame = ctx->cur_frame;
+ struct op_context *op_ctx = ctx->cur_op_ctx;
+ struct package_length *pkg;
+ struct code_block *block;
+
+ block = code_block_array_alloc(&cur_frame->code_blocks);
+ if (uacpi_unlikely(block == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ pkg = &item_array_at(&op_ctx->items, 0)->pkg;
+
+ // Disarm the tracked package so that we don't skip the Scope
+ op_ctx->tracked_pkg_idx = 0;
+
+ switch (op_ctx->op->code) {
+ case UACPI_AML_OP_IfOp:
+ block->type = CODE_BLOCK_IF;
+ break;
+ case UACPI_AML_OP_ElseOp:
+ block->type = CODE_BLOCK_ELSE;
+ break;
+ case UACPI_AML_OP_WhileOp:
+ block->type = CODE_BLOCK_WHILE;
+
+ if (pkg->begin == cur_frame->prev_while_code_offset) {
+ uacpi_u64 cur_ticks;
+
+ cur_ticks = uacpi_kernel_get_nanoseconds_since_boot();
+
+ if (uacpi_unlikely(cur_ticks > block->expiration_point)) {
+ uacpi_error("loop time out after running for %u seconds\n",
+ g_uacpi_rt_ctx.loop_timeout_seconds);
+ code_block_array_pop(&cur_frame->code_blocks);
+ return UACPI_STATUS_AML_LOOP_TIMEOUT;
+ }
+
+ block->expiration_point = cur_frame->prev_while_expiration;
+ } else {
+ /*
+ * Calculate the expiration point for this loop.
+ * If a loop is executed past this point, it will get aborted.
+ */
+ block->expiration_point = uacpi_kernel_get_nanoseconds_since_boot();
+ block->expiration_point +=
+ g_uacpi_rt_ctx.loop_timeout_seconds * UACPI_NANOSECONDS_PER_SEC;
+ }
+ break;
+ case UACPI_AML_OP_ScopeOp:
+ case UACPI_AML_OP_DeviceOp:
+ case UACPI_AML_OP_ProcessorOp:
+ case UACPI_AML_OP_PowerResOp:
+ case UACPI_AML_OP_ThermalZoneOp:
+ block->type = CODE_BLOCK_SCOPE;
+ block->node = item_array_at(&op_ctx->items, 1)->node;
+ break;
+ default:
+ code_block_array_pop(&cur_frame->code_blocks);
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+
+ // -1 because we want to re-evaluate at the start of the op next time
+ block->begin = pkg->begin - 1;
+ block->end = pkg->end;
+ ctx->cur_block = block;
+
+ cur_frame->last_while = find_last_block(&cur_frame->code_blocks,
+ CODE_BLOCK_WHILE);
+ update_scope(cur_frame);
+ return UACPI_STATUS_OK;
+}
+
+static void frame_reset_post_end_block(struct execution_context *ctx,
+ enum code_block_type type)
+{
+ struct call_frame *frame = ctx->cur_frame;
+
+ if (type == CODE_BLOCK_WHILE) {
+ struct code_block *block = ctx->cur_block;
+
+ // + 1 here to skip the WhileOp and get to the PkgLength
+ frame->prev_while_code_offset = block->begin + 1;
+ frame->prev_while_expiration = block->expiration_point;
+ }
+
+ code_block_array_pop(&frame->code_blocks);
+ ctx->cur_block = code_block_array_last(&frame->code_blocks);
+
+ if (type == CODE_BLOCK_WHILE) {
+ frame->last_while = find_last_block(&frame->code_blocks, type);
+ } else if (type == CODE_BLOCK_SCOPE) {
+ update_scope(frame);
+ }
+}
+
+static void debug_store_no_recurse(const uacpi_char *prefix, uacpi_object *src)
+{
+ switch (src->type) {
+ case UACPI_OBJECT_UNINITIALIZED:
+ uacpi_trace("%s Uninitialized\n", prefix);
+ break;
+ case UACPI_OBJECT_STRING:
+ uacpi_trace("%s String => \"%s\"\n", prefix, src->buffer->text);
+ break;
+ case UACPI_OBJECT_INTEGER:
+ if (g_uacpi_rt_ctx.is_rev1) {
+ uacpi_trace(
+ "%s Integer => 0x%08X\n", prefix, (uacpi_u32)src->integer
+ );
+ } else {
+ uacpi_trace(
+ "%s Integer => 0x%016"UACPI_PRIX64"\n", prefix,
+ UACPI_FMT64(src->integer)
+ );
+ }
+ break;
+ case UACPI_OBJECT_REFERENCE:
+ uacpi_trace("%s Reference @%p => %p\n", prefix, src, src->inner_object);
+ break;
+ case UACPI_OBJECT_PACKAGE:
+ uacpi_trace(
+ "%s Package @%p (%p) (%zu elements)\n",
+ prefix, src, src->package, src->package->count
+ );
+ break;
+ case UACPI_OBJECT_BUFFER:
+ uacpi_trace(
+ "%s Buffer @%p (%p) (%zu bytes)\n",
+ prefix, src, src->buffer, src->buffer->size
+ );
+ break;
+ case UACPI_OBJECT_OPERATION_REGION:
+ uacpi_trace(
+ "%s OperationRegion (ASID %d) 0x%016"UACPI_PRIX64
+ " -> 0x%016"UACPI_PRIX64"\n", prefix,
+ src->op_region->space, UACPI_FMT64(src->op_region->offset),
+ UACPI_FMT64(src->op_region->offset + src->op_region->length)
+ );
+ break;
+ case UACPI_OBJECT_POWER_RESOURCE:
+ uacpi_trace(
+ "%s Power Resource %d %d\n",
+ prefix, src->power_resource.system_level,
+ src->power_resource.resource_order
+ );
+ break;
+ case UACPI_OBJECT_PROCESSOR:
+ uacpi_trace(
+ "%s Processor[%d] 0x%08X (%d)\n",
+ prefix, src->processor->id, src->processor->block_address,
+ src->processor->block_length
+ );
+ break;
+ case UACPI_OBJECT_BUFFER_INDEX:
+ uacpi_trace(
+ "%s Buffer Index %p[%zu] => 0x%02X\n",
+ prefix, src->buffer_index.buffer->data, src->buffer_index.idx,
+ *buffer_index_cursor(&src->buffer_index)
+ );
+ break;
+ case UACPI_OBJECT_MUTEX:
+ uacpi_trace(
+ "%s Mutex @%p (%p => %p) sync level %d\n",
+ prefix, src, src->mutex, src->mutex->handle,
+ src->mutex->sync_level
+ );
+ break;
+ case UACPI_OBJECT_METHOD:
+ uacpi_trace("%s Method @%p (%p)\n", prefix, src, src->method);
+ break;
+ default:
+ uacpi_trace(
+ "%s %s @%p\n",
+ prefix, uacpi_object_type_to_string(src->type), src
+ );
+ }
+}
+
+static uacpi_status debug_store(uacpi_object *src)
+{
+ /*
+ * Don't bother running the body if current log level is not set to trace.
+ * All DebugOp logging is done as TRACE exclusively.
+ */
+ if (!uacpi_should_log(UACPI_LOG_TRACE))
+ return UACPI_STATUS_OK;
+
+ src = uacpi_unwrap_internal_reference(src);
+
+ debug_store_no_recurse("[AML DEBUG]", src);
+
+ if (src->type == UACPI_OBJECT_PACKAGE) {
+ uacpi_package *pkg = src->package;
+ uacpi_size i;
+
+ for (i = 0; i < pkg->count; ++i) {
+ uacpi_object *obj = pkg->objects[i];
+ if (obj->type == UACPI_OBJECT_REFERENCE &&
+ obj->flags == UACPI_REFERENCE_KIND_PKG_INDEX)
+ obj = obj->inner_object;
+
+ debug_store_no_recurse("Element:", obj);
+ }
+ }
+
+ return UACPI_STATUS_OK;
+}
+
+/*
+ * NOTE: this function returns the parent object
+ */
+static uacpi_object *reference_unwind(uacpi_object *obj)
+{
+ uacpi_object *parent = obj;
+
+ while (obj) {
+ if (obj->type != UACPI_OBJECT_REFERENCE)
+ return parent;
+
+ parent = obj;
+ obj = parent->inner_object;
+ }
+
+ // This should be unreachable
+ return UACPI_NULL;
+}
+
+static uacpi_iteration_decision opregion_try_detach_from_parent(
+ void *user, uacpi_namespace_node *node, uacpi_u32 node_depth
+)
+{
+ uacpi_object *target_object = user;
+ UACPI_UNUSED(node_depth);
+
+ if (node->object == target_object) {
+ uacpi_opregion_uninstall_handler(node);
+ return UACPI_ITERATION_DECISION_BREAK;
+ }
+
+ return UACPI_ITERATION_DECISION_CONTINUE;
+}
+
+static void object_replace_child(uacpi_object *parent, uacpi_object *new_child)
+{
+ if (parent->flags == UACPI_REFERENCE_KIND_NAMED &&
+ uacpi_object_is(parent->inner_object, UACPI_OBJECT_OPERATION_REGION)) {
+
+ /*
+ * We're doing a CopyObject or similar to a namespace node that is an
+ * operation region. Try to find the parent node and manually detach
+ * the handler.
+ */
+ opregion_try_detach_from_parent(parent, uacpi_namespace_root(), 0);
+ uacpi_namespace_do_for_each_child(
+ uacpi_namespace_root(), opregion_try_detach_from_parent, UACPI_NULL,
+ UACPI_OBJECT_OPERATION_REGION_BIT, UACPI_MAX_DEPTH_ANY,
+ UACPI_SHOULD_LOCK_NO, UACPI_PERMANENT_ONLY_NO, parent
+ );
+ }
+
+ uacpi_object_detach_child(parent);
+ uacpi_object_attach_child(parent, new_child);
+}
+
+/*
+ * Breakdown of what happens here:
+ *
+ * CopyObject(..., Obj) where Obj is:
+ * 1. LocalX -> Overwrite LocalX.
+ * 2. NAME -> Overwrite NAME.
+ * 3. ArgX -> Overwrite ArgX unless ArgX is a reference, in that case
+ * overwrite the referenced object.
+ * 4. RefOf -> Not allowed here.
+ * 5. Index -> Overwrite Object stored at the index.
+ */
+ static uacpi_status copy_object_to_reference(uacpi_object *dst,
+ uacpi_object *src)
+{
+ uacpi_status ret;
+ uacpi_object *src_obj, *new_obj;
+
+ switch (dst->flags) {
+ case UACPI_REFERENCE_KIND_ARG: {
+ uacpi_object *referenced_obj;
+
+ referenced_obj = uacpi_unwrap_internal_reference(dst);
+ if (referenced_obj->type == UACPI_OBJECT_REFERENCE) {
+ dst = reference_unwind(referenced_obj);
+ break;
+ }
+
+ UACPI_FALLTHROUGH;
+ }
+ case UACPI_REFERENCE_KIND_LOCAL:
+ case UACPI_REFERENCE_KIND_PKG_INDEX:
+ case UACPI_REFERENCE_KIND_NAMED:
+ break;
+ default:
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+
+ src_obj = uacpi_unwrap_internal_reference(src);
+
+ new_obj = uacpi_create_object(UACPI_OBJECT_UNINITIALIZED);
+ if (uacpi_unlikely(new_obj == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ ret = uacpi_object_assign(new_obj, src_obj,
+ UACPI_ASSIGN_BEHAVIOR_DEEP_COPY);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ object_replace_child(dst, new_obj);
+ uacpi_object_unref(new_obj);
+
+ return UACPI_STATUS_OK;
+}
+
+/*
+ * if Store(..., Obj) where Obj is:
+ * 1. LocalX/Index -> OVERWRITE unless the object is a reference, in that
+ * case store to the referenced object _with_ implicit
+ * cast.
+ * 2. ArgX -> OVERWRITE unless the object is a reference, in that
+ * case OVERWRITE the referenced object.
+ * 3. NAME -> Store with implicit cast.
+ * 4. RefOf -> Not allowed here.
+ */
+static uacpi_status store_to_reference(
+ uacpi_object *dst, uacpi_object *src, uacpi_data_view *wtr_response
+)
+{
+ uacpi_object *src_obj;
+ uacpi_bool overwrite = UACPI_FALSE;
+
+ switch (dst->flags) {
+ case UACPI_REFERENCE_KIND_LOCAL:
+ case UACPI_REFERENCE_KIND_ARG:
+ case UACPI_REFERENCE_KIND_PKG_INDEX: {
+ uacpi_object *referenced_obj;
+
+ if (dst->flags == UACPI_REFERENCE_KIND_PKG_INDEX)
+ referenced_obj = dst->inner_object;
+ else
+ referenced_obj = uacpi_unwrap_internal_reference(dst);
+
+ if (referenced_obj->type == UACPI_OBJECT_REFERENCE) {
+ overwrite = dst->flags == UACPI_REFERENCE_KIND_ARG;
+ dst = reference_unwind(referenced_obj);
+ break;
+ }
+
+ overwrite = UACPI_TRUE;
+ break;
+ }
+ case UACPI_REFERENCE_KIND_NAMED:
+ dst = reference_unwind(dst);
+ break;
+ default:
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+
+ src_obj = uacpi_unwrap_internal_reference(src);
+ overwrite |= dst->inner_object->type == UACPI_OBJECT_UNINITIALIZED;
+
+ if (overwrite) {
+ uacpi_status ret;
+ uacpi_object *new_obj;
+
+ new_obj = uacpi_create_object(UACPI_OBJECT_UNINITIALIZED);
+ if (uacpi_unlikely(new_obj == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ ret = uacpi_object_assign(new_obj, src_obj,
+ UACPI_ASSIGN_BEHAVIOR_DEEP_COPY);
+ if (uacpi_unlikely_error(ret)) {
+ uacpi_object_unref(new_obj);
+ return ret;
+ }
+
+ object_replace_child(dst, new_obj);
+ uacpi_object_unref(new_obj);
+ return UACPI_STATUS_OK;
+ }
+
+ return object_assign_with_implicit_cast(
+ dst->inner_object, src_obj, wtr_response
+ );
+}
+
+static uacpi_status handle_ref_or_deref_of(struct execution_context *ctx)
+{
+ struct op_context *op_ctx = ctx->cur_op_ctx;
+ uacpi_object *dst, *src;
+
+ src = item_array_at(&op_ctx->items, 0)->obj;
+
+ if (op_ctx->op->code == UACPI_AML_OP_CondRefOfOp)
+ dst = item_array_at(&op_ctx->items, 2)->obj;
+ else
+ dst = item_array_at(&op_ctx->items, 1)->obj;
+
+ if (op_ctx->op->code == UACPI_AML_OP_DerefOfOp) {
+ uacpi_bool was_a_reference = UACPI_FALSE;
+
+ if (src->type == UACPI_OBJECT_REFERENCE) {
+ was_a_reference = UACPI_TRUE;
+
+ /*
+ * Explicit dereferencing [DerefOf] behavior:
+ * Simply grabs the bottom-most object that is not a reference.
+ * This mimics the behavior of NT Acpi.sys: any DerfOf fetches
+ * the bottom-most reference. Note that this is different from
+ * ACPICA where DerefOf dereferences one level.
+ */
+ src = reference_unwind(src)->inner_object;
+ }
+
+ if (src->type == UACPI_OBJECT_BUFFER_INDEX) {
+ uacpi_buffer_index *buf_idx = &src->buffer_index;
+
+ dst->type = UACPI_OBJECT_INTEGER;
+ uacpi_memcpy_zerout(
+ &dst->integer, buffer_index_cursor(buf_idx),
+ sizeof(dst->integer), 1
+ );
+ return UACPI_STATUS_OK;
+ }
+
+ if (!was_a_reference) {
+ uacpi_error(
+ "invalid DerefOf argument: %s, expected a reference\n",
+ uacpi_object_type_to_string(src->type)
+ );
+ return UACPI_STATUS_AML_INCOMPATIBLE_OBJECT_TYPE;
+ }
+
+ return uacpi_object_assign(dst, src,
+ UACPI_ASSIGN_BEHAVIOR_SHALLOW_COPY);
+ }
+
+ dst->type = UACPI_OBJECT_REFERENCE;
+ dst->inner_object = src;
+ uacpi_object_ref(src);
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status do_binary_math(
+ uacpi_object *arg0, uacpi_object *arg1,
+ uacpi_object *tgt0, uacpi_object *tgt1,
+ uacpi_aml_op op
+)
+{
+ uacpi_u64 lhs, rhs, res;
+ uacpi_bool should_negate = UACPI_FALSE;
+
+ lhs = arg0->integer;
+ rhs = arg1->integer;
+
+ switch (op)
+ {
+ case UACPI_AML_OP_AddOp:
+ res = lhs + rhs;
+ break;
+ case UACPI_AML_OP_SubtractOp:
+ res = lhs - rhs;
+ break;
+ case UACPI_AML_OP_MultiplyOp:
+ res = lhs * rhs;
+ break;
+ case UACPI_AML_OP_ShiftLeftOp:
+ case UACPI_AML_OP_ShiftRightOp:
+ if (rhs <= (g_uacpi_rt_ctx.is_rev1 ? 31 : 63)) {
+ if (op == UACPI_AML_OP_ShiftLeftOp)
+ res = lhs << rhs;
+ else
+ res = lhs >> rhs;
+ } else {
+ res = 0;
+ }
+ break;
+ case UACPI_AML_OP_NandOp:
+ should_negate = UACPI_TRUE;
+ UACPI_FALLTHROUGH;
+ case UACPI_AML_OP_AndOp:
+ res = rhs & lhs;
+ break;
+ case UACPI_AML_OP_NorOp:
+ should_negate = UACPI_TRUE;
+ UACPI_FALLTHROUGH;
+ case UACPI_AML_OP_OrOp:
+ res = rhs | lhs;
+ break;
+ case UACPI_AML_OP_XorOp:
+ res = rhs ^ lhs;
+ break;
+ case UACPI_AML_OP_DivideOp:
+ if (uacpi_unlikely(rhs == 0)) {
+ uacpi_error("attempted to divide by zero\n");
+ return UACPI_STATUS_AML_BAD_ENCODING;
+ }
+ tgt1->integer = lhs / rhs;
+ res = lhs % rhs;
+ break;
+ case UACPI_AML_OP_ModOp:
+ if (uacpi_unlikely(rhs == 0)) {
+ uacpi_error("attempted to calculate modulo of zero\n");
+ return UACPI_STATUS_AML_BAD_ENCODING;
+ }
+ res = lhs % rhs;
+ break;
+ default:
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+
+ if (should_negate)
+ res = ~res;
+
+ tgt0->integer = res;
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status handle_binary_math(struct execution_context *ctx)
+{
+ uacpi_object *arg0, *arg1, *tgt0, *tgt1;
+ struct item_array *items = &ctx->cur_op_ctx->items;
+ uacpi_aml_op op = ctx->cur_op_ctx->op->code;
+
+ arg0 = item_array_at(items, 0)->obj;
+ arg1 = item_array_at(items, 1)->obj;
+
+ if (op == UACPI_AML_OP_DivideOp) {
+ tgt0 = item_array_at(items, 4)->obj;
+ tgt1 = item_array_at(items, 5)->obj;
+ } else {
+ tgt0 = item_array_at(items, 3)->obj;
+ tgt1 = UACPI_NULL;
+ }
+
+ return do_binary_math(arg0, arg1, tgt0, tgt1, op);
+}
+
+static uacpi_status handle_unary_math(struct execution_context *ctx)
+{
+ uacpi_object *arg, *tgt;
+ struct item_array *items = &ctx->cur_op_ctx->items;
+ uacpi_aml_op op = ctx->cur_op_ctx->op->code;
+
+ arg = item_array_at(items, 0)->obj;
+ tgt = item_array_at(items, 2)->obj;
+
+ switch (op) {
+ case UACPI_AML_OP_NotOp:
+ tgt->integer = ~arg->integer;
+ truncate_number_if_needed(tgt);
+ break;
+ case UACPI_AML_OP_FindSetRightBitOp:
+ tgt->integer = uacpi_bit_scan_forward(arg->integer);
+ break;
+ case UACPI_AML_OP_FindSetLeftBitOp:
+ tgt->integer = uacpi_bit_scan_backward(arg->integer);
+ break;
+ default:
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status ensure_valid_idx(uacpi_object *obj, uacpi_size idx,
+ uacpi_size src_size)
+{
+ if (uacpi_likely(idx < src_size))
+ return UACPI_STATUS_OK;
+
+ uacpi_error(
+ "invalid index %zu, %s@%p has %zu elements\n",
+ idx, uacpi_object_type_to_string(obj->type), obj, src_size
+ );
+ return UACPI_STATUS_AML_OUT_OF_BOUNDS_INDEX;
+}
+
+static uacpi_status handle_index(struct execution_context *ctx)
+{
+ uacpi_status ret;
+ struct op_context *op_ctx = ctx->cur_op_ctx;
+ uacpi_object *src;
+ struct item *dst;
+ uacpi_size idx;
+
+ src = item_array_at(&op_ctx->items, 0)->obj;
+ idx = item_array_at(&op_ctx->items, 1)->obj->integer;
+ dst = item_array_at(&op_ctx->items, 3);
+
+ switch (src->type) {
+ case UACPI_OBJECT_BUFFER:
+ case UACPI_OBJECT_STRING: {
+ uacpi_buffer_index *buf_idx;
+ uacpi_data_view buf;
+ get_object_storage(src, &buf, UACPI_FALSE);
+
+ ret = ensure_valid_idx(src, idx, buf.length);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ dst->type = ITEM_OBJECT;
+ dst->obj = uacpi_create_object(UACPI_OBJECT_BUFFER_INDEX);
+ if (uacpi_unlikely(dst->obj == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ buf_idx = &dst->obj->buffer_index;
+ buf_idx->idx = idx;
+ buf_idx->buffer = src->buffer;
+ uacpi_shareable_ref(buf_idx->buffer);
+
+ break;
+ }
+ case UACPI_OBJECT_PACKAGE: {
+ uacpi_package *pkg = src->package;
+ uacpi_object *obj;
+
+ ret = ensure_valid_idx(src, idx, pkg->count);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ /*
+ * Lazily transform the package element into an internal reference
+ * to itself of type PKG_INDEX. This is needed to support stuff like
+ * CopyObject(..., Index(pkg, X)) where the new object must be
+ * propagated to anyone else with a currently alive index object.
+ *
+ * Sidenote: Yes, IndexOp is not a SimpleName, so technically it is
+ * illegal to CopyObject to it. However, yet again we fall
+ * victim to the NT ACPI driver implementation, which allows
+ * it just fine.
+ */
+ obj = pkg->objects[idx];
+ if (obj->type != UACPI_OBJECT_REFERENCE ||
+ obj->flags != UACPI_REFERENCE_KIND_PKG_INDEX) {
+
+ obj = uacpi_create_internal_reference(
+ UACPI_REFERENCE_KIND_PKG_INDEX, obj
+ );
+ if (uacpi_unlikely(obj == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ pkg->objects[idx] = obj;
+ uacpi_object_unref(obj->inner_object);
+ }
+
+ dst->obj = obj;
+ dst->type = ITEM_OBJECT;
+ uacpi_object_ref(dst->obj);
+ break;
+ }
+ default:
+ uacpi_error(
+ "invalid argument for Index: %s, "
+ "expected String/Buffer/Package\n",
+ uacpi_object_type_to_string(src->type)
+ );
+ return UACPI_STATUS_AML_INCOMPATIBLE_OBJECT_TYPE;
+ }
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_u64 object_to_integer(const uacpi_object *obj,
+ uacpi_size max_buffer_bytes)
+{
+ uacpi_u64 dst;
+
+ switch (obj->type) {
+ case UACPI_OBJECT_INTEGER:
+ dst = obj->integer;
+ break;
+ case UACPI_OBJECT_BUFFER: {
+ uacpi_size bytes;
+ bytes = UACPI_MIN(max_buffer_bytes, obj->buffer->size);
+ uacpi_memcpy_zerout(&dst, obj->buffer->data, sizeof(dst), bytes);
+ break;
+ }
+ case UACPI_OBJECT_STRING:
+ uacpi_string_to_integer(
+ obj->buffer->text, obj->buffer->size, UACPI_BASE_AUTO, &dst
+ );
+ break;
+ default:
+ dst = 0;
+ break;
+ }
+
+ return dst;
+}
+
+static uacpi_status integer_to_string(
+ uacpi_u64 integer, uacpi_buffer *str, uacpi_bool is_hex
+)
+{
+ int repr_len;
+ uacpi_char int_buf[21];
+ uacpi_size final_size;
+
+ repr_len = uacpi_snprintf(
+ int_buf, sizeof(int_buf),
+ is_hex ? "%"UACPI_PRIX64 : "%"UACPI_PRIu64,
+ UACPI_FMT64(integer)
+ );
+ if (uacpi_unlikely(repr_len < 0))
+ return UACPI_STATUS_INVALID_ARGUMENT;
+
+ // 0x prefix + repr + \0
+ final_size = (is_hex ? 2 : 0) + repr_len + 1;
+
+ str->data = uacpi_kernel_alloc(final_size);
+ if (uacpi_unlikely(str->data == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ if (is_hex) {
+ str->text[0] = '0';
+ str->text[1] = 'x';
+ }
+ uacpi_memcpy(str->text + (is_hex ? 2 : 0), int_buf, repr_len + 1);
+ str->size = final_size;
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status buffer_to_string(
+ uacpi_buffer *buf, uacpi_buffer *str, uacpi_bool is_hex
+)
+{
+ int repr_len;
+ uacpi_char int_buf[5];
+ uacpi_size i, final_size;
+ uacpi_char *cursor;
+
+ if (is_hex) {
+ final_size = 4 * buf->size;
+ } else {
+ final_size = 0;
+
+ for (i = 0; i < buf->size; ++i) {
+ uacpi_u8 value = ((uacpi_u8*)buf->data)[i];
+
+ if (value < 10)
+ final_size += 1;
+ else if (value < 100)
+ final_size += 2;
+ else
+ final_size += 3;
+ }
+ }
+
+ // Comma for every value but one
+ final_size += buf->size - 1;
+
+ // Null terminator
+ final_size += 1;
+
+ str->data = uacpi_kernel_alloc(final_size);
+ if (uacpi_unlikely(str->data == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ cursor = str->data;
+
+ for (i = 0; i < buf->size; ++i) {
+ repr_len = uacpi_snprintf(
+ int_buf, sizeof(int_buf),
+ is_hex ? "0x%02X" : "%d",
+ ((uacpi_u8*)buf->data)[i]
+ );
+ if (uacpi_unlikely(repr_len < 0)) {
+ uacpi_free(str->data, final_size);
+ str->data = UACPI_NULL;
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+
+ uacpi_memcpy(cursor, int_buf, repr_len + 1);
+ cursor += repr_len;
+
+ if (i != buf->size - 1)
+ *cursor++ = ',';
+ }
+
+ str->size = final_size;
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status do_make_empty_object(uacpi_buffer *buf,
+ uacpi_bool is_string)
+{
+ buf->text = uacpi_kernel_alloc_zeroed(sizeof(uacpi_char));
+ if (uacpi_unlikely(buf->text == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ if (is_string)
+ buf->size = sizeof(uacpi_char);
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status make_null_string(uacpi_buffer *buf)
+{
+ return do_make_empty_object(buf, UACPI_TRUE);
+}
+
+static uacpi_status make_null_buffer(uacpi_buffer *buf)
+{
+ /*
+ * Allocate at least 1 byte just to be safe,
+ * even for empty buffers. We still set the
+ * size to 0 though.
+ */
+ return do_make_empty_object(buf, UACPI_FALSE);
+}
+
+static uacpi_status handle_to(struct execution_context *ctx)
+{
+ uacpi_status ret = UACPI_STATUS_OK;
+ struct op_context *op_ctx = ctx->cur_op_ctx;
+ uacpi_object *src, *dst;
+
+ src = item_array_at(&op_ctx->items, 0)->obj;
+ dst = item_array_at(&op_ctx->items, 2)->obj;
+
+ switch (op_ctx->op->code) {
+ case UACPI_AML_OP_ToIntegerOp:
+ // NT always takes the first 8 bytes, even for revision 1
+ dst->integer = object_to_integer(src, 8);
+ break;
+
+ case UACPI_AML_OP_ToHexStringOp:
+ case UACPI_AML_OP_ToDecimalStringOp: {
+ uacpi_bool is_hex = op_ctx->op->code == UACPI_AML_OP_ToHexStringOp;
+
+ if (src->type == UACPI_OBJECT_INTEGER) {
+ ret = integer_to_string(src->integer, dst->buffer, is_hex);
+ break;
+ } else if (src->type == UACPI_OBJECT_BUFFER) {
+ if (uacpi_unlikely(src->buffer->size == 0))
+ return make_null_string(dst->buffer);
+
+ ret = buffer_to_string(src->buffer, dst->buffer, is_hex);
+ break;
+ }
+ UACPI_FALLTHROUGH;
+ }
+ case UACPI_AML_OP_ToBufferOp: {
+ uacpi_data_view buf;
+ uacpi_u8 *dst_buf;
+
+ ret = get_object_storage(src, &buf, UACPI_TRUE);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ if (uacpi_unlikely(buf.length == 0))
+ return make_null_buffer(dst->buffer);
+
+ dst_buf = uacpi_kernel_alloc(buf.length);
+ if (uacpi_unlikely(dst_buf == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ uacpi_memcpy(dst_buf, buf.bytes, buf.length);
+ dst->buffer->data = dst_buf;
+ dst->buffer->size = buf.length;
+ break;
+ }
+
+ default:
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+
+ return ret;
+}
+
+static uacpi_status handle_to_string(struct execution_context *ctx)
+{
+ struct op_context *op_ctx = ctx->cur_op_ctx;
+ uacpi_buffer *src_buf, *dst_buf;
+ uacpi_size req_len, len;
+
+ src_buf = item_array_at(&op_ctx->items, 0)->obj->buffer;
+ req_len = item_array_at(&op_ctx->items, 1)->obj->integer;
+ dst_buf = item_array_at(&op_ctx->items, 3)->obj->buffer;
+
+ len = UACPI_MIN(req_len, src_buf->size);
+ if (uacpi_unlikely(len == 0))
+ return make_null_string(dst_buf);
+
+ len = uacpi_strnlen(src_buf->text, len);
+
+ dst_buf->text = uacpi_kernel_alloc(len + 1);
+ if (uacpi_unlikely(dst_buf->text == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ uacpi_memcpy(dst_buf->text, src_buf->data, len);
+ dst_buf->text[len] = '\0';
+ dst_buf->size = len + 1;
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status handle_mid(struct execution_context *ctx)
+{
+ struct op_context *op_ctx = ctx->cur_op_ctx;
+ uacpi_object *src, *dst;
+ uacpi_data_view src_buf;
+ uacpi_buffer *dst_buf;
+ uacpi_size idx, len;
+ uacpi_bool is_string;
+
+ src = item_array_at(&op_ctx->items, 0)->obj;
+ if (uacpi_unlikely(src->type != UACPI_OBJECT_STRING &&
+ src->type != UACPI_OBJECT_BUFFER)) {
+ uacpi_error(
+ "invalid argument for Mid: %s, expected String/Buffer\n",
+ uacpi_object_type_to_string(src->type)
+ );
+ return UACPI_STATUS_AML_INCOMPATIBLE_OBJECT_TYPE;
+ }
+
+ idx = item_array_at(&op_ctx->items, 1)->obj->integer;
+ len = item_array_at(&op_ctx->items, 2)->obj->integer;
+ dst = item_array_at(&op_ctx->items, 4)->obj;
+ dst_buf = dst->buffer;
+
+ is_string = src->type == UACPI_OBJECT_STRING;
+ get_object_storage(src, &src_buf, UACPI_FALSE);
+
+ if (uacpi_unlikely(src_buf.length == 0 || idx >= src_buf.length ||
+ len == 0)) {
+ if (src->type == UACPI_OBJECT_STRING) {
+ dst->type = UACPI_OBJECT_STRING;
+ return make_null_string(dst_buf);
+ }
+
+ return make_null_buffer(dst_buf);
+ }
+
+ // Guaranteed to be at least 1 here
+ len = UACPI_MIN(len, src_buf.length - idx);
+
+ dst_buf->data = uacpi_kernel_alloc(len + is_string);
+ if (uacpi_unlikely(dst_buf->data == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ uacpi_memcpy(dst_buf->data, (uacpi_u8*)src_buf.bytes + idx, len);
+ dst_buf->size = len;
+
+ if (is_string) {
+ dst_buf->text[dst_buf->size++] = '\0';
+ dst->type = UACPI_OBJECT_STRING;
+ }
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status handle_concatenate(struct execution_context *ctx)
+{
+ uacpi_status ret = UACPI_STATUS_OK;
+ struct op_context *op_ctx = ctx->cur_op_ctx;
+ uacpi_object *arg0, *arg1, *dst;
+ uacpi_u8 *dst_buf;
+ uacpi_size buf_size = 0;
+
+ arg0 = item_array_at(&op_ctx->items, 0)->obj;
+ arg1 = item_array_at(&op_ctx->items, 1)->obj;
+ dst = item_array_at(&op_ctx->items, 3)->obj;
+
+ switch (arg0->type) {
+ case UACPI_OBJECT_INTEGER: {
+ uacpi_u64 arg1_as_int;
+ uacpi_size int_size;
+
+ int_size = sizeof_int();
+ buf_size = int_size * 2;
+
+ dst_buf = uacpi_kernel_alloc(buf_size);
+ if (uacpi_unlikely(dst_buf == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ arg1_as_int = object_to_integer(arg1, 8);
+
+ uacpi_memcpy(dst_buf, &arg0->integer, int_size);
+ uacpi_memcpy(dst_buf+ int_size, &arg1_as_int, int_size);
+ break;
+ }
+ case UACPI_OBJECT_BUFFER: {
+ uacpi_buffer *arg0_buf = arg0->buffer;
+ uacpi_data_view arg1_buf = { 0 };
+
+ get_object_storage(arg1, &arg1_buf, UACPI_TRUE);
+ buf_size = arg0_buf->size + arg1_buf.length;
+
+ dst_buf = uacpi_kernel_alloc(buf_size);
+ if (uacpi_unlikely(dst_buf == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ uacpi_memcpy(dst_buf, arg0_buf->data, arg0_buf->size);
+ uacpi_memcpy(dst_buf + arg0_buf->size, arg1_buf.bytes, arg1_buf.length);
+ break;
+ }
+ case UACPI_OBJECT_STRING: {
+ uacpi_char int_buf[17];
+ void *arg1_ptr;
+ uacpi_size arg0_size, arg1_size;
+ uacpi_buffer *arg0_buf = arg0->buffer;
+
+ switch (arg1->type) {
+ case UACPI_OBJECT_INTEGER: {
+ int size;
+ size = uacpi_snprintf(int_buf, sizeof(int_buf), "%"UACPI_PRIx64,
+ UACPI_FMT64(arg1->integer));
+ if (size < 0)
+ return UACPI_STATUS_INVALID_ARGUMENT;
+
+ arg1_ptr = int_buf;
+ arg1_size = size + 1;
+ break;
+ }
+ case UACPI_OBJECT_STRING:
+ arg1_ptr = arg1->buffer->data;
+ arg1_size = arg1->buffer->size;
+ break;
+ case UACPI_OBJECT_BUFFER: {
+ uacpi_buffer tmp_buf;
+
+ ret = buffer_to_string(arg1->buffer, &tmp_buf, UACPI_TRUE);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ arg1_ptr = tmp_buf.data;
+ arg1_size = tmp_buf.size;
+ break;
+ }
+ default:
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+
+ arg0_size = arg0_buf->size ? arg0_buf->size - 1 : arg0_buf->size;
+ buf_size = arg0_size + arg1_size;
+
+ dst_buf = uacpi_kernel_alloc(buf_size);
+ if (uacpi_unlikely(dst_buf == UACPI_NULL)) {
+ ret = UACPI_STATUS_OUT_OF_MEMORY;
+ goto cleanup;
+ }
+
+ uacpi_memcpy(dst_buf, arg0_buf->data, arg0_size);
+ uacpi_memcpy(dst_buf + arg0_size, arg1_ptr, arg1_size);
+ dst->type = UACPI_OBJECT_STRING;
+
+ cleanup:
+ if (arg1->type == UACPI_OBJECT_BUFFER)
+ uacpi_free(arg1_ptr, arg1_size);
+ break;
+ }
+ default:
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+
+ if (uacpi_likely_success(ret)) {
+ dst->buffer->data = dst_buf;
+ dst->buffer->size = buf_size;
+ }
+ return ret;
+}
+
+static uacpi_status handle_concatenate_res(struct execution_context *ctx)
+{
+ uacpi_status ret;
+ struct op_context *op_ctx = ctx->cur_op_ctx;
+ uacpi_data_view buffer;
+ uacpi_object *arg0, *arg1, *dst;
+ uacpi_u8 *dst_buf;
+ uacpi_size dst_size, arg0_size, arg1_size;
+
+ arg0 = item_array_at(&op_ctx->items, 0)->obj;
+ arg1 = item_array_at(&op_ctx->items, 1)->obj;
+ dst = item_array_at(&op_ctx->items, 3)->obj;
+
+ uacpi_buffer_to_view(arg0->buffer, &buffer);
+ ret = uacpi_find_aml_resource_end_tag(buffer, &arg0_size);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ uacpi_buffer_to_view(arg1->buffer, &buffer);
+ ret = uacpi_find_aml_resource_end_tag(buffer, &arg1_size);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ dst_size = arg0_size + arg1_size + sizeof(struct acpi_resource_end_tag);
+
+ dst_buf = uacpi_kernel_alloc(dst_size);
+ if (uacpi_unlikely(dst_buf == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ dst->buffer->data = dst_buf;
+ dst->buffer->size = dst_size;
+
+ uacpi_memcpy(dst_buf, arg0->buffer->data, arg0_size);
+ uacpi_memcpy(dst_buf + arg0_size, arg1->buffer->data, arg1_size);
+
+ /*
+ * Small item (0), End Tag (0x0F), length 1
+ * Leave the checksum as 0
+ */
+ dst_buf[dst_size - 2] =
+ (ACPI_RESOURCE_END_TAG << ACPI_SMALL_ITEM_NAME_IDX) |
+ (sizeof(struct acpi_resource_end_tag) - 1);
+ dst_buf[dst_size - 1] = 0;
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status handle_sizeof(struct execution_context *ctx)
+{
+ struct op_context *op_ctx = ctx->cur_op_ctx;
+ uacpi_object *src, *dst;
+
+ src = item_array_at(&op_ctx->items, 0)->obj;
+ dst = item_array_at(&op_ctx->items, 1)->obj;
+
+ if (uacpi_likely(src->type == UACPI_OBJECT_REFERENCE))
+ src = reference_unwind(src)->inner_object;
+
+ switch (src->type) {
+ case UACPI_OBJECT_STRING:
+ case UACPI_OBJECT_BUFFER: {
+ uacpi_data_view buf;
+ get_object_storage(src, &buf, UACPI_FALSE);
+
+ dst->integer = buf.length;
+ break;
+ }
+
+ case UACPI_OBJECT_PACKAGE:
+ dst->integer = src->package->count;
+ break;
+
+ default:
+ uacpi_error(
+ "invalid argument for Sizeof: %s, "
+ "expected String/Buffer/Package\n",
+ uacpi_object_type_to_string(src->type)
+ );
+ return UACPI_STATUS_AML_INCOMPATIBLE_OBJECT_TYPE;
+ }
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status handle_object_type(struct execution_context *ctx)
+{
+ struct op_context *op_ctx = ctx->cur_op_ctx;
+ uacpi_object *src, *dst;
+
+ src = item_array_at(&op_ctx->items, 0)->obj;
+ dst = item_array_at(&op_ctx->items, 1)->obj;
+
+ if (uacpi_likely(src->type == UACPI_OBJECT_REFERENCE))
+ src = reference_unwind(src)->inner_object;
+
+ dst->integer = src->type;
+ if (dst->integer == UACPI_OBJECT_BUFFER_INDEX)
+ dst->integer = UACPI_OBJECT_BUFFER_FIELD;
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status handle_timer(struct execution_context *ctx)
+{
+ struct op_context *op_ctx = ctx->cur_op_ctx;
+ uacpi_object *dst;
+
+ dst = item_array_at(&op_ctx->items, 0)->obj;
+ dst->integer = uacpi_kernel_get_nanoseconds_since_boot() / 100;
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status handle_stall_or_sleep(struct execution_context *ctx)
+{
+ struct op_context *op_ctx = ctx->cur_op_ctx;
+ uacpi_u64 time;
+
+ time = item_array_at(&op_ctx->items, 0)->obj->integer;
+
+ if (op_ctx->op->code == UACPI_AML_OP_SleepOp) {
+ /*
+ * ACPICA doesn't allow sleeps longer than 2 seconds,
+ * so we shouldn't either.
+ */
+ if (time > 2000)
+ time = 2000;
+
+ uacpi_namespace_write_unlock();
+ uacpi_kernel_sleep(time);
+ uacpi_namespace_write_lock();
+ } else {
+ // Spec says this must evaluate to a ByteData
+ if (time > 0xFF)
+ time = 0xFF;
+ uacpi_kernel_stall(time);
+ }
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status handle_bcd(struct execution_context *ctx)
+{
+ struct op_context *op_ctx = ctx->cur_op_ctx;
+ uacpi_u64 src, dst = 0;
+ uacpi_size i;
+ uacpi_object *dst_obj;
+
+ src = item_array_at(&op_ctx->items, 0)->obj->integer;
+ dst_obj = item_array_at(&op_ctx->items, 2)->obj;
+ i = 64;
+
+ /*
+ * NOTE: ACPICA just errors out for invalid BCD, but NT allows it just fine.
+ * FromBCD matches NT behavior 1:1 even for invalid BCD, but ToBCD
+ * produces different results when the input is too large.
+ */
+ if (op_ctx->op->code == UACPI_AML_OP_FromBCDOp) {
+ do {
+ i -= 4;
+ dst *= 10;
+ dst += (src >> i) & 0xF;
+ } while (i);
+ } else {
+ while (src != 0) {
+ dst >>= 4;
+ i -= 4;
+ dst |= (src % 10) << 60;
+ src /= 10;
+ }
+
+ dst >>= (i % 64);
+ }
+
+ dst_obj->integer = dst;
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status handle_unload(struct execution_context *ctx)
+{
+ UACPI_UNUSED(ctx);
+
+ /*
+ * Technically this doesn't exist in the wild, from the dumps that I have
+ * the only user of the Unload opcode is the Surface Pro 3, which triggers
+ * an unload of some I2C-related table as a response to some event.
+ *
+ * This op has been long deprecated by the specification exactly because
+ * it hasn't really been used by anyone and the fact that it introduces
+ * an enormous layer of complexity, which no driver is really prepared to
+ * deal with (aka namespace nodes disappearing under its feet).
+ *
+ * Just pretend we have actually unloaded whatever the AML asked for, if it
+ * ever tries to re-load this table that will just skip opcodes that create
+ * already existing objects, which should be good enough and mostly
+ * transparent to the AML.
+ */
+ uacpi_warn("refusing to unload a table from AML\n");
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status handle_logical_not(struct execution_context *ctx)
+{
+ struct op_context *op_ctx = ctx->cur_op_ctx;
+ uacpi_object *src, *dst;
+
+ src = item_array_at(&op_ctx->items, 0)->obj;
+ dst = item_array_at(&op_ctx->items, 1)->obj;
+
+ dst->type = UACPI_OBJECT_INTEGER;
+ dst->integer = src->integer ? 0 : ones();
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_bool handle_logical_equality(uacpi_object *lhs, uacpi_object *rhs)
+{
+ uacpi_bool res = UACPI_FALSE;
+
+ if (lhs->type == UACPI_OBJECT_STRING || lhs->type == UACPI_OBJECT_BUFFER) {
+ res = lhs->buffer->size == rhs->buffer->size;
+
+ if (res && lhs->buffer->size) {
+ res = uacpi_memcmp(
+ lhs->buffer->data,
+ rhs->buffer->data,
+ lhs->buffer->size
+ ) == 0;
+ }
+ } else if (lhs->type == UACPI_OBJECT_INTEGER) {
+ res = lhs->integer == rhs->integer;
+ }
+
+ return res;
+}
+
+static uacpi_bool handle_logical_less_or_greater(
+ uacpi_aml_op op, uacpi_object *lhs, uacpi_object *rhs
+)
+{
+ if (lhs->type == UACPI_OBJECT_STRING || lhs->type == UACPI_OBJECT_BUFFER) {
+ int res;
+ uacpi_buffer *lhs_buf, *rhs_buf;
+
+ lhs_buf = lhs->buffer;
+ rhs_buf = rhs->buffer;
+
+ res = uacpi_memcmp(lhs_buf->data, rhs_buf->data,
+ UACPI_MIN(lhs_buf->size, rhs_buf->size));
+ if (res == 0) {
+ if (lhs_buf->size < rhs_buf->size)
+ res = -1;
+ else if (lhs_buf->size > rhs_buf->size)
+ res = 1;
+ }
+
+ if (op == UACPI_AML_OP_LLessOp)
+ return res < 0;
+
+ return res > 0;
+ }
+
+ if (op == UACPI_AML_OP_LLessOp)
+ return lhs->integer < rhs->integer;
+
+ return lhs->integer > rhs->integer;
+}
+
+static uacpi_status handle_binary_logic(struct execution_context *ctx)
+{
+ struct op_context *op_ctx = ctx->cur_op_ctx;
+ uacpi_aml_op op = op_ctx->op->code;
+ uacpi_object *lhs, *rhs, *dst;
+ uacpi_bool res;
+
+ lhs = item_array_at(&op_ctx->items, 0)->obj;
+ rhs = item_array_at(&op_ctx->items, 1)->obj;
+ dst = item_array_at(&op_ctx->items, 2)->obj;
+
+ switch (op) {
+ case UACPI_AML_OP_LEqualOp:
+ case UACPI_AML_OP_LLessOp:
+ case UACPI_AML_OP_LGreaterOp:
+ // TODO: typecheck at parse time
+ if (lhs->type != rhs->type) {
+ uacpi_error(
+ "don't know how to do a logical comparison of '%s' and '%s'\n",
+ uacpi_object_type_to_string(lhs->type),
+ uacpi_object_type_to_string(rhs->type)
+ );
+ return UACPI_STATUS_AML_INCOMPATIBLE_OBJECT_TYPE;
+ }
+
+ if (op == UACPI_AML_OP_LEqualOp)
+ res = handle_logical_equality(lhs, rhs);
+ else
+ res = handle_logical_less_or_greater(op, lhs, rhs);
+ break;
+ default: {
+ uacpi_u64 lhs_int, rhs_int;
+
+ // NT only looks at the first 4 bytes of a buffer
+ lhs_int = object_to_integer(lhs, 4);
+ rhs_int = object_to_integer(rhs, 4);
+
+ if (op == UACPI_AML_OP_LandOp)
+ res = lhs_int && rhs_int;
+ else
+ res = lhs_int || rhs_int;
+ break;
+ }
+ }
+
+ dst->integer = res ? ones() : 0;
+ return UACPI_STATUS_OK;
+}
+
+enum match_op {
+ MTR = 0,
+ MEQ = 1,
+ MLE = 2,
+ MLT = 3,
+ MGE = 4,
+ MGT = 5,
+};
+
+static uacpi_bool match_one(enum match_op op, uacpi_u64 lhs, uacpi_u64 rhs)
+{
+ switch (op) {
+ case MTR:
+ return UACPI_TRUE;
+ case MEQ:
+ return lhs == rhs;
+ case MLE:
+ return lhs <= rhs;
+ case MLT:
+ return lhs < rhs;
+ case MGE:
+ return lhs >= rhs;
+ case MGT:
+ return lhs > rhs;
+ default:
+ return UACPI_FALSE;
+ }
+}
+
+static uacpi_status handle_match(struct execution_context *ctx)
+{
+ struct op_context *op_ctx = ctx->cur_op_ctx;
+ uacpi_package *pkg;
+ uacpi_u64 operand0, operand1, start_idx, i;
+ enum match_op mop0, mop1;
+ uacpi_object *dst;
+
+ pkg = item_array_at(&op_ctx->items, 0)->obj->package;
+ mop0 = item_array_at(&op_ctx->items, 1)->immediate;
+ operand0 = item_array_at(&op_ctx->items, 2)->obj->integer;
+ mop1 = item_array_at(&op_ctx->items, 3)->immediate;
+ operand1 = item_array_at(&op_ctx->items, 4)->obj->integer;
+ start_idx = item_array_at(&op_ctx->items, 5)->obj->integer;
+ dst = item_array_at(&op_ctx->items, 6)->obj;
+
+ for (i = start_idx; i < pkg->count; ++i) {
+ uacpi_object *obj = pkg->objects[i];
+
+ if (obj->type != UACPI_OBJECT_INTEGER)
+ continue;
+
+ if (match_one(mop0, obj->integer, operand0) &&
+ match_one(mop1, obj->integer, operand1))
+ break;
+ }
+
+ if (i < pkg->count)
+ dst->integer = i;
+ else
+ dst->integer = ones();
+
+ return UACPI_STATUS_OK;
+}
+
+/*
+ * PkgLength :=
+ * PkgLeadByte |
+ * <pkgleadbyte bytedata> |
+ * <pkgleadbyte bytedata bytedata> | <pkgleadbyte bytedata bytedata bytedata>
+ * PkgLeadByte :=
+ * <bit 7-6: bytedata count that follows (0-3)>
+ * <bit 5-4: only used if pkglength < 63>
+ * <bit 3-0: least significant package length nybble>
+ */
+static uacpi_status parse_package_length(struct call_frame *frame,
+ struct package_length *out_pkg)
+{
+ uacpi_u32 left, size;
+ uacpi_u8 *data, marker_length;
+
+ out_pkg->begin = frame->code_offset;
+ marker_length = 1;
+
+ left = call_frame_code_bytes_left(frame);
+ if (uacpi_unlikely(left < 1))
+ return UACPI_STATUS_AML_BAD_ENCODING;
+
+ data = call_frame_cursor(frame);
+ marker_length += *data >> 6;
+
+ if (uacpi_unlikely(left < marker_length))
+ return UACPI_STATUS_AML_BAD_ENCODING;
+
+ switch (marker_length) {
+ case 1:
+ size = *data & 0x3F;
+ break;
+ case 2:
+ case 3:
+ case 4: {
+ uacpi_u32 temp_byte = 0;
+
+ size = *data & 0xF;
+ uacpi_memcpy(&temp_byte, data + 1, marker_length - 1);
+
+ // marker_length - 1 is at most 3, so this shift is safe
+ size |= temp_byte << 4;
+ break;
+ }
+ }
+
+ frame->code_offset += marker_length;
+
+ out_pkg->end = out_pkg->begin + size;
+ if (uacpi_unlikely(out_pkg->end < out_pkg->begin)) {
+ uacpi_error(
+ "PkgLength overflow: start=%u, size=%u\n", out_pkg->begin, size
+ );
+ return UACPI_STATUS_AML_BAD_ENCODING;
+ }
+
+ return UACPI_STATUS_OK;
+}
+
+/*
+ * ByteData
+ * // bit 0-2: ArgCount (0-7)
+ * // bit 3: SerializeFlag
+ * // 0 NotSerialized
+ * // 1 Serialized
+ * // bit 4-7: SyncLevel (0x00-0x0f)
+ */
+static void init_method_flags(uacpi_control_method *method, uacpi_u8 flags_byte)
+{
+ method->args = flags_byte & 0x7;
+ method->is_serialized = (flags_byte >> 3) & 1;
+ method->sync_level = flags_byte >> 4;
+}
+
+static uacpi_status handle_create_method(struct execution_context *ctx)
+{
+ struct op_context *op_ctx = ctx->cur_op_ctx;
+ struct uacpi_control_method *this_method, *method;
+ struct package_length *pkg;
+ struct uacpi_namespace_node *node;
+ struct uacpi_object *dst;
+ uacpi_u32 method_begin_offset, method_size;
+
+ this_method = ctx->cur_frame->method;
+ pkg = &item_array_at(&op_ctx->items, 0)->pkg;
+ node = item_array_at(&op_ctx->items, 1)->node;
+ method_begin_offset = item_array_at(&op_ctx->items, 3)->immediate;
+
+ if (uacpi_unlikely(pkg->end < pkg->begin ||
+ pkg->end < method_begin_offset ||
+ pkg->end > this_method->size)) {
+ uacpi_error(
+ "invalid method %.4s bounds [%u..%u] (parent size is %u)\n",
+ node->name.text, method_begin_offset, pkg->end, this_method->size
+ );
+ return UACPI_STATUS_AML_BAD_ENCODING;
+ }
+
+ dst = item_array_at(&op_ctx->items, 4)->obj;
+
+ method = dst->method;
+ method_size = pkg->end - method_begin_offset;
+
+ if (method_size) {
+ method->code = uacpi_kernel_alloc(method_size);
+ if (uacpi_unlikely(method->code == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ uacpi_memcpy(
+ method->code,
+ ctx->cur_frame->method->code + method_begin_offset,
+ method_size
+ );
+ method->size = method_size;
+ method->owns_code = 1;
+ }
+
+ init_method_flags(method, item_array_at(&op_ctx->items, 2)->immediate);
+
+ node->object = uacpi_create_internal_reference(UACPI_REFERENCE_KIND_NAMED,
+ dst);
+ if (uacpi_unlikely(node->object == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status handle_create_mutex_or_event(struct execution_context *ctx)
+{
+ struct op_context *op_ctx = ctx->cur_op_ctx;
+ uacpi_namespace_node *node;
+ uacpi_object *dst;
+
+ node = item_array_at(&op_ctx->items, 0)->node;
+
+ if (op_ctx->op->code == UACPI_AML_OP_MutexOp) {
+ dst = item_array_at(&op_ctx->items, 2)->obj;
+
+ // bits 0-3: SyncLevel (0x00-0x0f), bits 4-7: Reserved (must be 0)
+ dst->mutex->sync_level = item_array_at(&op_ctx->items, 1)->immediate;
+ dst->mutex->sync_level &= 0xF;
+ } else {
+ dst = item_array_at(&op_ctx->items, 1)->obj;
+ }
+
+ node->object = uacpi_create_internal_reference(
+ UACPI_REFERENCE_KIND_NAMED,
+ dst
+ );
+ if (uacpi_unlikely(node->object == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status handle_event_ctl(struct execution_context *ctx)
+{
+ struct op_context *op_ctx = ctx->cur_op_ctx;
+ uacpi_object *obj;
+
+ obj = uacpi_unwrap_internal_reference(
+ item_array_at(&op_ctx->items, 0)->obj
+ );
+ if (uacpi_unlikely(obj->type != UACPI_OBJECT_EVENT)) {
+ uacpi_error(
+ "%s: invalid argument '%s', expected an Event object\n",
+ op_ctx->op->name, uacpi_object_type_to_string(obj->type)
+ );
+ return UACPI_STATUS_AML_INCOMPATIBLE_OBJECT_TYPE;
+ }
+
+ switch (op_ctx->op->code)
+ {
+ case UACPI_AML_OP_SignalOp:
+ uacpi_kernel_signal_event(obj->event->handle);
+ break;
+ case UACPI_AML_OP_ResetOp:
+ uacpi_kernel_reset_event(obj->event->handle);
+ break;
+ case UACPI_AML_OP_WaitOp: {
+ uacpi_u64 timeout;
+ uacpi_bool ret;
+
+ timeout = item_array_at(&op_ctx->items, 1)->obj->integer;
+ if (timeout > 0xFFFF)
+ timeout = 0xFFFF;
+
+ uacpi_namespace_write_unlock();
+ ret = uacpi_kernel_wait_for_event(obj->event->handle, timeout);
+ uacpi_namespace_write_lock();
+
+ /*
+ * The return value here is inverted, we return 0 for success and Ones
+ * for timeout and everything else.
+ */
+ if (ret)
+ item_array_at(&op_ctx->items, 2)->obj->integer = 0;
+ break;
+ }
+ default:
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status handle_mutex_ctl(struct execution_context *ctx)
+{
+ struct op_context *op_ctx = ctx->cur_op_ctx;
+ uacpi_object *obj;
+
+ obj = uacpi_unwrap_internal_reference(
+ item_array_at(&op_ctx->items, 0)->obj
+ );
+ if (uacpi_unlikely(obj->type != UACPI_OBJECT_MUTEX)) {
+ uacpi_error(
+ "%s: invalid argument '%s', expected a Mutex object\n",
+ op_ctx->op->name, uacpi_object_type_to_string(obj->type)
+ );
+ return UACPI_STATUS_AML_INCOMPATIBLE_OBJECT_TYPE;
+ }
+
+ switch (op_ctx->op->code)
+ {
+ case UACPI_AML_OP_AcquireOp: {
+ uacpi_u64 timeout;
+ uacpi_u64 *return_value;
+ uacpi_status ret;
+
+ return_value = &item_array_at(&op_ctx->items, 2)->obj->integer;
+
+ if (uacpi_unlikely(ctx->sync_level > obj->mutex->sync_level)) {
+ uacpi_warn(
+ "ignoring attempt to acquire mutex @%p with a lower sync level "
+ "(%d < %d)\n", obj->mutex, obj->mutex->sync_level,
+ ctx->sync_level
+ );
+ break;
+ }
+
+ timeout = item_array_at(&op_ctx->items, 1)->immediate;
+ if (timeout > 0xFFFF)
+ timeout = 0xFFFF;
+
+ if (uacpi_this_thread_owns_aml_mutex(obj->mutex)) {
+ ret = uacpi_acquire_aml_mutex(obj->mutex, timeout);
+ if (uacpi_likely_success(ret))
+ *return_value = 0;
+ break;
+ }
+
+ ret = uacpi_acquire_aml_mutex(obj->mutex, timeout);
+ if (uacpi_unlikely_error(ret))
+ break;
+
+ ret = held_mutexes_array_push(&ctx->held_mutexes, obj->mutex);
+ if (uacpi_unlikely_error(ret)) {
+ uacpi_release_aml_mutex(obj->mutex);
+ return ret;
+ }
+
+ ctx->sync_level = obj->mutex->sync_level;
+ *return_value = 0;
+ break;
+ }
+
+ case UACPI_AML_OP_ReleaseOp: {
+ uacpi_status ret;
+
+ if (!uacpi_this_thread_owns_aml_mutex(obj->mutex)) {
+ uacpi_warn(
+ "attempted to release not-previously-acquired mutex object "
+ "@%p (%p)\n", obj->mutex, obj->mutex->handle
+ );
+ break;
+ }
+
+ ret = held_mutexes_array_remove_and_release(
+ &ctx->held_mutexes, obj->mutex,
+ FORCE_RELEASE_NO
+ );
+ if (uacpi_likely_success(ret)) {
+ uacpi_mutex **last_mutex;
+
+ last_mutex = held_mutexes_array_last(&ctx->held_mutexes);
+ if (last_mutex == UACPI_NULL) {
+ ctx->sync_level = 0;
+ break;
+ }
+
+ ctx->sync_level = (*last_mutex)->sync_level;
+ }
+ break;
+ }
+
+ default:
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status handle_notify(struct execution_context *ctx)
+{
+ uacpi_status ret;
+ struct op_context *op_ctx = ctx->cur_op_ctx;
+ struct uacpi_namespace_node *node;
+ uacpi_u64 value;
+
+ node = item_array_at(&op_ctx->items, 0)->node;
+ value = item_array_at(&op_ctx->items, 1)->obj->integer;
+
+ ret = uacpi_notify_all(node, value);
+ if (uacpi_likely_success(ret))
+ return ret;
+
+ if (ret == UACPI_STATUS_NO_HANDLER) {
+ const uacpi_char *path;
+
+ path = uacpi_namespace_node_generate_absolute_path(node);
+ uacpi_warn(
+ "ignoring firmware Notify(%s, 0x%"UACPI_PRIX64") request, "
+ "no listeners\n", path, UACPI_FMT64(value)
+ );
+ uacpi_free_dynamic_string(path);
+
+ return UACPI_STATUS_OK;
+ }
+
+ if (ret == UACPI_STATUS_INVALID_ARGUMENT) {
+ uacpi_error("Notify() called on an invalid object %.4s\n",
+ node->name.text);
+ return UACPI_STATUS_AML_INCOMPATIBLE_OBJECT_TYPE;
+ }
+
+ return ret;
+}
+
+static uacpi_status handle_firmware_request(struct execution_context *ctx)
+{
+ struct op_context *op_ctx = ctx->cur_op_ctx;
+ uacpi_firmware_request req = { 0 };
+
+ switch (op_ctx->op->code) {
+ case UACPI_AML_OP_BreakPointOp:
+ req.type = UACPI_FIRMWARE_REQUEST_TYPE_BREAKPOINT;
+ req.breakpoint.ctx = ctx;
+ break;
+ case UACPI_AML_OP_FatalOp:
+ req.type = UACPI_FIRMWARE_REQUEST_TYPE_FATAL;
+ req.fatal.type = item_array_at(&op_ctx->items, 0)->immediate;
+ req.fatal.code = item_array_at(&op_ctx->items, 1)->immediate;
+ req.fatal.arg = item_array_at(&op_ctx->items, 2)->obj->integer;
+ break;
+ default:
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+
+ uacpi_namespace_write_unlock();
+ uacpi_kernel_handle_firmware_request(&req);
+ uacpi_namespace_write_lock();
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status handle_create_named(struct execution_context *ctx)
+{
+ struct op_context *op_ctx = ctx->cur_op_ctx;
+ struct uacpi_namespace_node *node;
+ uacpi_object *src;
+
+ node = item_array_at(&op_ctx->items, 0)->node;
+ src = item_array_at(&op_ctx->items, 1)->obj;
+
+ node->object = uacpi_create_internal_reference(UACPI_REFERENCE_KIND_NAMED,
+ src);
+ if (uacpi_unlikely(node->object == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_object_type buffer_field_get_read_type(
+ struct uacpi_buffer_field *field
+)
+{
+ if (field->bit_length > (g_uacpi_rt_ctx.is_rev1 ? 32u : 64u) ||
+ field->force_buffer)
+ return UACPI_OBJECT_BUFFER;
+
+ return UACPI_OBJECT_INTEGER;
+}
+
+static uacpi_status field_get_read_type(
+ uacpi_object *obj, uacpi_object_type *out_type
+)
+{
+ if (obj->type == UACPI_OBJECT_BUFFER_FIELD) {
+ *out_type = buffer_field_get_read_type(&obj->buffer_field);
+ return UACPI_STATUS_OK;
+ }
+
+ return uacpi_field_unit_get_read_type(obj->field_unit, out_type);
+}
+
+static uacpi_status field_byte_size(
+ uacpi_object *obj, uacpi_size *out_size
+)
+{
+ uacpi_size bit_length;
+
+ if (obj->type == UACPI_OBJECT_BUFFER_FIELD) {
+ bit_length = obj->buffer_field.bit_length;
+ } else {
+ uacpi_status ret;
+
+ ret = uacpi_field_unit_get_bit_length(obj->field_unit, &bit_length);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+ }
+
+ *out_size = uacpi_round_up_bits_to_bytes(bit_length);
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status handle_field_read(struct execution_context *ctx)
+{
+ uacpi_status ret;
+ struct op_context *op_ctx = ctx->cur_op_ctx;
+ struct uacpi_namespace_node *node;
+ uacpi_object *src_obj, *dst_obj;
+ uacpi_size dst_size;
+ void *dst = UACPI_NULL;
+ uacpi_data_view wtr_response = { 0 };
+
+ node = item_array_at(&op_ctx->items, 0)->node;
+ src_obj = uacpi_namespace_node_get_object(node);
+ dst_obj = item_array_at(&op_ctx->items, 1)->obj;
+
+ if (op_ctx->op->code == UACPI_AML_OP_InternalOpReadFieldAsBuffer) {
+ uacpi_buffer *buf;
+
+ ret = field_byte_size(src_obj, &dst_size);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ if (dst_size != 0) {
+ buf = dst_obj->buffer;
+
+ dst = uacpi_kernel_alloc_zeroed(dst_size);
+ if (dst == UACPI_NULL)
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ buf->data = dst;
+ buf->size = dst_size;
+ }
+ } else {
+ dst = &dst_obj->integer;
+ dst_size = sizeof(uacpi_u64);
+ }
+
+ if (src_obj->type == UACPI_OBJECT_BUFFER_FIELD) {
+ uacpi_read_buffer_field(&src_obj->buffer_field, dst);
+ return UACPI_STATUS_OK;
+ }
+
+ ret = uacpi_read_field_unit(
+ src_obj->field_unit, dst, dst_size, &wtr_response
+ );
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ if (wtr_response.data != UACPI_NULL) {
+ uacpi_buffer *buf;
+
+ buf = dst_obj->buffer;
+ buf->data = wtr_response.data;
+ buf->size = wtr_response.length;
+ }
+
+ return ret;
+}
+
+static uacpi_status handle_create_buffer_field(struct execution_context *ctx)
+{
+ struct op_context *op_ctx = ctx->cur_op_ctx;
+ struct uacpi_namespace_node *node;
+ uacpi_buffer *src_buf;
+ uacpi_object *field_obj;
+ uacpi_buffer_field *field;
+
+ /*
+ * Layout of items here:
+ * [0] -> Type checked source buffer object
+ * [1] -> Byte/bit index integer object
+ * [2] ( if CreateField) -> bit length integer object
+ * [3] (2 if not CreateField) -> the new namespace node
+ * [4] (3 if not CreateField) -> the buffer field object we're creating here
+ */
+ src_buf = item_array_at(&op_ctx->items, 0)->obj->buffer;
+
+ if (op_ctx->op->code == UACPI_AML_OP_CreateFieldOp) {
+ uacpi_object *idx_obj, *len_obj;
+
+ idx_obj = item_array_at(&op_ctx->items, 1)->obj;
+ len_obj = item_array_at(&op_ctx->items, 2)->obj;
+ node = item_array_at(&op_ctx->items, 3)->node;
+ field_obj = item_array_at(&op_ctx->items, 4)->obj;
+ field = &field_obj->buffer_field;
+
+ field->bit_index = idx_obj->integer;
+
+ if (uacpi_unlikely(!len_obj->integer ||
+ len_obj->integer > 0xFFFFFFFF)) {
+ uacpi_error("invalid bit field length (%u)\n", field->bit_length);
+ return UACPI_STATUS_AML_BAD_ENCODING;
+ }
+
+ field->bit_length = len_obj->integer;
+ field->force_buffer = UACPI_TRUE;
+ } else {
+ uacpi_object *idx_obj;
+
+ idx_obj = item_array_at(&op_ctx->items, 1)->obj;
+ node = item_array_at(&op_ctx->items, 2)->node;
+ field_obj = item_array_at(&op_ctx->items, 3)->obj;
+ field = &field_obj->buffer_field;
+
+ field->bit_index = idx_obj->integer;
+ switch (op_ctx->op->code) {
+ case UACPI_AML_OP_CreateBitFieldOp:
+ field->bit_length = 1;
+ break;
+ case UACPI_AML_OP_CreateByteFieldOp:
+ field->bit_length = 8;
+ break;
+ case UACPI_AML_OP_CreateWordFieldOp:
+ field->bit_length = 16;
+ break;
+ case UACPI_AML_OP_CreateDWordFieldOp:
+ field->bit_length = 32;
+ break;
+ case UACPI_AML_OP_CreateQWordFieldOp:
+ field->bit_length = 64;
+ break;
+ default:
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+
+ if (op_ctx->op->code != UACPI_AML_OP_CreateBitFieldOp)
+ field->bit_index *= 8;
+ }
+
+ if (uacpi_unlikely((field->bit_index + field->bit_length) >
+ src_buf->size * 8)) {
+ uacpi_error(
+ "invalid buffer field: bits [%zu..%zu], buffer size is %zu bytes\n",
+ field->bit_index, field->bit_index + field->bit_length,
+ src_buf->size
+ );
+ return UACPI_STATUS_AML_OUT_OF_BOUNDS_INDEX;
+ }
+
+ field->backing = src_buf;
+ uacpi_shareable_ref(field->backing);
+ node->object = uacpi_create_internal_reference(UACPI_REFERENCE_KIND_NAMED,
+ field_obj);
+ if (uacpi_unlikely(node->object == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status handle_control_flow(struct execution_context *ctx)
+{
+ struct call_frame *frame = ctx->cur_frame;
+ struct op_context *op_ctx = ctx->cur_op_ctx;
+
+ if (uacpi_unlikely(frame->last_while == UACPI_NULL)) {
+ uacpi_error(
+ "attempting to %s outside of a While block\n",
+ op_ctx->op->code == UACPI_AML_OP_BreakOp ? "Break" : "Continue"
+ );
+ return UACPI_STATUS_AML_BAD_ENCODING;
+ }
+
+ for (;;) {
+ if (ctx->cur_block != frame->last_while) {
+ frame_reset_post_end_block(ctx, ctx->cur_block->type);
+ continue;
+ }
+
+ if (op_ctx->op->code == UACPI_AML_OP_BreakOp)
+ frame->code_offset = ctx->cur_block->end;
+ else
+ frame->code_offset = ctx->cur_block->begin;
+ frame_reset_post_end_block(ctx, ctx->cur_block->type);
+ break;
+ }
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status create_named_scope(struct op_context *op_ctx)
+{
+ uacpi_namespace_node *node;
+ uacpi_object *obj;
+
+ node = item_array_at(&op_ctx->items, 1)->node;
+ obj = item_array_last(&op_ctx->items)->obj;
+
+ switch (op_ctx->op->code) {
+ case UACPI_AML_OP_ProcessorOp: {
+ uacpi_processor *proc = obj->processor;
+ proc->id = item_array_at(&op_ctx->items, 2)->immediate;
+ proc->block_address = item_array_at(&op_ctx->items, 3)->immediate;
+ proc->block_length = item_array_at(&op_ctx->items, 4)->immediate;
+ break;
+ }
+
+ case UACPI_AML_OP_PowerResOp: {
+ uacpi_power_resource *power_res = &obj->power_resource;
+ power_res->system_level = item_array_at(&op_ctx->items, 2)->immediate;
+ power_res->resource_order = item_array_at(&op_ctx->items, 3)->immediate;
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ node->object = uacpi_create_internal_reference(UACPI_REFERENCE_KIND_NAMED,
+ obj);
+ if (uacpi_unlikely(node->object == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status handle_code_block(struct execution_context *ctx)
+{
+ struct op_context *op_ctx = ctx->cur_op_ctx;
+
+ switch (op_ctx->op->code) {
+ case UACPI_AML_OP_ProcessorOp:
+ case UACPI_AML_OP_PowerResOp:
+ case UACPI_AML_OP_ThermalZoneOp:
+ case UACPI_AML_OP_DeviceOp: {
+ uacpi_status ret;
+
+ ret = create_named_scope(op_ctx);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ UACPI_FALLTHROUGH;
+ }
+ case UACPI_AML_OP_ScopeOp:
+ case UACPI_AML_OP_IfOp:
+ case UACPI_AML_OP_ElseOp:
+ case UACPI_AML_OP_WhileOp: {
+ break;
+ }
+ default:
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+
+ return begin_block_execution(ctx);
+}
+
+static uacpi_status handle_return(struct execution_context *ctx)
+{
+ uacpi_status ret;
+ uacpi_object *dst = UACPI_NULL;
+
+ ctx->cur_frame->code_offset = ctx->cur_frame->method->size;
+ ret = method_get_ret_object(ctx, &dst);
+
+ if (uacpi_unlikely_error(ret))
+ return ret;
+ if (dst == UACPI_NULL)
+ return UACPI_STATUS_OK;
+
+ /*
+ * Should be possible to move here if method returns a literal
+ * like Return(Buffer { ... }), otherwise we have to copy just to
+ * be safe.
+ */
+ return uacpi_object_assign(
+ dst,
+ item_array_at(&ctx->cur_op_ctx->items, 0)->obj,
+ UACPI_ASSIGN_BEHAVIOR_DEEP_COPY
+ );
+}
+
+static void refresh_ctx_pointers(struct execution_context *ctx)
+{
+ struct call_frame *frame = ctx->cur_frame;
+
+ if (frame == UACPI_NULL) {
+ ctx->cur_op_ctx = UACPI_NULL;
+ ctx->prev_op_ctx = UACPI_NULL;
+ ctx->cur_block = UACPI_NULL;
+ return;
+ }
+
+ ctx->cur_op_ctx = op_context_array_last(&frame->pending_ops);
+ ctx->prev_op_ctx = op_context_array_one_before_last(&frame->pending_ops);
+ ctx->cur_block = code_block_array_last(&frame->code_blocks);
+}
+
+static uacpi_bool ctx_has_non_preempted_op(struct execution_context *ctx)
+{
+ return ctx->cur_op_ctx && !ctx->cur_op_ctx->preempted;
+}
+
+enum op_trace_action_type {
+ OP_TRACE_ACTION_BEGIN,
+ OP_TRACE_ACTION_RESUME,
+ OP_TRACE_ACTION_END,
+};
+
+static const uacpi_char *const op_trace_action_types[3] = {
+ [OP_TRACE_ACTION_BEGIN] = "BEGIN",
+ [OP_TRACE_ACTION_RESUME] = "RESUME",
+ [OP_TRACE_ACTION_END] = "END",
+};
+
+static inline void trace_op(
+ const struct uacpi_op_spec *op, enum op_trace_action_type action
+)
+{
+ uacpi_debug(
+ "%s OP '%s' (0x%04X)\n",
+ op_trace_action_types[action], op->name, op->code
+ );
+}
+
+static inline void trace_pop(uacpi_u8 pop)
+{
+ uacpi_debug(" pOP: %s (0x%02X)\n", uacpi_parse_op_to_string(pop), pop);
+}
+
+static uacpi_status frame_push_args(struct call_frame *frame,
+ struct op_context *op_ctx)
+{
+ uacpi_size i;
+
+ /*
+ * MethodCall items:
+ * items[0] -> method namespace node
+ * items[1] -> immediate that was used for parsing the arguments
+ * items[2...nargs-1] -> method arguments
+ * items[-1] -> return value object
+ *
+ * Here we only care about the arguments though.
+ */
+ for (i = 2; i < item_array_size(&op_ctx->items) - 1; i++) {
+ uacpi_object *src, *dst;
+
+ src = item_array_at(&op_ctx->items, i)->obj;
+
+ dst = uacpi_create_internal_reference(UACPI_REFERENCE_KIND_ARG, src);
+ if (uacpi_unlikely(dst == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ frame->args[i - 2] = dst;
+ }
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status frame_setup_base_scope(struct call_frame *frame,
+ uacpi_namespace_node *scope,
+ uacpi_control_method *method)
+{
+ struct code_block *block;
+
+ block = code_block_array_alloc(&frame->code_blocks);
+ if (uacpi_unlikely(block == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ block->type = CODE_BLOCK_SCOPE;
+ block->node = scope;
+ block->begin = 0;
+ block->end = method->size;
+ frame->method = method;
+ frame->cur_scope = scope;
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status push_new_frame(struct execution_context *ctx,
+ struct call_frame **out_frame)
+{
+ struct call_frame_array *call_stack = &ctx->call_stack;
+ struct call_frame *prev_frame;
+
+ *out_frame = call_frame_array_calloc(call_stack);
+ if (uacpi_unlikely(*out_frame == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ /*
+ * Allocating a new frame might have reallocated the dynamic buffer so our
+ * execution_context members might now be pointing to freed memory.
+ * Refresh them here.
+ */
+ prev_frame = call_frame_array_one_before_last(call_stack);
+ ctx->cur_frame = prev_frame;
+ refresh_ctx_pointers(ctx);
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_bool maybe_end_block(struct execution_context *ctx)
+{
+ struct code_block *block = ctx->cur_block;
+ struct call_frame *cur_frame = ctx->cur_frame;
+
+ if (!block)
+ return UACPI_FALSE;
+ if (cur_frame->code_offset != block->end)
+ return UACPI_FALSE;
+
+ if (block->type == CODE_BLOCK_WHILE)
+ cur_frame->code_offset = block->begin;
+
+ frame_reset_post_end_block(ctx, block->type);
+ return UACPI_TRUE;
+}
+
+static uacpi_status store_to_target(
+ uacpi_object *dst, uacpi_object *src, uacpi_data_view *wtr_response
+)
+{
+ uacpi_status ret;
+
+ switch (dst->type) {
+ case UACPI_OBJECT_DEBUG:
+ ret = debug_store(src);
+ break;
+ case UACPI_OBJECT_REFERENCE:
+ ret = store_to_reference(dst, src, wtr_response);
+ break;
+
+ case UACPI_OBJECT_BUFFER_INDEX:
+ src = uacpi_unwrap_internal_reference(src);
+ ret = object_assign_with_implicit_cast(dst, src, wtr_response);
+ break;
+
+ case UACPI_OBJECT_INTEGER:
+ // NULL target
+ if (dst->integer == 0) {
+ ret = UACPI_STATUS_OK;
+ break;
+ }
+ UACPI_FALLTHROUGH;
+ default:
+ uacpi_error("attempted to store to an invalid target: %s\n",
+ uacpi_object_type_to_string(dst->type));
+ ret = UACPI_STATUS_AML_INCOMPATIBLE_OBJECT_TYPE;
+ }
+
+ return ret;
+}
+
+static uacpi_status handle_copy_object_or_store(struct execution_context *ctx)
+{
+ uacpi_object *src, *dst;
+ struct op_context *op_ctx = ctx->cur_op_ctx;
+
+ src = item_array_at(&op_ctx->items, 0)->obj;
+ dst = item_array_at(&op_ctx->items, 1)->obj;
+
+ if (op_ctx->op->code == UACPI_AML_OP_StoreOp) {
+ uacpi_status ret;
+ uacpi_data_view wtr_response = { 0 };
+
+ ret = store_to_target(dst, src, &wtr_response);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ /*
+ * This was a write-then-read field access since we got a response
+ * buffer back from this store. Now we have to return this buffer
+ * as a prvalue from the StoreOp so that it can be used by AML to
+ * retrieve the response.
+ */
+ if (wtr_response.data != UACPI_NULL) {
+ uacpi_object *wtr_response_obj;
+
+ wtr_response_obj = uacpi_create_object(UACPI_OBJECT_BUFFER);
+ if (uacpi_unlikely(wtr_response_obj == UACPI_NULL)) {
+ uacpi_free(wtr_response.data, wtr_response.length);
+ return UACPI_STATUS_OUT_OF_MEMORY;
+ }
+
+ wtr_response_obj->buffer->data = wtr_response.data;
+ wtr_response_obj->buffer->size = wtr_response.length;
+
+ uacpi_object_unref(src);
+ item_array_at(&op_ctx->items, 0)->obj = wtr_response_obj;
+ }
+
+ return ret;
+ }
+
+ if (dst->type != UACPI_OBJECT_REFERENCE)
+ return UACPI_STATUS_AML_INCOMPATIBLE_OBJECT_TYPE;
+
+ return copy_object_to_reference(dst, src);
+}
+
+static uacpi_status handle_inc_dec(struct execution_context *ctx)
+{
+ uacpi_object *src, *dst;
+ struct op_context *op_ctx = ctx->cur_op_ctx;
+ uacpi_bool field_allowed = UACPI_FALSE;
+ uacpi_object_type true_src_type;
+ uacpi_status ret;
+
+ src = item_array_at(&op_ctx->items, 0)->obj;
+ dst = item_array_at(&op_ctx->items, 1)->obj;
+
+ if (src->type == UACPI_OBJECT_REFERENCE) {
+ /*
+ * Increment/Decrement are the only two operators that modify the value
+ * in-place, thus we need very specific dereference rules here.
+ *
+ * Reading buffer fields & field units is only allowed if we were passed
+ * a namestring directly as opposed to some nested reference chain
+ * containing a field at the bottom.
+ */
+ if (src->flags == UACPI_REFERENCE_KIND_NAMED)
+ field_allowed = src->inner_object->type != UACPI_OBJECT_REFERENCE;
+
+ src = reference_unwind(src)->inner_object;
+ } // else buffer index
+
+ true_src_type = src->type;
+
+ switch (true_src_type) {
+ case UACPI_OBJECT_INTEGER:
+ dst->integer = src->integer;
+ break;
+ case UACPI_OBJECT_FIELD_UNIT:
+ case UACPI_OBJECT_BUFFER_FIELD:
+ if (uacpi_unlikely(!field_allowed))
+ goto out_bad_type;
+
+ ret = field_get_read_type(src, &true_src_type);
+ if (uacpi_unlikely_error(ret))
+ goto out_bad_type;
+ if (true_src_type != UACPI_OBJECT_INTEGER)
+ goto out_bad_type;
+
+ if (src->type == UACPI_OBJECT_FIELD_UNIT) {
+ ret = uacpi_read_field_unit(
+ src->field_unit, &dst->integer, sizeof_int(),
+ UACPI_NULL
+ );
+ if (uacpi_unlikely_error(ret))
+ return ret;
+ } else {
+ uacpi_read_buffer_field(&src->buffer_field, &dst->integer);
+ }
+ break;
+ case UACPI_OBJECT_BUFFER_INDEX:
+ dst->integer = *buffer_index_cursor(&src->buffer_index);
+ break;
+ default:
+ goto out_bad_type;
+ }
+
+ if (op_ctx->op->code == UACPI_AML_OP_IncrementOp)
+ dst->integer++;
+ else
+ dst->integer--;
+
+ return UACPI_STATUS_OK;
+
+out_bad_type:
+ uacpi_error("Increment/Decrement: invalid object type '%s'\n",
+ uacpi_object_type_to_string(true_src_type));
+ return UACPI_STATUS_AML_INCOMPATIBLE_OBJECT_TYPE;
+}
+
+static uacpi_status enter_method(
+ struct execution_context *ctx, struct call_frame *new_frame,
+ uacpi_control_method *method
+)
+{
+ uacpi_status ret = UACPI_STATUS_OK;
+
+ uacpi_shareable_ref(method);
+
+ if (!method->is_serialized)
+ return ret;
+
+ if (uacpi_unlikely(ctx->sync_level > method->sync_level)) {
+ uacpi_error(
+ "cannot invoke method @%p, sync level %d is too low "
+ "(current is %d)\n",
+ method, method->sync_level, ctx->sync_level
+ );
+ return UACPI_STATUS_AML_SYNC_LEVEL_TOO_HIGH;
+ }
+
+ if (method->mutex == UACPI_NULL) {
+ method->mutex = uacpi_create_mutex();
+ if (uacpi_unlikely(method->mutex == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+ method->mutex->sync_level = method->sync_level;
+ }
+
+ if (!uacpi_this_thread_owns_aml_mutex(method->mutex)) {
+ ret = uacpi_acquire_aml_mutex(method->mutex, 0xFFFF);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ ret = held_mutexes_array_push(&ctx->held_mutexes, method->mutex);
+ if (uacpi_unlikely_error(ret)) {
+ uacpi_release_aml_mutex(method->mutex);
+ return ret;
+ }
+ }
+
+ new_frame->prev_sync_level = ctx->sync_level;
+ ctx->sync_level = method->sync_level;
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status push_op(struct execution_context *ctx)
+{
+ struct call_frame *frame = ctx->cur_frame;
+ struct op_context *op_ctx;
+
+ op_ctx = op_context_array_calloc(&frame->pending_ops);
+ if (op_ctx == UACPI_NULL)
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ op_ctx->op = ctx->cur_op;
+ refresh_ctx_pointers(ctx);
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_bool pop_item(struct op_context *op_ctx)
+{
+ struct item *item;
+
+ if (item_array_size(&op_ctx->items) == 0)
+ return UACPI_FALSE;
+
+ item = item_array_last(&op_ctx->items);
+
+ if (item->type == ITEM_OBJECT)
+ uacpi_object_unref(item->obj);
+
+ if (item->type == ITEM_NAMESPACE_NODE)
+ uacpi_namespace_node_unref(item->node);
+
+ item_array_pop(&op_ctx->items);
+ return UACPI_TRUE;
+}
+
+static void pop_op(struct execution_context *ctx)
+{
+ struct call_frame *frame = ctx->cur_frame;
+ struct op_context *cur_op_ctx = ctx->cur_op_ctx;
+
+ while (pop_item(cur_op_ctx));
+
+ item_array_clear(&cur_op_ctx->items);
+ op_context_array_pop(&frame->pending_ops);
+ refresh_ctx_pointers(ctx);
+}
+
+static void call_frame_clear(struct call_frame *frame)
+{
+ uacpi_size i;
+ op_context_array_clear(&frame->pending_ops);
+ code_block_array_clear(&frame->code_blocks);
+
+ while (temp_namespace_node_array_size(&frame->temp_nodes) != 0) {
+ uacpi_namespace_node *node;
+
+ node = *temp_namespace_node_array_last(&frame->temp_nodes);
+ uacpi_namespace_node_uninstall(node);
+ temp_namespace_node_array_pop(&frame->temp_nodes);
+ }
+ temp_namespace_node_array_clear(&frame->temp_nodes);
+
+ for (i = 0; i < 7; ++i)
+ uacpi_object_unref(frame->args[i]);
+ for (i = 0; i < 8; ++i)
+ uacpi_object_unref(frame->locals[i]);
+
+ uacpi_method_unref(frame->method);
+}
+
+static uacpi_u8 parse_op_generates_item[0x100] = {
+ [UACPI_PARSE_OP_SIMPLE_NAME] = ITEM_EMPTY_OBJECT,
+ [UACPI_PARSE_OP_SUPERNAME] = ITEM_EMPTY_OBJECT,
+ [UACPI_PARSE_OP_SUPERNAME_OR_UNRESOLVED] = ITEM_EMPTY_OBJECT,
+ [UACPI_PARSE_OP_TERM_ARG] = ITEM_EMPTY_OBJECT,
+ [UACPI_PARSE_OP_TERM_ARG_UNWRAP_INTERNAL] = ITEM_EMPTY_OBJECT,
+ [UACPI_PARSE_OP_TERM_ARG_OR_NAMED_OBJECT] = ITEM_EMPTY_OBJECT,
+ [UACPI_PARSE_OP_TERM_ARG_OR_NAMED_OBJECT_OR_UNRESOLVED] = ITEM_EMPTY_OBJECT,
+ [UACPI_PARSE_OP_OPERAND] = ITEM_EMPTY_OBJECT,
+ [UACPI_PARSE_OP_STRING] = ITEM_EMPTY_OBJECT,
+ [UACPI_PARSE_OP_COMPUTATIONAL_DATA] = ITEM_EMPTY_OBJECT,
+ [UACPI_PARSE_OP_TARGET] = ITEM_EMPTY_OBJECT,
+ [UACPI_PARSE_OP_PKGLEN] = ITEM_PACKAGE_LENGTH,
+ [UACPI_PARSE_OP_TRACKED_PKGLEN] = ITEM_PACKAGE_LENGTH,
+ [UACPI_PARSE_OP_CREATE_NAMESTRING] = ITEM_NAMESPACE_NODE,
+ [UACPI_PARSE_OP_CREATE_NAMESTRING_OR_NULL_IF_LOAD] = ITEM_NAMESPACE_NODE,
+ [UACPI_PARSE_OP_EXISTING_NAMESTRING] = ITEM_NAMESPACE_NODE,
+ [UACPI_PARSE_OP_EXISTING_NAMESTRING_OR_NULL] = ITEM_NAMESPACE_NODE,
+ [UACPI_PARSE_OP_EXISTING_NAMESTRING_OR_NULL_IF_LOAD] = ITEM_NAMESPACE_NODE,
+ [UACPI_PARSE_OP_LOAD_INLINE_IMM_AS_OBJECT] = ITEM_OBJECT,
+ [UACPI_PARSE_OP_LOAD_INLINE_IMM] = ITEM_IMMEDIATE,
+ [UACPI_PARSE_OP_LOAD_ZERO_IMM] = ITEM_IMMEDIATE,
+ [UACPI_PARSE_OP_LOAD_IMM] = ITEM_IMMEDIATE,
+ [UACPI_PARSE_OP_LOAD_IMM_AS_OBJECT] = ITEM_OBJECT,
+ [UACPI_PARSE_OP_LOAD_FALSE_OBJECT] = ITEM_OBJECT,
+ [UACPI_PARSE_OP_LOAD_TRUE_OBJECT] = ITEM_OBJECT,
+ [UACPI_PARSE_OP_OBJECT_ALLOC] = ITEM_OBJECT,
+ [UACPI_PARSE_OP_OBJECT_ALLOC_TYPED] = ITEM_OBJECT,
+ [UACPI_PARSE_OP_EMPTY_OBJECT_ALLOC] = ITEM_EMPTY_OBJECT,
+ [UACPI_PARSE_OP_OBJECT_CONVERT_TO_SHALLOW_COPY] = ITEM_OBJECT,
+ [UACPI_PARSE_OP_OBJECT_CONVERT_TO_DEEP_COPY] = ITEM_OBJECT,
+ [UACPI_PARSE_OP_RECORD_AML_PC] = ITEM_IMMEDIATE,
+};
+
+static const uacpi_u8 *op_decode_cursor(const struct op_context *ctx)
+{
+ const struct uacpi_op_spec *spec = ctx->op;
+
+ if (spec->properties & UACPI_OP_PROPERTY_OUT_OF_LINE)
+ return &spec->indirect_decode_ops[ctx->pc];
+
+ return &spec->decode_ops[ctx->pc];
+}
+
+static uacpi_u8 op_decode_byte(struct op_context *ctx)
+{
+ uacpi_u8 byte;
+
+ byte = *op_decode_cursor(ctx);
+ ctx->pc++;
+
+ return byte;
+}
+
+static uacpi_aml_op op_decode_aml_op(struct op_context *op_ctx)
+{
+ uacpi_aml_op op = 0;
+
+ op |= op_decode_byte(op_ctx);
+ op |= op_decode_byte(op_ctx) << 8;
+
+ return op;
+}
+
+// MSVC doesn't support __VA_OPT__ so we do this weirdness
+#define EXEC_OP_DO_LVL(lvl, reason, ...) \
+ uacpi_##lvl("Op 0x%04X ('%s'): "reason"\n", \
+ op_ctx->op->code, op_ctx->op->name __VA_ARGS__)
+
+#define EXEC_OP_DO_ERR(reason, ...) EXEC_OP_DO_LVL(error, reason, __VA_ARGS__)
+#define EXEC_OP_DO_WARN(reason, ...) EXEC_OP_DO_LVL(warn, reason, __VA_ARGS__)
+
+#define EXEC_OP_ERR_2(reason, arg0, arg1) EXEC_OP_DO_ERR(reason, ,arg0, arg1)
+#define EXEC_OP_ERR_1(reason, arg0) EXEC_OP_DO_ERR(reason, ,arg0)
+#define EXEC_OP_ERR(reason) EXEC_OP_DO_ERR(reason)
+
+#define EXEC_OP_WARN(reason) EXEC_OP_DO_WARN(reason)
+
+#define SPEC_SIMPLE_NAME "SimpleName := NameString | ArgObj | LocalObj"
+#define SPEC_SUPER_NAME \
+ "SuperName := SimpleName | DebugObj | ReferenceTypeOpcode"
+#define SPEC_TERM_ARG \
+ "TermArg := ExpressionOpcode | DataObject | ArgObj | LocalObj"
+#define SPEC_OPERAND "Operand := TermArg => Integer"
+#define SPEC_STRING "String := TermArg => String"
+#define SPEC_TARGET "Target := SuperName | NullName"
+
+#define SPEC_COMPUTATIONAL_DATA \
+ "ComputationalData := ByteConst | WordConst | DWordConst | QWordConst " \
+ "| String | ConstObj | RevisionOp | DefBuffer"
+
+static uacpi_bool op_wants_supername(enum uacpi_parse_op op)
+{
+ switch (op) {
+ case UACPI_PARSE_OP_SIMPLE_NAME:
+ case UACPI_PARSE_OP_SUPERNAME:
+ case UACPI_PARSE_OP_SUPERNAME_OR_UNRESOLVED:
+ case UACPI_PARSE_OP_TARGET:
+ return UACPI_TRUE;
+ default:
+ return UACPI_FALSE;
+ }
+}
+
+static uacpi_bool op_wants_term_arg_or_operand(enum uacpi_parse_op op)
+{
+ switch (op) {
+ case UACPI_PARSE_OP_TERM_ARG:
+ case UACPI_PARSE_OP_TERM_ARG_UNWRAP_INTERNAL:
+ case UACPI_PARSE_OP_OPERAND:
+ case UACPI_PARSE_OP_STRING:
+ case UACPI_PARSE_OP_COMPUTATIONAL_DATA:
+ return UACPI_TRUE;
+ default:
+ return UACPI_FALSE;
+ }
+}
+
+static uacpi_bool op_allows_unresolved(enum uacpi_parse_op op)
+{
+ switch (op) {
+ case UACPI_PARSE_OP_SUPERNAME_OR_UNRESOLVED:
+ case UACPI_PARSE_OP_TERM_ARG_OR_NAMED_OBJECT_OR_UNRESOLVED:
+ case UACPI_PARSE_OP_EXISTING_NAMESTRING_OR_NULL:
+ return UACPI_TRUE;
+ default:
+ return UACPI_FALSE;
+ }
+}
+
+static uacpi_bool op_allows_unresolved_if_load(enum uacpi_parse_op op)
+{
+ switch (op) {
+ case UACPI_PARSE_OP_CREATE_NAMESTRING_OR_NULL_IF_LOAD:
+ case UACPI_PARSE_OP_EXISTING_NAMESTRING_OR_NULL_IF_LOAD:
+ return UACPI_TRUE;
+ default:
+ return UACPI_FALSE;
+ }
+}
+
+static uacpi_status op_typecheck(const struct op_context *op_ctx,
+ const struct op_context *cur_op_ctx)
+{
+ const uacpi_char *expected_type_str;
+ uacpi_u8 ok_mask = 0;
+ uacpi_u8 props = cur_op_ctx->op->properties;
+
+ switch (*op_decode_cursor(op_ctx)) {
+ // SimpleName := NameString | ArgObj | LocalObj
+ case UACPI_PARSE_OP_SIMPLE_NAME:
+ expected_type_str = SPEC_SIMPLE_NAME;
+ ok_mask |= UACPI_OP_PROPERTY_SIMPLE_NAME;
+ break;
+
+ // Target := SuperName | NullName
+ case UACPI_PARSE_OP_TARGET:
+ expected_type_str = SPEC_TARGET;
+ ok_mask |= UACPI_OP_PROPERTY_TARGET | UACPI_OP_PROPERTY_SUPERNAME;
+ break;
+
+ // SuperName := SimpleName | DebugObj | ReferenceTypeOpcode
+ case UACPI_PARSE_OP_SUPERNAME:
+ case UACPI_PARSE_OP_SUPERNAME_OR_UNRESOLVED:
+ expected_type_str = SPEC_SUPER_NAME;
+ ok_mask |= UACPI_OP_PROPERTY_SUPERNAME;
+ break;
+
+ // TermArg := ExpressionOpcode | DataObject | ArgObj | LocalObj
+ case UACPI_PARSE_OP_TERM_ARG:
+ case UACPI_PARSE_OP_TERM_ARG_UNWRAP_INTERNAL:
+ case UACPI_PARSE_OP_TERM_ARG_OR_NAMED_OBJECT:
+ case UACPI_PARSE_OP_TERM_ARG_OR_NAMED_OBJECT_OR_UNRESOLVED:
+ case UACPI_PARSE_OP_OPERAND:
+ case UACPI_PARSE_OP_STRING:
+ case UACPI_PARSE_OP_COMPUTATIONAL_DATA:
+ expected_type_str = SPEC_TERM_ARG;
+ ok_mask |= UACPI_OP_PROPERTY_TERM_ARG;
+ break;
+ default:
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+
+ if (!(props & ok_mask)) {
+ EXEC_OP_ERR_2("invalid argument: '%s', expected a %s",
+ cur_op_ctx->op->name, expected_type_str);
+ return UACPI_STATUS_AML_INCOMPATIBLE_OBJECT_TYPE;
+ }
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status typecheck_obj(
+ const struct op_context *op_ctx,
+ const uacpi_object *obj,
+ enum uacpi_object_type expected_type,
+ const uacpi_char *spec_desc
+)
+{
+ if (uacpi_likely(obj->type == expected_type))
+ return UACPI_STATUS_OK;
+
+ EXEC_OP_ERR_2("invalid argument type: %s, expected a %s",
+ uacpi_object_type_to_string(obj->type), spec_desc);
+ return UACPI_STATUS_AML_INCOMPATIBLE_OBJECT_TYPE;
+}
+
+static uacpi_status typecheck_operand(
+ const struct op_context *op_ctx,
+ const uacpi_object *obj
+)
+{
+ return typecheck_obj(op_ctx, obj, UACPI_OBJECT_INTEGER, SPEC_OPERAND);
+}
+
+static uacpi_status typecheck_string(
+ const struct op_context *op_ctx,
+ const uacpi_object *obj
+)
+{
+ return typecheck_obj(op_ctx, obj, UACPI_OBJECT_STRING, SPEC_STRING);
+}
+
+static uacpi_status typecheck_computational_data(
+ const struct op_context *op_ctx,
+ const uacpi_object *obj
+)
+{
+ switch (obj->type) {
+ case UACPI_OBJECT_STRING:
+ case UACPI_OBJECT_BUFFER:
+ case UACPI_OBJECT_INTEGER:
+ return UACPI_STATUS_OK;
+ default:
+ EXEC_OP_ERR_2(
+ "invalid argument type: %s, expected a %s",
+ uacpi_object_type_to_string(obj->type),
+ SPEC_COMPUTATIONAL_DATA
+ );
+ return UACPI_STATUS_AML_INCOMPATIBLE_OBJECT_TYPE;
+ }
+}
+
+static void emit_op_skip_warn(const struct op_context *op_ctx)
+{
+ EXEC_OP_WARN("skipping due to previous errors");
+}
+
+static void trace_named_object_lookup_or_creation_failure(
+ struct call_frame *frame, uacpi_size offset, enum uacpi_parse_op op,
+ uacpi_status ret, enum uacpi_log_level level
+)
+{
+ static const uacpi_char *oom_prefix = "<...>";
+ static const uacpi_char *empty_string = "";
+ static const uacpi_char *unknown_path = "<unknown-path>";
+ static const uacpi_char *invalid_path = "<invalid-path>";
+
+ uacpi_status conv_ret;
+ const uacpi_char *action;
+ const uacpi_char *requested_path_to_print;
+ const uacpi_char *middle_part = UACPI_NULL;
+ const uacpi_char *prefix_path = UACPI_NULL;
+ uacpi_char *requested_path = UACPI_NULL;
+ uacpi_size length;
+ uacpi_bool is_create;
+
+ is_create = op == UACPI_PARSE_OP_CREATE_NAMESTRING ||
+ op == UACPI_PARSE_OP_CREATE_NAMESTRING_OR_NULL_IF_LOAD;
+
+ if (is_create)
+ action = "create";
+ else
+ action = "lookup";
+
+ conv_ret = name_string_to_path(
+ frame, offset, &requested_path, &length
+ );
+ if (uacpi_unlikely_error(conv_ret)) {
+ if (conv_ret == UACPI_STATUS_OUT_OF_MEMORY)
+ requested_path_to_print = unknown_path;
+ else
+ requested_path_to_print = invalid_path;
+ } else {
+ requested_path_to_print = requested_path;
+ }
+
+ if (requested_path && requested_path[0] != '\\') {
+ prefix_path = uacpi_namespace_node_generate_absolute_path(
+ frame->cur_scope
+ );
+ if (uacpi_unlikely(prefix_path == UACPI_NULL))
+ prefix_path = oom_prefix;
+
+ if (prefix_path[1] != '\0')
+ middle_part = ".";
+ } else {
+ prefix_path = empty_string;
+ }
+
+ if (middle_part == UACPI_NULL)
+ middle_part = empty_string;
+
+ if (length == 5 && !is_create) {
+ uacpi_log_lvl(
+ level,
+ "unable to %s named object '%s' within (or above) "
+ "scope '%s': %s\n", action, requested_path_to_print,
+ prefix_path, uacpi_status_to_string(ret)
+ );
+ } else {
+ uacpi_log_lvl(
+ level,
+ "unable to %s named object '%s%s%s': %s\n",
+ action, prefix_path, middle_part,
+ requested_path_to_print, uacpi_status_to_string(ret)
+ );
+ }
+
+ uacpi_free(requested_path, length);
+ if (prefix_path != oom_prefix && prefix_path != empty_string)
+ uacpi_free_dynamic_string(prefix_path);
+}
+
+static uacpi_status uninstalled_op_handler(struct execution_context *ctx)
+{
+ struct op_context *op_ctx = ctx->cur_op_ctx;
+
+ EXEC_OP_ERR("no dedicated handler installed");
+ return UACPI_STATUS_UNIMPLEMENTED;
+}
+
+enum op_handler {
+ OP_HANDLER_UNINSTALLED = 0,
+ OP_HANDLER_LOCAL,
+ OP_HANDLER_ARG,
+ OP_HANDLER_STRING,
+ OP_HANDLER_BINARY_MATH,
+ OP_HANDLER_CONTROL_FLOW,
+ OP_HANDLER_CODE_BLOCK,
+ OP_HANDLER_RETURN,
+ OP_HANDLER_CREATE_METHOD,
+ OP_HANDLER_COPY_OBJECT_OR_STORE,
+ OP_HANDLER_INC_DEC,
+ OP_HANDLER_REF_OR_DEREF_OF,
+ OP_HANDLER_LOGICAL_NOT,
+ OP_HANDLER_BINARY_LOGIC,
+ OP_HANDLER_NAMED_OBJECT,
+ OP_HANDLER_BUFFER,
+ OP_HANDLER_PACKAGE,
+ OP_HANDLER_CREATE_NAMED,
+ OP_HANDLER_CREATE_BUFFER_FIELD,
+ OP_HANDLER_READ_FIELD,
+ OP_HANDLER_ALIAS,
+ OP_HANDLER_CONCATENATE,
+ OP_HANDLER_CONCATENATE_RES,
+ OP_HANDLER_SIZEOF,
+ OP_HANDLER_UNARY_MATH,
+ OP_HANDLER_INDEX,
+ OP_HANDLER_OBJECT_TYPE,
+ OP_HANDLER_CREATE_OP_REGION,
+ OP_HANDLER_CREATE_DATA_REGION,
+ OP_HANDLER_CREATE_FIELD,
+ OP_HANDLER_TO,
+ OP_HANDLER_TO_STRING,
+ OP_HANDLER_TIMER,
+ OP_HANDLER_MID,
+ OP_HANDLER_MATCH,
+ OP_HANDLER_CREATE_MUTEX_OR_EVENT,
+ OP_HANDLER_BCD,
+ OP_HANDLER_UNLOAD,
+ OP_HANDLER_LOAD_TABLE,
+ OP_HANDLER_LOAD,
+ OP_HANDLER_STALL_OR_SLEEP,
+ OP_HANDLER_EVENT_CTL,
+ OP_HANDLER_MUTEX_CTL,
+ OP_HANDLER_NOTIFY,
+ OP_HANDLER_FIRMWARE_REQUEST,
+};
+
+static uacpi_status (*op_handlers[])(struct execution_context *ctx) = {
+ /*
+ * All OPs that don't have a handler dispatch to here if
+ * UACPI_PARSE_OP_INVOKE_HANDLER is reached.
+ */
+ [OP_HANDLER_UNINSTALLED] = uninstalled_op_handler,
+ [OP_HANDLER_LOCAL] = handle_local,
+ [OP_HANDLER_ARG] = handle_arg,
+ [OP_HANDLER_NAMED_OBJECT] = handle_named_object,
+ [OP_HANDLER_STRING] = handle_string,
+ [OP_HANDLER_BINARY_MATH] = handle_binary_math,
+ [OP_HANDLER_CONTROL_FLOW] = handle_control_flow,
+ [OP_HANDLER_CODE_BLOCK] = handle_code_block,
+ [OP_HANDLER_RETURN] = handle_return,
+ [OP_HANDLER_CREATE_METHOD] = handle_create_method,
+ [OP_HANDLER_CREATE_MUTEX_OR_EVENT] = handle_create_mutex_or_event,
+ [OP_HANDLER_COPY_OBJECT_OR_STORE] = handle_copy_object_or_store,
+ [OP_HANDLER_INC_DEC] = handle_inc_dec,
+ [OP_HANDLER_REF_OR_DEREF_OF] = handle_ref_or_deref_of,
+ [OP_HANDLER_LOGICAL_NOT] = handle_logical_not,
+ [OP_HANDLER_BINARY_LOGIC] = handle_binary_logic,
+ [OP_HANDLER_BUFFER] = handle_buffer,
+ [OP_HANDLER_PACKAGE] = handle_package,
+ [OP_HANDLER_CREATE_NAMED] = handle_create_named,
+ [OP_HANDLER_CREATE_BUFFER_FIELD] = handle_create_buffer_field,
+ [OP_HANDLER_READ_FIELD] = handle_field_read,
+ [OP_HANDLER_TO] = handle_to,
+ [OP_HANDLER_ALIAS] = handle_create_alias,
+ [OP_HANDLER_CONCATENATE] = handle_concatenate,
+ [OP_HANDLER_CONCATENATE_RES] = handle_concatenate_res,
+ [OP_HANDLER_SIZEOF] = handle_sizeof,
+ [OP_HANDLER_UNARY_MATH] = handle_unary_math,
+ [OP_HANDLER_INDEX] = handle_index,
+ [OP_HANDLER_OBJECT_TYPE] = handle_object_type,
+ [OP_HANDLER_CREATE_OP_REGION] = handle_create_op_region,
+ [OP_HANDLER_CREATE_DATA_REGION] = handle_create_data_region,
+ [OP_HANDLER_CREATE_FIELD] = handle_create_field,
+ [OP_HANDLER_TIMER] = handle_timer,
+ [OP_HANDLER_TO_STRING] = handle_to_string,
+ [OP_HANDLER_MID] = handle_mid,
+ [OP_HANDLER_MATCH] = handle_match,
+ [OP_HANDLER_BCD] = handle_bcd,
+ [OP_HANDLER_UNLOAD] = handle_unload,
+ [OP_HANDLER_LOAD_TABLE] = handle_load_table,
+ [OP_HANDLER_LOAD] = handle_load,
+ [OP_HANDLER_STALL_OR_SLEEP] = handle_stall_or_sleep,
+ [OP_HANDLER_EVENT_CTL] = handle_event_ctl,
+ [OP_HANDLER_MUTEX_CTL] = handle_mutex_ctl,
+ [OP_HANDLER_NOTIFY] = handle_notify,
+ [OP_HANDLER_FIRMWARE_REQUEST] = handle_firmware_request,
+};
+
+static uacpi_u8 handler_idx_of_op[0x100] = {
+ [UACPI_AML_OP_Local0Op] = OP_HANDLER_LOCAL,
+ [UACPI_AML_OP_Local1Op] = OP_HANDLER_LOCAL,
+ [UACPI_AML_OP_Local2Op] = OP_HANDLER_LOCAL,
+ [UACPI_AML_OP_Local3Op] = OP_HANDLER_LOCAL,
+ [UACPI_AML_OP_Local4Op] = OP_HANDLER_LOCAL,
+ [UACPI_AML_OP_Local5Op] = OP_HANDLER_LOCAL,
+ [UACPI_AML_OP_Local6Op] = OP_HANDLER_LOCAL,
+ [UACPI_AML_OP_Local7Op] = OP_HANDLER_LOCAL,
+
+ [UACPI_AML_OP_Arg0Op] = OP_HANDLER_ARG,
+ [UACPI_AML_OP_Arg1Op] = OP_HANDLER_ARG,
+ [UACPI_AML_OP_Arg2Op] = OP_HANDLER_ARG,
+ [UACPI_AML_OP_Arg3Op] = OP_HANDLER_ARG,
+ [UACPI_AML_OP_Arg4Op] = OP_HANDLER_ARG,
+ [UACPI_AML_OP_Arg5Op] = OP_HANDLER_ARG,
+ [UACPI_AML_OP_Arg6Op] = OP_HANDLER_ARG,
+
+ [UACPI_AML_OP_StringPrefix] = OP_HANDLER_STRING,
+
+ [UACPI_AML_OP_AddOp] = OP_HANDLER_BINARY_MATH,
+ [UACPI_AML_OP_SubtractOp] = OP_HANDLER_BINARY_MATH,
+ [UACPI_AML_OP_MultiplyOp] = OP_HANDLER_BINARY_MATH,
+ [UACPI_AML_OP_DivideOp] = OP_HANDLER_BINARY_MATH,
+ [UACPI_AML_OP_ShiftLeftOp] = OP_HANDLER_BINARY_MATH,
+ [UACPI_AML_OP_ShiftRightOp] = OP_HANDLER_BINARY_MATH,
+ [UACPI_AML_OP_AndOp] = OP_HANDLER_BINARY_MATH,
+ [UACPI_AML_OP_NandOp] = OP_HANDLER_BINARY_MATH,
+ [UACPI_AML_OP_OrOp] = OP_HANDLER_BINARY_MATH,
+ [UACPI_AML_OP_NorOp] = OP_HANDLER_BINARY_MATH,
+ [UACPI_AML_OP_XorOp] = OP_HANDLER_BINARY_MATH,
+ [UACPI_AML_OP_ModOp] = OP_HANDLER_BINARY_MATH,
+
+ [UACPI_AML_OP_IfOp] = OP_HANDLER_CODE_BLOCK,
+ [UACPI_AML_OP_ElseOp] = OP_HANDLER_CODE_BLOCK,
+ [UACPI_AML_OP_WhileOp] = OP_HANDLER_CODE_BLOCK,
+ [UACPI_AML_OP_ScopeOp] = OP_HANDLER_CODE_BLOCK,
+
+ [UACPI_AML_OP_ContinueOp] = OP_HANDLER_CONTROL_FLOW,
+ [UACPI_AML_OP_BreakOp] = OP_HANDLER_CONTROL_FLOW,
+
+ [UACPI_AML_OP_ReturnOp] = OP_HANDLER_RETURN,
+
+ [UACPI_AML_OP_MethodOp] = OP_HANDLER_CREATE_METHOD,
+
+ [UACPI_AML_OP_StoreOp] = OP_HANDLER_COPY_OBJECT_OR_STORE,
+ [UACPI_AML_OP_CopyObjectOp] = OP_HANDLER_COPY_OBJECT_OR_STORE,
+
+ [UACPI_AML_OP_IncrementOp] = OP_HANDLER_INC_DEC,
+ [UACPI_AML_OP_DecrementOp] = OP_HANDLER_INC_DEC,
+
+ [UACPI_AML_OP_RefOfOp] = OP_HANDLER_REF_OR_DEREF_OF,
+ [UACPI_AML_OP_DerefOfOp] = OP_HANDLER_REF_OR_DEREF_OF,
+
+ [UACPI_AML_OP_LnotOp] = OP_HANDLER_LOGICAL_NOT,
+
+ [UACPI_AML_OP_LEqualOp] = OP_HANDLER_BINARY_LOGIC,
+ [UACPI_AML_OP_LandOp] = OP_HANDLER_BINARY_LOGIC,
+ [UACPI_AML_OP_LorOp] = OP_HANDLER_BINARY_LOGIC,
+ [UACPI_AML_OP_LGreaterOp] = OP_HANDLER_BINARY_LOGIC,
+ [UACPI_AML_OP_LLessOp] = OP_HANDLER_BINARY_LOGIC,
+
+ [UACPI_AML_OP_InternalOpNamedObject] = OP_HANDLER_NAMED_OBJECT,
+
+ [UACPI_AML_OP_BufferOp] = OP_HANDLER_BUFFER,
+
+ [UACPI_AML_OP_PackageOp] = OP_HANDLER_PACKAGE,
+ [UACPI_AML_OP_VarPackageOp] = OP_HANDLER_PACKAGE,
+
+ [UACPI_AML_OP_NameOp] = OP_HANDLER_CREATE_NAMED,
+
+ [UACPI_AML_OP_CreateBitFieldOp] = OP_HANDLER_CREATE_BUFFER_FIELD,
+ [UACPI_AML_OP_CreateByteFieldOp] = OP_HANDLER_CREATE_BUFFER_FIELD,
+ [UACPI_AML_OP_CreateWordFieldOp] = OP_HANDLER_CREATE_BUFFER_FIELD,
+ [UACPI_AML_OP_CreateDWordFieldOp] = OP_HANDLER_CREATE_BUFFER_FIELD,
+ [UACPI_AML_OP_CreateQWordFieldOp] = OP_HANDLER_CREATE_BUFFER_FIELD,
+
+ [UACPI_AML_OP_InternalOpReadFieldAsBuffer] = OP_HANDLER_READ_FIELD,
+ [UACPI_AML_OP_InternalOpReadFieldAsInteger] = OP_HANDLER_READ_FIELD,
+
+ [UACPI_AML_OP_ToIntegerOp] = OP_HANDLER_TO,
+ [UACPI_AML_OP_ToBufferOp] = OP_HANDLER_TO,
+ [UACPI_AML_OP_ToDecimalStringOp] = OP_HANDLER_TO,
+ [UACPI_AML_OP_ToHexStringOp] = OP_HANDLER_TO,
+ [UACPI_AML_OP_ToStringOp] = OP_HANDLER_TO_STRING,
+
+ [UACPI_AML_OP_AliasOp] = OP_HANDLER_ALIAS,
+
+ [UACPI_AML_OP_ConcatOp] = OP_HANDLER_CONCATENATE,
+ [UACPI_AML_OP_ConcatResOp] = OP_HANDLER_CONCATENATE_RES,
+
+ [UACPI_AML_OP_SizeOfOp] = OP_HANDLER_SIZEOF,
+
+ [UACPI_AML_OP_NotOp] = OP_HANDLER_UNARY_MATH,
+ [UACPI_AML_OP_FindSetLeftBitOp] = OP_HANDLER_UNARY_MATH,
+ [UACPI_AML_OP_FindSetRightBitOp] = OP_HANDLER_UNARY_MATH,
+
+ [UACPI_AML_OP_IndexOp] = OP_HANDLER_INDEX,
+
+ [UACPI_AML_OP_ObjectTypeOp] = OP_HANDLER_OBJECT_TYPE,
+
+ [UACPI_AML_OP_MidOp] = OP_HANDLER_MID,
+
+ [UACPI_AML_OP_MatchOp] = OP_HANDLER_MATCH,
+
+ [UACPI_AML_OP_NotifyOp] = OP_HANDLER_NOTIFY,
+
+ [UACPI_AML_OP_BreakPointOp] = OP_HANDLER_FIRMWARE_REQUEST,
+};
+
+#define EXT_OP_IDX(op) (op & 0xFF)
+
+static uacpi_u8 handler_idx_of_ext_op[0x100] = {
+ [EXT_OP_IDX(UACPI_AML_OP_CreateFieldOp)] = OP_HANDLER_CREATE_BUFFER_FIELD,
+ [EXT_OP_IDX(UACPI_AML_OP_CondRefOfOp)] = OP_HANDLER_REF_OR_DEREF_OF,
+ [EXT_OP_IDX(UACPI_AML_OP_OpRegionOp)] = OP_HANDLER_CREATE_OP_REGION,
+ [EXT_OP_IDX(UACPI_AML_OP_DeviceOp)] = OP_HANDLER_CODE_BLOCK,
+ [EXT_OP_IDX(UACPI_AML_OP_ProcessorOp)] = OP_HANDLER_CODE_BLOCK,
+ [EXT_OP_IDX(UACPI_AML_OP_PowerResOp)] = OP_HANDLER_CODE_BLOCK,
+ [EXT_OP_IDX(UACPI_AML_OP_ThermalZoneOp)] = OP_HANDLER_CODE_BLOCK,
+ [EXT_OP_IDX(UACPI_AML_OP_TimerOp)] = OP_HANDLER_TIMER,
+ [EXT_OP_IDX(UACPI_AML_OP_MutexOp)] = OP_HANDLER_CREATE_MUTEX_OR_EVENT,
+ [EXT_OP_IDX(UACPI_AML_OP_EventOp)] = OP_HANDLER_CREATE_MUTEX_OR_EVENT,
+
+ [EXT_OP_IDX(UACPI_AML_OP_FieldOp)] = OP_HANDLER_CREATE_FIELD,
+ [EXT_OP_IDX(UACPI_AML_OP_IndexFieldOp)] = OP_HANDLER_CREATE_FIELD,
+ [EXT_OP_IDX(UACPI_AML_OP_BankFieldOp)] = OP_HANDLER_CREATE_FIELD,
+
+ [EXT_OP_IDX(UACPI_AML_OP_FromBCDOp)] = OP_HANDLER_BCD,
+ [EXT_OP_IDX(UACPI_AML_OP_ToBCDOp)] = OP_HANDLER_BCD,
+
+ [EXT_OP_IDX(UACPI_AML_OP_DataRegionOp)] = OP_HANDLER_CREATE_DATA_REGION,
+
+ [EXT_OP_IDX(UACPI_AML_OP_LoadTableOp)] = OP_HANDLER_LOAD_TABLE,
+ [EXT_OP_IDX(UACPI_AML_OP_LoadOp)] = OP_HANDLER_LOAD,
+ [EXT_OP_IDX(UACPI_AML_OP_UnloadOp)] = OP_HANDLER_UNLOAD,
+
+ [EXT_OP_IDX(UACPI_AML_OP_StallOp)] = OP_HANDLER_STALL_OR_SLEEP,
+ [EXT_OP_IDX(UACPI_AML_OP_SleepOp)] = OP_HANDLER_STALL_OR_SLEEP,
+
+ [EXT_OP_IDX(UACPI_AML_OP_SignalOp)] = OP_HANDLER_EVENT_CTL,
+ [EXT_OP_IDX(UACPI_AML_OP_ResetOp)] = OP_HANDLER_EVENT_CTL,
+ [EXT_OP_IDX(UACPI_AML_OP_WaitOp)] = OP_HANDLER_EVENT_CTL,
+
+ [EXT_OP_IDX(UACPI_AML_OP_AcquireOp)] = OP_HANDLER_MUTEX_CTL,
+ [EXT_OP_IDX(UACPI_AML_OP_ReleaseOp)] = OP_HANDLER_MUTEX_CTL,
+
+ [EXT_OP_IDX(UACPI_AML_OP_FatalOp)] = OP_HANDLER_FIRMWARE_REQUEST,
+};
+
+enum method_call_type {
+ METHOD_CALL_NATIVE,
+ METHOD_CALL_AML,
+ METHOD_CALL_TABLE_LOAD,
+};
+
+static uacpi_status prepare_method_call(
+ struct execution_context *ctx, uacpi_namespace_node *node,
+ uacpi_control_method *method, enum method_call_type type,
+ const uacpi_object_array *args
+)
+{
+ uacpi_status ret;
+ struct call_frame *frame;
+
+ if (uacpi_unlikely(call_frame_array_size(&ctx->call_stack) >=
+ g_uacpi_rt_ctx.max_call_stack_depth))
+ return UACPI_STATUS_AML_CALL_STACK_DEPTH_LIMIT;
+
+ ret = push_new_frame(ctx, &frame);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ ret = enter_method(ctx, frame, method);
+ if (uacpi_unlikely_error(ret))
+ goto method_dispatch_error;
+
+ if (type == METHOD_CALL_NATIVE) {
+ uacpi_u8 arg_count;
+
+ arg_count = args ? args->count : 0;
+ if (uacpi_unlikely(arg_count != method->args)) {
+ uacpi_error(
+ "invalid number of arguments %zu to call %.4s, expected %d\n",
+ args ? args->count : 0, node->name.text, method->args
+ );
+
+ ret = UACPI_STATUS_INVALID_ARGUMENT;
+ goto method_dispatch_error;
+ }
+
+ if (args != UACPI_NULL) {
+ uacpi_u8 i;
+
+ for (i = 0; i < method->args; ++i) {
+ frame->args[i] = args->objects[i];
+ uacpi_object_ref(args->objects[i]);
+ }
+ }
+ } else if (type == METHOD_CALL_AML) {
+ ret = frame_push_args(frame, ctx->cur_op_ctx);
+ if (uacpi_unlikely_error(ret))
+ goto method_dispatch_error;
+ }
+
+ ret = frame_setup_base_scope(frame, node, method);
+ if (uacpi_unlikely_error(ret))
+ goto method_dispatch_error;
+
+ ctx->cur_frame = frame;
+ ctx->cur_op_ctx = UACPI_NULL;
+ ctx->prev_op_ctx = UACPI_NULL;
+ ctx->cur_block = code_block_array_last(&ctx->cur_frame->code_blocks);
+
+ if (method->native_call) {
+ uacpi_object *retval;
+
+ ret = method_get_ret_object(ctx, &retval);
+ if (uacpi_unlikely_error(ret))
+ goto method_dispatch_error;
+
+ return method->handler(ctx, retval);
+ }
+
+ return UACPI_STATUS_OK;
+
+method_dispatch_error:
+ call_frame_clear(frame);
+ call_frame_array_pop(&ctx->call_stack);
+ return ret;
+}
+
+static void apply_tracked_pkg(
+ struct call_frame *frame, struct op_context *op_ctx
+)
+{
+ struct item *item;
+
+ if (op_ctx->tracked_pkg_idx == 0)
+ return;
+
+ item = item_array_at(&op_ctx->items, op_ctx->tracked_pkg_idx - 1);
+ frame->code_offset = item->pkg.end;
+}
+
+static uacpi_status exec_op(struct execution_context *ctx)
+{
+ uacpi_status ret = UACPI_STATUS_OK;
+ struct call_frame *frame = ctx->cur_frame;
+ struct op_context *op_ctx;
+ struct item *item = UACPI_NULL;
+ enum uacpi_parse_op prev_op = 0, op;
+
+ /*
+ * Allocate a new op context if previous is preempted (looking for a
+ * dynamic argument), or doesn't exist at all.
+ */
+ if (!ctx_has_non_preempted_op(ctx)) {
+ ret = push_op(ctx);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+ } else {
+ trace_op(ctx->cur_op_ctx->op, OP_TRACE_ACTION_RESUME);
+ }
+
+ if (ctx->prev_op_ctx)
+ prev_op = *op_decode_cursor(ctx->prev_op_ctx);
+
+ for (;;) {
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ op_ctx = ctx->cur_op_ctx;
+ frame = ctx->cur_frame;
+
+ if (op_ctx->pc == 0 && ctx->prev_op_ctx) {
+ /*
+ * Type check the current arg type against what is expected by the
+ * preempted op. This check is able to catch most type violations
+ * with the only exception being Operand as we only know whether
+ * that evaluates to an integer after the fact.
+ */
+ ret = op_typecheck(ctx->prev_op_ctx, ctx->cur_op_ctx);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+ }
+
+ op = op_decode_byte(op_ctx);
+ trace_pop(op);
+
+ if (parse_op_generates_item[op] != ITEM_NONE) {
+ item = item_array_alloc(&op_ctx->items);
+ if (uacpi_unlikely(item == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ item->type = parse_op_generates_item[op];
+ if (item->type == ITEM_OBJECT) {
+ enum uacpi_object_type type = UACPI_OBJECT_UNINITIALIZED;
+
+ if (op == UACPI_PARSE_OP_OBJECT_ALLOC_TYPED)
+ type = op_decode_byte(op_ctx);
+
+ item->obj = uacpi_create_object(type);
+ if (uacpi_unlikely(item->obj == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+ } else {
+ uacpi_memzero(&item->immediate, sizeof(item->immediate));
+ }
+ } else if (item == UACPI_NULL) {
+ item = item_array_last(&op_ctx->items);
+ }
+
+ switch (op) {
+ case UACPI_PARSE_OP_END:
+ case UACPI_PARSE_OP_SKIP_WITH_WARN_IF_NULL: {
+ trace_op(ctx->cur_op_ctx->op, OP_TRACE_ACTION_END);
+
+ if (op == UACPI_PARSE_OP_SKIP_WITH_WARN_IF_NULL) {
+ uacpi_u8 idx;
+
+ idx = op_decode_byte(op_ctx);
+ if (item_array_at(&op_ctx->items, idx)->handle != UACPI_NULL)
+ break;
+
+ emit_op_skip_warn(op_ctx);
+ }
+
+ apply_tracked_pkg(frame, op_ctx);
+
+ pop_op(ctx);
+ if (ctx->cur_op_ctx) {
+ ctx->cur_op_ctx->preempted = UACPI_FALSE;
+ ctx->cur_op_ctx->pc++;
+ }
+
+ return UACPI_STATUS_OK;
+ }
+
+ case UACPI_PARSE_OP_EMIT_SKIP_WARN:
+ emit_op_skip_warn(op_ctx);
+ break;
+
+ case UACPI_PARSE_OP_SIMPLE_NAME:
+ case UACPI_PARSE_OP_SUPERNAME:
+ case UACPI_PARSE_OP_SUPERNAME_OR_UNRESOLVED:
+ case UACPI_PARSE_OP_TERM_ARG:
+ case UACPI_PARSE_OP_TERM_ARG_UNWRAP_INTERNAL:
+ case UACPI_PARSE_OP_TERM_ARG_OR_NAMED_OBJECT:
+ case UACPI_PARSE_OP_TERM_ARG_OR_NAMED_OBJECT_OR_UNRESOLVED:
+ case UACPI_PARSE_OP_OPERAND:
+ case UACPI_PARSE_OP_STRING:
+ case UACPI_PARSE_OP_COMPUTATIONAL_DATA:
+ case UACPI_PARSE_OP_TARGET:
+ /*
+ * Preempt this op parsing for now as we wait for the dynamic arg
+ * to be parsed.
+ */
+ op_ctx->preempted = UACPI_TRUE;
+ op_ctx->pc--;
+ return UACPI_STATUS_OK;
+
+ case UACPI_PARSE_OP_TRACKED_PKGLEN:
+ op_ctx->tracked_pkg_idx = item_array_size(&op_ctx->items);
+ UACPI_FALLTHROUGH;
+ case UACPI_PARSE_OP_PKGLEN:
+ ret = parse_package_length(frame, &item->pkg);
+ break;
+
+ case UACPI_PARSE_OP_LOAD_INLINE_IMM:
+ case UACPI_PARSE_OP_LOAD_INLINE_IMM_AS_OBJECT: {
+ void *dst;
+ uacpi_u8 src_width;
+
+ if (op == UACPI_PARSE_OP_LOAD_INLINE_IMM_AS_OBJECT) {
+ item->obj->type = UACPI_OBJECT_INTEGER;
+ dst = &item->obj->integer;
+ src_width = 8;
+ } else {
+ dst = &item->immediate;
+ src_width = op_decode_byte(op_ctx);
+ }
+
+ uacpi_memcpy_zerout(
+ dst, op_decode_cursor(op_ctx),
+ sizeof(uacpi_u64), src_width
+ );
+ op_ctx->pc += src_width;
+ break;
+ }
+
+ case UACPI_PARSE_OP_LOAD_ZERO_IMM:
+ break;
+
+ case UACPI_PARSE_OP_LOAD_IMM:
+ case UACPI_PARSE_OP_LOAD_IMM_AS_OBJECT: {
+ uacpi_u8 width;
+ void *dst;
+
+ width = op_decode_byte(op_ctx);
+ if (uacpi_unlikely(call_frame_code_bytes_left(frame) < width))
+ return UACPI_STATUS_AML_BAD_ENCODING;
+
+ if (op == UACPI_PARSE_OP_LOAD_IMM_AS_OBJECT) {
+ item->obj->type = UACPI_OBJECT_INTEGER;
+ item->obj->integer = 0;
+ dst = &item->obj->integer;
+ } else {
+ dst = item->immediate_bytes;
+ }
+
+ uacpi_memcpy(dst, call_frame_cursor(frame), width);
+ frame->code_offset += width;
+ break;
+ }
+
+ case UACPI_PARSE_OP_LOAD_FALSE_OBJECT:
+ case UACPI_PARSE_OP_LOAD_TRUE_OBJECT: {
+ uacpi_object *obj = item->obj;
+ obj->type = UACPI_OBJECT_INTEGER;
+ obj->integer = op == UACPI_PARSE_OP_LOAD_FALSE_OBJECT ? 0 : ones();
+ break;
+ }
+
+ case UACPI_PARSE_OP_RECORD_AML_PC:
+ item->immediate = frame->code_offset;
+ break;
+
+ case UACPI_PARSE_OP_TRUNCATE_NUMBER:
+ truncate_number_if_needed(item->obj);
+ break;
+
+ case UACPI_PARSE_OP_TYPECHECK: {
+ enum uacpi_object_type expected_type;
+
+ expected_type = op_decode_byte(op_ctx);
+
+ if (uacpi_unlikely(item->obj->type != expected_type)) {
+ EXEC_OP_ERR_2("bad object type: expected %s, got %s!",
+ uacpi_object_type_to_string(expected_type),
+ uacpi_object_type_to_string(item->obj->type));
+ ret = UACPI_STATUS_AML_INCOMPATIBLE_OBJECT_TYPE;
+ }
+
+ break;
+ }
+
+ case UACPI_PARSE_OP_BAD_OPCODE:
+ case UACPI_PARSE_OP_UNREACHABLE:
+ EXEC_OP_ERR("invalid/unexpected opcode");
+ ret = UACPI_STATUS_AML_INVALID_OPCODE;
+ break;
+
+ case UACPI_PARSE_OP_AML_PC_DECREMENT:
+ frame->code_offset--;
+ break;
+
+ case UACPI_PARSE_OP_IMM_DECREMENT:
+ item_array_at(&op_ctx->items, op_decode_byte(op_ctx))->immediate--;
+ break;
+
+ case UACPI_PARSE_OP_ITEM_POP:
+ pop_item(op_ctx);
+ item = item_array_last(&op_ctx->items);
+ break;
+
+ case UACPI_PARSE_OP_IF_HAS_DATA: {
+ uacpi_size pkg_idx = op_ctx->tracked_pkg_idx - 1;
+ struct package_length *pkg;
+ uacpi_u8 bytes_skip;
+
+ bytes_skip = op_decode_byte(op_ctx);
+ pkg = &item_array_at(&op_ctx->items, pkg_idx)->pkg;
+
+ if (frame->code_offset >= pkg->end)
+ op_ctx->pc += bytes_skip;
+
+ break;
+ }
+
+ case UACPI_PARSE_OP_IF_NOT_NULL:
+ case UACPI_PARSE_OP_IF_NULL:
+ case UACPI_PARSE_OP_IF_LAST_NULL:
+ case UACPI_PARSE_OP_IF_LAST_NOT_NULL: {
+ uacpi_u8 idx, bytes_skip;
+ uacpi_bool is_null, skip_if_null;
+
+ if (op == UACPI_PARSE_OP_IF_LAST_NULL ||
+ op == UACPI_PARSE_OP_IF_LAST_NOT_NULL) {
+ is_null = item->handle == UACPI_NULL;
+ } else {
+ idx = op_decode_byte(op_ctx);
+ is_null = item_array_at(&op_ctx->items, idx)->handle == UACPI_NULL;
+ }
+
+ bytes_skip = op_decode_byte(op_ctx);
+ skip_if_null = op == UACPI_PARSE_OP_IF_NOT_NULL ||
+ op == UACPI_PARSE_OP_IF_LAST_NOT_NULL;
+
+ if (is_null == skip_if_null)
+ op_ctx->pc += bytes_skip;
+
+ break;
+ }
+
+ case UACPI_PARSE_OP_IF_LAST_EQUALS: {
+ uacpi_u8 value, bytes_skip;
+
+ value = op_decode_byte(op_ctx);
+ bytes_skip = op_decode_byte(op_ctx);
+
+ if (item->immediate != value)
+ op_ctx->pc += bytes_skip;
+
+ break;
+ }
+
+ case UACPI_PARSE_OP_IF_LAST_FALSE:
+ case UACPI_PARSE_OP_IF_LAST_TRUE: {
+ uacpi_u8 bytes_skip;
+ uacpi_bool is_false, skip_if_false;
+
+ bytes_skip = op_decode_byte(op_ctx);
+ is_false = item->obj->integer == 0;
+ skip_if_false = op == UACPI_PARSE_OP_IF_LAST_TRUE;
+
+ if (is_false == skip_if_false)
+ op_ctx->pc += bytes_skip;
+
+ break;
+ }
+
+ case UACPI_PARSE_OP_JMP: {
+ op_ctx->pc = op_decode_byte(op_ctx);
+ break;
+ }
+
+ case UACPI_PARSE_OP_CREATE_NAMESTRING:
+ case UACPI_PARSE_OP_CREATE_NAMESTRING_OR_NULL_IF_LOAD:
+ case UACPI_PARSE_OP_EXISTING_NAMESTRING:
+ case UACPI_PARSE_OP_EXISTING_NAMESTRING_OR_NULL:
+ case UACPI_PARSE_OP_EXISTING_NAMESTRING_OR_NULL_IF_LOAD: {
+ uacpi_size offset = frame->code_offset;
+ enum resolve_behavior behavior;
+
+ if (op == UACPI_PARSE_OP_CREATE_NAMESTRING ||
+ op == UACPI_PARSE_OP_CREATE_NAMESTRING_OR_NULL_IF_LOAD)
+ behavior = RESOLVE_CREATE_LAST_NAMESEG_FAIL_IF_EXISTS;
+ else
+ behavior = RESOLVE_FAIL_IF_DOESNT_EXIST;
+
+ ret = resolve_name_string(frame, behavior, &item->node);
+
+ if (ret == UACPI_STATUS_NOT_FOUND) {
+ uacpi_bool is_ok;
+
+ if (prev_op) {
+ is_ok = op_allows_unresolved(prev_op);
+ is_ok &= op_allows_unresolved(op);
+ } else {
+ // This is the only standalone op where we allow unresolved
+ is_ok = op_ctx->op->code == UACPI_AML_OP_ExternalOp;
+ }
+
+ if (is_ok)
+ ret = UACPI_STATUS_OK;
+ }
+
+ if (uacpi_unlikely_error(ret)) {
+ enum uacpi_log_level lvl = UACPI_LOG_ERROR;
+ uacpi_status trace_ret = ret;
+ uacpi_bool abort_whileif = UACPI_FALSE;
+
+ if (frame->method->named_objects_persist &&
+ (ret == UACPI_STATUS_AML_OBJECT_ALREADY_EXISTS ||
+ ret == UACPI_STATUS_NOT_FOUND)) {
+ struct op_context *first_ctx;
+
+ first_ctx = op_context_array_at(&frame->pending_ops, 0);
+ abort_whileif = first_ctx->op->code == UACPI_AML_OP_WhileOp ||
+ first_ctx->op->code == UACPI_AML_OP_IfOp;
+
+ if (op_allows_unresolved_if_load(op) || abort_whileif) {
+ lvl = UACPI_LOG_WARN;
+ ret = UACPI_STATUS_OK;
+ }
+ }
+
+ trace_named_object_lookup_or_creation_failure(
+ frame, offset, op, trace_ret, lvl
+ );
+
+ if (abort_whileif) {
+ while (op_context_array_size(&frame->pending_ops) != 1)
+ pop_op(ctx);
+
+ op_ctx = op_context_array_at(&frame->pending_ops, 0);
+ op_ctx->pc++;
+ op_ctx->preempted = UACPI_FALSE;
+ break;
+ }
+
+ if (ret == UACPI_STATUS_NOT_FOUND)
+ ret = UACPI_STATUS_AML_UNDEFINED_REFERENCE;
+ }
+
+ if (behavior == RESOLVE_CREATE_LAST_NAMESEG_FAIL_IF_EXISTS &&
+ !frame->method->named_objects_persist)
+ item->node->flags |= UACPI_NAMESPACE_NODE_FLAG_TEMPORARY;
+
+ break;
+ }
+
+ case UACPI_PARSE_OP_INVOKE_HANDLER: {
+ uacpi_aml_op code = op_ctx->op->code;
+ uacpi_u8 idx;
+
+ if (code <= 0xFF)
+ idx = handler_idx_of_op[code];
+ else
+ idx = handler_idx_of_ext_op[EXT_OP_IDX(code)];
+
+ ret = op_handlers[idx](ctx);
+ break;
+ }
+
+ case UACPI_PARSE_OP_INSTALL_NAMESPACE_NODE:
+ item = item_array_at(&op_ctx->items, op_decode_byte(op_ctx));
+ ret = do_install_node_item(frame, item);
+ break;
+
+ case UACPI_PARSE_OP_OBJECT_TRANSFER_TO_PREV:
+ case UACPI_PARSE_OP_OBJECT_COPY_TO_PREV: {
+ uacpi_object *src;
+ struct item *dst;
+
+ if (!ctx->prev_op_ctx)
+ break;
+
+ switch (prev_op) {
+ case UACPI_PARSE_OP_TERM_ARG_UNWRAP_INTERNAL:
+ case UACPI_PARSE_OP_COMPUTATIONAL_DATA:
+ case UACPI_PARSE_OP_OPERAND:
+ case UACPI_PARSE_OP_STRING:
+ src = uacpi_unwrap_internal_reference(item->obj);
+
+ if (prev_op == UACPI_PARSE_OP_OPERAND)
+ ret = typecheck_operand(ctx->prev_op_ctx, src);
+ else if (prev_op == UACPI_PARSE_OP_STRING)
+ ret = typecheck_string(ctx->prev_op_ctx, src);
+ else if (prev_op == UACPI_PARSE_OP_COMPUTATIONAL_DATA)
+ ret = typecheck_computational_data(ctx->prev_op_ctx, src);
+
+ break;
+ case UACPI_PARSE_OP_SUPERNAME:
+ case UACPI_PARSE_OP_SUPERNAME_OR_UNRESOLVED:
+ src = item->obj;
+ break;
+
+ case UACPI_PARSE_OP_SIMPLE_NAME:
+ case UACPI_PARSE_OP_TERM_ARG:
+ case UACPI_PARSE_OP_TERM_ARG_OR_NAMED_OBJECT:
+ case UACPI_PARSE_OP_TERM_ARG_OR_NAMED_OBJECT_OR_UNRESOLVED:
+ case UACPI_PARSE_OP_TARGET:
+ src = item->obj;
+ break;
+
+ default:
+ EXEC_OP_ERR_1("don't know how to copy/transfer object to %d",
+ prev_op);
+ ret = UACPI_STATUS_INVALID_ARGUMENT;
+ break;
+ }
+
+ if (uacpi_likely_success(ret)) {
+ dst = item_array_last(&ctx->prev_op_ctx->items);
+ dst->type = ITEM_OBJECT;
+
+ if (op == UACPI_PARSE_OP_OBJECT_TRANSFER_TO_PREV) {
+ dst->obj = src;
+ uacpi_object_ref(dst->obj);
+ } else {
+ dst->obj = uacpi_create_object(UACPI_OBJECT_UNINITIALIZED);
+ if (uacpi_unlikely(dst->obj == UACPI_NULL)) {
+ ret = UACPI_STATUS_OUT_OF_MEMORY;
+ break;
+ }
+
+ ret = uacpi_object_assign(dst->obj, src,
+ UACPI_ASSIGN_BEHAVIOR_DEEP_COPY);
+ }
+ }
+ break;
+ }
+
+ case UACPI_PARSE_OP_STORE_TO_TARGET:
+ case UACPI_PARSE_OP_STORE_TO_TARGET_INDIRECT: {
+ uacpi_object *dst, *src;
+
+ dst = item_array_at(&op_ctx->items, op_decode_byte(op_ctx))->obj;
+
+ if (op == UACPI_PARSE_OP_STORE_TO_TARGET_INDIRECT) {
+ src = item_array_at(&op_ctx->items,
+ op_decode_byte(op_ctx))->obj;
+ } else {
+ src = item->obj;
+ }
+
+ ret = store_to_target(dst, src, UACPI_NULL);
+ break;
+ }
+
+ // Nothing to do here, object is allocated automatically
+ case UACPI_PARSE_OP_OBJECT_ALLOC:
+ case UACPI_PARSE_OP_OBJECT_ALLOC_TYPED:
+ case UACPI_PARSE_OP_EMPTY_OBJECT_ALLOC:
+ break;
+
+ case UACPI_PARSE_OP_OBJECT_CONVERT_TO_SHALLOW_COPY:
+ case UACPI_PARSE_OP_OBJECT_CONVERT_TO_DEEP_COPY: {
+ uacpi_object *temp = item->obj;
+ enum uacpi_assign_behavior behavior;
+
+ item_array_pop(&op_ctx->items);
+ item = item_array_last(&op_ctx->items);
+
+ if (op == UACPI_PARSE_OP_OBJECT_CONVERT_TO_SHALLOW_COPY)
+ behavior = UACPI_ASSIGN_BEHAVIOR_SHALLOW_COPY;
+ else
+ behavior = UACPI_ASSIGN_BEHAVIOR_DEEP_COPY;
+
+ ret = uacpi_object_assign(temp, item->obj, behavior);
+ if (uacpi_unlikely_error(ret))
+ break;
+
+ uacpi_object_unref(item->obj);
+ item->obj = temp;
+ break;
+ }
+
+ case UACPI_PARSE_OP_DISPATCH_METHOD_CALL: {
+ struct uacpi_namespace_node *node;
+ struct uacpi_control_method *method;
+
+ node = item_array_at(&op_ctx->items, 0)->node;
+ method = uacpi_namespace_node_get_object(node)->method;
+
+ ret = prepare_method_call(
+ ctx, node, method, METHOD_CALL_AML, UACPI_NULL
+ );
+ return ret;
+ }
+
+ case UACPI_PARSE_OP_DISPATCH_TABLE_LOAD: {
+ struct uacpi_namespace_node *node;
+ struct uacpi_control_method *method;
+
+ node = item_array_at(&op_ctx->items, 0)->node;
+ method = item_array_at(&op_ctx->items, 1)->obj->method;
+
+ ret = prepare_method_call(
+ ctx, node, method, METHOD_CALL_TABLE_LOAD, UACPI_NULL
+ );
+ return ret;
+ }
+
+ case UACPI_PARSE_OP_CONVERT_NAMESTRING: {
+ uacpi_aml_op new_op = UACPI_AML_OP_InternalOpNamedObject;
+ uacpi_object *obj;
+
+ if (item->node == UACPI_NULL) {
+ if (!op_allows_unresolved(prev_op))
+ ret = UACPI_STATUS_NOT_FOUND;
+ break;
+ }
+
+ obj = uacpi_namespace_node_get_object(item->node);
+
+ switch (obj->type) {
+ case UACPI_OBJECT_METHOD: {
+ uacpi_bool should_invoke;
+
+ switch (prev_op) {
+ case UACPI_PARSE_OP_TERM_ARG_OR_NAMED_OBJECT:
+ case UACPI_PARSE_OP_TERM_ARG_OR_NAMED_OBJECT_OR_UNRESOLVED:
+ should_invoke = UACPI_FALSE;
+ break;
+ default:
+ should_invoke = !op_wants_supername(prev_op);
+ }
+
+ if (!should_invoke)
+ break;
+
+ new_op = UACPI_AML_OP_InternalOpMethodCall0Args;
+ new_op += obj->method->args;
+ break;
+ }
+
+ case UACPI_OBJECT_BUFFER_FIELD:
+ case UACPI_OBJECT_FIELD_UNIT: {
+ uacpi_object_type type;
+
+ if (!op_wants_term_arg_or_operand(prev_op))
+ break;
+
+ ret = field_get_read_type(obj, &type);
+ if (uacpi_unlikely_error(ret)) {
+ const uacpi_char *field_path;
+
+ field_path = uacpi_namespace_node_generate_absolute_path(
+ item->node
+ );
+
+ uacpi_error(
+ "unable to perform a read from field %s: "
+ "parent opregion gone\n", field_path
+ );
+ uacpi_free_absolute_path(field_path);
+ }
+
+ switch (type) {
+ case UACPI_OBJECT_BUFFER:
+ new_op = UACPI_AML_OP_InternalOpReadFieldAsBuffer;
+ break;
+ case UACPI_OBJECT_INTEGER:
+ new_op = UACPI_AML_OP_InternalOpReadFieldAsInteger;
+ break;
+ default:
+ ret = UACPI_STATUS_INVALID_ARGUMENT;
+ continue;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ op_ctx->pc = 0;
+ op_ctx->op = uacpi_get_op_spec(new_op);
+ break;
+ }
+
+ case UACPI_PARSE_OP_SWITCH_TO_NEXT_IF_EQUALS: {
+ uacpi_aml_op op, target_op;
+ uacpi_u32 cur_offset;
+ uacpi_u8 op_length;
+
+ cur_offset = frame->code_offset;
+ apply_tracked_pkg(frame, op_ctx);
+ op_length = peek_next_op(frame, &op);
+
+ target_op = op_decode_aml_op(op_ctx);
+ if (op_length == 0 || op != target_op) {
+ // Revert tracked package
+ frame->code_offset = cur_offset;
+ break;
+ }
+
+ frame->code_offset += op_length;
+ op_ctx->switched_from = op_ctx->op->code;
+ op_ctx->op = uacpi_get_op_spec(target_op);
+ op_ctx->pc = 0;
+ break;
+ }
+
+ case UACPI_PARSE_OP_IF_SWITCHED_FROM: {
+ uacpi_aml_op target_op;
+ uacpi_u8 skip_bytes;
+
+ target_op = op_decode_aml_op(op_ctx);
+ skip_bytes = op_decode_byte(op_ctx);
+
+ if (op_ctx->switched_from != target_op)
+ op_ctx->pc += skip_bytes;
+ break;
+ }
+
+ default:
+ EXEC_OP_ERR_1("unhandled parser op '%d'", op);
+ ret = UACPI_STATUS_UNIMPLEMENTED;
+ break;
+ }
+ }
+}
+
+static void ctx_reload_post_ret(struct execution_context *ctx)
+{
+ uacpi_control_method *method = ctx->cur_frame->method;
+
+ if (method->is_serialized) {
+ held_mutexes_array_remove_and_release(
+ &ctx->held_mutexes, method->mutex, FORCE_RELEASE_YES
+ );
+ ctx->sync_level = ctx->cur_frame->prev_sync_level;
+ }
+
+ call_frame_clear(ctx->cur_frame);
+ call_frame_array_pop(&ctx->call_stack);
+
+ ctx->cur_frame = call_frame_array_last(&ctx->call_stack);
+ refresh_ctx_pointers(ctx);
+}
+
+static void trace_method_abort(struct code_block *block, uacpi_size depth)
+{
+ static const uacpi_char *unknown_path = "<unknown>";
+ uacpi_char oom_absolute_path[9] = "<?>.";
+
+ const uacpi_char *absolute_path;
+
+ if (block != UACPI_NULL && block->type == CODE_BLOCK_SCOPE) {
+ absolute_path = uacpi_namespace_node_generate_absolute_path(block->node);
+ if (uacpi_unlikely(absolute_path == UACPI_NULL))
+ uacpi_memcpy(oom_absolute_path + 4, block->node->name.text, 4);
+ } else {
+ absolute_path = unknown_path;
+ }
+
+ uacpi_error(" #%zu in %s()\n", depth, absolute_path);
+
+ if (absolute_path != oom_absolute_path && absolute_path != unknown_path)
+ uacpi_free_dynamic_string(absolute_path);
+}
+
+static void stack_unwind(struct execution_context *ctx)
+{
+ uacpi_size depth;
+ uacpi_bool should_stop;
+
+ /*
+ * Non-empty call stack here means the execution was aborted at some point,
+ * probably due to a bytecode error.
+ */
+ depth = call_frame_array_size(&ctx->call_stack);
+
+ if (depth != 0) {
+ uacpi_size idx = 0;
+ uacpi_bool table_level_code;
+
+ do {
+ table_level_code = ctx->cur_frame->method->named_objects_persist;
+
+ if (table_level_code && idx != 0)
+ /*
+ * This isn't the first frame that we are aborting.
+ * If this is table-level code, we have just unwound a call
+ * chain that had triggered an abort. Stop here, no need to
+ * abort table load because of it.
+ */
+ break;
+
+ while (op_context_array_size(&ctx->cur_frame->pending_ops) != 0)
+ pop_op(ctx);
+
+ trace_method_abort(
+ code_block_array_at(&ctx->cur_frame->code_blocks, 0), idx
+ );
+
+ should_stop = idx++ == 0 && table_level_code;
+ ctx_reload_post_ret(ctx);
+ } while (--depth && !should_stop);
+ }
+}
+
+static void execution_context_release(struct execution_context *ctx)
+{
+ if (ctx->ret)
+ uacpi_object_unref(ctx->ret);
+
+ while (held_mutexes_array_size(&ctx->held_mutexes) != 0) {
+ held_mutexes_array_remove_and_release(
+ &ctx->held_mutexes,
+ *held_mutexes_array_last(&ctx->held_mutexes),
+ FORCE_RELEASE_YES
+ );
+ }
+
+ call_frame_array_clear(&ctx->call_stack);
+ held_mutexes_array_clear(&ctx->held_mutexes);
+ uacpi_free(ctx, sizeof(*ctx));
+}
+
+uacpi_status uacpi_execute_control_method(
+ uacpi_namespace_node *scope, uacpi_control_method *method,
+ const uacpi_object_array *args, uacpi_object **out_obj
+)
+{
+ uacpi_status ret = UACPI_STATUS_OK;
+ struct execution_context *ctx;
+
+ ctx = uacpi_kernel_alloc_zeroed(sizeof(*ctx));
+ if (uacpi_unlikely(ctx == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ if (out_obj != UACPI_NULL) {
+ ctx->ret = uacpi_create_object(UACPI_OBJECT_UNINITIALIZED);
+ if (uacpi_unlikely(ctx->ret == UACPI_NULL)) {
+ ret = UACPI_STATUS_OUT_OF_MEMORY;
+ goto out;
+ }
+ }
+
+ ret = prepare_method_call(ctx, scope, method, METHOD_CALL_NATIVE, args);
+ if (uacpi_unlikely_error(ret))
+ goto out;
+
+ for (;;) {
+ if (!ctx_has_non_preempted_op(ctx)) {
+ if (ctx->cur_frame == UACPI_NULL)
+ break;
+
+ if (maybe_end_block(ctx))
+ continue;
+
+ if (!call_frame_has_code(ctx->cur_frame)) {
+ ctx_reload_post_ret(ctx);
+ continue;
+ }
+
+ ret = get_op(ctx);
+ if (uacpi_unlikely_error(ret))
+ goto handle_method_abort;
+
+ trace_op(ctx->cur_op, OP_TRACE_ACTION_BEGIN);
+ }
+
+ ret = exec_op(ctx);
+ if (uacpi_unlikely_error(ret))
+ goto handle_method_abort;
+
+ continue;
+
+ handle_method_abort:
+ uacpi_error("aborting %s due to previous error: %s\n",
+ ctx->cur_frame->method->named_objects_persist ?
+ "table load" : "method invocation",
+ uacpi_status_to_string(ret));
+ stack_unwind(ctx);
+
+ /*
+ * Having a frame here implies that we just aborted a dynamic table
+ * load. Signal to the caller that it failed by setting the return
+ * value to false.
+ */
+ if (ctx->cur_frame) {
+ struct item *it;
+
+ it = item_array_last(&ctx->cur_op_ctx->items);
+ if (it != UACPI_NULL && it->obj != UACPI_NULL)
+ it->obj->integer = 0;
+ }
+ }
+
+out:
+ if (ctx->ret != UACPI_NULL) {
+ uacpi_object *ret_obj = UACPI_NULL;
+
+ if (ctx->ret->type != UACPI_OBJECT_UNINITIALIZED) {
+ ret_obj = ctx->ret;
+ uacpi_object_ref(ret_obj);
+ }
+
+ *out_obj = ret_obj;
+ }
+
+ execution_context_release(ctx);
+ return ret;
+}
+
+uacpi_status uacpi_osi(uacpi_handle handle, uacpi_object *retval)
+{
+ struct execution_context *ctx = handle;
+ uacpi_bool is_supported;
+ uacpi_status ret;
+ uacpi_object *arg;
+
+ arg = uacpi_unwrap_internal_reference(ctx->cur_frame->args[0]);
+ if (arg->type != UACPI_OBJECT_STRING) {
+ uacpi_error("_OSI: invalid argument type %s, expected a String\n",
+ uacpi_object_type_to_string(arg->type));
+ return UACPI_STATUS_AML_INCOMPATIBLE_OBJECT_TYPE;
+ }
+
+ if (retval == UACPI_NULL)
+ return UACPI_STATUS_OK;
+
+ retval->type = UACPI_OBJECT_INTEGER;
+
+ ret = uacpi_handle_osi(arg->buffer->text, &is_supported);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ retval->integer = is_supported ? ones() : 0;
+
+ uacpi_trace("_OSI(%s) => reporting as %ssupported\n",
+ arg->buffer->text, is_supported ? "" : "un");
+ return UACPI_STATUS_OK;
+}
+
+#endif // !UACPI_BAREBONES_MODE
diff --git a/sys/dev/acpi/uacpi/io.c b/sys/dev/acpi/uacpi/io.c
new file mode 100644
index 0000000..7d10005
--- /dev/null
+++ b/sys/dev/acpi/uacpi/io.c
@@ -0,0 +1,1116 @@
+#include <uacpi/internal/io.h>
+#include <uacpi/internal/stdlib.h>
+#include <uacpi/internal/log.h>
+#include <uacpi/internal/opregion.h>
+#include <uacpi/internal/utilities.h>
+#include <uacpi/internal/mutex.h>
+#include <uacpi/internal/namespace.h>
+
+#ifndef UACPI_BAREBONES_MODE
+
+uacpi_size uacpi_round_up_bits_to_bytes(uacpi_size bit_length)
+{
+ return UACPI_ALIGN_UP(bit_length, 8, uacpi_size) / 8;
+}
+
+static void cut_misaligned_tail(
+ uacpi_u8 *data, uacpi_size offset, uacpi_u32 bit_length
+)
+{
+ uacpi_u8 remainder = bit_length & 7;
+
+ if (remainder == 0)
+ return;
+
+ data[offset] &= ((1ull << remainder) - 1);
+}
+
+struct bit_span
+{
+ union {
+ uacpi_u8 *data;
+ const uacpi_u8 *const_data;
+ };
+ uacpi_u64 index;
+ uacpi_u64 length;
+};
+
+static uacpi_size bit_span_offset(struct bit_span *span, uacpi_size bits)
+{
+ uacpi_size delta = UACPI_MIN(span->length, bits);
+
+ span->index += delta;
+ span->length -= delta;
+
+ return delta;
+}
+
+static void bit_copy(struct bit_span *dst, struct bit_span *src)
+{
+ uacpi_u8 src_shift, dst_shift, bits = 0;
+ uacpi_u16 dst_mask;
+ uacpi_u8 *dst_ptr, *src_ptr;
+ uacpi_u64 dst_count, src_count;
+
+ dst_ptr = dst->data + (dst->index / 8);
+ src_ptr = src->data + (src->index / 8);
+
+ dst_count = dst->length;
+ dst_shift = dst->index & 7;
+
+ src_count = src->length;
+ src_shift = src->index & 7;
+
+ while (dst_count)
+ {
+ bits = 0;
+
+ if (src_count) {
+ bits = *src_ptr >> src_shift;
+
+ if (src_shift && src_count > (uacpi_u32)(8 - src_shift))
+ bits |= *(src_ptr + 1) << (8 - src_shift);
+
+ if (src_count < 8) {
+ bits &= (1 << src_count) - 1;
+ src_count = 0;
+ } else {
+ src_count -= 8;
+ src_ptr++;
+ }
+ }
+
+ dst_mask = (dst_count < 8 ? (1 << dst_count) - 1 : 0xFF) << dst_shift;
+ *dst_ptr = (*dst_ptr & ~dst_mask) | ((bits << dst_shift) & dst_mask);
+
+ if (dst_shift && dst_count > (uacpi_u32)(8 - dst_shift)) {
+ dst_mask >>= 8;
+ *(dst_ptr + 1) &= ~dst_mask;
+ *(dst_ptr + 1) |= (bits >> (8 - dst_shift)) & dst_mask;
+ }
+
+ dst_count = dst_count > 8 ? dst_count - 8 : 0;
+ ++dst_ptr;
+ }
+}
+
+static void do_misaligned_buffer_read(
+ const uacpi_buffer_field *field, uacpi_u8 *dst
+)
+{
+ struct bit_span src_span = { 0 };
+ struct bit_span dst_span = { 0 };
+
+ src_span.index = field->bit_index;
+ src_span.length = field->bit_length;
+ src_span.const_data = field->backing->data;
+
+ dst_span.data = dst;
+ dst_span.length = uacpi_round_up_bits_to_bytes(field->bit_length) * 8;
+ bit_copy(&dst_span, &src_span);
+}
+
+void uacpi_read_buffer_field(
+ const uacpi_buffer_field *field, void *dst
+)
+{
+ if (!(field->bit_index & 7)) {
+ uacpi_u8 *src = field->backing->data;
+ uacpi_size count;
+
+ count = uacpi_round_up_bits_to_bytes(field->bit_length);
+ uacpi_memcpy(dst, src + (field->bit_index / 8), count);
+ cut_misaligned_tail(dst, count - 1, field->bit_length);
+ return;
+ }
+
+ do_misaligned_buffer_read(field, dst);
+}
+
+static void do_write_misaligned_buffer_field(
+ uacpi_buffer_field *field,
+ const void *src, uacpi_size size
+)
+{
+ struct bit_span src_span = { 0 };
+ struct bit_span dst_span = { 0 };
+
+ src_span.length = size * 8;
+ src_span.const_data = src;
+
+ dst_span.index = field->bit_index;
+ dst_span.length = field->bit_length;
+ dst_span.data = field->backing->data;
+
+ bit_copy(&dst_span, &src_span);
+}
+
+void uacpi_write_buffer_field(
+ uacpi_buffer_field *field,
+ const void *src, uacpi_size size
+)
+{
+ if (!(field->bit_index & 7)) {
+ uacpi_u8 *dst, last_byte, tail_shift;
+ uacpi_size count;
+
+ dst = field->backing->data;
+ dst += field->bit_index / 8;
+ count = uacpi_round_up_bits_to_bytes(field->bit_length);
+
+ last_byte = dst[count - 1];
+ tail_shift = field->bit_length & 7;
+
+ uacpi_memcpy_zerout(dst, src, count, size);
+ if (tail_shift) {
+ uacpi_u8 last_shift = 8 - tail_shift;
+ dst[count - 1] = dst[count - 1] << last_shift;
+ dst[count - 1] >>= last_shift;
+ dst[count - 1] |= (last_byte >> tail_shift) << tail_shift;
+ }
+
+ return;
+ }
+
+ do_write_misaligned_buffer_field(field, src, size);
+}
+
+static uacpi_status access_field_unit(
+ uacpi_field_unit *field, uacpi_u32 offset, uacpi_region_op op,
+ union uacpi_opregion_io_data data
+)
+{
+ uacpi_status ret = UACPI_STATUS_OK;
+
+ if (field->lock_rule) {
+ ret = uacpi_acquire_aml_mutex(
+ g_uacpi_rt_ctx.global_lock_mutex, 0xFFFF
+ );
+ if (uacpi_unlikely_error(ret))
+ return ret;
+ }
+
+ switch (field->kind) {
+ case UACPI_FIELD_UNIT_KIND_BANK:
+ ret = uacpi_write_field_unit(
+ field->bank_selection, &field->bank_value, sizeof(field->bank_value),
+ UACPI_NULL
+ );
+ break;
+ case UACPI_FIELD_UNIT_KIND_NORMAL:
+ break;
+ case UACPI_FIELD_UNIT_KIND_INDEX:
+ ret = uacpi_write_field_unit(
+ field->index, &offset, sizeof(offset),
+ UACPI_NULL
+ );
+ if (uacpi_unlikely_error(ret))
+ goto out;
+
+ switch (op) {
+ case UACPI_REGION_OP_READ:
+ ret = uacpi_read_field_unit(
+ field->data, data.integer, field->access_width_bytes,
+ UACPI_NULL
+ );
+ break;
+ case UACPI_REGION_OP_WRITE:
+ ret = uacpi_write_field_unit(
+ field->data, data.integer, field->access_width_bytes,
+ UACPI_NULL
+ );
+ break;
+ default:
+ ret = UACPI_STATUS_INVALID_ARGUMENT;
+ break;
+ }
+
+ goto out;
+
+ default:
+ uacpi_error("invalid field unit kind %d\n", field->kind);
+ ret = UACPI_STATUS_INVALID_ARGUMENT;
+ }
+
+ if (uacpi_unlikely_error(ret))
+ goto out;
+
+ ret = uacpi_dispatch_opregion_io(field, offset, op, data);
+
+out:
+ if (field->lock_rule)
+ uacpi_release_aml_mutex(g_uacpi_rt_ctx.global_lock_mutex);
+ return ret;
+}
+
+#define SERIAL_HEADER_SIZE 2
+#define IPMI_DATA_SIZE 64
+
+static uacpi_status wtr_buffer_size(
+ uacpi_field_unit *field, uacpi_address_space space,
+ uacpi_size *out_size
+)
+{
+ switch (space) {
+ case UACPI_ADDRESS_SPACE_IPMI:
+ *out_size = SERIAL_HEADER_SIZE + IPMI_DATA_SIZE;
+ break;
+ case UACPI_ADDRESS_SPACE_PRM:
+ *out_size = 26;
+ break;
+ case UACPI_ADDRESS_SPACE_FFIXEDHW:
+ *out_size = 256;
+ break;
+ case UACPI_ADDRESS_SPACE_GENERIC_SERIAL_BUS:
+ case UACPI_ADDRESS_SPACE_SMBUS: {
+ uacpi_size size_for_protocol = SERIAL_HEADER_SIZE;
+
+ switch (field->attributes) {
+ case UACPI_ACCESS_ATTRIBUTE_QUICK:
+ break; // + 0
+ case UACPI_ACCESS_ATTRIBUTE_SEND_RECEIVE:
+ case UACPI_ACCESS_ATTRIBUTE_BYTE:
+ size_for_protocol += 1;
+ break;
+
+ case UACPI_ACCESS_ATTRIBUTE_WORD:
+ case UACPI_ACCESS_ATTRIBUTE_PROCESS_CALL:
+ size_for_protocol += 2;
+ break;
+
+ case UACPI_ACCESS_ATTRIBUTE_BYTES:
+ size_for_protocol += field->access_length;
+ break;
+
+ case UACPI_ACCESS_ATTRIBUTE_BLOCK:
+ case UACPI_ACCESS_ATTRIBUTE_BLOCK_PROCESS_CALL:
+ case UACPI_ACCESS_ATTRIBUTE_RAW_BYTES:
+ case UACPI_ACCESS_ATTRIBUTE_RAW_PROCESS_BYTES:
+ size_for_protocol += 255;
+ break;
+
+ default:
+ uacpi_error(
+ "unsupported field@%p access attribute %d\n",
+ field, field->attributes
+ );
+ return UACPI_STATUS_UNIMPLEMENTED;
+ }
+
+ *out_size = size_for_protocol;
+ break;
+ }
+ default:
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status handle_special_field(
+ uacpi_field_unit *field, uacpi_data_view buf,
+ uacpi_region_op op, uacpi_data_view *wtr_response,
+ uacpi_bool *did_handle
+)
+{
+ uacpi_status ret = UACPI_STATUS_OK;
+ uacpi_object *obj;
+ uacpi_operation_region *region;
+ uacpi_u64 in_out;
+ uacpi_data_view wtr_buffer;
+ union uacpi_opregion_io_data data;
+
+ *did_handle = UACPI_FALSE;
+
+ if (field->kind == UACPI_FIELD_UNIT_KIND_INDEX)
+ return ret;
+
+ obj = uacpi_namespace_node_get_object_typed(
+ field->region, UACPI_OBJECT_OPERATION_REGION_BIT
+ );
+ if (uacpi_unlikely(obj == UACPI_NULL)) {
+ ret = UACPI_STATUS_INVALID_ARGUMENT;
+ uacpi_trace_region_error(
+ field->region, "attempted access to deleted", ret
+ );
+ goto out_handled;
+ }
+ region = obj->op_region;
+
+ switch (region->space) {
+ case UACPI_ADDRESS_SPACE_GENERAL_PURPOSE_IO:
+ if (op == UACPI_REGION_OP_WRITE) {
+ uacpi_memcpy_zerout(
+ &in_out, buf.const_data, sizeof(in_out), buf.length
+ );
+ }
+
+ data.integer = &in_out;
+ ret = access_field_unit(field, 0, op, data);
+ if (uacpi_unlikely_error(ret))
+ goto out_handled;
+
+ if (op == UACPI_REGION_OP_READ)
+ uacpi_memcpy_zerout(buf.data, &in_out, buf.length, sizeof(in_out));
+ goto out_handled;
+ case UACPI_ADDRESS_SPACE_IPMI:
+ case UACPI_ADDRESS_SPACE_PRM:
+ if (uacpi_unlikely(op == UACPI_REGION_OP_READ)) {
+ ret = UACPI_STATUS_AML_INCOMPATIBLE_OBJECT_TYPE;
+ uacpi_trace_region_error(
+ field->region, "attempted to read from a write-only", ret
+ );
+ goto out_handled;
+ }
+ UACPI_FALLTHROUGH;
+ case UACPI_ADDRESS_SPACE_FFIXEDHW:
+ case UACPI_ADDRESS_SPACE_GENERIC_SERIAL_BUS:
+ case UACPI_ADDRESS_SPACE_SMBUS:
+ goto do_wtr;
+ default:
+ return ret;
+ }
+
+do_wtr:
+ ret = wtr_buffer_size(field, region->space, &wtr_buffer.length);
+ if (uacpi_unlikely_error(ret))
+ goto out_handled;
+
+ wtr_buffer.data = uacpi_kernel_alloc(wtr_buffer.length);
+ if (uacpi_unlikely(wtr_buffer.data == UACPI_NULL)) {
+ ret = UACPI_STATUS_OUT_OF_MEMORY;
+ goto out_handled;
+ }
+
+ uacpi_memcpy_zerout(
+ wtr_buffer.data, buf.const_data, wtr_buffer.length, buf.length
+ );
+ data.buffer = wtr_buffer;
+ ret = access_field_unit(
+ field, field->byte_offset,
+ op, data
+ );
+ if (uacpi_unlikely_error(ret)) {
+ uacpi_free(wtr_buffer.data, wtr_buffer.length);
+ goto out_handled;
+ }
+
+ if (wtr_response != UACPI_NULL)
+ *wtr_response = wtr_buffer;
+
+out_handled:
+ *did_handle = UACPI_TRUE;
+ return ret;
+}
+
+static uacpi_status do_read_misaligned_field_unit(
+ uacpi_field_unit *field, uacpi_u8 *dst, uacpi_size size
+)
+{
+ uacpi_status ret;
+ uacpi_size reads_to_do;
+ uacpi_u64 out;
+ uacpi_u32 byte_offset = field->byte_offset;
+ uacpi_u32 bits_left = field->bit_length;
+ uacpi_u8 width_access_bits = field->access_width_bytes * 8;
+
+ struct bit_span src_span = { 0 };
+ struct bit_span dst_span = { 0 };
+
+ src_span.data = (uacpi_u8*)&out;
+ src_span.index = field->bit_offset_within_first_byte;
+
+ dst_span.data = dst;
+ dst_span.index = 0;
+ dst_span.length = size * 8;
+
+ reads_to_do = UACPI_ALIGN_UP(
+ field->bit_offset_within_first_byte + field->bit_length,
+ width_access_bits,
+ uacpi_u32
+ );
+ reads_to_do /= width_access_bits;
+
+ while (reads_to_do-- > 0) {
+ union uacpi_opregion_io_data data;
+
+ src_span.length = UACPI_MIN(
+ bits_left, width_access_bits - src_span.index
+ );
+
+ data.integer = &out;
+ ret = access_field_unit(
+ field, byte_offset, UACPI_REGION_OP_READ,
+ data
+ );
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ bit_copy(&dst_span, &src_span);
+ bits_left -= src_span.length;
+ src_span.index = 0;
+
+ bit_span_offset(&dst_span, src_span.length);
+ byte_offset += field->access_width_bytes;
+ }
+
+ return UACPI_STATUS_OK;
+}
+
+uacpi_status uacpi_read_field_unit(
+ uacpi_field_unit *field, void *dst, uacpi_size size,
+ uacpi_data_view *wtr_response
+)
+{
+ uacpi_status ret;
+ uacpi_u32 field_byte_length;
+ uacpi_bool did_handle;
+ uacpi_data_view data_view = { 0 };
+
+ data_view.data = dst;
+ data_view.length = size;
+
+ ret = handle_special_field(
+ field, data_view, UACPI_REGION_OP_READ,
+ wtr_response, &did_handle
+ );
+ if (did_handle)
+ return ret;
+
+ field_byte_length = uacpi_round_up_bits_to_bytes(field->bit_length);
+
+ /*
+ * Very simple fast case:
+ * - Bit offset within first byte is 0
+ * AND
+ * - Field size is <= access width
+ */
+ if (field->bit_offset_within_first_byte == 0 &&
+ field_byte_length <= field->access_width_bytes)
+ {
+ uacpi_u64 out;
+ union uacpi_opregion_io_data data;
+
+ data.integer = &out;
+ ret = access_field_unit(
+ field, field->byte_offset, UACPI_REGION_OP_READ,
+ data
+ );
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ uacpi_memcpy_zerout(dst, &out, size, field_byte_length);
+ if (size >= field_byte_length)
+ cut_misaligned_tail(dst, field_byte_length - 1, field->bit_length);
+
+ return UACPI_STATUS_OK;
+ }
+
+ // Slow case
+ return do_read_misaligned_field_unit(field, dst, size);
+}
+
+static uacpi_status write_generic_field_unit(
+ uacpi_field_unit *field, const void *src, uacpi_size size
+)
+{
+ uacpi_status ret;
+ uacpi_u32 bits_left, byte_offset = field->byte_offset;
+ uacpi_u8 width_access_bits = field->access_width_bytes * 8;
+ uacpi_u64 in;
+ struct bit_span src_span = { 0 };
+ struct bit_span dst_span = { 0 };
+
+ src_span.const_data = src;
+ src_span.index = 0;
+ src_span.length = size * 8;
+
+ dst_span.data = (uacpi_u8 *)&in;
+ dst_span.index = field->bit_offset_within_first_byte;
+
+ bits_left = field->bit_length;
+
+ while (bits_left) {
+ union uacpi_opregion_io_data data;
+
+ in = 0;
+ dst_span.length = UACPI_MIN(
+ width_access_bits - dst_span.index, bits_left
+ );
+
+ if (dst_span.index != 0 || dst_span.length < width_access_bits) {
+ switch (field->update_rule) {
+ case UACPI_UPDATE_RULE_PRESERVE:
+ data.integer = &in;
+ ret = access_field_unit(
+ field, byte_offset, UACPI_REGION_OP_READ,
+ data
+ );
+ if (uacpi_unlikely_error(ret))
+ return ret;
+ break;
+ case UACPI_UPDATE_RULE_WRITE_AS_ONES:
+ in = ~in;
+ break;
+ case UACPI_UPDATE_RULE_WRITE_AS_ZEROES:
+ break;
+ default:
+ uacpi_error("invalid field@%p update rule %d\n",
+ field, field->update_rule);
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+ }
+
+ bit_copy(&dst_span, &src_span);
+ bit_span_offset(&src_span, dst_span.length);
+
+ data.integer = &in;
+
+ ret = access_field_unit(
+ field, byte_offset, UACPI_REGION_OP_WRITE,
+ data
+ );
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ bits_left -= dst_span.length;
+ dst_span.index = 0;
+ byte_offset += field->access_width_bytes;
+ }
+
+ return UACPI_STATUS_OK;
+}
+
+uacpi_status uacpi_write_field_unit(
+ uacpi_field_unit *field, const void *src, uacpi_size size,
+ uacpi_data_view *wtr_response
+)
+{
+ uacpi_status ret;
+ uacpi_bool did_handle;
+ uacpi_data_view data_view = { 0 };
+
+ data_view.const_data = src;
+ data_view.length = size;
+
+ ret = handle_special_field(
+ field, data_view, UACPI_REGION_OP_WRITE,
+ wtr_response, &did_handle
+ );
+ if (did_handle)
+ return ret;
+
+ return write_generic_field_unit(field, src, size);
+}
+
+uacpi_status uacpi_field_unit_get_read_type(
+ struct uacpi_field_unit *field, uacpi_object_type *out_type
+)
+{
+ uacpi_object *obj;
+
+ if (field->kind == UACPI_FIELD_UNIT_KIND_INDEX)
+ goto out_basic_field;
+
+ obj = uacpi_namespace_node_get_object_typed(
+ field->region, UACPI_OBJECT_OPERATION_REGION_BIT
+ );
+ if (uacpi_unlikely(obj == UACPI_NULL))
+ return UACPI_STATUS_INVALID_ARGUMENT;
+
+ if (uacpi_is_buffer_access_address_space(obj->op_region->space)) {
+ *out_type = UACPI_OBJECT_BUFFER;
+ return UACPI_STATUS_OK;
+ }
+
+out_basic_field:
+ if (field->bit_length > (g_uacpi_rt_ctx.is_rev1 ? 32u : 64u))
+ *out_type = UACPI_OBJECT_BUFFER;
+ else
+ *out_type = UACPI_OBJECT_INTEGER;
+
+ return UACPI_STATUS_OK;
+}
+
+uacpi_status uacpi_field_unit_get_bit_length(
+ struct uacpi_field_unit *field, uacpi_size *out_length
+)
+{
+ uacpi_object *obj;
+
+ if (field->kind == UACPI_FIELD_UNIT_KIND_INDEX)
+ goto out_basic_field;
+
+ obj = uacpi_namespace_node_get_object_typed(
+ field->region, UACPI_OBJECT_OPERATION_REGION_BIT
+ );
+ if (uacpi_unlikely(obj == UACPI_NULL))
+ return UACPI_STATUS_INVALID_ARGUMENT;
+
+ if (uacpi_is_buffer_access_address_space(obj->op_region->space)) {
+ /*
+ * Bit length is protocol specific, the data will be returned
+ * via the write-then-read response buffer.
+ */
+ *out_length = 0;
+ return UACPI_STATUS_OK;
+ }
+
+out_basic_field:
+ *out_length = field->bit_length;
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_u8 gas_get_access_bit_width(const struct acpi_gas *gas)
+{
+ /*
+ * Same algorithm as ACPICA.
+ *
+ * The reason we do this is apparently GAS bit offset being non-zero means
+ * that it's an APEI register, as opposed to FADT, which needs special
+ * handling. In the case of a FADT register we want to ignore the specified
+ * access size.
+ */
+ uacpi_u8 access_bit_width;
+
+ if (gas->register_bit_offset == 0 &&
+ UACPI_IS_POWER_OF_TWO(gas->register_bit_width, uacpi_u8) &&
+ UACPI_IS_ALIGNED(gas->register_bit_width, 8, uacpi_u8)) {
+ access_bit_width = gas->register_bit_width;
+ } else if (gas->access_size) {
+ access_bit_width = gas->access_size * 8;
+ } else {
+ uacpi_u8 msb;
+
+ msb = uacpi_bit_scan_backward(
+ (gas->register_bit_offset + gas->register_bit_width) - 1
+ );
+ access_bit_width = 1 << msb;
+
+ if (access_bit_width <= 8) {
+ access_bit_width = 8;
+ } else {
+ /*
+ * Keep backing off to previous power of two until we find one
+ * that is aligned to the address specified in GAS.
+ */
+ while (!UACPI_IS_ALIGNED(
+ gas->address, access_bit_width / 8, uacpi_u64
+ ))
+ access_bit_width /= 2;
+ }
+ }
+
+ return UACPI_MIN(
+ access_bit_width,
+ gas->address_space_id == UACPI_ADDRESS_SPACE_SYSTEM_IO ? 32 : 64
+ );
+}
+
+static uacpi_status gas_validate(
+ const struct acpi_gas *gas, uacpi_u8 *access_bit_width,
+ uacpi_u8 *bit_width
+)
+{
+ uacpi_size total_width, aligned_width;
+
+ if (uacpi_unlikely(gas == UACPI_NULL))
+ return UACPI_STATUS_INVALID_ARGUMENT;
+
+ if (!gas->address)
+ return UACPI_STATUS_NOT_FOUND;
+
+ if (gas->address_space_id != UACPI_ADDRESS_SPACE_SYSTEM_IO &&
+ gas->address_space_id != UACPI_ADDRESS_SPACE_SYSTEM_MEMORY) {
+ uacpi_warn("unsupported GAS address space '%s' (%d)\n",
+ uacpi_address_space_to_string(gas->address_space_id),
+ gas->address_space_id);
+ return UACPI_STATUS_UNIMPLEMENTED;
+ }
+
+ if (gas->access_size > 4) {
+ uacpi_warn("unsupported GAS access size %d\n",
+ gas->access_size);
+ return UACPI_STATUS_UNIMPLEMENTED;
+ }
+
+ *access_bit_width = gas_get_access_bit_width(gas);
+
+ total_width = gas->register_bit_offset + gas->register_bit_width;
+ aligned_width = UACPI_ALIGN_UP(total_width, *access_bit_width, uacpi_size);
+
+ if (uacpi_unlikely(aligned_width > 64)) {
+ uacpi_warn(
+ "GAS register total width is too large: %zu\n", total_width
+ );
+ return UACPI_STATUS_UNIMPLEMENTED;
+ }
+
+ *bit_width = total_width;
+ return UACPI_STATUS_OK;
+}
+
+/*
+ * Apparently both reading and writing GAS works differently from operation
+ * region in that bit offsets are not respected when writing the data.
+ *
+ * Let's follow ACPICA's approach here so that we don't accidentally
+ * break any quirky hardware.
+ */
+uacpi_status uacpi_gas_read_mapped(
+ const uacpi_mapped_gas *gas, uacpi_u64 *out_value
+)
+{
+ uacpi_status ret;
+ uacpi_u8 access_byte_width;
+ uacpi_u8 bit_offset, bits_left, index = 0;
+ uacpi_u64 data, mask = 0xFFFFFFFFFFFFFFFF;
+ uacpi_size offset = 0;
+
+ bit_offset = gas->bit_offset;
+ bits_left = gas->total_bit_width;
+
+ access_byte_width = gas->access_bit_width / 8;
+
+ if (access_byte_width < 8)
+ mask = ~(mask << gas->access_bit_width);
+
+ *out_value = 0;
+
+ while (bits_left) {
+ if (bit_offset >= gas->access_bit_width) {
+ data = 0;
+ bit_offset -= gas->access_bit_width;
+ } else {
+ ret = gas->read(gas->mapping, offset, access_byte_width, &data);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+ }
+
+ *out_value |= (data & mask) << (index * gas->access_bit_width);
+ bits_left -= UACPI_MIN(bits_left, gas->access_bit_width);
+ ++index;
+ offset += access_byte_width;
+ }
+
+ return UACPI_STATUS_OK;
+}
+
+uacpi_status uacpi_gas_write_mapped(
+ const uacpi_mapped_gas *gas, uacpi_u64 in_value
+)
+{
+ uacpi_status ret;
+ uacpi_u8 access_byte_width;
+ uacpi_u8 bit_offset, bits_left, index = 0;
+ uacpi_u64 data, mask = 0xFFFFFFFFFFFFFFFF;
+ uacpi_size offset = 0;
+
+ bit_offset = gas->bit_offset;
+ bits_left = gas->total_bit_width;
+ access_byte_width = gas->access_bit_width / 8;
+
+ if (access_byte_width < 8)
+ mask = ~(mask << gas->access_bit_width);
+
+ while (bits_left) {
+ data = (in_value >> (index * gas->access_bit_width)) & mask;
+
+ if (bit_offset >= gas->access_bit_width) {
+ bit_offset -= gas->access_bit_width;
+ } else {
+ ret = gas->write(gas->mapping, offset, access_byte_width, data);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+ }
+
+ bits_left -= UACPI_MIN(bits_left, gas->access_bit_width);
+ ++index;
+ offset += access_byte_width;
+ }
+
+ return UACPI_STATUS_OK;
+}
+
+static void unmap_gas_io(uacpi_handle io_handle, uacpi_size size)
+{
+ UACPI_UNUSED(size);
+ uacpi_kernel_io_unmap(io_handle);
+}
+
+uacpi_status uacpi_map_gas_noalloc(
+ const struct acpi_gas *gas, uacpi_mapped_gas *out_mapped
+)
+{
+ uacpi_status ret;
+ uacpi_u8 access_bit_width, total_width;
+
+ ret = gas_validate(gas, &access_bit_width, &total_width);
+ if (ret != UACPI_STATUS_OK)
+ return ret;
+
+ if (gas->address_space_id == UACPI_ADDRESS_SPACE_SYSTEM_MEMORY) {
+ out_mapped->mapping = uacpi_kernel_map(gas->address, total_width / 8);
+ if (uacpi_unlikely(out_mapped->mapping == UACPI_NULL))
+ return UACPI_STATUS_MAPPING_FAILED;
+
+ out_mapped->read = uacpi_system_memory_read;
+ out_mapped->write = uacpi_system_memory_write;
+ out_mapped->unmap = uacpi_kernel_unmap;
+ } else { // IO, validated by gas_validate above
+ ret = uacpi_kernel_io_map(gas->address, total_width / 8, &out_mapped->mapping);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ out_mapped->read = uacpi_system_io_read;
+ out_mapped->write = uacpi_system_io_write;
+ out_mapped->unmap = unmap_gas_io;
+ }
+
+ out_mapped->access_bit_width = access_bit_width;
+ out_mapped->total_bit_width = total_width;
+ out_mapped->bit_offset = gas->register_bit_offset;
+
+ return UACPI_STATUS_OK;
+}
+
+uacpi_status uacpi_map_gas(
+ const struct acpi_gas *gas, uacpi_mapped_gas **out_mapped
+)
+{
+ uacpi_status ret;
+ uacpi_mapped_gas *mapping;
+
+ mapping = uacpi_kernel_alloc(sizeof(*mapping));
+ if (uacpi_unlikely(mapping == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ ret = uacpi_map_gas_noalloc(gas, mapping);
+ if (uacpi_unlikely_error(ret)) {
+ uacpi_free(mapping, sizeof(*mapping));
+ return ret;
+ }
+
+ *out_mapped = mapping;
+ return ret;
+}
+
+void uacpi_unmap_gas_nofree(uacpi_mapped_gas *gas)
+{
+ gas->unmap(gas->mapping, gas->access_bit_width / 8);
+}
+
+void uacpi_unmap_gas(uacpi_mapped_gas *gas)
+{
+ uacpi_unmap_gas_nofree(gas);
+ uacpi_free(gas, sizeof(*gas));
+}
+
+uacpi_status uacpi_gas_read(const struct acpi_gas *gas, uacpi_u64 *out_value)
+{
+ uacpi_status ret;
+ uacpi_mapped_gas mapping;
+
+ ret = uacpi_map_gas_noalloc(gas, &mapping);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ ret = uacpi_gas_read_mapped(&mapping, out_value);
+ uacpi_unmap_gas_nofree(&mapping);
+
+ return ret;
+}
+
+uacpi_status uacpi_gas_write(const struct acpi_gas *gas, uacpi_u64 in_value)
+{
+ uacpi_status ret;
+ uacpi_mapped_gas mapping;
+
+ ret = uacpi_map_gas_noalloc(gas, &mapping);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ ret = uacpi_gas_write_mapped(&mapping, in_value);
+ uacpi_unmap_gas_nofree(&mapping);
+
+ return ret;
+}
+
+uacpi_status uacpi_system_memory_read(
+ void *ptr, uacpi_size offset, uacpi_u8 width, uacpi_u64 *out
+)
+{
+ ptr = UACPI_PTR_ADD(ptr, offset);
+
+ switch (width) {
+ case 1:
+ *out = *(volatile uacpi_u8*)ptr;
+ break;
+ case 2:
+ *out = *(volatile uacpi_u16*)ptr;
+ break;
+ case 4:
+ *out = *(volatile uacpi_u32*)ptr;
+ break;
+ case 8:
+ *out = *(volatile uacpi_u64*)ptr;
+ break;
+ default:
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+
+ return UACPI_STATUS_OK;
+}
+
+uacpi_status uacpi_system_memory_write(
+ void *ptr, uacpi_size offset, uacpi_u8 width, uacpi_u64 in
+)
+{
+ ptr = UACPI_PTR_ADD(ptr, offset);
+
+ switch (width) {
+ case 1:
+ *(volatile uacpi_u8*)ptr = in;
+ break;
+ case 2:
+ *(volatile uacpi_u16*)ptr = in;
+ break;
+ case 4:
+ *(volatile uacpi_u32*)ptr = in;
+ break;
+ case 8:
+ *(volatile uacpi_u64*)ptr = in;
+ break;
+ default:
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+
+ return UACPI_STATUS_OK;
+}
+
+union integer_data {
+ uacpi_u8 byte;
+ uacpi_u16 word;
+ uacpi_u32 dword;
+ uacpi_u64 qword;
+};
+
+uacpi_status uacpi_system_io_read(
+ uacpi_handle handle, uacpi_size offset, uacpi_u8 width, uacpi_u64 *out
+)
+{
+ uacpi_status ret;
+ union integer_data data = {
+ .qword = 0,
+ };
+
+ switch (width) {
+ case 1:
+ ret = uacpi_kernel_io_read8(handle, offset, &data.byte);
+ break;
+ case 2:
+ ret = uacpi_kernel_io_read16(handle, offset, &data.word);
+ break;
+ case 4:
+ ret = uacpi_kernel_io_read32(handle, offset, &data.dword);
+ break;
+ default:
+ uacpi_error(
+ "invalid SystemIO read %p@%zu width=%d\n",
+ handle, offset, width
+ );
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+
+ if (uacpi_likely_success(ret))
+ *out = data.qword;
+ return ret;
+}
+
+uacpi_status uacpi_system_io_write(
+ uacpi_handle handle, uacpi_size offset, uacpi_u8 width, uacpi_u64 in
+)
+{
+ uacpi_status ret;
+
+ switch (width) {
+ case 1:
+ ret = uacpi_kernel_io_write8(handle, offset, in);
+ break;
+ case 2:
+ ret = uacpi_kernel_io_write16(handle, offset, in);
+ break;
+ case 4:
+ ret = uacpi_kernel_io_write32(handle, offset, in);
+ break;
+ default:
+ uacpi_error(
+ "invalid SystemIO write %p@%zu width=%d\n",
+ handle, offset, width
+ );
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+
+ return ret;
+}
+
+uacpi_status uacpi_pci_read(
+ uacpi_handle handle, uacpi_size offset, uacpi_u8 width, uacpi_u64 *out
+)
+{
+ uacpi_status ret;
+ union integer_data data = {
+ .qword = 0,
+ };
+
+ switch (width) {
+ case 1:
+ ret = uacpi_kernel_pci_read8(handle, offset, &data.byte);
+ break;
+ case 2:
+ ret = uacpi_kernel_pci_read16(handle, offset, &data.word);
+ break;
+ case 4:
+ ret = uacpi_kernel_pci_read32(handle, offset, &data.dword);
+ break;
+ default:
+ uacpi_error(
+ "invalid PCI_Config read %p@%zu width=%d\n",
+ handle, offset, width
+ );
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+
+ if (uacpi_likely_success(ret))
+ *out = data.qword;
+ return ret;
+}
+
+uacpi_status uacpi_pci_write(
+ uacpi_handle handle, uacpi_size offset, uacpi_u8 width, uacpi_u64 in
+)
+{
+ uacpi_status ret;
+
+ switch (width) {
+ case 1:
+ ret = uacpi_kernel_pci_write8(handle, offset, in);
+ break;
+ case 2:
+ ret = uacpi_kernel_pci_write16(handle, offset, in);
+ break;
+ case 4:
+ ret = uacpi_kernel_pci_write32(handle, offset, in);
+ break;
+ default:
+ uacpi_error(
+ "invalid PCI_Config write %p@%zu width=%d\n",
+ handle, offset, width
+ );
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+
+ return ret;
+}
+
+#endif // !UACPI_BAREBONES_MODE
diff --git a/sys/dev/acpi/uacpi/mutex.c b/sys/dev/acpi/uacpi/mutex.c
new file mode 100644
index 0000000..44cbac3
--- /dev/null
+++ b/sys/dev/acpi/uacpi/mutex.c
@@ -0,0 +1,396 @@
+#include <uacpi/platform/atomic.h>
+#include <uacpi/internal/mutex.h>
+#include <uacpi/internal/log.h>
+#include <uacpi/internal/registers.h>
+#include <uacpi/internal/context.h>
+#include <uacpi/kernel_api.h>
+#include <uacpi/internal/namespace.h>
+
+#ifndef UACPI_BAREBONES_MODE
+
+#ifndef UACPI_REDUCED_HARDWARE
+
+#define GLOBAL_LOCK_PENDING (1 << 0)
+
+#define GLOBAL_LOCK_OWNED_BIT 1
+#define GLOBAL_LOCK_OWNED (1 << GLOBAL_LOCK_OWNED_BIT)
+
+#define GLOBAL_LOCK_MASK 3u
+
+static uacpi_bool try_acquire_global_lock_from_firmware(uacpi_u32 *lock)
+{
+ uacpi_u32 value, new_value;
+ uacpi_bool was_owned;
+
+ value = *(volatile uacpi_u32*)lock;
+ do {
+ was_owned = (value & GLOBAL_LOCK_OWNED) >> GLOBAL_LOCK_OWNED_BIT;
+
+ // Clear both owned & pending bits.
+ new_value = value & ~GLOBAL_LOCK_MASK;
+
+ // Set owned unconditionally
+ new_value |= GLOBAL_LOCK_OWNED;
+
+ // Set pending iff the lock was owned at the time of reading
+ if (was_owned)
+ new_value |= GLOBAL_LOCK_PENDING;
+ } while (!uacpi_atomic_cmpxchg32(lock, &value, new_value));
+
+ return !was_owned;
+}
+
+static uacpi_bool do_release_global_lock_to_firmware(uacpi_u32 *lock)
+{
+ uacpi_u32 value, new_value;
+
+ value = *(volatile uacpi_u32*)lock;
+ do {
+ new_value = value & ~GLOBAL_LOCK_MASK;
+ } while (!uacpi_atomic_cmpxchg32(lock, &value, new_value));
+
+ return value & GLOBAL_LOCK_PENDING;
+}
+
+static uacpi_status uacpi_acquire_global_lock_from_firmware(void)
+{
+ uacpi_cpu_flags flags;
+ uacpi_u16 spins = 0;
+ uacpi_bool success;
+
+ if (!g_uacpi_rt_ctx.has_global_lock)
+ return UACPI_STATUS_OK;
+
+ flags = uacpi_kernel_lock_spinlock(g_uacpi_rt_ctx.global_lock_spinlock);
+ for (;;) {
+ spins++;
+ uacpi_trace(
+ "trying to acquire the global lock from firmware... (attempt %u)\n",
+ spins
+ );
+
+ success = try_acquire_global_lock_from_firmware(
+ &g_uacpi_rt_ctx.facs->global_lock
+ );
+ if (success)
+ break;
+
+ if (uacpi_unlikely(spins == 0xFFFF))
+ break;
+
+ g_uacpi_rt_ctx.global_lock_pending = UACPI_TRUE;
+ uacpi_trace(
+ "global lock is owned by firmware, waiting for a release "
+ "notification...\n"
+ );
+ uacpi_kernel_unlock_spinlock(g_uacpi_rt_ctx.global_lock_spinlock, flags);
+
+ uacpi_kernel_wait_for_event(g_uacpi_rt_ctx.global_lock_event, 0xFFFF);
+ flags = uacpi_kernel_lock_spinlock(g_uacpi_rt_ctx.global_lock_spinlock);
+ }
+
+ g_uacpi_rt_ctx.global_lock_pending = UACPI_FALSE;
+ uacpi_kernel_unlock_spinlock(g_uacpi_rt_ctx.global_lock_spinlock, flags);
+
+ if (uacpi_unlikely(!success)) {
+ uacpi_error("unable to acquire global lock after %u attempts\n", spins);
+ return UACPI_STATUS_HARDWARE_TIMEOUT;
+ }
+
+ uacpi_trace("global lock successfully acquired after %u attempt%s\n",
+ spins, spins > 1 ? "s" : "");
+ return UACPI_STATUS_OK;
+}
+
+static void uacpi_release_global_lock_to_firmware(void)
+{
+ if (!g_uacpi_rt_ctx.has_global_lock)
+ return;
+
+ uacpi_trace("releasing the global lock to firmware...\n");
+ if (do_release_global_lock_to_firmware(&g_uacpi_rt_ctx.facs->global_lock)) {
+ uacpi_trace("notifying firmware of the global lock release since the "
+ "pending bit was set\n");
+ uacpi_write_register_field(UACPI_REGISTER_FIELD_GBL_RLS, 1);
+ }
+}
+#endif
+
+UACPI_ALWAYS_OK_FOR_REDUCED_HARDWARE(
+ uacpi_status uacpi_acquire_global_lock_from_firmware(void)
+)
+UACPI_STUB_IF_REDUCED_HARDWARE(
+ void uacpi_release_global_lock_to_firmware(void)
+)
+
+uacpi_status uacpi_acquire_native_mutex_with_timeout(
+ uacpi_handle mtx, uacpi_u16 timeout
+)
+{
+ uacpi_status ret;
+
+ if (uacpi_unlikely(mtx == UACPI_NULL))
+ return UACPI_STATUS_INVALID_ARGUMENT;
+
+ ret = uacpi_kernel_acquire_mutex(mtx, timeout);
+ if (uacpi_likely_success(ret))
+ return ret;
+
+ if (uacpi_unlikely(ret != UACPI_STATUS_TIMEOUT || timeout == 0xFFFF)) {
+ uacpi_error(
+ "unexpected status %08X (%s) while acquiring %p (timeout=%04X)\n",
+ ret, uacpi_status_to_string(ret), mtx, timeout
+ );
+ }
+
+ return ret;
+}
+
+uacpi_status uacpi_acquire_global_lock(uacpi_u16 timeout, uacpi_u32 *out_seq)
+{
+ uacpi_status ret;
+
+ UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_SUBSYSTEM_INITIALIZED);
+
+ if (uacpi_unlikely(out_seq == UACPI_NULL))
+ return UACPI_STATUS_INVALID_ARGUMENT;
+
+ ret = uacpi_acquire_native_mutex_with_timeout(
+ g_uacpi_rt_ctx.global_lock_mutex->handle, timeout
+ );
+ if (ret != UACPI_STATUS_OK)
+ return ret;
+
+ ret = uacpi_acquire_global_lock_from_firmware();
+ if (uacpi_unlikely_error(ret)) {
+ uacpi_release_native_mutex(g_uacpi_rt_ctx.global_lock_mutex->handle);
+ return ret;
+ }
+
+ if (uacpi_unlikely(g_uacpi_rt_ctx.global_lock_seq_num == 0xFFFFFFFF))
+ g_uacpi_rt_ctx.global_lock_seq_num = 0;
+
+ *out_seq = g_uacpi_rt_ctx.global_lock_seq_num++;
+ g_uacpi_rt_ctx.global_lock_acquired = UACPI_TRUE;
+ return UACPI_STATUS_OK;
+}
+
+uacpi_status uacpi_release_global_lock(uacpi_u32 seq)
+{
+ UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_SUBSYSTEM_INITIALIZED);
+
+ if (uacpi_unlikely(!g_uacpi_rt_ctx.global_lock_acquired ||
+ seq != g_uacpi_rt_ctx.global_lock_seq_num))
+ return UACPI_STATUS_INVALID_ARGUMENT;
+
+ g_uacpi_rt_ctx.global_lock_acquired = UACPI_FALSE;
+ uacpi_release_global_lock_to_firmware();
+ uacpi_release_native_mutex(g_uacpi_rt_ctx.global_lock_mutex->handle);
+
+ return UACPI_STATUS_OK;
+}
+
+uacpi_bool uacpi_this_thread_owns_aml_mutex(uacpi_mutex *mutex)
+{
+ uacpi_thread_id id;
+
+ id = UACPI_ATOMIC_LOAD_THREAD_ID(&mutex->owner);
+ return id == uacpi_kernel_get_thread_id();
+}
+
+uacpi_status uacpi_acquire_aml_mutex(uacpi_mutex *mutex, uacpi_u16 timeout)
+{
+ uacpi_thread_id this_id;
+ uacpi_status ret = UACPI_STATUS_OK;
+
+ this_id = uacpi_kernel_get_thread_id();
+ if (UACPI_ATOMIC_LOAD_THREAD_ID(&mutex->owner) == this_id) {
+ if (uacpi_unlikely(mutex->depth == 0xFFFF)) {
+ uacpi_warn(
+ "failing an attempt to acquire mutex @%p, too many recursive "
+ "acquires\n", mutex
+ );
+ return UACPI_STATUS_DENIED;
+ }
+
+ mutex->depth++;
+ return ret;
+ }
+
+ uacpi_namespace_write_unlock();
+ ret = uacpi_acquire_native_mutex_with_timeout(mutex->handle, timeout);
+ if (ret != UACPI_STATUS_OK)
+ goto out;
+
+ if (mutex->handle == g_uacpi_rt_ctx.global_lock_mutex->handle) {
+ ret = uacpi_acquire_global_lock_from_firmware();
+ if (uacpi_unlikely_error(ret)) {
+ uacpi_release_native_mutex(mutex->handle);
+ goto out;
+ }
+ }
+
+ UACPI_ATOMIC_STORE_THREAD_ID(&mutex->owner, this_id);
+ mutex->depth = 1;
+
+out:
+ uacpi_namespace_write_lock();
+ return ret;
+}
+
+uacpi_status uacpi_release_aml_mutex(uacpi_mutex *mutex)
+{
+ if (mutex->depth-- > 1)
+ return UACPI_STATUS_OK;
+
+ if (mutex->handle == g_uacpi_rt_ctx.global_lock_mutex->handle)
+ uacpi_release_global_lock_to_firmware();
+
+ UACPI_ATOMIC_STORE_THREAD_ID(&mutex->owner, UACPI_THREAD_ID_NONE);
+ uacpi_release_native_mutex(mutex->handle);
+
+ return UACPI_STATUS_OK;
+}
+
+uacpi_status uacpi_recursive_lock_init(struct uacpi_recursive_lock *lock)
+{
+ lock->mutex = uacpi_kernel_create_mutex();
+ if (uacpi_unlikely(lock->mutex == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ lock->owner = UACPI_THREAD_ID_NONE;
+ lock->depth = 0;
+
+ return UACPI_STATUS_OK;
+}
+
+uacpi_status uacpi_recursive_lock_deinit(struct uacpi_recursive_lock *lock)
+{
+ if (uacpi_unlikely(lock->depth)) {
+ uacpi_warn(
+ "de-initializing active recursive lock %p with depth=%zu\n",
+ lock, lock->depth
+ );
+ lock->depth = 0;
+ }
+
+ lock->owner = UACPI_THREAD_ID_NONE;
+
+ if (lock->mutex != UACPI_NULL) {
+ uacpi_kernel_free_mutex(lock->mutex);
+ lock->mutex = UACPI_NULL;
+ }
+
+ return UACPI_STATUS_OK;
+}
+
+uacpi_status uacpi_recursive_lock_acquire(struct uacpi_recursive_lock *lock)
+{
+ uacpi_thread_id this_id;
+ uacpi_status ret = UACPI_STATUS_OK;
+
+ this_id = uacpi_kernel_get_thread_id();
+ if (UACPI_ATOMIC_LOAD_THREAD_ID(&lock->owner) == this_id) {
+ lock->depth++;
+ return ret;
+ }
+
+ ret = uacpi_acquire_native_mutex(lock->mutex);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ UACPI_ATOMIC_STORE_THREAD_ID(&lock->owner, this_id);
+ lock->depth = 1;
+ return ret;
+}
+
+uacpi_status uacpi_recursive_lock_release(struct uacpi_recursive_lock *lock)
+{
+ if (lock->depth-- > 1)
+ return UACPI_STATUS_OK;
+
+ UACPI_ATOMIC_STORE_THREAD_ID(&lock->owner, UACPI_THREAD_ID_NONE);
+ return uacpi_release_native_mutex(lock->mutex);
+}
+
+uacpi_status uacpi_rw_lock_init(struct uacpi_rw_lock *lock)
+{
+ lock->read_mutex = uacpi_kernel_create_mutex();
+ if (uacpi_unlikely(lock->read_mutex == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ lock->write_mutex = uacpi_kernel_create_mutex();
+ if (uacpi_unlikely(lock->write_mutex == UACPI_NULL)) {
+ uacpi_kernel_free_mutex(lock->read_mutex);
+ lock->read_mutex = UACPI_NULL;
+ return UACPI_STATUS_OUT_OF_MEMORY;
+ }
+
+ lock->num_readers = 0;
+ return UACPI_STATUS_OK;
+}
+
+uacpi_status uacpi_rw_lock_deinit(struct uacpi_rw_lock *lock)
+{
+ if (uacpi_unlikely(lock->num_readers)) {
+ uacpi_warn("de-initializing rw_lock %p with %zu active readers\n",
+ lock, lock->num_readers);
+ lock->num_readers = 0;
+ }
+
+ if (lock->read_mutex != UACPI_NULL) {
+ uacpi_kernel_free_mutex(lock->read_mutex);
+ lock->read_mutex = UACPI_NULL;
+ }
+ if (lock->write_mutex != UACPI_NULL) {
+ uacpi_kernel_free_mutex(lock->write_mutex);
+ lock->write_mutex = UACPI_NULL;
+ }
+
+ return UACPI_STATUS_OK;
+}
+
+uacpi_status uacpi_rw_lock_read(struct uacpi_rw_lock *lock)
+{
+ uacpi_status ret;
+
+ ret = uacpi_acquire_native_mutex(lock->read_mutex);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ if (lock->num_readers++ == 0) {
+ ret = uacpi_acquire_native_mutex(lock->write_mutex);
+ if (uacpi_unlikely_error(ret))
+ lock->num_readers = 0;
+ }
+
+ uacpi_kernel_release_mutex(lock->read_mutex);
+ return ret;
+}
+
+uacpi_status uacpi_rw_unlock_read(struct uacpi_rw_lock *lock)
+{
+ uacpi_status ret;
+
+ ret = uacpi_acquire_native_mutex(lock->read_mutex);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ if (lock->num_readers-- == 1)
+ uacpi_release_native_mutex(lock->write_mutex);
+
+ uacpi_kernel_release_mutex(lock->read_mutex);
+ return ret;
+}
+
+uacpi_status uacpi_rw_lock_write(struct uacpi_rw_lock *lock)
+{
+ return uacpi_acquire_native_mutex(lock->write_mutex);
+}
+
+uacpi_status uacpi_rw_unlock_write(struct uacpi_rw_lock *lock)
+{
+ return uacpi_release_native_mutex(lock->write_mutex);
+}
+
+#endif // !UACPI_BAREBONES_MODE
diff --git a/sys/dev/acpi/uacpi/namespace.c b/sys/dev/acpi/uacpi/namespace.c
new file mode 100644
index 0000000..e847dea
--- /dev/null
+++ b/sys/dev/acpi/uacpi/namespace.c
@@ -0,0 +1,1081 @@
+#include <uacpi/namespace.h>
+#include <uacpi/internal/namespace.h>
+#include <uacpi/internal/types.h>
+#include <uacpi/internal/stdlib.h>
+#include <uacpi/internal/interpreter.h>
+#include <uacpi/internal/opregion.h>
+#include <uacpi/internal/log.h>
+#include <uacpi/internal/utilities.h>
+#include <uacpi/internal/mutex.h>
+#include <uacpi/kernel_api.h>
+
+#ifndef UACPI_BAREBONES_MODE
+
+#define UACPI_REV_VALUE 2
+#define UACPI_OS_VALUE "Microsoft Windows NT"
+
+#define MAKE_PREDEFINED(c0, c1, c2, c3) \
+ { \
+ .name.text = { c0, c1, c2, c3 }, \
+ .flags = UACPI_NAMESPACE_NODE_PREDEFINED \
+ }
+
+static uacpi_namespace_node
+predefined_namespaces[UACPI_PREDEFINED_NAMESPACE_MAX + 1] = {
+ [UACPI_PREDEFINED_NAMESPACE_ROOT] = MAKE_PREDEFINED('\\', 0, 0, 0),
+ [UACPI_PREDEFINED_NAMESPACE_GPE] = MAKE_PREDEFINED('_', 'G', 'P', 'E'),
+ [UACPI_PREDEFINED_NAMESPACE_PR] = MAKE_PREDEFINED('_', 'P', 'R', '_'),
+ [UACPI_PREDEFINED_NAMESPACE_SB] = MAKE_PREDEFINED('_', 'S', 'B', '_'),
+ [UACPI_PREDEFINED_NAMESPACE_SI] = MAKE_PREDEFINED('_', 'S', 'I', '_'),
+ [UACPI_PREDEFINED_NAMESPACE_TZ] = MAKE_PREDEFINED('_', 'T', 'Z', '_'),
+ [UACPI_PREDEFINED_NAMESPACE_GL] = MAKE_PREDEFINED('_', 'G', 'L', '_'),
+ [UACPI_PREDEFINED_NAMESPACE_OS] = MAKE_PREDEFINED('_', 'O', 'S', '_'),
+ [UACPI_PREDEFINED_NAMESPACE_OSI] = MAKE_PREDEFINED('_', 'O', 'S', 'I'),
+ [UACPI_PREDEFINED_NAMESPACE_REV] = MAKE_PREDEFINED('_', 'R', 'E', 'V'),
+};
+
+static struct uacpi_rw_lock namespace_lock;
+
+uacpi_status uacpi_namespace_read_lock(void)
+{
+ return uacpi_rw_lock_read(&namespace_lock);
+}
+
+uacpi_status uacpi_namespace_read_unlock(void)
+{
+ return uacpi_rw_unlock_read(&namespace_lock);
+}
+
+uacpi_status uacpi_namespace_write_lock(void)
+{
+ return uacpi_rw_lock_write(&namespace_lock);
+}
+
+uacpi_status uacpi_namespace_write_unlock(void)
+{
+ return uacpi_rw_unlock_write(&namespace_lock);
+}
+
+static uacpi_object *make_object_for_predefined(
+ enum uacpi_predefined_namespace ns
+)
+{
+ uacpi_object *obj;
+
+ switch (ns) {
+ case UACPI_PREDEFINED_NAMESPACE_ROOT:
+ /*
+ * The real root object is stored in the global context, whereas the \
+ * node gets a placeholder uninitialized object instead. This is to
+ * protect against CopyObject(JUNK, \), so that all of the opregion and
+ * notify handlers are preserved if AML decides to do that.
+ */
+ g_uacpi_rt_ctx.root_object = uacpi_create_object(UACPI_OBJECT_DEVICE);
+ if (uacpi_unlikely(g_uacpi_rt_ctx.root_object == UACPI_NULL))
+ return UACPI_NULL;
+
+ obj = uacpi_create_object(UACPI_OBJECT_UNINITIALIZED);
+ break;
+
+ case UACPI_PREDEFINED_NAMESPACE_OS:
+ obj = uacpi_create_object(UACPI_OBJECT_STRING);
+ if (uacpi_unlikely(obj == UACPI_NULL))
+ return obj;
+
+ obj->buffer->text = uacpi_kernel_alloc(sizeof(UACPI_OS_VALUE));
+ if (uacpi_unlikely(obj->buffer->text == UACPI_NULL)) {
+ uacpi_object_unref(obj);
+ return UACPI_NULL;
+ }
+
+ obj->buffer->size = sizeof(UACPI_OS_VALUE);
+ uacpi_memcpy(obj->buffer->text, UACPI_OS_VALUE, obj->buffer->size);
+ break;
+
+ case UACPI_PREDEFINED_NAMESPACE_REV:
+ obj = uacpi_create_object(UACPI_OBJECT_INTEGER);
+ if (uacpi_unlikely(obj == UACPI_NULL))
+ return obj;
+
+ obj->integer = UACPI_REV_VALUE;
+ break;
+
+ case UACPI_PREDEFINED_NAMESPACE_GL:
+ obj = uacpi_create_object(UACPI_OBJECT_MUTEX);
+ if (uacpi_likely(obj != UACPI_NULL)) {
+ uacpi_shareable_ref(obj->mutex);
+ g_uacpi_rt_ctx.global_lock_mutex = obj->mutex;
+ }
+ break;
+
+ case UACPI_PREDEFINED_NAMESPACE_OSI:
+ obj = uacpi_create_object(UACPI_OBJECT_METHOD);
+ if (uacpi_unlikely(obj == UACPI_NULL))
+ return obj;
+
+ obj->method->native_call = UACPI_TRUE;
+ obj->method->handler = uacpi_osi;
+ obj->method->args = 1;
+ break;
+
+ default:
+ obj = uacpi_create_object(UACPI_OBJECT_UNINITIALIZED);
+ break;
+ }
+
+ return obj;
+}
+
+static void namespace_node_detach_object(uacpi_namespace_node *node)
+{
+ uacpi_object *object;
+
+ object = uacpi_namespace_node_get_object(node);
+ if (object != UACPI_NULL) {
+ if (object->type == UACPI_OBJECT_OPERATION_REGION)
+ uacpi_opregion_uninstall_handler(node);
+
+ uacpi_object_unref(node->object);
+ node->object = UACPI_NULL;
+ }
+}
+
+static void free_namespace_node(uacpi_handle handle)
+{
+ uacpi_namespace_node *node = handle;
+
+ if (uacpi_likely(!uacpi_namespace_node_is_predefined(node))) {
+ uacpi_free(node, sizeof(*node));
+ return;
+ }
+
+ node->flags = UACPI_NAMESPACE_NODE_PREDEFINED;
+ node->object = UACPI_NULL;
+ node->parent = UACPI_NULL;
+ node->child = UACPI_NULL;
+ node->next = UACPI_NULL;
+}
+
+uacpi_status uacpi_initialize_namespace(void)
+{
+ enum uacpi_predefined_namespace ns;
+ uacpi_object *obj;
+ uacpi_namespace_node *node;
+ uacpi_status ret;
+
+ ret = uacpi_rw_lock_init(&namespace_lock);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ for (ns = 0; ns <= UACPI_PREDEFINED_NAMESPACE_MAX; ns++) {
+ node = &predefined_namespaces[ns];
+ uacpi_shareable_init(node);
+
+ obj = make_object_for_predefined(ns);
+ if (uacpi_unlikely(obj == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ node->object = uacpi_create_internal_reference(
+ UACPI_REFERENCE_KIND_NAMED, obj
+ );
+ if (uacpi_unlikely(node->object == UACPI_NULL)) {
+ uacpi_object_unref(obj);
+ return UACPI_STATUS_OUT_OF_MEMORY;
+ }
+
+ uacpi_object_unref(obj);
+ }
+
+ for (ns = UACPI_PREDEFINED_NAMESPACE_GPE;
+ ns <= UACPI_PREDEFINED_NAMESPACE_MAX; ns++) {
+
+ /*
+ * Skip the installation of \_OSI if it was disabled by user.
+ * We still create the object, but it's not attached to the namespace.
+ */
+ if (ns == UACPI_PREDEFINED_NAMESPACE_OSI &&
+ uacpi_check_flag(UACPI_FLAG_NO_OSI))
+ continue;
+
+ uacpi_namespace_node_install(
+ uacpi_namespace_root(), &predefined_namespaces[ns]
+ );
+ }
+
+ return UACPI_STATUS_OK;
+}
+
+void uacpi_deinitialize_namespace(void)
+{
+ uacpi_status ret;
+ uacpi_namespace_node *current, *next = UACPI_NULL;
+ uacpi_u32 depth = 1;
+
+ current = uacpi_namespace_root();
+
+ ret = uacpi_namespace_write_lock();
+
+ while (depth) {
+ next = next == UACPI_NULL ? current->child : next->next;
+
+ /*
+ * The previous value of 'next' was the last child of this subtree,
+ * we can now remove the entire scope of 'current->child'
+ */
+ if (next == UACPI_NULL) {
+ depth--;
+
+ // Wipe the subtree
+ while (current->child != UACPI_NULL)
+ uacpi_namespace_node_uninstall(current->child);
+
+ // Reset the pointers back as if this iteration never happened
+ next = current;
+ current = current->parent;
+
+ continue;
+ }
+
+ /*
+ * We have more nodes to process, proceed to the next one, either the
+ * child of the 'next' node, if one exists, or its peer
+ */
+ if (next->child) {
+ depth++;
+ current = next;
+ next = UACPI_NULL;
+ }
+
+ // This node has no children, move on to its peer
+ }
+
+ namespace_node_detach_object(uacpi_namespace_root());
+ free_namespace_node(uacpi_namespace_root());
+
+ if (ret == UACPI_STATUS_OK)
+ uacpi_namespace_write_unlock();
+
+ uacpi_object_unref(g_uacpi_rt_ctx.root_object);
+ g_uacpi_rt_ctx.root_object = UACPI_NULL;
+
+ uacpi_mutex_unref(g_uacpi_rt_ctx.global_lock_mutex);
+ g_uacpi_rt_ctx.global_lock_mutex = UACPI_NULL;
+
+ uacpi_rw_lock_deinit(&namespace_lock);
+}
+
+uacpi_namespace_node *uacpi_namespace_root(void)
+{
+ return &predefined_namespaces[UACPI_PREDEFINED_NAMESPACE_ROOT];
+}
+
+uacpi_namespace_node *uacpi_namespace_get_predefined(
+ enum uacpi_predefined_namespace ns
+)
+{
+ if (uacpi_unlikely(ns > UACPI_PREDEFINED_NAMESPACE_MAX)) {
+ uacpi_warn("requested invalid predefined namespace %d\n", ns);
+ return UACPI_NULL;
+ }
+
+ return &predefined_namespaces[ns];
+}
+
+uacpi_namespace_node *uacpi_namespace_node_alloc(uacpi_object_name name)
+{
+ uacpi_namespace_node *ret;
+
+ ret = uacpi_kernel_alloc_zeroed(sizeof(*ret));
+ if (uacpi_unlikely(ret == UACPI_NULL))
+ return ret;
+
+ uacpi_shareable_init(ret);
+ ret->name = name;
+ return ret;
+}
+
+void uacpi_namespace_node_unref(uacpi_namespace_node *node)
+{
+ uacpi_shareable_unref_and_delete_if_last(node, free_namespace_node);
+}
+
+uacpi_status uacpi_namespace_node_install(
+ uacpi_namespace_node *parent,
+ uacpi_namespace_node *node
+)
+{
+ if (parent == UACPI_NULL)
+ parent = uacpi_namespace_root();
+
+ if (uacpi_unlikely(uacpi_namespace_node_is_dangling(node))) {
+ uacpi_warn("attempting to install a dangling namespace node %.4s\n",
+ node->name.text);
+ return UACPI_STATUS_NAMESPACE_NODE_DANGLING;
+ }
+
+ if (parent->child == UACPI_NULL) {
+ parent->child = node;
+ } else {
+ uacpi_namespace_node *prev = parent->child;
+
+ while (prev->next != UACPI_NULL)
+ prev = prev->next;
+
+ prev->next = node;
+ }
+
+ node->parent = parent;
+ return UACPI_STATUS_OK;
+}
+
+uacpi_bool uacpi_namespace_node_is_alias(uacpi_namespace_node *node)
+{
+ return node->flags & UACPI_NAMESPACE_NODE_FLAG_ALIAS;
+}
+
+uacpi_bool uacpi_namespace_node_is_dangling(uacpi_namespace_node *node)
+{
+ return node->flags & UACPI_NAMESPACE_NODE_FLAG_DANGLING;
+}
+
+uacpi_bool uacpi_namespace_node_is_temporary(uacpi_namespace_node *node)
+{
+ return node->flags & UACPI_NAMESPACE_NODE_FLAG_TEMPORARY;
+}
+
+uacpi_bool uacpi_namespace_node_is_predefined(uacpi_namespace_node *node)
+{
+ return node->flags & UACPI_NAMESPACE_NODE_PREDEFINED;
+}
+
+uacpi_status uacpi_namespace_node_uninstall(uacpi_namespace_node *node)
+{
+ uacpi_namespace_node *prev;
+
+ if (uacpi_unlikely(uacpi_namespace_node_is_dangling(node))) {
+ uacpi_warn("attempting to uninstall a dangling namespace node %.4s\n",
+ node->name.text);
+ return UACPI_STATUS_INTERNAL_ERROR;
+ }
+
+ /*
+ * The way to trigger this is as follows:
+ *
+ * Method (FOO) {
+ * // Temporary device, will be deleted upon returning from FOO
+ * Device (\BAR) {
+ * }
+ *
+ * //
+ * // Load TBL where TBL is:
+ * // Scope (\BAR) {
+ * // Name (TEST, 123)
+ * // }
+ * //
+ * Load(TBL)
+ * }
+ *
+ * In the above example, TEST is a permanent node attached by bad AML to a
+ * temporary node created inside the FOO method at \BAR. The cleanup code
+ * will attempt to remove the \BAR device upon exit from FOO, but that is
+ * no longer possible as there's now a permanent child attached to it.
+ */
+ if (uacpi_unlikely(node->child != UACPI_NULL)) {
+ uacpi_warn(
+ "refusing to uninstall node %.4s with a child (%.4s)\n",
+ node->name.text, node->child->name.text
+ );
+ return UACPI_STATUS_DENIED;
+ }
+
+ /*
+ * Even though namespace_node is reference-counted it still has an 'invalid'
+ * state that is entered after it is uninstalled from the global namespace.
+ *
+ * Reference counting is only needed to combat dangling pointer issues
+ * whereas bad AML might try to prolong a local object lifetime by
+ * returning it from a method, or CopyObject it somewhere. In that case the
+ * namespace node object itself is still alive, but no longer has a valid
+ * object associated with it.
+ *
+ * Example:
+ * Method (BAD) {
+ * OperationRegion(REG, SystemMemory, 0xDEADBEEF, 4)
+ * Field (REG, AnyAcc, NoLock) {
+ * FILD, 8,
+ * }
+ *
+ * Return (RefOf(FILD))
+ * }
+ *
+ * // Local0 is now the sole owner of the 'FILD' object that under the
+ * // hood is still referencing the 'REG' operation region object from
+ * // the 'BAD' method.
+ * Local0 = DerefOf(BAD())
+ *
+ * This is done to prevent potential very deep recursion where an object
+ * frees a namespace node that frees an attached object that frees a
+ * namespace node as well as potential infinite cycles between a namespace
+ * node and an object.
+ */
+ namespace_node_detach_object(node);
+
+ prev = node->parent ? node->parent->child : UACPI_NULL;
+
+ if (prev == node) {
+ node->parent->child = node->next;
+ } else {
+ while (uacpi_likely(prev != UACPI_NULL) && prev->next != node)
+ prev = prev->next;
+
+ if (uacpi_unlikely(prev == UACPI_NULL)) {
+ uacpi_warn(
+ "trying to uninstall a node %.4s (%p) not linked to any peer\n",
+ node->name.text, node
+ );
+ return UACPI_STATUS_INTERNAL_ERROR;
+ }
+
+ prev->next = node->next;
+ }
+
+ node->flags |= UACPI_NAMESPACE_NODE_FLAG_DANGLING;
+ uacpi_namespace_node_unref(node);
+
+ return UACPI_STATUS_OK;
+}
+
+uacpi_namespace_node *uacpi_namespace_node_find_sub_node(
+ uacpi_namespace_node *parent,
+ uacpi_object_name name
+)
+{
+ uacpi_namespace_node *node;
+
+ if (parent == UACPI_NULL)
+ parent = uacpi_namespace_root();
+
+ node = parent->child;
+
+ while (node) {
+ if (node->name.id == name.id)
+ return node;
+
+ node = node->next;
+ }
+
+ return UACPI_NULL;
+}
+
+static uacpi_object_name segment_to_name(
+ const uacpi_char **string, uacpi_size *in_out_size
+)
+{
+ uacpi_object_name out_name;
+ const uacpi_char *cursor = *string;
+ uacpi_size offset, bytes_left = *in_out_size;
+
+ for (offset = 0; offset < 4; offset++) {
+ if (bytes_left < 1 || *cursor == '.') {
+ out_name.text[offset] = '_';
+ continue;
+ }
+
+ out_name.text[offset] = *cursor++;
+ bytes_left--;
+ }
+
+ *string = cursor;
+ *in_out_size = bytes_left;
+ return out_name;
+}
+
+uacpi_status uacpi_namespace_node_resolve(
+ uacpi_namespace_node *parent, const uacpi_char *path,
+ enum uacpi_should_lock should_lock,
+ enum uacpi_may_search_above_parent may_search_above_parent,
+ enum uacpi_permanent_only permanent_only,
+ uacpi_namespace_node **out_node
+)
+{
+ uacpi_namespace_node *cur_node = parent;
+ uacpi_status ret = UACPI_STATUS_OK;
+ const uacpi_char *cursor = path;
+ uacpi_size bytes_left;
+ uacpi_char prev_char = 0;
+ uacpi_bool single_nameseg = UACPI_TRUE;
+
+ if (cur_node == UACPI_NULL)
+ cur_node = uacpi_namespace_root();
+
+ bytes_left = uacpi_strlen(path);
+
+ if (should_lock == UACPI_SHOULD_LOCK_YES) {
+ ret = uacpi_namespace_read_lock();
+ if (uacpi_unlikely_error(ret))
+ return ret;
+ }
+
+ for (;;) {
+ if (bytes_left == 0)
+ goto out;
+
+ switch (*cursor) {
+ case '\\':
+ single_nameseg = UACPI_FALSE;
+
+ if (prev_char == '^') {
+ ret = UACPI_STATUS_INVALID_ARGUMENT;
+ goto out;
+ }
+
+ cur_node = uacpi_namespace_root();
+ break;
+ case '^':
+ single_nameseg = UACPI_FALSE;
+
+ // Tried to go behind root
+ if (uacpi_unlikely(cur_node == uacpi_namespace_root())) {
+ ret = UACPI_STATUS_INVALID_ARGUMENT;
+ goto out;
+ }
+
+ cur_node = cur_node->parent;
+ break;
+ default:
+ break;
+ }
+
+ prev_char = *cursor;
+
+ switch (prev_char) {
+ case '^':
+ case '\\':
+ cursor++;
+ bytes_left--;
+ break;
+ default:
+ break;
+ }
+
+ if (prev_char != '^')
+ break;
+ }
+
+ while (bytes_left != 0) {
+ uacpi_object_name nameseg;
+
+ if (*cursor == '.') {
+ cursor++;
+ bytes_left--;
+ }
+
+ nameseg = segment_to_name(&cursor, &bytes_left);
+ if (bytes_left != 0 && single_nameseg)
+ single_nameseg = UACPI_FALSE;
+
+ cur_node = uacpi_namespace_node_find_sub_node(cur_node, nameseg);
+ if (cur_node == UACPI_NULL) {
+ if (may_search_above_parent == UACPI_MAY_SEARCH_ABOVE_PARENT_NO ||
+ !single_nameseg)
+ goto out;
+
+ parent = parent->parent;
+
+ while (parent) {
+ cur_node = uacpi_namespace_node_find_sub_node(parent, nameseg);
+ if (cur_node != UACPI_NULL)
+ goto out;
+
+ parent = parent->parent;
+ }
+
+ goto out;
+ }
+ }
+
+out:
+ if (uacpi_unlikely(ret == UACPI_STATUS_INVALID_ARGUMENT)) {
+ uacpi_warn("invalid path '%s'\n", path);
+ goto out_read_unlock;
+ }
+
+ if (cur_node == UACPI_NULL) {
+ ret = UACPI_STATUS_NOT_FOUND;
+ goto out_read_unlock;
+ }
+
+ if (uacpi_namespace_node_is_temporary(cur_node) &&
+ permanent_only == UACPI_PERMANENT_ONLY_YES) {
+ uacpi_warn("denying access to temporary namespace node '%.4s'\n",
+ cur_node->name.text);
+ ret = UACPI_STATUS_DENIED;
+ goto out_read_unlock;
+ }
+
+ if (out_node != UACPI_NULL)
+ *out_node = cur_node;
+
+out_read_unlock:
+ if (should_lock == UACPI_SHOULD_LOCK_YES)
+ uacpi_namespace_read_unlock();
+ return ret;
+}
+
+uacpi_status uacpi_namespace_node_find(
+ uacpi_namespace_node *parent, const uacpi_char *path,
+ uacpi_namespace_node **out_node
+)
+{
+ return uacpi_namespace_node_resolve(
+ parent, path, UACPI_SHOULD_LOCK_YES, UACPI_MAY_SEARCH_ABOVE_PARENT_NO,
+ UACPI_PERMANENT_ONLY_YES, out_node
+ );
+}
+
+uacpi_status uacpi_namespace_node_resolve_from_aml_namepath(
+ uacpi_namespace_node *scope,
+ const uacpi_char *path,
+ uacpi_namespace_node **out_node
+)
+{
+ return uacpi_namespace_node_resolve(
+ scope, path, UACPI_SHOULD_LOCK_YES, UACPI_MAY_SEARCH_ABOVE_PARENT_YES,
+ UACPI_PERMANENT_ONLY_YES, out_node
+ );
+}
+
+uacpi_object *uacpi_namespace_node_get_object(const uacpi_namespace_node *node)
+{
+ if (node == UACPI_NULL || node->object == UACPI_NULL)
+ return UACPI_NULL;
+
+ return uacpi_unwrap_internal_reference(node->object);
+}
+
+uacpi_object *uacpi_namespace_node_get_object_typed(
+ const uacpi_namespace_node *node, uacpi_object_type_bits type_mask
+)
+{
+ uacpi_object *obj;
+
+ obj = uacpi_namespace_node_get_object(node);
+ if (uacpi_unlikely(obj == UACPI_NULL))
+ return obj;
+
+ if (!uacpi_object_is_one_of(obj, type_mask))
+ return UACPI_NULL;
+
+ return obj;
+}
+
+uacpi_status uacpi_namespace_node_acquire_object_typed(
+ const uacpi_namespace_node *node, uacpi_object_type_bits type_mask,
+ uacpi_object **out_obj
+)
+{
+ uacpi_status ret;
+ uacpi_object *obj;
+
+ ret = uacpi_namespace_read_lock();
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ obj = uacpi_namespace_node_get_object(node);
+
+ if (uacpi_unlikely(obj == UACPI_NULL) ||
+ !uacpi_object_is_one_of(obj, type_mask)) {
+ ret = UACPI_STATUS_INVALID_ARGUMENT;
+ goto out;
+ }
+
+ uacpi_object_ref(obj);
+ *out_obj = obj;
+
+out:
+ uacpi_namespace_read_unlock();
+ return ret;
+}
+
+uacpi_status uacpi_namespace_node_acquire_object(
+ const uacpi_namespace_node *node, uacpi_object **out_obj
+)
+{
+ return uacpi_namespace_node_acquire_object_typed(
+ node, UACPI_OBJECT_ANY_BIT, out_obj
+ );
+}
+
+enum action {
+ ACTION_REACQUIRE,
+ ACTION_PUT,
+};
+
+static uacpi_status object_mutate_refcount(
+ uacpi_object *obj, void (*cb)(uacpi_object*)
+)
+{
+ uacpi_status ret = UACPI_STATUS_OK;
+
+ if (uacpi_likely(!uacpi_object_is(obj, UACPI_OBJECT_REFERENCE))) {
+ cb(obj);
+ return ret;
+ }
+
+ /*
+ * Reference objects must be (un)referenced under at least a read lock, as
+ * this requires walking down the entire reference chain and dropping each
+ * object ref-count by 1. This might race with the interpreter and
+ * object_replace_child in case an object in the chain is CopyObject'ed
+ * into.
+ */
+ ret = uacpi_namespace_read_lock();
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ cb(obj);
+
+ uacpi_namespace_read_unlock();
+ return ret;
+}
+
+uacpi_status uacpi_namespace_node_reacquire_object(
+ uacpi_object *obj
+)
+{
+ return object_mutate_refcount(obj, uacpi_object_ref);
+}
+
+uacpi_status uacpi_namespace_node_release_object(uacpi_object *obj)
+{
+ return object_mutate_refcount(obj, uacpi_object_unref);
+}
+
+uacpi_object_name uacpi_namespace_node_name(const uacpi_namespace_node *node)
+{
+ return node->name;
+}
+
+uacpi_status uacpi_namespace_node_type_unlocked(
+ const uacpi_namespace_node *node, uacpi_object_type *out_type
+)
+{
+ uacpi_object *obj;
+
+ if (uacpi_unlikely(node == UACPI_NULL))
+ return UACPI_STATUS_INVALID_ARGUMENT;
+
+ obj = uacpi_namespace_node_get_object(node);
+ if (uacpi_unlikely(obj == UACPI_NULL))
+ return UACPI_STATUS_NOT_FOUND;
+
+ *out_type = obj->type;
+ return UACPI_STATUS_OK;
+}
+
+uacpi_status uacpi_namespace_node_type(
+ const uacpi_namespace_node *node, uacpi_object_type *out_type
+)
+{
+ uacpi_status ret;
+
+ ret = uacpi_namespace_read_lock();
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ ret = uacpi_namespace_node_type_unlocked(node, out_type);
+
+ uacpi_namespace_read_unlock();
+ return ret;
+}
+
+uacpi_status uacpi_namespace_node_is_one_of_unlocked(
+ const uacpi_namespace_node *node, uacpi_object_type_bits type_mask, uacpi_bool *out
+)
+{
+ uacpi_object *obj;
+
+ if (uacpi_unlikely(node == UACPI_NULL))
+ return UACPI_STATUS_INVALID_ARGUMENT;
+
+ obj = uacpi_namespace_node_get_object(node);
+ if (uacpi_unlikely(obj == UACPI_NULL))
+ return UACPI_STATUS_NOT_FOUND;
+
+ *out = uacpi_object_is_one_of(obj, type_mask);
+
+ return UACPI_STATUS_OK;
+}
+
+uacpi_status uacpi_namespace_node_is_one_of(
+ const uacpi_namespace_node *node, uacpi_object_type_bits type_mask,
+ uacpi_bool *out
+)
+{
+ uacpi_status ret;
+
+ ret = uacpi_namespace_read_lock();
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ ret = uacpi_namespace_node_is_one_of_unlocked(node,type_mask, out);
+
+ uacpi_namespace_read_unlock();
+ return ret;
+}
+
+uacpi_status uacpi_namespace_node_is(
+ const uacpi_namespace_node *node, uacpi_object_type type, uacpi_bool *out
+)
+{
+ return uacpi_namespace_node_is_one_of(
+ node, 1u << type, out
+ );
+}
+
+uacpi_status uacpi_namespace_do_for_each_child(
+ uacpi_namespace_node *node, uacpi_iteration_callback descending_callback,
+ uacpi_iteration_callback ascending_callback,
+ uacpi_object_type_bits type_mask, uacpi_u32 max_depth,
+ enum uacpi_should_lock should_lock,
+ enum uacpi_permanent_only permanent_only, void *user
+)
+{
+ uacpi_status ret = UACPI_STATUS_OK;
+ uacpi_iteration_decision decision;
+ uacpi_iteration_callback cb;
+ uacpi_bool walking_up = UACPI_FALSE, matches = UACPI_FALSE;
+ uacpi_u32 depth = 1;
+
+ UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_SUBSYSTEM_INITIALIZED);
+
+ if (uacpi_unlikely(descending_callback == UACPI_NULL &&
+ ascending_callback == UACPI_NULL))
+ return UACPI_STATUS_INVALID_ARGUMENT;
+
+ if (uacpi_unlikely(node == UACPI_NULL || max_depth == 0))
+ return UACPI_STATUS_INVALID_ARGUMENT;
+
+ if (should_lock == UACPI_SHOULD_LOCK_YES) {
+ ret = uacpi_namespace_read_lock();
+ if (uacpi_unlikely_error(ret))
+ return ret;
+ }
+
+ if (node->child == UACPI_NULL)
+ goto out;
+
+ node = node->child;
+
+ while (depth) {
+ uacpi_namespace_node_is_one_of_unlocked(node, type_mask, &matches);
+ if (!matches) {
+ decision = UACPI_ITERATION_DECISION_CONTINUE;
+ goto do_next;
+ }
+
+ if (permanent_only == UACPI_PERMANENT_ONLY_YES &&
+ uacpi_namespace_node_is_temporary(node)) {
+ decision = UACPI_ITERATION_DECISION_NEXT_PEER;
+ goto do_next;
+ }
+
+ cb = walking_up ? ascending_callback : descending_callback;
+ if (cb != UACPI_NULL) {
+ if (should_lock == UACPI_SHOULD_LOCK_YES) {
+ ret = uacpi_namespace_read_unlock();
+ if (uacpi_unlikely_error(ret))
+ return ret;
+ }
+
+ decision = cb(user, node, depth);
+ if (decision == UACPI_ITERATION_DECISION_BREAK)
+ return ret;
+
+ if (should_lock == UACPI_SHOULD_LOCK_YES) {
+ ret = uacpi_namespace_read_lock();
+ if (uacpi_unlikely_error(ret))
+ return ret;
+ }
+ } else {
+ decision = UACPI_ITERATION_DECISION_CONTINUE;
+ }
+
+ do_next:
+ if (walking_up) {
+ if (node->next) {
+ node = node->next;
+ walking_up = UACPI_FALSE;
+ continue;
+ }
+
+ depth--;
+ node = node->parent;
+ continue;
+ }
+
+ switch (decision) {
+ case UACPI_ITERATION_DECISION_CONTINUE:
+ if ((depth != max_depth) && (node->child != UACPI_NULL)) {
+ node = node->child;
+ depth++;
+ continue;
+ }
+ UACPI_FALLTHROUGH;
+ case UACPI_ITERATION_DECISION_NEXT_PEER:
+ walking_up = UACPI_TRUE;
+ continue;
+ default:
+ ret = UACPI_STATUS_INVALID_ARGUMENT;
+ goto out;
+ }
+ }
+
+out:
+ if (should_lock == UACPI_SHOULD_LOCK_YES)
+ uacpi_namespace_read_unlock();
+ return ret;
+}
+
+uacpi_status uacpi_namespace_for_each_child_simple(
+ uacpi_namespace_node *parent, uacpi_iteration_callback callback, void *user
+)
+{
+ return uacpi_namespace_do_for_each_child(
+ parent, callback, UACPI_NULL, UACPI_OBJECT_ANY_BIT, UACPI_MAX_DEPTH_ANY,
+ UACPI_SHOULD_LOCK_YES, UACPI_PERMANENT_ONLY_YES, user
+ );
+}
+
+uacpi_status uacpi_namespace_for_each_child(
+ uacpi_namespace_node *parent, uacpi_iteration_callback descending_callback,
+ uacpi_iteration_callback ascending_callback,
+ uacpi_object_type_bits type_mask, uacpi_u32 max_depth, void *user
+)
+{
+ return uacpi_namespace_do_for_each_child(
+ parent, descending_callback, ascending_callback, type_mask, max_depth,
+ UACPI_SHOULD_LOCK_YES, UACPI_PERMANENT_ONLY_YES, user
+ );
+}
+
+uacpi_status uacpi_namespace_node_next_typed(
+ uacpi_namespace_node *parent, uacpi_namespace_node **iter,
+ uacpi_object_type_bits type_mask
+)
+{
+ uacpi_status ret;
+ uacpi_bool is_one_of;
+ uacpi_namespace_node *node;
+
+ UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_SUBSYSTEM_INITIALIZED);
+
+ if (uacpi_unlikely(parent == UACPI_NULL && *iter == UACPI_NULL))
+ return UACPI_STATUS_INVALID_ARGUMENT;
+
+ ret = uacpi_namespace_read_lock();
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ node = *iter;
+ if (node == UACPI_NULL)
+ node = parent->child;
+ else
+ node = node->next;
+
+ for (; node != UACPI_NULL; node = node->next) {
+ if (uacpi_namespace_node_is_temporary(node))
+ continue;
+
+ ret = uacpi_namespace_node_is_one_of_unlocked(
+ node, type_mask, &is_one_of
+ );
+ if (uacpi_unlikely_error(ret))
+ break;
+ if (is_one_of)
+ break;
+ }
+
+ uacpi_namespace_read_unlock();
+ if (node == UACPI_NULL)
+ return UACPI_STATUS_NOT_FOUND;
+
+ if (uacpi_likely_success(ret))
+ *iter = node;
+ return ret;
+}
+
+uacpi_status uacpi_namespace_node_next(
+ uacpi_namespace_node *parent, uacpi_namespace_node **iter
+)
+{
+ return uacpi_namespace_node_next_typed(
+ parent, iter, UACPI_OBJECT_ANY_BIT
+ );
+}
+
+uacpi_size uacpi_namespace_node_depth(const uacpi_namespace_node *node)
+{
+ uacpi_size depth = 0;
+
+ while (node->parent) {
+ depth++;
+ node = node->parent;
+ }
+
+ return depth;
+}
+
+uacpi_namespace_node *uacpi_namespace_node_parent(
+ uacpi_namespace_node *node
+)
+{
+ return node->parent;
+}
+
+const uacpi_char *uacpi_namespace_node_generate_absolute_path(
+ const uacpi_namespace_node *node
+)
+{
+ uacpi_size depth, offset;
+ uacpi_size bytes_needed;
+ uacpi_char *path;
+
+ depth = uacpi_namespace_node_depth(node) + 1;
+
+ // \ only needs 1 byte, the rest is 4 bytes
+ bytes_needed = 1 + (depth - 1) * sizeof(uacpi_object_name);
+
+ // \ and the first NAME don't need a '.', every other segment does
+ bytes_needed += depth > 2 ? depth - 2 : 0;
+
+ // Null terminator
+ bytes_needed += 1;
+
+ path = uacpi_kernel_alloc(bytes_needed);
+ if (uacpi_unlikely(path == UACPI_NULL))
+ return path;
+
+ path[0] = '\\';
+
+ offset = bytes_needed - 1;
+ path[offset] = '\0';
+
+ while (node != uacpi_namespace_root()) {
+ offset -= sizeof(uacpi_object_name);
+ uacpi_memcpy(&path[offset], node->name.text, sizeof(uacpi_object_name));
+
+ node = node->parent;
+ if (node != uacpi_namespace_root())
+ path[--offset] = '.';
+ }
+
+ return path;
+}
+
+void uacpi_free_absolute_path(const uacpi_char *path)
+{
+ uacpi_free_dynamic_string(path);
+}
+
+#endif // !UACPI_BAREBONES_MODE
diff --git a/sys/dev/acpi/uacpi/notify.c b/sys/dev/acpi/uacpi/notify.c
new file mode 100644
index 0000000..b413df9
--- /dev/null
+++ b/sys/dev/acpi/uacpi/notify.c
@@ -0,0 +1,255 @@
+#include <uacpi/internal/notify.h>
+#include <uacpi/internal/shareable.h>
+#include <uacpi/internal/namespace.h>
+#include <uacpi/internal/log.h>
+#include <uacpi/internal/mutex.h>
+#include <uacpi/internal/utilities.h>
+#include <uacpi/internal/stdlib.h>
+#include <uacpi/kernel_api.h>
+
+#ifndef UACPI_BAREBONES_MODE
+
+static uacpi_handle notify_mutex;
+
+uacpi_status uacpi_initialize_notify(void)
+{
+ notify_mutex = uacpi_kernel_create_mutex();
+ if (uacpi_unlikely(notify_mutex == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ return UACPI_STATUS_OK;
+}
+
+void uacpi_deinitialize_notify(void)
+{
+ if (notify_mutex != UACPI_NULL)
+ uacpi_kernel_free_mutex(notify_mutex);
+
+ notify_mutex = UACPI_NULL;
+}
+
+struct notification_ctx {
+ uacpi_namespace_node *node;
+ uacpi_u64 value;
+ uacpi_object *node_object;
+};
+
+static void free_notification_ctx(struct notification_ctx *ctx)
+{
+ uacpi_namespace_node_release_object(ctx->node_object);
+ uacpi_namespace_node_unref(ctx->node);
+ uacpi_free(ctx, sizeof(*ctx));
+}
+
+static void do_notify(uacpi_handle opaque)
+{
+ struct notification_ctx *ctx = opaque;
+ uacpi_device_notify_handler *handler;
+ uacpi_bool did_notify_root = UACPI_FALSE;
+
+ handler = ctx->node_object->handlers->notify_head;
+
+ for (;;) {
+ if (handler == UACPI_NULL) {
+ if (did_notify_root) {
+ free_notification_ctx(ctx);
+ return;
+ }
+
+ handler = g_uacpi_rt_ctx.root_object->handlers->notify_head;
+ did_notify_root = UACPI_TRUE;
+ continue;
+ }
+
+ handler->callback(handler->user_context, ctx->node, ctx->value);
+ handler = handler->next;
+ }
+}
+
+uacpi_status uacpi_notify_all(uacpi_namespace_node *node, uacpi_u64 value)
+{
+ uacpi_status ret;
+ struct notification_ctx *ctx;
+ uacpi_object *node_object;
+
+ node_object = uacpi_namespace_node_get_object_typed(
+ node, UACPI_OBJECT_DEVICE_BIT | UACPI_OBJECT_THERMAL_ZONE_BIT |
+ UACPI_OBJECT_PROCESSOR_BIT
+ );
+ if (uacpi_unlikely(node_object == UACPI_NULL))
+ return UACPI_STATUS_INVALID_ARGUMENT;
+
+ ret = uacpi_acquire_native_mutex(notify_mutex);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ if (node_object->handlers->notify_head == UACPI_NULL &&
+ g_uacpi_rt_ctx.root_object->handlers->notify_head == UACPI_NULL) {
+ ret = UACPI_STATUS_NO_HANDLER;
+ goto out;
+ }
+
+ ctx = uacpi_kernel_alloc(sizeof(*ctx));
+ if (uacpi_unlikely(ctx == UACPI_NULL)) {
+ ret = UACPI_STATUS_OUT_OF_MEMORY;
+ goto out;
+ }
+
+ ctx->node = node;
+ // In case this node goes out of scope
+ uacpi_shareable_ref(node);
+
+ ctx->value = value;
+ ctx->node_object = uacpi_namespace_node_get_object(node);
+ uacpi_object_ref(ctx->node_object);
+
+ ret = uacpi_kernel_schedule_work(UACPI_WORK_NOTIFICATION, do_notify, ctx);
+ if (uacpi_unlikely_error(ret)) {
+ uacpi_warn("unable to schedule notification work: %s\n",
+ uacpi_status_to_string(ret));
+ free_notification_ctx(ctx);
+ }
+
+out:
+ uacpi_release_native_mutex(notify_mutex);
+ return ret;
+}
+
+static uacpi_device_notify_handler *handler_container(
+ uacpi_handlers *handlers, uacpi_notify_handler target_handler
+)
+{
+ uacpi_device_notify_handler *handler = handlers->notify_head;
+
+ while (handler) {
+ if (handler->callback == target_handler)
+ return handler;
+
+ handler = handler->next;
+ }
+
+ return UACPI_NULL;
+}
+
+uacpi_status uacpi_install_notify_handler(
+ uacpi_namespace_node *node, uacpi_notify_handler handler,
+ uacpi_handle handler_context
+)
+{
+ uacpi_status ret;
+ uacpi_object *obj;
+ uacpi_handlers *handlers;
+ uacpi_device_notify_handler *new_handler;
+
+ UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_SUBSYSTEM_INITIALIZED);
+
+ if (node == uacpi_namespace_root()) {
+ obj = g_uacpi_rt_ctx.root_object;
+ } else {
+ ret = uacpi_namespace_node_acquire_object_typed(
+ node, UACPI_OBJECT_DEVICE_BIT | UACPI_OBJECT_THERMAL_ZONE_BIT |
+ UACPI_OBJECT_PROCESSOR_BIT, &obj
+ );
+ if (uacpi_unlikely_error(ret))
+ return ret;
+ }
+
+ ret = uacpi_acquire_native_mutex(notify_mutex);
+ if (uacpi_unlikely_error(ret))
+ goto out_no_mutex;
+
+ uacpi_kernel_wait_for_work_completion();
+
+ handlers = obj->handlers;
+
+ if (handler_container(handlers, handler) != UACPI_NULL) {
+ ret = UACPI_STATUS_ALREADY_EXISTS;
+ goto out;
+ }
+
+ new_handler = uacpi_kernel_alloc_zeroed(sizeof(*new_handler));
+ if (uacpi_unlikely(new_handler == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ new_handler->callback = handler;
+ new_handler->user_context = handler_context;
+ new_handler->next = handlers->notify_head;
+
+ handlers->notify_head = new_handler;
+
+out:
+ uacpi_release_native_mutex(notify_mutex);
+out_no_mutex:
+ if (node != uacpi_namespace_root())
+ uacpi_object_unref(obj);
+
+ return ret;
+}
+
+uacpi_status uacpi_uninstall_notify_handler(
+ uacpi_namespace_node *node, uacpi_notify_handler handler
+)
+{
+ uacpi_status ret;
+ uacpi_object *obj;
+ uacpi_handlers *handlers;
+ uacpi_device_notify_handler *prev_handler, *containing = UACPI_NULL;
+
+ UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_SUBSYSTEM_INITIALIZED);
+
+ if (node == uacpi_namespace_root()) {
+ obj = g_uacpi_rt_ctx.root_object;
+ } else {
+ ret = uacpi_namespace_node_acquire_object_typed(
+ node, UACPI_OBJECT_DEVICE_BIT | UACPI_OBJECT_THERMAL_ZONE_BIT |
+ UACPI_OBJECT_PROCESSOR_BIT, &obj
+ );
+ if (uacpi_unlikely_error(ret))
+ return ret;
+ }
+
+ ret = uacpi_acquire_native_mutex(notify_mutex);
+ if (uacpi_unlikely_error(ret))
+ goto out_no_mutex;
+
+ uacpi_kernel_wait_for_work_completion();
+
+ handlers = obj->handlers;
+
+ containing = handler_container(handlers, handler);
+ if (containing == UACPI_NULL) {
+ ret = UACPI_STATUS_NOT_FOUND;
+ goto out;
+ }
+
+ prev_handler = handlers->notify_head;
+
+ // Are we the last linked handler?
+ if (prev_handler == containing) {
+ handlers->notify_head = containing->next;
+ goto out;
+ }
+
+ // Nope, we're somewhere in the middle. Do a search.
+ while (prev_handler) {
+ if (prev_handler->next == containing) {
+ prev_handler->next = containing->next;
+ goto out;
+ }
+
+ prev_handler = prev_handler->next;
+ }
+
+out:
+ uacpi_release_native_mutex(notify_mutex);
+out_no_mutex:
+ if (node != uacpi_namespace_root())
+ uacpi_object_unref(obj);
+
+ if (uacpi_likely_success(ret))
+ uacpi_free(containing, sizeof(*containing));
+
+ return ret;
+}
+
+#endif // !UACPI_BAREBONES_MODE
diff --git a/sys/dev/acpi/uacpi/opcodes.c b/sys/dev/acpi/uacpi/opcodes.c
new file mode 100644
index 0000000..3665631
--- /dev/null
+++ b/sys/dev/acpi/uacpi/opcodes.c
@@ -0,0 +1,272 @@
+#include <uacpi/internal/opcodes.h>
+
+#ifndef UACPI_BAREBONES_MODE
+
+#define UACPI_OP(opname, opcode, props, ...) \
+ { #opname, { .decode_ops = __VA_ARGS__ }, .properties = props, .code = opcode },
+
+#define UACPI_OUT_OF_LINE_OP(opname, opcode, out_of_line_buf, props) \
+ { \
+ .name = #opname, \
+ { .indirect_decode_ops = out_of_line_buf }, \
+ .properties = props, \
+ .code = opcode, \
+ },
+
+static const struct uacpi_op_spec opcode_table[0x100] = {
+ UACPI_ENUMERATE_OPCODES
+};
+
+static const struct uacpi_op_spec ext_opcode_table[] = {
+ UACPI_ENUMERATE_EXT_OPCODES
+};
+
+#define _(op) (op & 0x00FF)
+
+static const uacpi_u8 ext_op_to_idx[0x100] = {
+ [_(UACPI_AML_OP_MutexOp)] = 1, [_(UACPI_AML_OP_EventOp)] = 2,
+ [_(UACPI_AML_OP_CondRefOfOp)] = 3, [_(UACPI_AML_OP_CreateFieldOp)] = 4,
+ [_(UACPI_AML_OP_LoadTableOp)] = 5, [_(UACPI_AML_OP_LoadOp)] = 6,
+ [_(UACPI_AML_OP_StallOp)] = 7, [_(UACPI_AML_OP_SleepOp)] = 8,
+ [_(UACPI_AML_OP_AcquireOp)] = 9, [_(UACPI_AML_OP_SignalOp)] = 10,
+ [_(UACPI_AML_OP_WaitOp)] = 11, [_(UACPI_AML_OP_ResetOp)] = 12,
+ [_(UACPI_AML_OP_ReleaseOp)] = 13, [_(UACPI_AML_OP_FromBCDOp)] = 14,
+ [_(UACPI_AML_OP_ToBCDOp)] = 15, [_(UACPI_AML_OP_UnloadOp)] = 16,
+ [_(UACPI_AML_OP_RevisionOp)] = 17, [_(UACPI_AML_OP_DebugOp)] = 18,
+ [_(UACPI_AML_OP_FatalOp)] = 19, [_(UACPI_AML_OP_TimerOp)] = 20,
+ [_(UACPI_AML_OP_OpRegionOp)] = 21, [_(UACPI_AML_OP_FieldOp)] = 22,
+ [_(UACPI_AML_OP_DeviceOp)] = 23, [_(UACPI_AML_OP_ProcessorOp)] = 24,
+ [_(UACPI_AML_OP_PowerResOp)] = 25, [_(UACPI_AML_OP_ThermalZoneOp)] = 26,
+ [_(UACPI_AML_OP_IndexFieldOp)] = 27, [_(UACPI_AML_OP_BankFieldOp)] = 28,
+ [_(UACPI_AML_OP_DataRegionOp)] = 29,
+};
+
+const struct uacpi_op_spec *uacpi_get_op_spec(uacpi_aml_op op)
+{
+ if (op > 0xFF)
+ return &ext_opcode_table[ext_op_to_idx[_(op)]];
+
+ return &opcode_table[op];
+}
+
+#define PARSE_FIELD_ELEMENTS(parse_loop_pc) \
+ /* Parse every field element found inside */ \
+ UACPI_PARSE_OP_IF_HAS_DATA, 44, \
+ /* Look at the first byte */ \
+ UACPI_PARSE_OP_LOAD_IMM, 1, \
+ \
+ /* ReservedField := 0x00 PkgLength */ \
+ UACPI_PARSE_OP_IF_LAST_EQUALS, 0x00, 3, \
+ UACPI_PARSE_OP_PKGLEN, \
+ UACPI_PARSE_OP_JMP, parse_loop_pc, \
+ \
+ /* AccessField := 0x01 AccessType AccessAttrib */ \
+ UACPI_PARSE_OP_IF_LAST_EQUALS, 0x01, 6, \
+ UACPI_PARSE_OP_LOAD_IMM, 1, \
+ UACPI_PARSE_OP_LOAD_IMM, 1, \
+ UACPI_PARSE_OP_JMP, parse_loop_pc, \
+ \
+ /* ConnectField := <0x02 NameString> | <0x02 BufferData> */ \
+ UACPI_PARSE_OP_IF_LAST_EQUALS, 0x02, 5, \
+ UACPI_PARSE_OP_TERM_ARG_UNWRAP_INTERNAL, \
+ UACPI_PARSE_OP_TYPECHECK, UACPI_OBJECT_BUFFER, \
+ UACPI_PARSE_OP_JMP, parse_loop_pc, \
+ \
+ /* ExtendedAccessField := 0x03 AccessType ExtendedAccessAttrib \
+ * AccessLength */ \
+ UACPI_PARSE_OP_IF_LAST_EQUALS, 0x03, 8, \
+ UACPI_PARSE_OP_LOAD_IMM, 1, \
+ UACPI_PARSE_OP_LOAD_IMM, 1, \
+ UACPI_PARSE_OP_LOAD_IMM, 1, \
+ UACPI_PARSE_OP_JMP, parse_loop_pc, \
+ \
+ /* NamedField := NameSeg PkgLength */ \
+ \
+ /* \
+ * Discard the immediate, as it's the first byte of the \
+ * nameseg. We don't need it. \
+ */ \
+ UACPI_PARSE_OP_ITEM_POP, \
+ UACPI_PARSE_OP_AML_PC_DECREMENT, \
+ UACPI_PARSE_OP_CREATE_NAMESTRING, \
+ UACPI_PARSE_OP_PKGLEN, \
+ UACPI_PARSE_OP_OBJECT_ALLOC_TYPED, UACPI_OBJECT_FIELD_UNIT, \
+ UACPI_PARSE_OP_JMP, parse_loop_pc, \
+ \
+ UACPI_PARSE_OP_INVOKE_HANDLER, \
+ UACPI_PARSE_OP_END
+
+uacpi_u8 uacpi_field_op_decode_ops[] = {
+ UACPI_PARSE_OP_TRACKED_PKGLEN,
+ UACPI_PARSE_OP_EXISTING_NAMESTRING,
+ UACPI_PARSE_OP_LOAD_IMM, 1,
+ PARSE_FIELD_ELEMENTS(4),
+};
+
+uacpi_u8 uacpi_bank_field_op_decode_ops[] = {
+ UACPI_PARSE_OP_TRACKED_PKGLEN,
+ UACPI_PARSE_OP_EXISTING_NAMESTRING,
+ UACPI_PARSE_OP_EXISTING_NAMESTRING,
+ UACPI_PARSE_OP_OPERAND,
+ UACPI_PARSE_OP_LOAD_IMM, 1,
+ PARSE_FIELD_ELEMENTS(6),
+};
+
+uacpi_u8 uacpi_index_field_op_decode_ops[] = {
+ UACPI_PARSE_OP_TRACKED_PKGLEN,
+ UACPI_PARSE_OP_EXISTING_NAMESTRING,
+ UACPI_PARSE_OP_EXISTING_NAMESTRING,
+ UACPI_PARSE_OP_LOAD_IMM, 1,
+ PARSE_FIELD_ELEMENTS(5),
+};
+
+uacpi_u8 uacpi_load_op_decode_ops[] = {
+ // Storage for the scope pointer, this is left as 0 in case of errors
+ UACPI_PARSE_OP_LOAD_ZERO_IMM,
+ UACPI_PARSE_OP_OBJECT_ALLOC_TYPED, UACPI_OBJECT_METHOD,
+ UACPI_PARSE_OP_TERM_ARG_UNWRAP_INTERNAL,
+ UACPI_PARSE_OP_TARGET,
+
+ /*
+ * Invoke the handler here to initialize the table. If this fails, it's
+ * expected to keep the item 0 as NULL, which is checked below to return
+ * false to the caller of Load.
+ */
+ UACPI_PARSE_OP_INVOKE_HANDLER,
+ UACPI_PARSE_OP_IF_NULL, 0, 3,
+ UACPI_PARSE_OP_LOAD_FALSE_OBJECT,
+ UACPI_PARSE_OP_JMP, 15,
+
+ UACPI_PARSE_OP_LOAD_TRUE_OBJECT,
+ UACPI_PARSE_OP_DISPATCH_TABLE_LOAD,
+
+ /*
+ * Invoke the handler a second time to initialize any AML GPE handlers that
+ * might've been loaded from this table.
+ */
+ UACPI_PARSE_OP_INVOKE_HANDLER,
+ UACPI_PARSE_OP_STORE_TO_TARGET, 3,
+ UACPI_PARSE_OP_OBJECT_TRANSFER_TO_PREV,
+ UACPI_PARSE_OP_END,
+};
+
+uacpi_u8 uacpi_load_table_op_decode_ops[] = {
+ // Storage for the scope pointer, this is left as 0 in case of errors
+ UACPI_PARSE_OP_LOAD_ZERO_IMM,
+ UACPI_PARSE_OP_OBJECT_ALLOC_TYPED, UACPI_OBJECT_METHOD,
+ // Index of the table we are going to be loaded to unref it later
+ UACPI_PARSE_OP_LOAD_ZERO_IMM,
+ // Storage for the target pointer, this is left as 0 if none was requested
+ UACPI_PARSE_OP_LOAD_ZERO_IMM,
+
+ UACPI_PARSE_OP_LOAD_INLINE_IMM, 1, 5,
+ UACPI_PARSE_OP_IF_NOT_NULL, 4, 5,
+ UACPI_PARSE_OP_STRING,
+ UACPI_PARSE_OP_IMM_DECREMENT, 4,
+ UACPI_PARSE_OP_JMP, 8,
+ UACPI_PARSE_OP_TERM_ARG_UNWRAP_INTERNAL,
+
+ /*
+ * Invoke the handler here to initialize the table. If this fails, it's
+ * expected to keep the item 0 as NULL, which is checked below to return
+ * false to the caller of Load.
+ */
+ UACPI_PARSE_OP_INVOKE_HANDLER,
+ UACPI_PARSE_OP_IF_NULL, 0, 3,
+ UACPI_PARSE_OP_LOAD_FALSE_OBJECT,
+ UACPI_PARSE_OP_OBJECT_TRANSFER_TO_PREV,
+ UACPI_PARSE_OP_END,
+
+ UACPI_PARSE_OP_LOAD_TRUE_OBJECT,
+ UACPI_PARSE_OP_DISPATCH_TABLE_LOAD,
+
+ /*
+ * Invoke the handler a second time to block the store to target in case
+ * the load above failed, as well as do any AML GPE handler initialization.
+ */
+ UACPI_PARSE_OP_INVOKE_HANDLER,
+
+ // If we were given a target to store to, do the store
+ UACPI_PARSE_OP_IF_NOT_NULL, 3, 3,
+ UACPI_PARSE_OP_STORE_TO_TARGET_INDIRECT, 3, 10,
+
+ UACPI_PARSE_OP_OBJECT_TRANSFER_TO_PREV,
+ UACPI_PARSE_OP_END,
+};
+
+#define POP(x) UACPI_PARSE_OP_##x
+
+static
+const uacpi_char *const pop_names[UACPI_PARSE_OP_MAX + 1] = {
+ [POP(END)] = "<END-OF-OP>",
+ [POP(SKIP_WITH_WARN_IF_NULL)] = "SKIP_WITH_WARN_IF_NULL",
+ [POP(EMIT_SKIP_WARN)] = "EMIT_SKIP_WARN",
+ [POP(SIMPLE_NAME)] = "SIMPLE_NAME",
+ [POP(SUPERNAME)] = "SUPERNAME",
+ [POP(SUPERNAME_OR_UNRESOLVED)] = "SUPERNAME_OR_UNRESOLVED",
+ [POP(TERM_ARG)] = "TERM_ARG",
+ [POP(TERM_ARG_UNWRAP_INTERNAL)] = "TERM_ARG_UNWRAP_INTERNAL",
+ [POP(TERM_ARG_OR_NAMED_OBJECT)] = "TERM_ARG_OR_NAMED_OBJECT",
+ [POP(TERM_ARG_OR_NAMED_OBJECT_OR_UNRESOLVED)] = "TERM_ARG_OR_NAMED_OBJECT_OR_UNRESOLVED",
+ [POP(OPERAND)] = "OPERAND",
+ [POP(STRING)] = "STRING",
+ [POP(COMPUTATIONAL_DATA)] = "COMPUTATIONAL_DATA",
+ [POP(TARGET)] = "TARGET",
+ [POP(PKGLEN)] = "PKGLEN",
+ [POP(TRACKED_PKGLEN)] = "TRACKED_PKGLEN",
+ [POP(CREATE_NAMESTRING)] = "CREATE_NAMESTRING",
+ [POP(CREATE_NAMESTRING_OR_NULL_IF_LOAD)] = "CREATE_NAMESTRING_OR_NULL_IF_LOAD",
+ [POP(EXISTING_NAMESTRING)] = "EXISTING_NAMESTRING",
+ [POP(EXISTING_NAMESTRING_OR_NULL)] = "EXISTING_NAMESTRING_OR_NULL",
+ [POP(EXISTING_NAMESTRING_OR_NULL_IF_LOAD)] = "EXISTING_NAMESTRING_OR_NULL_IF_LOAD",
+ [POP(INVOKE_HANDLER)] = "INVOKE_HANDLER",
+ [POP(OBJECT_ALLOC)] = "OBJECT_ALLOC",
+ [POP(EMPTY_OBJECT_ALLOC)] = "EMPTY_OBJECT_ALLOC",
+ [POP(OBJECT_CONVERT_TO_SHALLOW_COPY)] = "OBJECT_CONVERT_TO_SHALLOW_COPY",
+ [POP(OBJECT_CONVERT_TO_DEEP_COPY)] = "OBJECT_CONVERT_TO_DEEP_COPY",
+ [POP(OBJECT_ALLOC_TYPED)] = "OBJECT_ALLOC_TYPED",
+ [POP(RECORD_AML_PC)] = "RECORD_AML_PC",
+ [POP(LOAD_INLINE_IMM_AS_OBJECT)] = "LOAD_INLINE_IMM_AS_OBJECT",
+ [POP(LOAD_INLINE_IMM)] = "LOAD_INLINE_IMM",
+ [POP(LOAD_ZERO_IMM)] = "LOAD_ZERO_IMM",
+ [POP(LOAD_IMM)] = "LOAD_IMM",
+ [POP(LOAD_IMM_AS_OBJECT)] = "LOAD_IMM_AS_OBJECT",
+ [POP(LOAD_FALSE_OBJECT)] = "LOAD_FALSE_OBJECT",
+ [POP(LOAD_TRUE_OBJECT)] = "LOAD_TRUE_OBJECT",
+ [POP(TRUNCATE_NUMBER)] = "TRUNCATE_NUMBER",
+ [POP(TYPECHECK)] = "TYPECHECK",
+ [POP(INSTALL_NAMESPACE_NODE)] = "INSTALL_NAMESPACE_NODE",
+ [POP(OBJECT_TRANSFER_TO_PREV)] = "OBJECT_TRANSFER_TO_PREV",
+ [POP(OBJECT_COPY_TO_PREV)] = "OBJECT_COPY_TO_PREV",
+ [POP(STORE_TO_TARGET)] = "STORE_TO_TARGET",
+ [POP(STORE_TO_TARGET_INDIRECT)] = "STORE_TO_TARGET_INDIRECT",
+ [POP(UNREACHABLE)] = "UNREACHABLE",
+ [POP(BAD_OPCODE)] = "BAD_OPCODE",
+ [POP(AML_PC_DECREMENT)] = "AML_PC_DECREMENT",
+ [POP(IMM_DECREMENT)] = "IMM_DECREMENT",
+ [POP(ITEM_POP)] = "ITEM_POP",
+ [POP(DISPATCH_METHOD_CALL)] = "DISPATCH_METHOD_CALL",
+ [POP(DISPATCH_TABLE_LOAD)] = "DISPATCH_TABLE_LOAD",
+ [POP(CONVERT_NAMESTRING)] = "CONVERT_NAMESTRING",
+ [POP(IF_HAS_DATA)] = "IF_HAS_DATA",
+ [POP(IF_NULL)] = "IF_NULL",
+ [POP(IF_LAST_NULL)] = "IF_LAST_NULL",
+ [POP(IF_NOT_NULL)] = "IF_NOT_NULL",
+ [POP(IF_LAST_NOT_NULL)] = "IF_NOT_NULL",
+ [POP(IF_LAST_EQUALS)] = "IF_LAST_EQUALS",
+ [POP(IF_LAST_FALSE)] = "IF_LAST_FALSE",
+ [POP(IF_LAST_TRUE)] = "IF_LAST_TRUE",
+ [POP(SWITCH_TO_NEXT_IF_EQUALS)] = "SWITCH_TO_NEXT_IF_EQUALS",
+ [POP(IF_SWITCHED_FROM)] = "IF_SWITCHED_FROM",
+ [POP(JMP)] = "JMP",
+};
+
+const uacpi_char *uacpi_parse_op_to_string(enum uacpi_parse_op op)
+{
+ if (uacpi_unlikely(op > UACPI_PARSE_OP_MAX))
+ return "<INVALID-OP>";
+
+ return pop_names[op];
+}
+
+#endif // !UACPI_BAREBONES_MODE
diff --git a/sys/dev/acpi/uacpi/opregion.c b/sys/dev/acpi/uacpi/opregion.c
new file mode 100644
index 0000000..ec0bc37
--- /dev/null
+++ b/sys/dev/acpi/uacpi/opregion.c
@@ -0,0 +1,1056 @@
+#include <uacpi/kernel_api.h>
+
+#include <uacpi/internal/opregion.h>
+#include <uacpi/internal/namespace.h>
+#include <uacpi/internal/stdlib.h>
+#include <uacpi/internal/log.h>
+#include <uacpi/internal/utilities.h>
+#include <uacpi/internal/mutex.h>
+#include <uacpi/internal/interpreter.h>
+
+#ifndef UACPI_BAREBONES_MODE
+
+struct uacpi_recursive_lock g_opregion_lock;
+
+uacpi_status uacpi_initialize_opregion(void)
+{
+ return uacpi_recursive_lock_init(&g_opregion_lock);
+}
+
+void uacpi_deinitialize_opregion(void)
+{
+ uacpi_recursive_lock_deinit(&g_opregion_lock);
+}
+
+void uacpi_trace_region_error(
+ uacpi_namespace_node *node, uacpi_char *message, uacpi_status ret
+)
+{
+ const uacpi_char *path, *space_string = "<unknown>";
+ uacpi_object *obj;
+
+ path = uacpi_namespace_node_generate_absolute_path(node);
+
+ obj = uacpi_namespace_node_get_object_typed(
+ node, UACPI_OBJECT_OPERATION_REGION_BIT
+ );
+ if (uacpi_likely(obj != UACPI_NULL))
+ space_string = uacpi_address_space_to_string(obj->op_region->space);
+
+ uacpi_error(
+ "%s (%s) operation region %s: %s\n",
+ message, space_string, path, uacpi_status_to_string(ret)
+ );
+ uacpi_free_dynamic_string(path);
+}
+
+static void trace_region_io(
+ uacpi_field_unit *field, uacpi_address_space space, uacpi_u64 offset,
+ uacpi_region_op op, union uacpi_opregion_io_data data
+)
+{
+ const uacpi_char *path;
+ const uacpi_char *type_str;
+
+ if (!uacpi_should_log(UACPI_LOG_TRACE))
+ return;
+
+ switch (op) {
+ case UACPI_REGION_OP_READ:
+ type_str = "read from";
+ break;
+ case UACPI_REGION_OP_WRITE:
+ type_str = "write to";
+ break;
+ default:
+ type_str = "<INVALID-OP>";
+ }
+
+ path = uacpi_namespace_node_generate_absolute_path(field->region);
+
+ switch (space) {
+ case UACPI_ADDRESS_SPACE_IPMI:
+ case UACPI_ADDRESS_SPACE_PRM:
+ case UACPI_ADDRESS_SPACE_FFIXEDHW:
+ uacpi_trace(
+ "write-then-read from [%s] %s[0x%016"UACPI_PRIX64"] = "
+ "<buffer of %zu bytes>\n", path,
+ uacpi_address_space_to_string(space),
+ UACPI_FMT64(offset), data.buffer.length
+ );
+ break;
+ case UACPI_ADDRESS_SPACE_SMBUS:
+ case UACPI_ADDRESS_SPACE_GENERIC_SERIAL_BUS:
+ uacpi_trace(
+ "%s [%s] %s[0x%016"UACPI_PRIX64"] = "
+ "<buffer of %zu bytes>\n", type_str, path,
+ uacpi_address_space_to_string(space),
+ UACPI_FMT64(offset), data.buffer.length
+ );
+ break;
+ case UACPI_ADDRESS_SPACE_GENERAL_PURPOSE_IO:
+ uacpi_trace(
+ "%s [%s] %s pins[%u..%u] = 0x%"UACPI_PRIX64"\n",
+ type_str, path, uacpi_address_space_to_string(space),
+ field->pin_offset, (field->pin_offset + field->bit_length) - 1,
+ UACPI_FMT64(*data.integer)
+ );
+ break;
+ default:
+ uacpi_trace(
+ "%s [%s] (%d bytes) %s[0x%016"UACPI_PRIX64"] = 0x%"UACPI_PRIX64"\n",
+ type_str, path, field->access_width_bytes,
+ uacpi_address_space_to_string(space),
+ UACPI_FMT64(offset), UACPI_FMT64(*data.integer)
+ );
+ break;
+ }
+
+ uacpi_free_dynamic_string(path);
+}
+
+static uacpi_bool space_needs_reg(enum uacpi_address_space space)
+{
+ if (space == UACPI_ADDRESS_SPACE_SYSTEM_MEMORY ||
+ space == UACPI_ADDRESS_SPACE_SYSTEM_IO ||
+ space == UACPI_ADDRESS_SPACE_TABLE_DATA)
+ return UACPI_FALSE;
+
+ return UACPI_TRUE;
+}
+
+static uacpi_status region_run_reg(
+ uacpi_namespace_node *node, uacpi_u8 connection_code
+)
+{
+ uacpi_status ret;
+ uacpi_namespace_node *reg_node;
+ uacpi_object_array method_args;
+ uacpi_object *reg_obj, *args[2];
+
+ ret = uacpi_namespace_node_resolve(
+ node->parent, "_REG", UACPI_SHOULD_LOCK_NO,
+ UACPI_MAY_SEARCH_ABOVE_PARENT_NO, UACPI_PERMANENT_ONLY_NO, &reg_node
+ );
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ reg_obj = uacpi_namespace_node_get_object_typed(
+ reg_node, UACPI_OBJECT_METHOD_BIT
+ );
+ if (uacpi_unlikely(reg_obj == UACPI_NULL))
+ return UACPI_STATUS_OK;
+
+ args[0] = uacpi_create_object(UACPI_OBJECT_INTEGER);
+ if (uacpi_unlikely(args[0] == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ args[1] = uacpi_create_object(UACPI_OBJECT_INTEGER);
+ if (uacpi_unlikely(args[1] == UACPI_NULL)) {
+ uacpi_object_unref(args[0]);
+ return UACPI_STATUS_OUT_OF_MEMORY;
+ }
+
+ args[0]->integer = uacpi_namespace_node_get_object(node)->op_region->space;
+ args[1]->integer = connection_code;
+ method_args.objects = args;
+ method_args.count = 2;
+
+ ret = uacpi_execute_control_method(
+ reg_node, reg_obj->method, &method_args, UACPI_NULL
+ );
+ if (uacpi_unlikely_error(ret))
+ uacpi_trace_region_error(node, "error during _REG execution for", ret);
+
+ uacpi_object_unref(args[0]);
+ uacpi_object_unref(args[1]);
+ return ret;
+}
+
+uacpi_address_space_handlers *uacpi_node_get_address_space_handlers(
+ uacpi_namespace_node *node
+)
+{
+ uacpi_object *object;
+
+ if (node == uacpi_namespace_root())
+ return g_uacpi_rt_ctx.root_object->address_space_handlers;
+
+ object = uacpi_namespace_node_get_object(node);
+ if (uacpi_unlikely(object == UACPI_NULL))
+ return UACPI_NULL;
+
+ switch (object->type) {
+ case UACPI_OBJECT_DEVICE:
+ case UACPI_OBJECT_PROCESSOR:
+ case UACPI_OBJECT_THERMAL_ZONE:
+ return object->address_space_handlers;
+ default:
+ return UACPI_NULL;
+ }
+}
+
+static uacpi_address_space_handler *find_handler(
+ uacpi_address_space_handlers *handlers,
+ enum uacpi_address_space space
+)
+{
+ uacpi_address_space_handler *handler = handlers->head;
+
+ while (handler) {
+ if (handler->space == space)
+ return handler;
+
+ handler = handler->next;
+ }
+
+ return UACPI_NULL;
+}
+
+static uacpi_operation_region *find_previous_region_link(
+ uacpi_operation_region *region
+)
+{
+ uacpi_address_space_handler *handler = region->handler;
+ uacpi_operation_region *parent = handler->regions;
+
+ if (parent == region)
+ // This is the last attached region, it has no previous link
+ return region;
+
+ while (parent->next != region) {
+ parent = parent->next;
+
+ if (uacpi_unlikely(parent == UACPI_NULL))
+ return UACPI_NULL;
+ }
+
+ return parent;
+}
+
+uacpi_status uacpi_opregion_attach(uacpi_namespace_node *node)
+{
+ uacpi_object *obj;
+ uacpi_operation_region *region;
+ uacpi_address_space_handler *handler;
+ uacpi_status ret;
+ uacpi_region_attach_data attach_data = { 0 };
+
+ if (uacpi_namespace_node_is_dangling(node))
+ return UACPI_STATUS_NAMESPACE_NODE_DANGLING;
+
+ obj = uacpi_namespace_node_get_object_typed(
+ node, UACPI_OBJECT_OPERATION_REGION_BIT
+ );
+ if (uacpi_unlikely(obj == UACPI_NULL))
+ return UACPI_STATUS_INVALID_ARGUMENT;
+
+ region = obj->op_region;
+
+ if (region->handler == UACPI_NULL)
+ return UACPI_STATUS_NO_HANDLER;
+ if (region->state_flags & UACPI_OP_REGION_STATE_ATTACHED)
+ return UACPI_STATUS_OK;
+
+ handler = region->handler;
+ attach_data.region_node = node;
+
+ switch (region->space) {
+ case UACPI_ADDRESS_SPACE_PCC:
+ if (region->length) {
+ region->internal_buffer = uacpi_kernel_alloc_zeroed(region->length);
+ if (uacpi_unlikely(region->internal_buffer == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+ }
+
+ attach_data.pcc_info.buffer.bytes = region->internal_buffer;
+ attach_data.pcc_info.buffer.length = region->length;
+ attach_data.pcc_info.subspace_id = region->offset;
+ break;
+ case UACPI_ADDRESS_SPACE_GENERAL_PURPOSE_IO:
+ attach_data.gpio_info.num_pins = region->length;
+ break;
+ default:
+ attach_data.generic_info.base = region->offset;
+ attach_data.generic_info.length = region->length;
+ break;
+ }
+
+ attach_data.handler_context = handler->user_context;
+
+ uacpi_object_ref(obj);
+ uacpi_namespace_write_unlock();
+ ret = handler->callback(UACPI_REGION_OP_ATTACH, &attach_data);
+ uacpi_namespace_write_lock();
+
+ if (uacpi_unlikely_error(ret)) {
+ uacpi_object_unref(obj);
+ return ret;
+ }
+
+ region->state_flags |= UACPI_OP_REGION_STATE_ATTACHED;
+ region->user_context = attach_data.out_region_context;
+ uacpi_object_unref(obj);
+ return ret;
+}
+
+static void region_install_handler(
+ uacpi_namespace_node *node, uacpi_address_space_handler *handler
+)
+{
+ uacpi_operation_region *region;
+
+ region = uacpi_namespace_node_get_object(node)->op_region;
+ region->handler = handler;
+ uacpi_shareable_ref(handler);
+
+ region->next = handler->regions;
+ handler->regions = region;
+}
+
+enum unreg {
+ UNREG_NO = 0,
+ UNREG_YES,
+};
+
+static void region_uninstall_handler(
+ uacpi_namespace_node *node, enum unreg unreg
+)
+{
+ uacpi_status ret;
+ uacpi_object *obj;
+ uacpi_address_space_handler *handler;
+ uacpi_operation_region *region, *link;
+
+ obj = uacpi_namespace_node_get_object_typed(
+ node, UACPI_OBJECT_OPERATION_REGION_BIT
+ );
+ if (uacpi_unlikely(obj == UACPI_NULL))
+ return;
+
+ region = obj->op_region;
+
+ handler = region->handler;
+ if (handler == UACPI_NULL)
+ return;
+
+ link = find_previous_region_link(region);
+ if (uacpi_unlikely(link == UACPI_NULL)) {
+ uacpi_error("operation region @%p not in the handler@%p list(?)\n",
+ region, handler);
+ goto out;
+ } else if (link == region) {
+ link = link->next;
+ handler->regions = link;
+ } else {
+ link->next = region->next;
+ }
+
+out:
+ if (region->state_flags & UACPI_OP_REGION_STATE_ATTACHED) {
+ uacpi_region_detach_data detach_data = { 0 };
+
+ detach_data.region_node = node;
+ detach_data.region_context = region->user_context;
+ detach_data.handler_context = handler->user_context;
+
+ uacpi_shareable_ref(node);
+ uacpi_namespace_write_unlock();
+
+ ret = handler->callback(UACPI_REGION_OP_DETACH, &detach_data);
+
+ uacpi_namespace_write_lock();
+ uacpi_namespace_node_unref(node);
+
+ if (uacpi_unlikely_error(ret)) {
+ uacpi_trace_region_error(
+ node, "error during handler detach for", ret
+ );
+ }
+ }
+
+ if ((region->state_flags & UACPI_OP_REGION_STATE_REG_EXECUTED) &&
+ unreg == UNREG_YES) {
+ region_run_reg(node, ACPI_REG_DISCONNECT);
+ region->state_flags &= ~UACPI_OP_REGION_STATE_REG_EXECUTED;
+ }
+
+ uacpi_address_space_handler_unref(region->handler);
+ region->handler = UACPI_NULL;
+ region->state_flags &= ~UACPI_OP_REGION_STATE_ATTACHED;
+}
+
+static uacpi_status upgrade_to_opregion_lock(void)
+{
+ uacpi_status ret;
+
+ /*
+ * Drop the namespace lock, and reacquire it after the opregion lock
+ * so we keep the ordering with user API.
+ */
+ uacpi_namespace_write_unlock();
+
+ ret = uacpi_recursive_lock_acquire(&g_opregion_lock);
+ uacpi_namespace_write_lock();
+ return ret;
+}
+
+void uacpi_opregion_uninstall_handler(uacpi_namespace_node *node)
+{
+ if (uacpi_unlikely_error(upgrade_to_opregion_lock()))
+ return;
+
+ region_uninstall_handler(node, UNREG_YES);
+
+ uacpi_recursive_lock_release(&g_opregion_lock);
+}
+
+uacpi_bool uacpi_address_space_handler_is_default(
+ uacpi_address_space_handler *handler
+)
+{
+ return handler->flags & UACPI_ADDRESS_SPACE_HANDLER_DEFAULT;
+}
+
+enum opregion_iter_action {
+ OPREGION_ITER_ACTION_UNINSTALL,
+ OPREGION_ITER_ACTION_INSTALL,
+};
+
+struct opregion_iter_ctx {
+ enum opregion_iter_action action;
+ uacpi_address_space_handler *handler;
+};
+
+static uacpi_iteration_decision do_install_or_uninstall_handler(
+ uacpi_handle opaque, uacpi_namespace_node *node, uacpi_u32 depth
+)
+{
+ struct opregion_iter_ctx *ctx = opaque;
+ uacpi_address_space_handlers *handlers;
+ uacpi_object *object;
+
+ UACPI_UNUSED(depth);
+
+ object = uacpi_namespace_node_get_object(node);
+ if (object->type == UACPI_OBJECT_OPERATION_REGION) {
+ uacpi_operation_region *region = object->op_region;
+
+ if (region->space != ctx->handler->space)
+ return UACPI_ITERATION_DECISION_CONTINUE;
+
+ if (ctx->action == OPREGION_ITER_ACTION_INSTALL) {
+ if (region->handler)
+ region_uninstall_handler(node, UNREG_NO);
+
+ region_install_handler(node, ctx->handler);
+ } else {
+ if (uacpi_unlikely(region->handler != ctx->handler)) {
+ uacpi_trace_region_error(
+ node, "handler mismatch for",
+ UACPI_STATUS_INTERNAL_ERROR
+ );
+ return UACPI_ITERATION_DECISION_CONTINUE;
+ }
+
+ region_uninstall_handler(node, UNREG_NO);
+ }
+
+ return UACPI_ITERATION_DECISION_CONTINUE;
+ }
+
+ handlers = uacpi_node_get_address_space_handlers(node);
+ if (handlers == UACPI_NULL)
+ return UACPI_ITERATION_DECISION_CONTINUE;
+
+ // Device already has a handler for this space installed
+ if (find_handler(handlers, ctx->handler->space) != UACPI_NULL)
+ return UACPI_ITERATION_DECISION_NEXT_PEER;
+
+ return UACPI_ITERATION_DECISION_CONTINUE;
+}
+
+struct reg_run_ctx {
+ uacpi_u8 space;
+ uacpi_u8 connection_code;
+ uacpi_size reg_executed;
+ uacpi_size reg_errors;
+};
+
+static uacpi_iteration_decision do_run_reg(
+ void *opaque, uacpi_namespace_node *node, uacpi_u32 depth
+)
+{
+ struct reg_run_ctx *ctx = opaque;
+ uacpi_operation_region *region;
+ uacpi_status ret;
+ uacpi_bool was_regged;
+
+ UACPI_UNUSED(depth);
+
+ region = uacpi_namespace_node_get_object(node)->op_region;
+
+ if (region->space != ctx->space)
+ return UACPI_ITERATION_DECISION_CONTINUE;
+
+ was_regged = region->state_flags & UACPI_OP_REGION_STATE_REG_EXECUTED;
+ if (was_regged == (ctx->connection_code == ACPI_REG_CONNECT))
+ return UACPI_ITERATION_DECISION_CONTINUE;
+
+ ret = region_run_reg(node, ctx->connection_code);
+ if (ctx->connection_code == ACPI_REG_DISCONNECT)
+ region->state_flags &= ~UACPI_OP_REGION_STATE_REG_EXECUTED;
+
+ if (ret == UACPI_STATUS_NOT_FOUND)
+ return UACPI_ITERATION_DECISION_CONTINUE;
+
+ if (ctx->connection_code == ACPI_REG_CONNECT)
+ region->state_flags |= UACPI_OP_REGION_STATE_REG_EXECUTED;
+
+ ctx->reg_executed++;
+
+ if (uacpi_unlikely_error(ret)) {
+ ctx->reg_errors++;
+ return UACPI_ITERATION_DECISION_CONTINUE;
+ }
+
+ return UACPI_ITERATION_DECISION_CONTINUE;
+}
+
+static uacpi_status reg_or_unreg_all_opregions(
+ uacpi_namespace_node *device_node, enum uacpi_address_space space,
+ uacpi_u8 connection_code
+)
+{
+ uacpi_address_space_handlers *handlers;
+ uacpi_bool is_connect;
+ enum uacpi_permanent_only perm_only;
+ struct reg_run_ctx ctx = { 0 };
+
+ ctx.space = space;
+ ctx.connection_code = connection_code;
+
+ handlers = uacpi_node_get_address_space_handlers(device_node);
+ if (uacpi_unlikely(handlers == UACPI_NULL))
+ return UACPI_STATUS_INVALID_ARGUMENT;
+
+ is_connect = connection_code == ACPI_REG_CONNECT;
+ if (uacpi_unlikely(is_connect &&
+ find_handler(handlers, space) == UACPI_NULL))
+ return UACPI_STATUS_NO_HANDLER;
+
+ /*
+ * We want to unreg non-permanent opregions as well, however,
+ * registering them is handled separately and should not be
+ * done by us.
+ */
+ perm_only = is_connect ? UACPI_PERMANENT_ONLY_YES : UACPI_PERMANENT_ONLY_NO;
+
+ uacpi_namespace_do_for_each_child(
+ device_node, do_run_reg, UACPI_NULL,
+ UACPI_OBJECT_OPERATION_REGION_BIT, UACPI_MAX_DEPTH_ANY,
+ UACPI_SHOULD_LOCK_NO, perm_only, &ctx
+ );
+
+ uacpi_trace(
+ "%sactivated all '%s' opregions controlled by '%.4s', "
+ "%zu _REG() calls (%zu errors)\n",
+ connection_code == ACPI_REG_CONNECT ? "" : "de",
+ uacpi_address_space_to_string(space),
+ device_node->name.text, ctx.reg_executed, ctx.reg_errors
+ );
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_address_space_handlers *extract_handlers(
+ uacpi_namespace_node *node
+)
+{
+ uacpi_object *handlers_obj;
+
+ if (node == uacpi_namespace_root())
+ return g_uacpi_rt_ctx.root_object->address_space_handlers;
+
+ handlers_obj = uacpi_namespace_node_get_object_typed(
+ node,
+ UACPI_OBJECT_DEVICE_BIT | UACPI_OBJECT_THERMAL_ZONE_BIT |
+ UACPI_OBJECT_PROCESSOR_BIT
+ );
+ if (uacpi_unlikely(handlers_obj == UACPI_NULL))
+ return UACPI_NULL;
+
+ return handlers_obj->address_space_handlers;
+}
+
+uacpi_status uacpi_reg_all_opregions(
+ uacpi_namespace_node *device_node,
+ enum uacpi_address_space space
+)
+{
+ uacpi_status ret;
+
+ UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_NAMESPACE_LOADED);
+
+ if (!space_needs_reg(space))
+ return UACPI_STATUS_OK;
+
+ ret = uacpi_recursive_lock_acquire(&g_opregion_lock);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ ret = uacpi_namespace_write_lock();
+ if (uacpi_unlikely_error(ret)) {
+ uacpi_recursive_lock_release(&g_opregion_lock);
+ return ret;
+ }
+
+ if (uacpi_unlikely(extract_handlers(device_node) == UACPI_NULL)) {
+ ret = UACPI_STATUS_INVALID_ARGUMENT;
+ goto out;
+ }
+
+ ret = reg_or_unreg_all_opregions(device_node, space, ACPI_REG_CONNECT);
+
+out:
+ uacpi_namespace_write_unlock();
+ uacpi_recursive_lock_release(&g_opregion_lock);
+ return ret;
+}
+
+uacpi_status uacpi_install_address_space_handler_with_flags(
+ uacpi_namespace_node *device_node, enum uacpi_address_space space,
+ uacpi_region_handler handler, uacpi_handle handler_context,
+ uacpi_u16 flags
+)
+{
+ uacpi_status ret;
+ uacpi_address_space_handlers *handlers;
+ uacpi_address_space_handler *this_handler, *new_handler;
+ struct opregion_iter_ctx iter_ctx;
+
+ ret = uacpi_recursive_lock_acquire(&g_opregion_lock);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ ret = uacpi_namespace_write_lock();
+ if (uacpi_unlikely_error(ret)) {
+ uacpi_recursive_lock_release(&g_opregion_lock);
+ return ret;
+ }
+
+ handlers = extract_handlers(device_node);
+ if (uacpi_unlikely(handlers == UACPI_NULL)) {
+ ret = UACPI_STATUS_INVALID_ARGUMENT;
+ goto out;
+ }
+
+ this_handler = find_handler(handlers, space);
+ if (this_handler != UACPI_NULL) {
+ ret = UACPI_STATUS_ALREADY_EXISTS;
+ goto out;
+ }
+
+ new_handler = uacpi_kernel_alloc(sizeof(*new_handler));
+ if (new_handler == UACPI_NULL) {
+ ret = UACPI_STATUS_OUT_OF_MEMORY;
+ goto out;
+ }
+ uacpi_shareable_init(new_handler);
+
+ new_handler->next = handlers->head;
+ new_handler->space = space;
+ new_handler->user_context = handler_context;
+ new_handler->callback = handler;
+ new_handler->regions = UACPI_NULL;
+ new_handler->flags = flags;
+ handlers->head = new_handler;
+
+ iter_ctx.handler = new_handler;
+ iter_ctx.action = OPREGION_ITER_ACTION_INSTALL;
+
+ uacpi_namespace_do_for_each_child(
+ device_node, do_install_or_uninstall_handler, UACPI_NULL,
+ UACPI_OBJECT_ANY_BIT, UACPI_MAX_DEPTH_ANY, UACPI_SHOULD_LOCK_NO,
+ UACPI_PERMANENT_ONLY_YES, &iter_ctx
+ );
+
+ if (!space_needs_reg(space))
+ goto out;
+
+ /*
+ * Installing an early address space handler, obviously not possible to
+ * execute any _REG methods here. Just return and hope that it is either
+ * a global address space handler, or a handler installed by a user who
+ * will run uacpi_reg_all_opregions manually after loading/initializing
+ * the namespace.
+ */
+ if (g_uacpi_rt_ctx.init_level < UACPI_INIT_LEVEL_NAMESPACE_LOADED)
+ goto out;
+
+ // Init level is NAMESPACE_INITIALIZED, so we can safely run _REG now
+ ret = reg_or_unreg_all_opregions(
+ device_node, space, ACPI_REG_CONNECT
+ );
+
+out:
+ uacpi_namespace_write_unlock();
+ uacpi_recursive_lock_release(&g_opregion_lock);
+ return ret;
+}
+
+uacpi_status uacpi_install_address_space_handler(
+ uacpi_namespace_node *device_node, enum uacpi_address_space space,
+ uacpi_region_handler handler, uacpi_handle handler_context
+)
+{
+ return uacpi_install_address_space_handler_with_flags(
+ device_node, space, handler, handler_context, 0
+ );
+}
+
+uacpi_status uacpi_uninstall_address_space_handler(
+ uacpi_namespace_node *device_node,
+ enum uacpi_address_space space
+)
+{
+ uacpi_status ret;
+ uacpi_address_space_handlers *handlers;
+ uacpi_address_space_handler *handler = UACPI_NULL, *prev_handler;
+ struct opregion_iter_ctx iter_ctx;
+
+ ret = uacpi_recursive_lock_acquire(&g_opregion_lock);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ ret = uacpi_namespace_write_lock();
+ if (uacpi_unlikely_error(ret)) {
+ uacpi_recursive_lock_release(&g_opregion_lock);
+ return ret;
+ }
+
+ handlers = extract_handlers(device_node);
+ if (uacpi_unlikely(handlers == UACPI_NULL)) {
+ ret = UACPI_STATUS_INVALID_ARGUMENT;
+ goto out;
+ }
+
+ handler = find_handler(handlers, space);
+ if (uacpi_unlikely(handler == UACPI_NULL)) {
+ ret = UACPI_STATUS_NO_HANDLER;
+ goto out;
+ }
+
+ iter_ctx.handler = handler;
+ iter_ctx.action = OPREGION_ITER_ACTION_UNINSTALL;
+
+ uacpi_namespace_do_for_each_child(
+ device_node, do_install_or_uninstall_handler, UACPI_NULL,
+ UACPI_OBJECT_ANY_BIT, UACPI_MAX_DEPTH_ANY, UACPI_SHOULD_LOCK_NO,
+ UACPI_PERMANENT_ONLY_NO, &iter_ctx
+ );
+
+ prev_handler = handlers->head;
+
+ // Are we the last linked handler?
+ if (prev_handler == handler) {
+ handlers->head = handler->next;
+ goto out_unreg;
+ }
+
+ // Nope, we're somewhere in the middle. Do a search.
+ while (prev_handler) {
+ if (prev_handler->next == handler) {
+ prev_handler->next = handler->next;
+ goto out;
+ }
+
+ prev_handler = prev_handler->next;
+ }
+
+out_unreg:
+ if (space_needs_reg(space))
+ reg_or_unreg_all_opregions(device_node, space, ACPI_REG_DISCONNECT);
+
+out:
+ if (handler != UACPI_NULL)
+ uacpi_address_space_handler_unref(handler);
+
+ uacpi_namespace_write_unlock();
+ uacpi_recursive_lock_release(&g_opregion_lock);
+ return ret;
+}
+
+uacpi_status uacpi_initialize_opregion_node(uacpi_namespace_node *node)
+{
+ uacpi_status ret;
+ uacpi_namespace_node *parent = node->parent;
+ uacpi_operation_region *region;
+ uacpi_address_space_handlers *handlers;
+ uacpi_address_space_handler *handler;
+
+ ret = upgrade_to_opregion_lock();
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ region = uacpi_namespace_node_get_object(node)->op_region;
+ ret = UACPI_STATUS_NOT_FOUND;
+
+ while (parent) {
+ handlers = uacpi_node_get_address_space_handlers(parent);
+ if (handlers != UACPI_NULL) {
+ handler = find_handler(handlers, region->space);
+
+ if (handler != UACPI_NULL) {
+ region_install_handler(node, handler);
+ ret = UACPI_STATUS_OK;
+ break;
+ }
+ }
+
+ parent = parent->parent;
+ }
+
+ if (ret != UACPI_STATUS_OK)
+ goto out;
+ if (!space_needs_reg(region->space))
+ goto out;
+ if (uacpi_get_current_init_level() < UACPI_INIT_LEVEL_NAMESPACE_LOADED)
+ goto out;
+
+ if (region_run_reg(node, ACPI_REG_CONNECT) != UACPI_STATUS_NOT_FOUND)
+ region->state_flags |= UACPI_OP_REGION_STATE_REG_EXECUTED;
+
+out:
+ uacpi_recursive_lock_release(&g_opregion_lock);
+ return ret;
+}
+
+uacpi_bool uacpi_is_buffer_access_address_space(uacpi_address_space space)
+{
+ switch (space) {
+ case UACPI_ADDRESS_SPACE_SMBUS:
+ case UACPI_ADDRESS_SPACE_IPMI:
+ case UACPI_ADDRESS_SPACE_GENERIC_SERIAL_BUS:
+ case UACPI_ADDRESS_SPACE_PRM:
+ case UACPI_ADDRESS_SPACE_FFIXEDHW:
+ return UACPI_TRUE;
+ default:
+ return UACPI_FALSE;
+ }
+}
+
+static uacpi_bool space_needs_bounds_checking(uacpi_address_space space)
+{
+ return !uacpi_is_buffer_access_address_space(space) &&
+ space != UACPI_ADDRESS_SPACE_GENERAL_PURPOSE_IO;
+}
+
+uacpi_status uacpi_dispatch_opregion_io(
+ uacpi_field_unit *field, uacpi_u32 offset, uacpi_region_op op,
+ union uacpi_opregion_io_data data
+)
+{
+ uacpi_status ret;
+ uacpi_object *obj;
+ uacpi_operation_region *region;
+ uacpi_address_space_handler *handler;
+ uacpi_address_space space;
+ uacpi_u64 abs_offset, offset_end = offset;
+ uacpi_bool is_oob = UACPI_FALSE;
+ uacpi_region_op orig_op = op;
+
+ union {
+ uacpi_region_rw_data rw;
+ uacpi_region_pcc_send_data pcc;
+ uacpi_region_gpio_rw_data gpio;
+ uacpi_region_ipmi_rw_data ipmi;
+ uacpi_region_ffixedhw_rw_data ffixedhw;
+ uacpi_region_prm_rw_data prm;
+ uacpi_region_serial_rw_data serial;
+ } handler_data;
+
+ ret = upgrade_to_opregion_lock();
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ ret = uacpi_opregion_attach(field->region);
+ if (uacpi_unlikely_error(ret)) {
+ uacpi_trace_region_error(
+ field->region, "unable to attach", ret
+ );
+ goto out;
+ }
+
+ obj = uacpi_namespace_node_get_object_typed(
+ field->region, UACPI_OBJECT_OPERATION_REGION_BIT
+ );
+ if (uacpi_unlikely(obj == UACPI_NULL)) {
+ ret = UACPI_STATUS_INVALID_ARGUMENT;
+ goto out;
+ }
+
+ region = obj->op_region;
+ space = region->space;
+ handler = region->handler;
+
+ abs_offset = region->offset + offset;
+ offset_end += field->access_width_bytes;
+
+ if (uacpi_likely(space_needs_bounds_checking(region->space)))
+ is_oob = region->length < offset_end || abs_offset < offset;
+ if (uacpi_unlikely(is_oob)) {
+ const uacpi_char *path;
+
+ path = uacpi_namespace_node_generate_absolute_path(field->region);
+ uacpi_error(
+ "out-of-bounds access to opregion %s[0x%"UACPI_PRIX64"->"
+ "0x%"UACPI_PRIX64"] at 0x%"UACPI_PRIX64" (idx=%u, width=%d)\n",
+ path, UACPI_FMT64(region->offset),
+ UACPI_FMT64(region->offset + region->length),
+ UACPI_FMT64(abs_offset), offset, field->access_width_bytes
+ );
+ uacpi_free_dynamic_string(path);
+ ret = UACPI_STATUS_AML_OUT_OF_BOUNDS_INDEX;
+ goto out;
+ }
+
+ handler_data.rw.region_context = region->user_context;
+ handler_data.rw.handler_context = handler->user_context;
+
+ switch (region->space) {
+ case UACPI_ADDRESS_SPACE_PCC: {
+ uacpi_u8 *cursor;
+
+ cursor = region->internal_buffer + offset;
+
+ /*
+ * Reads from PCC just return the current contents of the internal
+ * buffer.
+ */
+ if (op == UACPI_REGION_OP_READ) {
+ uacpi_memcpy_zerout(
+ data.integer, cursor, sizeof(*data.integer),
+ field->access_width_bytes
+ );
+ goto io_done;
+ }
+
+ uacpi_memcpy(cursor, data.integer, field->access_width_bytes);
+
+ /*
+ * Dispatch a PCC send command if this was a write to the command field
+ *
+ * ACPI 6.5: 14.3. Extended PCC Subspace Shared Memory Region
+ */
+ if (offset >= 12 && offset < 16) {
+ uacpi_memzero(&handler_data.pcc.buffer, sizeof(handler_data.pcc.buffer));
+ handler_data.pcc.buffer.bytes = region->internal_buffer;
+ handler_data.pcc.buffer.length = region->length;
+
+ op = UACPI_REGION_OP_PCC_SEND;
+ break;
+ }
+
+ // No dispatch needed, IO is done
+ goto io_done;
+ }
+ case UACPI_ADDRESS_SPACE_GENERAL_PURPOSE_IO:
+ handler_data.gpio.pin_offset = field->pin_offset;
+ handler_data.gpio.num_pins = field->bit_length;
+ handler_data.gpio.value = *data.integer;
+
+ ret = uacpi_object_get_string_or_buffer(
+ field->connection, &handler_data.gpio.connection
+ );
+ if (uacpi_unlikely_error(ret))
+ goto io_done;
+
+ op = op == UACPI_REGION_OP_READ ?
+ UACPI_REGION_OP_GPIO_READ : UACPI_REGION_OP_GPIO_WRITE;
+ break;
+ case UACPI_ADDRESS_SPACE_IPMI:
+ handler_data.ipmi.in_out_message = data.buffer;
+ handler_data.ipmi.command = abs_offset;
+ op = UACPI_REGION_OP_IPMI_COMMAND;
+ break;
+ case UACPI_ADDRESS_SPACE_FFIXEDHW:
+ handler_data.ffixedhw.in_out_message = data.buffer;
+ handler_data.ffixedhw.command = abs_offset;
+ op = UACPI_REGION_OP_FFIXEDHW_COMMAND;
+ break;
+ case UACPI_ADDRESS_SPACE_PRM:
+ handler_data.prm.in_out_message = data.buffer;
+ op = UACPI_REGION_OP_PRM_COMMAND;
+ break;
+ case UACPI_ADDRESS_SPACE_GENERIC_SERIAL_BUS:
+ case UACPI_ADDRESS_SPACE_SMBUS:
+ ret = uacpi_object_get_string_or_buffer(
+ field->connection, &handler_data.serial.connection
+ );
+ if (uacpi_unlikely_error(ret))
+ goto io_done;
+
+ handler_data.serial.command = abs_offset;
+ handler_data.serial.in_out_buffer = data.buffer;
+ handler_data.serial.access_attribute = field->attributes;
+
+ switch (field->attributes) {
+ case UACPI_ACCESS_ATTRIBUTE_BYTES:
+ case UACPI_ACCESS_ATTRIBUTE_RAW_BYTES:
+ case UACPI_ACCESS_ATTRIBUTE_RAW_PROCESS_BYTES:
+ handler_data.serial.access_length = field->access_length;
+ break;
+ default:
+ handler_data.serial.access_length = 0;
+ }
+
+ op = op == UACPI_REGION_OP_READ ?
+ UACPI_REGION_OP_SERIAL_READ : UACPI_REGION_OP_SERIAL_WRITE;
+ break;
+ default:
+ handler_data.rw.byte_width = field->access_width_bytes;
+ handler_data.rw.offset = abs_offset;
+ handler_data.rw.value = *data.integer;
+ break;
+ }
+
+ uacpi_object_ref(obj);
+ uacpi_namespace_write_unlock();
+
+ ret = handler->callback(op, &handler_data);
+
+ uacpi_namespace_write_lock();
+ uacpi_object_unref(obj);
+
+io_done:
+ if (uacpi_unlikely_error(ret)) {
+ uacpi_trace_region_error(field->region, "unable to perform IO", ret);
+ goto out;
+ }
+
+ if (orig_op == UACPI_REGION_OP_READ) {
+ switch (region->space) {
+ case UACPI_ADDRESS_SPACE_PCC:
+ case UACPI_ADDRESS_SPACE_IPMI:
+ case UACPI_ADDRESS_SPACE_FFIXEDHW:
+ case UACPI_ADDRESS_SPACE_PRM:
+ case UACPI_ADDRESS_SPACE_GENERIC_SERIAL_BUS:
+ case UACPI_ADDRESS_SPACE_SMBUS:
+ break;
+ case UACPI_ADDRESS_SPACE_GENERAL_PURPOSE_IO:
+ *data.integer = handler_data.gpio.value;
+ break;
+ default:
+ *data.integer = handler_data.rw.value;
+ break;
+ }
+ }
+
+ trace_region_io(field, space, abs_offset, orig_op, data);
+
+out:
+ uacpi_recursive_lock_release(&g_opregion_lock);
+ return ret;
+}
+
+#endif // !UACPI_BAREBONES_MODE
diff --git a/sys/dev/acpi/uacpi/osi.c b/sys/dev/acpi/uacpi/osi.c
new file mode 100644
index 0000000..0940261
--- /dev/null
+++ b/sys/dev/acpi/uacpi/osi.c
@@ -0,0 +1,388 @@
+#include <uacpi/platform/atomic.h>
+#include <uacpi/internal/osi.h>
+#include <uacpi/internal/helpers.h>
+#include <uacpi/internal/stdlib.h>
+#include <uacpi/internal/utilities.h>
+#include <uacpi/internal/mutex.h>
+#include <uacpi/kernel_api.h>
+
+#ifndef UACPI_BAREBONES_MODE
+
+struct registered_interface {
+ const uacpi_char *name;
+ uacpi_u8 weight;
+ uacpi_u8 kind;
+
+ // Only applicable for predefined host interfaces
+ uacpi_u8 host_type;
+
+ // Only applicable for predefined interfaces
+ uacpi_u8 disabled : 1;
+ uacpi_u8 dynamic : 1;
+
+ struct registered_interface *next;
+};
+
+static uacpi_handle interface_mutex;
+static struct registered_interface *registered_interfaces;
+static uacpi_interface_handler interface_handler;
+static uacpi_u32 latest_queried_interface;
+
+#define WINDOWS(string, interface) \
+ { \
+ .name = "Windows "string, \
+ .weight = UACPI_VENDOR_INTERFACE_WINDOWS_##interface, \
+ .kind = UACPI_INTERFACE_KIND_VENDOR, \
+ .host_type = 0, \
+ .disabled = 0, \
+ .dynamic = 0, \
+ .next = UACPI_NULL \
+ }
+
+#define HOST_FEATURE(string, type) \
+ { \
+ .name = string, \
+ .weight = 0, \
+ .kind = UACPI_INTERFACE_KIND_FEATURE, \
+ .host_type = UACPI_HOST_INTERFACE_##type, \
+ .disabled = 1, \
+ .dynamic = 0, \
+ .next = UACPI_NULL, \
+ }
+
+static struct registered_interface predefined_interfaces[] = {
+ // Vendor strings
+ WINDOWS("2000", 2000),
+ WINDOWS("2001", XP),
+ WINDOWS("2001 SP1", XP_SP1),
+ WINDOWS("2001.1", SERVER_2003),
+ WINDOWS("2001 SP2", XP_SP2),
+ WINDOWS("2001.1 SP1", SERVER_2003_SP1),
+ WINDOWS("2006", VISTA),
+ WINDOWS("2006.1", SERVER_2008),
+ WINDOWS("2006 SP1", VISTA_SP1),
+ WINDOWS("2006 SP2", VISTA_SP2),
+ WINDOWS("2009", 7),
+ WINDOWS("2012", 8),
+ WINDOWS("2013", 8_1),
+ WINDOWS("2015", 10),
+ WINDOWS("2016", 10_RS1),
+ WINDOWS("2017", 10_RS2),
+ WINDOWS("2017.2", 10_RS3),
+ WINDOWS("2018", 10_RS4),
+ WINDOWS("2018.2", 10_RS5),
+ WINDOWS("2019", 10_19H1),
+ WINDOWS("2020", 10_20H1),
+ WINDOWS("2021", 11),
+ WINDOWS("2022", 11_22H2),
+
+ // Feature strings
+ HOST_FEATURE("Module Device", MODULE_DEVICE),
+ HOST_FEATURE("Processor Device", PROCESSOR_DEVICE),
+ HOST_FEATURE("3.0 Thermal Model", 3_0_THERMAL_MODEL),
+ HOST_FEATURE("3.0 _SCP Extensions", 3_0_SCP_EXTENSIONS),
+ HOST_FEATURE("Processor Aggregator Device", PROCESSOR_AGGREGATOR_DEVICE),
+
+ // Interpreter features
+ { .name = "Extended Address Space Descriptor" },
+};
+
+uacpi_status uacpi_initialize_interfaces(void)
+{
+ uacpi_size i;
+
+ registered_interfaces = &predefined_interfaces[0];
+
+ interface_mutex = uacpi_kernel_create_mutex();
+ if (uacpi_unlikely(interface_mutex == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ for (i = 0; i < (UACPI_ARRAY_SIZE(predefined_interfaces) - 1); ++i)
+ predefined_interfaces[i].next = &predefined_interfaces[i + 1];
+
+ return UACPI_STATUS_OK;
+}
+
+void uacpi_deinitialize_interfaces(void)
+{
+ struct registered_interface *iface, *next_iface = registered_interfaces;
+
+ while (next_iface) {
+ iface = next_iface;
+ next_iface = iface->next;
+
+ iface->next = UACPI_NULL;
+
+ if (iface->dynamic) {
+ uacpi_free_dynamic_string(iface->name);
+ uacpi_free(iface, sizeof(*iface));
+ continue;
+ }
+
+ // Only features are disabled by default
+ iface->disabled = iface->kind == UACPI_INTERFACE_KIND_FEATURE ?
+ UACPI_TRUE : UACPI_FALSE;
+ }
+
+ if (interface_mutex)
+ uacpi_kernel_free_mutex(interface_mutex);
+
+ interface_mutex = UACPI_NULL;
+ interface_handler = UACPI_NULL;
+ latest_queried_interface = 0;
+ registered_interfaces = UACPI_NULL;
+}
+
+uacpi_vendor_interface uacpi_latest_queried_vendor_interface(void)
+{
+ return uacpi_atomic_load32(&latest_queried_interface);
+}
+
+static struct registered_interface *find_interface_unlocked(
+ const uacpi_char *name
+)
+{
+ struct registered_interface *interface = registered_interfaces;
+
+ while (interface) {
+ if (uacpi_strcmp(interface->name, name) == 0)
+ return interface;
+
+ interface = interface->next;
+ }
+
+ return UACPI_NULL;
+}
+
+static struct registered_interface *find_host_interface_unlocked(
+ uacpi_host_interface type
+)
+{
+ struct registered_interface *interface = registered_interfaces;
+
+ while (interface) {
+ if (interface->host_type == type)
+ return interface;
+
+ interface = interface->next;
+ }
+
+ return UACPI_NULL;
+}
+
+uacpi_status uacpi_install_interface(
+ const uacpi_char *name, uacpi_interface_kind kind
+)
+{
+ struct registered_interface *interface;
+ uacpi_status ret;
+ uacpi_char *name_copy;
+ uacpi_size name_size;
+
+ UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_SUBSYSTEM_INITIALIZED);
+
+ ret = uacpi_acquire_native_mutex(interface_mutex);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ interface = find_interface_unlocked(name);
+ if (interface != UACPI_NULL) {
+ if (interface->disabled)
+ interface->disabled = UACPI_FALSE;
+
+ ret = UACPI_STATUS_ALREADY_EXISTS;
+ goto out;
+ }
+
+ interface = uacpi_kernel_alloc(sizeof(*interface));
+ if (uacpi_unlikely(interface == UACPI_NULL)) {
+ ret = UACPI_STATUS_OUT_OF_MEMORY;
+ goto out;
+ }
+
+ name_size = uacpi_strlen(name) + 1;
+ name_copy = uacpi_kernel_alloc(name_size);
+ if (uacpi_unlikely(name_copy == UACPI_NULL)) {
+ uacpi_free(interface, sizeof(*interface));
+ ret = UACPI_STATUS_OUT_OF_MEMORY;
+ goto out;
+ }
+
+ uacpi_memcpy(name_copy, name, name_size);
+ interface->name = name_copy;
+ interface->weight = 0;
+ interface->kind = kind;
+ interface->host_type = 0;
+ interface->disabled = 0;
+ interface->dynamic = 1;
+ interface->next = registered_interfaces;
+ registered_interfaces = interface;
+
+out:
+ uacpi_release_native_mutex(interface_mutex);
+ return ret;
+}
+
+uacpi_status uacpi_uninstall_interface(const uacpi_char *name)
+{
+ struct registered_interface *cur, *prev;
+ uacpi_status ret;
+
+ UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_SUBSYSTEM_INITIALIZED);
+
+ ret = uacpi_acquire_native_mutex(interface_mutex);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ cur = registered_interfaces;
+ prev = cur;
+
+ ret = UACPI_STATUS_NOT_FOUND;
+ while (cur) {
+ if (uacpi_strcmp(cur->name, name) != 0) {
+ prev = cur;
+ cur = cur->next;
+ continue;
+ }
+
+ if (cur->dynamic) {
+ if (prev == cur) {
+ registered_interfaces = cur->next;
+ } else {
+ prev->next = cur->next;
+ }
+
+ uacpi_release_native_mutex(interface_mutex);
+ uacpi_free_dynamic_string(cur->name);
+ uacpi_free(cur, sizeof(*cur));
+ return UACPI_STATUS_OK;
+ }
+
+ /*
+ * If this interface was already disabled, pretend we didn't actually
+ * find it and keep ret as UACPI_STATUS_NOT_FOUND. The fact that it's
+ * still in the registered list is an implementation detail of
+ * predefined interfaces.
+ */
+ if (!cur->disabled) {
+ cur->disabled = UACPI_TRUE;
+ ret = UACPI_STATUS_OK;
+ }
+
+ break;
+ }
+
+ uacpi_release_native_mutex(interface_mutex);
+ return ret;
+}
+
+static uacpi_status configure_host_interface(
+ uacpi_host_interface type, uacpi_bool enabled
+)
+{
+ struct registered_interface *interface;
+ uacpi_status ret;
+
+ UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_SUBSYSTEM_INITIALIZED);
+
+ ret = uacpi_acquire_native_mutex(interface_mutex);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ interface = find_host_interface_unlocked(type);
+ if (interface == UACPI_NULL) {
+ ret = UACPI_STATUS_NOT_FOUND;
+ goto out;
+ }
+
+ interface->disabled = !enabled;
+out:
+ uacpi_release_native_mutex(interface_mutex);
+ return ret;
+}
+
+uacpi_status uacpi_enable_host_interface(uacpi_host_interface type)
+{
+ return configure_host_interface(type, UACPI_TRUE);
+}
+
+uacpi_status uacpi_disable_host_interface(uacpi_host_interface type)
+{
+ return configure_host_interface(type, UACPI_FALSE);
+}
+
+uacpi_status uacpi_set_interface_query_handler(
+ uacpi_interface_handler handler
+)
+{
+ uacpi_status ret;
+
+ UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_SUBSYSTEM_INITIALIZED);
+
+ ret = uacpi_acquire_native_mutex(interface_mutex);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ if (interface_handler != UACPI_NULL && handler != UACPI_NULL) {
+ ret = UACPI_STATUS_ALREADY_EXISTS;
+ goto out;
+ }
+
+ interface_handler = handler;
+out:
+ uacpi_release_native_mutex(interface_mutex);
+ return ret;
+}
+
+uacpi_status uacpi_bulk_configure_interfaces(
+ uacpi_interface_action action, uacpi_interface_kind kind
+)
+{
+ uacpi_status ret;
+ struct registered_interface *interface;
+
+ UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_SUBSYSTEM_INITIALIZED);
+
+ ret = uacpi_acquire_native_mutex(interface_mutex);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ interface = registered_interfaces;
+ while (interface) {
+ if (kind & interface->kind)
+ interface->disabled = (action == UACPI_INTERFACE_ACTION_DISABLE);
+
+ interface = interface->next;
+ }
+
+ uacpi_release_native_mutex(interface_mutex);
+ return ret;
+}
+
+uacpi_status uacpi_handle_osi(const uacpi_char *string, uacpi_bool *out_value)
+{
+ uacpi_status ret;
+ struct registered_interface *interface;
+ uacpi_bool is_supported = UACPI_FALSE;
+
+ ret = uacpi_acquire_native_mutex(interface_mutex);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ interface = find_interface_unlocked(string);
+ if (interface == UACPI_NULL)
+ goto out;
+
+ if (interface->weight > latest_queried_interface)
+ uacpi_atomic_store32(&latest_queried_interface, interface->weight);
+
+ is_supported = !interface->disabled;
+ if (interface_handler)
+ is_supported = interface_handler(string, is_supported);
+out:
+ uacpi_release_native_mutex(interface_mutex);
+ *out_value = is_supported;
+ return UACPI_STATUS_OK;
+}
+
+#endif // !UACPI_BAREBONES_MODE
diff --git a/sys/dev/acpi/uacpi/registers.c b/sys/dev/acpi/uacpi/registers.c
new file mode 100644
index 0000000..a52ce97
--- /dev/null
+++ b/sys/dev/acpi/uacpi/registers.c
@@ -0,0 +1,572 @@
+#include <uacpi/internal/registers.h>
+#include <uacpi/internal/stdlib.h>
+#include <uacpi/internal/context.h>
+#include <uacpi/internal/io.h>
+#include <uacpi/internal/log.h>
+#include <uacpi/platform/atomic.h>
+#include <uacpi/acpi.h>
+
+#ifndef UACPI_BAREBONES_MODE
+
+static uacpi_handle g_reg_lock;
+
+enum register_kind {
+ REGISTER_KIND_GAS,
+ REGISTER_KIND_IO,
+};
+
+enum register_access_kind {
+ REGISTER_ACCESS_KIND_PRESERVE,
+ REGISTER_ACCESS_KIND_WRITE_TO_CLEAR,
+ REGISTER_ACCESS_KIND_NORMAL,
+};
+
+struct register_spec {
+ uacpi_u8 kind;
+ uacpi_u8 access_kind;
+ uacpi_u8 access_width; // only REGISTER_KIND_IO
+ void *accessors[2];
+ uacpi_u64 write_only_mask;
+ uacpi_u64 preserve_mask;
+};
+
+static const struct register_spec g_registers[UACPI_REGISTER_MAX + 1] = {
+ [UACPI_REGISTER_PM1_STS] = {
+ .kind = REGISTER_KIND_GAS,
+ .access_kind = REGISTER_ACCESS_KIND_WRITE_TO_CLEAR,
+ .accessors = {
+ &g_uacpi_rt_ctx.pm1a_status_blk,
+ &g_uacpi_rt_ctx.pm1b_status_blk,
+ },
+ .preserve_mask = ACPI_PM1_STS_IGN0_MASK,
+ },
+ [UACPI_REGISTER_PM1_EN] = {
+ .kind = REGISTER_KIND_GAS,
+ .access_kind = REGISTER_ACCESS_KIND_PRESERVE,
+ .accessors = {
+ &g_uacpi_rt_ctx.pm1a_enable_blk,
+ &g_uacpi_rt_ctx.pm1b_enable_blk,
+ },
+ },
+ [UACPI_REGISTER_PM1_CNT] = {
+ .kind = REGISTER_KIND_GAS,
+ .access_kind = REGISTER_ACCESS_KIND_PRESERVE,
+ .accessors = {
+ &g_uacpi_rt_ctx.fadt.x_pm1a_cnt_blk,
+ &g_uacpi_rt_ctx.fadt.x_pm1b_cnt_blk,
+ },
+ .write_only_mask = ACPI_PM1_CNT_SLP_EN_MASK |
+ ACPI_PM1_CNT_GBL_RLS_MASK,
+ .preserve_mask = ACPI_PM1_CNT_PRESERVE_MASK,
+ },
+ [UACPI_REGISTER_PM_TMR] = {
+ .kind = REGISTER_KIND_GAS,
+ .access_kind = REGISTER_ACCESS_KIND_PRESERVE,
+ .accessors = { &g_uacpi_rt_ctx.fadt.x_pm_tmr_blk, },
+ },
+ [UACPI_REGISTER_PM2_CNT] = {
+ .kind = REGISTER_KIND_GAS,
+ .access_kind = REGISTER_ACCESS_KIND_PRESERVE,
+ .accessors = { &g_uacpi_rt_ctx.fadt.x_pm2_cnt_blk, },
+ .preserve_mask = ACPI_PM2_CNT_PRESERVE_MASK,
+ },
+ [UACPI_REGISTER_SLP_CNT] = {
+ .kind = REGISTER_KIND_GAS,
+ .access_kind = REGISTER_ACCESS_KIND_PRESERVE,
+ .accessors = { &g_uacpi_rt_ctx.fadt.sleep_control_reg, },
+ .write_only_mask = ACPI_SLP_CNT_SLP_EN_MASK,
+ .preserve_mask = ACPI_SLP_CNT_PRESERVE_MASK,
+ },
+ [UACPI_REGISTER_SLP_STS] = {
+ .kind = REGISTER_KIND_GAS,
+ .access_kind = REGISTER_ACCESS_KIND_WRITE_TO_CLEAR,
+ .accessors = { &g_uacpi_rt_ctx.fadt.sleep_status_reg, },
+ .preserve_mask = ACPI_SLP_STS_PRESERVE_MASK,
+ },
+ [UACPI_REGISTER_RESET] = {
+ .kind = REGISTER_KIND_GAS,
+ .access_kind = REGISTER_ACCESS_KIND_NORMAL,
+ .accessors = { &g_uacpi_rt_ctx.fadt.reset_reg, },
+ },
+ [UACPI_REGISTER_SMI_CMD] = {
+ .kind = REGISTER_KIND_IO,
+ .access_kind = REGISTER_ACCESS_KIND_NORMAL,
+ .access_width = 1,
+ .accessors = { &g_uacpi_rt_ctx.fadt.smi_cmd, },
+ },
+};
+
+enum register_mapping_state {
+ REGISTER_MAPPING_STATE_NONE = 0,
+ REGISTER_MAPPING_STATE_NOT_NEEDED,
+ REGISTER_MAPPING_STATE_MAPPED,
+};
+
+struct register_mapping {
+ uacpi_mapped_gas mappings[2];
+ uacpi_u8 states[2];
+};
+static struct register_mapping g_register_mappings[UACPI_REGISTER_MAX + 1];
+
+static uacpi_status map_one(
+ const struct register_spec *spec, struct register_mapping *mapping,
+ uacpi_u8 idx
+)
+{
+ uacpi_status ret = UACPI_STATUS_OK;
+
+ if (mapping->states[idx] != REGISTER_MAPPING_STATE_NONE)
+ return ret;
+
+ if (spec->kind == REGISTER_KIND_GAS) {
+ struct acpi_gas *gas = spec->accessors[idx];
+
+ if (gas == UACPI_NULL || gas->address == 0) {
+ mapping->states[idx] = REGISTER_MAPPING_STATE_NOT_NEEDED;
+ return ret;
+ }
+
+ ret = uacpi_map_gas_noalloc(gas, &mapping->mappings[idx]);
+ } else {
+ struct acpi_gas temp_gas = { 0 };
+
+ if (idx != 0) {
+ mapping->states[idx] = REGISTER_MAPPING_STATE_NOT_NEEDED;
+ return ret;
+ }
+
+ temp_gas.address_space_id = UACPI_ADDRESS_SPACE_SYSTEM_IO;
+ temp_gas.address = *(uacpi_u32*)spec->accessors[0];
+ temp_gas.register_bit_width = spec->access_width * 8;
+
+ ret = uacpi_map_gas_noalloc(&temp_gas, &mapping->mappings[idx]);
+ }
+
+ if (uacpi_likely_success(ret))
+ mapping->states[idx] = REGISTER_MAPPING_STATE_MAPPED;
+
+ return ret;
+}
+
+static uacpi_status ensure_register_mapped(
+ const struct register_spec *spec, struct register_mapping *mapping
+)
+{
+ uacpi_status ret;
+ uacpi_bool needs_mapping = UACPI_FALSE;
+ uacpi_u8 state;
+ uacpi_cpu_flags flags;
+
+ state = uacpi_atomic_load8(&mapping->states[0]);
+ needs_mapping |= state == REGISTER_MAPPING_STATE_NONE;
+
+ state = uacpi_atomic_load8(&mapping->states[1]);
+ needs_mapping |= state == REGISTER_MAPPING_STATE_NONE;
+
+ if (!needs_mapping)
+ return UACPI_STATUS_OK;
+
+ flags = uacpi_kernel_lock_spinlock(g_reg_lock);
+
+ ret = map_one(spec, mapping, 0);
+ if (uacpi_unlikely_error(ret))
+ goto out;
+
+ ret = map_one(spec, mapping, 1);
+out:
+ uacpi_kernel_unlock_spinlock(g_reg_lock, flags);
+ return ret;
+}
+
+static uacpi_status get_reg(
+ uacpi_u8 idx, const struct register_spec **out_spec,
+ struct register_mapping **out_mapping
+)
+{
+ if (idx > UACPI_REGISTER_MAX)
+ return UACPI_STATUS_INVALID_ARGUMENT;
+
+ *out_spec = &g_registers[idx];
+ *out_mapping = &g_register_mappings[idx];
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status do_read_one(
+ struct register_mapping *mapping, uacpi_u8 idx, uacpi_u64 *out_value
+)
+{
+ if (mapping->states[idx] != REGISTER_MAPPING_STATE_MAPPED)
+ return UACPI_STATUS_OK;
+
+ return uacpi_gas_read_mapped(&mapping->mappings[idx], out_value);
+}
+
+static uacpi_status do_read_register(
+ const struct register_spec *reg, struct register_mapping *mapping,
+ uacpi_u64 *out_value
+)
+{
+ uacpi_status ret;
+ uacpi_u64 value0 = 0, value1 = 0;
+
+ ret = do_read_one(mapping, 0, &value0);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ ret = do_read_one(mapping, 1, &value1);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ *out_value = value0 | value1;
+ if (reg->write_only_mask)
+ *out_value &= ~reg->write_only_mask;
+
+ return UACPI_STATUS_OK;
+}
+
+uacpi_status uacpi_read_register(
+ enum uacpi_register reg_enum, uacpi_u64 *out_value
+)
+{
+ uacpi_status ret;
+ const struct register_spec *reg;
+ struct register_mapping *mapping;
+
+ UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_SUBSYSTEM_INITIALIZED);
+
+ ret = get_reg(reg_enum, &reg, &mapping);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ ret = ensure_register_mapped(reg, mapping);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ return do_read_register(reg, mapping, out_value);
+}
+
+static uacpi_status do_write_one(
+ struct register_mapping *mapping, uacpi_u8 idx, uacpi_u64 in_value
+)
+{
+ if (mapping->states[idx] != REGISTER_MAPPING_STATE_MAPPED)
+ return UACPI_STATUS_OK;
+
+ return uacpi_gas_write_mapped(&mapping->mappings[idx], in_value);
+}
+
+static uacpi_status do_write_register(
+ const struct register_spec *reg, struct register_mapping *mapping,
+ uacpi_u64 in_value
+)
+{
+ uacpi_status ret;
+
+ if (reg->preserve_mask) {
+ in_value &= ~reg->preserve_mask;
+
+ if (reg->access_kind == REGISTER_ACCESS_KIND_PRESERVE) {
+ uacpi_u64 data;
+
+ ret = do_read_register(reg, mapping, &data);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ in_value |= data & reg->preserve_mask;
+ }
+ }
+
+ ret = do_write_one(mapping, 0, in_value);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ return do_write_one(mapping, 1, in_value);
+}
+
+uacpi_status uacpi_write_register(
+ enum uacpi_register reg_enum, uacpi_u64 in_value
+)
+{
+ uacpi_status ret;
+ const struct register_spec *reg;
+ struct register_mapping *mapping;
+
+ UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_SUBSYSTEM_INITIALIZED);
+
+ ret = get_reg(reg_enum, &reg, &mapping);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ ret = ensure_register_mapped(reg, mapping);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ return do_write_register(reg, mapping, in_value);
+}
+
+uacpi_status uacpi_write_registers(
+ enum uacpi_register reg_enum, uacpi_u64 in_value0, uacpi_u64 in_value1
+)
+{
+ uacpi_status ret;
+ const struct register_spec *reg;
+ struct register_mapping *mapping;
+
+ UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_SUBSYSTEM_INITIALIZED);
+
+ ret = get_reg(reg_enum, &reg, &mapping);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ ret = ensure_register_mapped(reg, mapping);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ ret = do_write_one(mapping, 0, in_value0);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ return do_write_one(mapping, 1, in_value1);
+}
+
+struct register_field {
+ uacpi_u8 reg;
+ uacpi_u8 offset;
+ uacpi_u16 mask;
+};
+
+static const struct register_field g_fields[UACPI_REGISTER_FIELD_MAX + 1] = {
+ [UACPI_REGISTER_FIELD_TMR_STS] = {
+ .reg = UACPI_REGISTER_PM1_STS,
+ .offset = ACPI_PM1_STS_TMR_STS_IDX,
+ .mask = ACPI_PM1_STS_TMR_STS_MASK,
+ },
+ [UACPI_REGISTER_FIELD_BM_STS] = {
+ .reg = UACPI_REGISTER_PM1_STS,
+ .offset = ACPI_PM1_STS_BM_STS_IDX,
+ .mask = ACPI_PM1_STS_BM_STS_MASK,
+ },
+ [UACPI_REGISTER_FIELD_GBL_STS] = {
+ .reg = UACPI_REGISTER_PM1_STS,
+ .offset = ACPI_PM1_STS_GBL_STS_IDX,
+ .mask = ACPI_PM1_STS_GBL_STS_MASK,
+ },
+ [UACPI_REGISTER_FIELD_PWRBTN_STS] = {
+ .reg = UACPI_REGISTER_PM1_STS,
+ .offset = ACPI_PM1_STS_PWRBTN_STS_IDX,
+ .mask = ACPI_PM1_STS_PWRBTN_STS_MASK,
+ },
+ [UACPI_REGISTER_FIELD_SLPBTN_STS] = {
+ .reg = UACPI_REGISTER_PM1_STS,
+ .offset = ACPI_PM1_STS_SLPBTN_STS_IDX,
+ .mask = ACPI_PM1_STS_SLPBTN_STS_MASK,
+ },
+ [UACPI_REGISTER_FIELD_RTC_STS] = {
+ .reg = UACPI_REGISTER_PM1_STS,
+ .offset = ACPI_PM1_STS_RTC_STS_IDX,
+ .mask = ACPI_PM1_STS_RTC_STS_MASK,
+ },
+ [UACPI_REGISTER_FIELD_HWR_WAK_STS] = {
+ .reg = UACPI_REGISTER_SLP_STS,
+ .offset = ACPI_SLP_STS_WAK_STS_IDX,
+ .mask = ACPI_SLP_STS_WAK_STS_MASK,
+ },
+ [UACPI_REGISTER_FIELD_WAK_STS] = {
+ .reg = UACPI_REGISTER_PM1_STS,
+ .offset = ACPI_PM1_STS_WAKE_STS_IDX,
+ .mask = ACPI_PM1_STS_WAKE_STS_MASK,
+ },
+ [UACPI_REGISTER_FIELD_PCIEX_WAKE_STS] = {
+ .reg = UACPI_REGISTER_PM1_STS,
+ .offset = ACPI_PM1_STS_PCIEXP_WAKE_STS_IDX,
+ .mask = ACPI_PM1_STS_PCIEXP_WAKE_STS_MASK,
+ },
+ [UACPI_REGISTER_FIELD_TMR_EN] = {
+ .reg = UACPI_REGISTER_PM1_EN,
+ .offset = ACPI_PM1_EN_TMR_EN_IDX,
+ .mask = ACPI_PM1_EN_TMR_EN_MASK,
+ },
+ [UACPI_REGISTER_FIELD_GBL_EN] = {
+ .reg = UACPI_REGISTER_PM1_EN,
+ .offset = ACPI_PM1_EN_GBL_EN_IDX,
+ .mask = ACPI_PM1_EN_GBL_EN_MASK,
+ },
+ [UACPI_REGISTER_FIELD_PWRBTN_EN] = {
+ .reg = UACPI_REGISTER_PM1_EN,
+ .offset = ACPI_PM1_EN_PWRBTN_EN_IDX,
+ .mask = ACPI_PM1_EN_PWRBTN_EN_MASK,
+ },
+ [UACPI_REGISTER_FIELD_SLPBTN_EN] = {
+ .reg = UACPI_REGISTER_PM1_EN,
+ .offset = ACPI_PM1_EN_SLPBTN_EN_IDX,
+ .mask = ACPI_PM1_EN_SLPBTN_EN_MASK,
+ },
+ [UACPI_REGISTER_FIELD_RTC_EN] = {
+ .reg = UACPI_REGISTER_PM1_EN,
+ .offset = ACPI_PM1_EN_RTC_EN_IDX,
+ .mask = ACPI_PM1_EN_RTC_EN_MASK,
+ },
+ [UACPI_REGISTER_FIELD_PCIEXP_WAKE_DIS] = {
+ .reg = UACPI_REGISTER_PM1_EN,
+ .offset = ACPI_PM1_EN_PCIEXP_WAKE_DIS_IDX,
+ .mask = ACPI_PM1_EN_PCIEXP_WAKE_DIS_MASK,
+ },
+ [UACPI_REGISTER_FIELD_SCI_EN] = {
+ .reg = UACPI_REGISTER_PM1_CNT,
+ .offset = ACPI_PM1_CNT_SCI_EN_IDX,
+ .mask = ACPI_PM1_CNT_SCI_EN_MASK,
+ },
+ [UACPI_REGISTER_FIELD_BM_RLD] = {
+ .reg = UACPI_REGISTER_PM1_CNT,
+ .offset = ACPI_PM1_CNT_BM_RLD_IDX,
+ .mask = ACPI_PM1_CNT_BM_RLD_MASK,
+ },
+ [UACPI_REGISTER_FIELD_GBL_RLS] = {
+ .reg = UACPI_REGISTER_PM1_CNT,
+ .offset = ACPI_PM1_CNT_GBL_RLS_IDX,
+ .mask = ACPI_PM1_CNT_GBL_RLS_MASK,
+ },
+ [UACPI_REGISTER_FIELD_SLP_TYP] = {
+ .reg = UACPI_REGISTER_PM1_CNT,
+ .offset = ACPI_PM1_CNT_SLP_TYP_IDX,
+ .mask = ACPI_PM1_CNT_SLP_TYP_MASK,
+ },
+ [UACPI_REGISTER_FIELD_SLP_EN] = {
+ .reg = UACPI_REGISTER_PM1_CNT,
+ .offset = ACPI_PM1_CNT_SLP_EN_IDX,
+ .mask = ACPI_PM1_CNT_SLP_EN_MASK,
+ },
+ [UACPI_REGISTER_FIELD_HWR_SLP_TYP] = {
+ .reg = UACPI_REGISTER_SLP_CNT,
+ .offset = ACPI_SLP_CNT_SLP_TYP_IDX,
+ .mask = ACPI_SLP_CNT_SLP_TYP_MASK,
+ },
+ [UACPI_REGISTER_FIELD_HWR_SLP_EN] = {
+ .reg = UACPI_REGISTER_SLP_CNT,
+ .offset = ACPI_SLP_CNT_SLP_EN_IDX,
+ .mask = ACPI_SLP_CNT_SLP_EN_MASK,
+ },
+ [UACPI_REGISTER_FIELD_ARB_DIS] = {
+ .reg = UACPI_REGISTER_PM2_CNT,
+ .offset = ACPI_PM2_CNT_ARB_DIS_IDX,
+ .mask = ACPI_PM2_CNT_ARB_DIS_MASK,
+ },
+};
+
+uacpi_status uacpi_initialize_registers(void)
+{
+ g_reg_lock = uacpi_kernel_create_spinlock();
+ if (uacpi_unlikely(g_reg_lock == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ return UACPI_STATUS_OK;
+}
+
+void uacpi_deinitialize_registers(void)
+{
+ uacpi_u8 i;
+ struct register_mapping *mapping;
+
+ if (g_reg_lock != UACPI_NULL) {
+ uacpi_kernel_free_spinlock(g_reg_lock);
+ g_reg_lock = UACPI_NULL;
+ }
+
+ for (i = 0; i <= UACPI_REGISTER_MAX; ++i) {
+ mapping = &g_register_mappings[i];
+
+ if (mapping->states[0] == REGISTER_MAPPING_STATE_MAPPED)
+ uacpi_unmap_gas_nofree(&mapping->mappings[0]);
+ if (mapping->states[1] == REGISTER_MAPPING_STATE_MAPPED)
+ uacpi_unmap_gas_nofree(&mapping->mappings[1]);
+ }
+
+ uacpi_memzero(&g_register_mappings, sizeof(g_register_mappings));
+}
+
+uacpi_status uacpi_read_register_field(
+ enum uacpi_register_field field_enum, uacpi_u64 *out_value
+)
+{
+ uacpi_status ret;
+ uacpi_u8 field_idx = field_enum;
+ const struct register_field *field;
+ const struct register_spec *reg;
+ struct register_mapping *mapping;
+
+ UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_SUBSYSTEM_INITIALIZED);
+
+ if (uacpi_unlikely(field_idx > UACPI_REGISTER_FIELD_MAX))
+ return UACPI_STATUS_INVALID_ARGUMENT;
+
+ field = &g_fields[field_idx];
+ reg = &g_registers[field->reg];
+ mapping = &g_register_mappings[field->reg];
+
+ ret = ensure_register_mapped(reg, mapping);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ ret = do_read_register(reg, mapping, out_value);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ *out_value = (*out_value & field->mask) >> field->offset;
+ return UACPI_STATUS_OK;
+}
+
+uacpi_status uacpi_write_register_field(
+ enum uacpi_register_field field_enum, uacpi_u64 in_value
+)
+{
+ uacpi_status ret;
+ uacpi_u8 field_idx = field_enum;
+ const struct register_field *field;
+ const struct register_spec *reg;
+ struct register_mapping *mapping;
+
+ uacpi_u64 data;
+ uacpi_cpu_flags flags;
+
+ UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_SUBSYSTEM_INITIALIZED);
+
+ if (uacpi_unlikely(field_idx > UACPI_REGISTER_FIELD_MAX))
+ return UACPI_STATUS_INVALID_ARGUMENT;
+
+ field = &g_fields[field_idx];
+ reg = &g_registers[field->reg];
+ mapping = &g_register_mappings[field->reg];
+
+ ret = ensure_register_mapped(reg, mapping);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ in_value = (in_value << field->offset) & field->mask;
+
+ flags = uacpi_kernel_lock_spinlock(g_reg_lock);
+
+ if (reg->kind == REGISTER_ACCESS_KIND_WRITE_TO_CLEAR) {
+ if (in_value == 0) {
+ ret = UACPI_STATUS_OK;
+ goto out;
+ }
+
+ ret = do_write_register(reg, mapping, in_value);
+ goto out;
+ }
+
+ ret = do_read_register(reg, mapping, &data);
+ if (uacpi_unlikely_error(ret))
+ goto out;
+
+ data &= ~field->mask;
+ data |= in_value;
+
+ ret = do_write_register(reg, mapping, data);
+
+out:
+ uacpi_kernel_unlock_spinlock(g_reg_lock, flags);
+ return ret;
+}
+
+#endif // !UACPI_BAREBONES_MODE
diff --git a/sys/dev/acpi/uacpi/resources.c b/sys/dev/acpi/uacpi/resources.c
new file mode 100644
index 0000000..a9bcb82
--- /dev/null
+++ b/sys/dev/acpi/uacpi/resources.c
@@ -0,0 +1,2569 @@
+#include <uacpi/types.h>
+#include <uacpi/acpi.h>
+#include <uacpi/internal/resources.h>
+#include <uacpi/internal/stdlib.h>
+#include <uacpi/internal/utilities.h>
+#include <uacpi/internal/log.h>
+#include <uacpi/internal/namespace.h>
+#include <uacpi/uacpi.h>
+
+#ifndef UACPI_BAREBONES_MODE
+
+#define LARGE_RESOURCE_BASE (ACPI_RESOURCE_END_TAG + 1)
+#define L(x) (x + LARGE_RESOURCE_BASE)
+
+/*
+ * Map raw AML resource types to the internal enum, this also takes care of type
+ * sanitization by returning UACPI_AML_RESOURCE_INVALID for any unknown type.
+ */
+static const uacpi_u8 aml_resource_to_type[256] = {
+ // Small items
+ [ACPI_RESOURCE_IRQ] = UACPI_AML_RESOURCE_IRQ,
+ [ACPI_RESOURCE_DMA] = UACPI_AML_RESOURCE_DMA,
+ [ACPI_RESOURCE_START_DEPENDENT] = UACPI_AML_RESOURCE_START_DEPENDENT,
+ [ACPI_RESOURCE_END_DEPENDENT] = UACPI_AML_RESOURCE_END_DEPENDENT,
+ [ACPI_RESOURCE_IO] = UACPI_AML_RESOURCE_IO,
+ [ACPI_RESOURCE_FIXED_IO] = UACPI_AML_RESOURCE_FIXED_IO,
+ [ACPI_RESOURCE_FIXED_DMA] = UACPI_AML_RESOURCE_FIXED_DMA,
+ [ACPI_RESOURCE_VENDOR_TYPE0] = UACPI_AML_RESOURCE_VENDOR_TYPE0,
+ [ACPI_RESOURCE_END_TAG] = UACPI_AML_RESOURCE_END_TAG,
+
+ // Large items
+ [L(ACPI_RESOURCE_MEMORY24)] = UACPI_AML_RESOURCE_MEMORY24,
+ [L(ACPI_RESOURCE_GENERIC_REGISTER)] = UACPI_AML_RESOURCE_GENERIC_REGISTER,
+ [L(ACPI_RESOURCE_VENDOR_TYPE1)] = UACPI_AML_RESOURCE_VENDOR_TYPE1,
+ [L(ACPI_RESOURCE_MEMORY32)] = UACPI_AML_RESOURCE_MEMORY32,
+ [L(ACPI_RESOURCE_FIXED_MEMORY32)] = UACPI_AML_RESOURCE_FIXED_MEMORY32,
+ [L(ACPI_RESOURCE_ADDRESS32)] = UACPI_AML_RESOURCE_ADDRESS32,
+ [L(ACPI_RESOURCE_ADDRESS16)] = UACPI_AML_RESOURCE_ADDRESS16,
+ [L(ACPI_RESOURCE_EXTENDED_IRQ)] = UACPI_AML_RESOURCE_EXTENDED_IRQ,
+ [L(ACPI_RESOURCE_ADDRESS64_EXTENDED)] = UACPI_AML_RESOURCE_ADDRESS64_EXTENDED,
+ [L(ACPI_RESOURCE_ADDRESS64)] = UACPI_AML_RESOURCE_ADDRESS64,
+ [L(ACPI_RESOURCE_GPIO_CONNECTION)] = UACPI_AML_RESOURCE_GPIO_CONNECTION,
+ [L(ACPI_RESOURCE_PIN_FUNCTION)] = UACPI_AML_RESOURCE_PIN_FUNCTION,
+ [L(ACPI_RESOURCE_SERIAL_CONNECTION)] = UACPI_AML_RESOURCE_SERIAL_CONNECTION,
+ [L(ACPI_RESOURCE_PIN_CONFIGURATION)] = UACPI_AML_RESOURCE_PIN_CONFIGURATION,
+ [L(ACPI_RESOURCE_PIN_GROUP)] = UACPI_AML_RESOURCE_PIN_GROUP,
+ [L(ACPI_RESOURCE_PIN_GROUP_FUNCTION)] = UACPI_AML_RESOURCE_PIN_GROUP_FUNCTION,
+ [L(ACPI_RESOURCE_PIN_GROUP_CONFIGURATION)] = UACPI_AML_RESOURCE_PIN_GROUP_CONFIGURATION,
+ [L(ACPI_RESOURCE_CLOCK_INPUT)] = UACPI_AML_RESOURCE_CLOCK_INPUT,
+};
+
+static const uacpi_u8 type_to_aml_resource[] = {
+ [UACPI_AML_RESOURCE_IRQ] = ACPI_RESOURCE_IRQ,
+ [UACPI_AML_RESOURCE_DMA] = ACPI_RESOURCE_DMA,
+ [UACPI_AML_RESOURCE_START_DEPENDENT] = ACPI_RESOURCE_START_DEPENDENT,
+ [UACPI_AML_RESOURCE_END_DEPENDENT] = ACPI_RESOURCE_END_DEPENDENT,
+ [UACPI_AML_RESOURCE_IO] = ACPI_RESOURCE_IO,
+ [UACPI_AML_RESOURCE_FIXED_IO] = ACPI_RESOURCE_FIXED_IO,
+ [UACPI_AML_RESOURCE_FIXED_DMA] = ACPI_RESOURCE_FIXED_DMA,
+ [UACPI_AML_RESOURCE_VENDOR_TYPE0] = ACPI_RESOURCE_VENDOR_TYPE0,
+ [UACPI_AML_RESOURCE_END_TAG] = ACPI_RESOURCE_END_TAG,
+
+ // Large items
+ [UACPI_AML_RESOURCE_MEMORY24] = ACPI_RESOURCE_MEMORY24,
+ [UACPI_AML_RESOURCE_GENERIC_REGISTER] = ACPI_RESOURCE_GENERIC_REGISTER,
+ [UACPI_AML_RESOURCE_VENDOR_TYPE1] = ACPI_RESOURCE_VENDOR_TYPE1,
+ [UACPI_AML_RESOURCE_MEMORY32] = ACPI_RESOURCE_MEMORY32,
+ [UACPI_AML_RESOURCE_FIXED_MEMORY32] = ACPI_RESOURCE_FIXED_MEMORY32,
+ [UACPI_AML_RESOURCE_ADDRESS32] = ACPI_RESOURCE_ADDRESS32,
+ [UACPI_AML_RESOURCE_ADDRESS16] = ACPI_RESOURCE_ADDRESS16,
+ [UACPI_AML_RESOURCE_EXTENDED_IRQ] = ACPI_RESOURCE_EXTENDED_IRQ,
+ [UACPI_AML_RESOURCE_ADDRESS64_EXTENDED] = ACPI_RESOURCE_ADDRESS64_EXTENDED,
+ [UACPI_AML_RESOURCE_ADDRESS64] = ACPI_RESOURCE_ADDRESS64,
+ [UACPI_AML_RESOURCE_GPIO_CONNECTION] = ACPI_RESOURCE_GPIO_CONNECTION,
+ [UACPI_AML_RESOURCE_PIN_FUNCTION] = ACPI_RESOURCE_PIN_FUNCTION,
+ [UACPI_AML_RESOURCE_SERIAL_CONNECTION] = ACPI_RESOURCE_SERIAL_CONNECTION,
+ [UACPI_AML_RESOURCE_PIN_CONFIGURATION] = ACPI_RESOURCE_PIN_CONFIGURATION,
+ [UACPI_AML_RESOURCE_PIN_GROUP] = ACPI_RESOURCE_PIN_GROUP,
+ [UACPI_AML_RESOURCE_PIN_GROUP_FUNCTION] = ACPI_RESOURCE_PIN_GROUP_FUNCTION,
+ [UACPI_AML_RESOURCE_PIN_GROUP_CONFIGURATION] = ACPI_RESOURCE_PIN_GROUP_CONFIGURATION,
+ [UACPI_AML_RESOURCE_CLOCK_INPUT] = ACPI_RESOURCE_CLOCK_INPUT,
+};
+
+static const uacpi_u8 native_resource_to_type[UACPI_RESOURCE_TYPE_MAX + 1] = {
+ [UACPI_RESOURCE_TYPE_IRQ] = UACPI_AML_RESOURCE_IRQ,
+ [UACPI_RESOURCE_TYPE_EXTENDED_IRQ] = UACPI_AML_RESOURCE_EXTENDED_IRQ,
+ [UACPI_RESOURCE_TYPE_DMA] = UACPI_AML_RESOURCE_DMA,
+ [UACPI_RESOURCE_TYPE_FIXED_DMA] = UACPI_AML_RESOURCE_FIXED_DMA,
+ [UACPI_RESOURCE_TYPE_IO] = UACPI_AML_RESOURCE_IO,
+ [UACPI_RESOURCE_TYPE_FIXED_IO] = UACPI_AML_RESOURCE_FIXED_IO,
+ [UACPI_RESOURCE_TYPE_ADDRESS16] = UACPI_AML_RESOURCE_ADDRESS16,
+ [UACPI_RESOURCE_TYPE_ADDRESS32] = UACPI_AML_RESOURCE_ADDRESS32,
+ [UACPI_RESOURCE_TYPE_ADDRESS64] = UACPI_AML_RESOURCE_ADDRESS64,
+ [UACPI_RESOURCE_TYPE_ADDRESS64_EXTENDED] = UACPI_AML_RESOURCE_ADDRESS64_EXTENDED,
+ [UACPI_RESOURCE_TYPE_MEMORY24] = UACPI_AML_RESOURCE_MEMORY24,
+ [UACPI_RESOURCE_TYPE_MEMORY32] = UACPI_AML_RESOURCE_MEMORY32,
+ [UACPI_RESOURCE_TYPE_FIXED_MEMORY32] = UACPI_AML_RESOURCE_FIXED_MEMORY32,
+ [UACPI_RESOURCE_TYPE_START_DEPENDENT] = UACPI_AML_RESOURCE_START_DEPENDENT,
+ [UACPI_RESOURCE_TYPE_END_DEPENDENT] = UACPI_AML_RESOURCE_END_DEPENDENT,
+ [UACPI_RESOURCE_TYPE_VENDOR_SMALL] = UACPI_AML_RESOURCE_VENDOR_TYPE0,
+ [UACPI_RESOURCE_TYPE_VENDOR_LARGE] = UACPI_AML_RESOURCE_VENDOR_TYPE1,
+ [UACPI_RESOURCE_TYPE_GENERIC_REGISTER] = UACPI_AML_RESOURCE_GENERIC_REGISTER,
+ [UACPI_RESOURCE_TYPE_GPIO_CONNECTION] = UACPI_AML_RESOURCE_GPIO_CONNECTION,
+ [UACPI_RESOURCE_TYPE_SERIAL_I2C_CONNECTION] = UACPI_AML_RESOURCE_SERIAL_CONNECTION,
+ [UACPI_RESOURCE_TYPE_SERIAL_SPI_CONNECTION] = UACPI_AML_RESOURCE_SERIAL_CONNECTION,
+ [UACPI_RESOURCE_TYPE_SERIAL_UART_CONNECTION] = UACPI_AML_RESOURCE_SERIAL_CONNECTION,
+ [UACPI_RESOURCE_TYPE_SERIAL_CSI2_CONNECTION] = UACPI_AML_RESOURCE_SERIAL_CONNECTION,
+ [UACPI_RESOURCE_TYPE_PIN_FUNCTION] = UACPI_AML_RESOURCE_PIN_FUNCTION,
+ [UACPI_RESOURCE_TYPE_PIN_CONFIGURATION] = UACPI_AML_RESOURCE_PIN_CONFIGURATION,
+ [UACPI_RESOURCE_TYPE_PIN_GROUP] = UACPI_AML_RESOURCE_PIN_GROUP,
+ [UACPI_RESOURCE_TYPE_PIN_GROUP_FUNCTION] = UACPI_AML_RESOURCE_PIN_GROUP_FUNCTION,
+ [UACPI_RESOURCE_TYPE_PIN_GROUP_CONFIGURATION] = UACPI_AML_RESOURCE_PIN_GROUP_CONFIGURATION,
+ [UACPI_RESOURCE_TYPE_CLOCK_INPUT] = UACPI_AML_RESOURCE_CLOCK_INPUT,
+ [UACPI_RESOURCE_TYPE_END_TAG] = UACPI_AML_RESOURCE_END_TAG,
+};
+
+#define SMALL_ITEM_HEADER_SIZE sizeof(struct acpi_small_item)
+#define LARGE_ITEM_HEADER_SIZE sizeof(struct acpi_large_item)
+
+static const uacpi_u8 aml_resource_kind_to_header_size[2] = {
+ [UACPI_AML_RESOURCE_KIND_SMALL] = SMALL_ITEM_HEADER_SIZE,
+ [UACPI_AML_RESOURCE_KIND_LARGE] = LARGE_ITEM_HEADER_SIZE,
+};
+
+static uacpi_size aml_size_with_header(const struct uacpi_resource_spec *spec)
+{
+ return spec->aml_size +
+ aml_resource_kind_to_header_size[spec->resource_kind];
+}
+
+static uacpi_size extra_size_for_native_irq_or_dma(
+ const struct uacpi_resource_spec *spec, void *data, uacpi_size size
+)
+{
+ uacpi_u16 mask;
+ uacpi_u8 i, total_bits, num_bits = 0;
+
+ UACPI_UNUSED(size);
+
+ if (spec->type == UACPI_AML_RESOURCE_IRQ) {
+ struct acpi_resource_irq *irq = data;
+ mask = irq->irq_mask;
+ total_bits = 16;
+ } else {
+ struct acpi_resource_dma *dma = data;
+ mask = dma->channel_mask;
+ total_bits = 8;
+ }
+
+ for (i = 0; i < total_bits; ++i)
+ num_bits += !!(mask & (1 << i));
+
+ return num_bits;
+}
+
+static uacpi_size size_for_aml_irq(
+ const struct uacpi_resource_spec *spec, uacpi_resource *resource
+)
+{
+ uacpi_resource_irq *irq = &resource->irq;
+ uacpi_size size;
+
+ size = aml_size_with_header(spec);
+
+ switch (irq->length_kind) {
+ case UACPI_RESOURCE_LENGTH_KIND_FULL:
+ goto out_full;
+ case UACPI_RESOURCE_LENGTH_KIND_ONE_LESS:
+ case UACPI_RESOURCE_LENGTH_KIND_DONT_CARE:
+ if (irq->triggering != UACPI_TRIGGERING_EDGE)
+ goto out_full;
+ if (irq->polarity != UACPI_POLARITY_ACTIVE_HIGH)
+ goto out_full;
+ if (irq->sharing != UACPI_EXCLUSIVE)
+ goto out_full;
+
+ return size - 1;
+ }
+
+out_full:
+ if (uacpi_unlikely(irq->length_kind ==
+ UACPI_RESOURCE_LENGTH_KIND_ONE_LESS)) {
+ uacpi_warn("requested IRQ resource length is "
+ "not compatible with specified flags, corrected\n");
+ }
+
+ return size;
+}
+
+static uacpi_size size_for_aml_start_dependent(
+ const struct uacpi_resource_spec *spec, uacpi_resource *resource
+)
+{
+ uacpi_resource_start_dependent *start_dep = &resource->start_dependent;
+ uacpi_size size;
+
+ size = aml_size_with_header(spec);
+ switch (start_dep->length_kind) {
+ case UACPI_RESOURCE_LENGTH_KIND_FULL:
+ goto out_full;
+ case UACPI_RESOURCE_LENGTH_KIND_ONE_LESS:
+ case UACPI_RESOURCE_LENGTH_KIND_DONT_CARE:
+ if (start_dep->compatibility != UACPI_ACCEPTABLE)
+ goto out_full;
+ if (start_dep->performance != UACPI_ACCEPTABLE)
+ goto out_full;
+
+ return size - 1;
+ }
+
+out_full:
+ if (uacpi_unlikely(start_dep->length_kind ==
+ UACPI_RESOURCE_LENGTH_KIND_ONE_LESS)) {
+ uacpi_warn("requested StartDependentFn resource length is "
+ "not compatible with specified flags, corrected\n");
+ }
+
+ return size;
+}
+
+static uacpi_size extra_size_for_native_vendor(
+ const struct uacpi_resource_spec *spec, void *data, uacpi_size size
+)
+{
+ UACPI_UNUSED(spec);
+ UACPI_UNUSED(data);
+ return size;
+}
+
+static uacpi_size size_for_aml_vendor(
+ const struct uacpi_resource_spec *spec, uacpi_resource *resource
+)
+{
+ uacpi_size size = resource->vendor.length;
+
+ UACPI_UNUSED(spec);
+
+ if (size > 7 || resource->type == UACPI_RESOURCE_TYPE_VENDOR_LARGE) {
+ size += aml_resource_kind_to_header_size[
+ UACPI_AML_RESOURCE_KIND_LARGE
+ ];
+
+ if (uacpi_unlikely(resource->type != UACPI_RESOURCE_TYPE_VENDOR_LARGE)) {
+ uacpi_warn("vendor data too large for small descriptor (%zu), "
+ "correcting to large\n", size);
+ resource->type = UACPI_RESOURCE_TYPE_VENDOR_LARGE;
+ }
+ } else {
+ size += aml_resource_kind_to_header_size[
+ UACPI_AML_RESOURCE_KIND_SMALL
+ ];
+ }
+
+ return size;
+}
+
+static uacpi_size extra_size_for_resource_source(
+ uacpi_size base_size, uacpi_size reported_size
+)
+{
+ uacpi_size string_length;
+
+ if (reported_size <= base_size)
+ return 0;
+
+ /*
+ * The remainder of the descriptor minus the resource index field
+ */
+ string_length = (reported_size - base_size) - 1;
+ return UACPI_ALIGN_UP(string_length, sizeof(void*), uacpi_size);
+}
+
+static uacpi_size size_for_aml_resource_source(
+ uacpi_resource_source *source, uacpi_bool with_index
+)
+{
+ uacpi_size length = source->length;
+
+ if (uacpi_unlikely(length && !source->index_present)) {
+ uacpi_warn("resource declares no source index with non-empty "
+ "string (%zu bytes), corrected\n", length);
+ source->index_present = UACPI_TRUE;
+ }
+
+ // If index is included in the dynamic resource source, add it to the length
+ if (with_index)
+ length += source->index_present;
+
+ return length;
+}
+
+static uacpi_size extra_size_for_native_address_or_clock_input(
+ const struct uacpi_resource_spec *spec, void *data, uacpi_size size
+)
+{
+ UACPI_UNUSED(data);
+ return extra_size_for_resource_source(spec->aml_size, size);
+}
+
+static uacpi_size size_for_aml_address_or_clock_input(
+ const struct uacpi_resource_spec *spec, uacpi_resource *resource
+)
+{
+ uacpi_resource_source *source;
+ bool has_index = UACPI_TRUE;
+
+ switch (resource->type) {
+ case UACPI_RESOURCE_TYPE_ADDRESS16:
+ source = &resource->address16.source;
+ break;
+ case UACPI_RESOURCE_TYPE_ADDRESS32:
+ source = &resource->address32.source;
+ break;
+ case UACPI_RESOURCE_TYPE_ADDRESS64:
+ source = &resource->address64.source;
+ break;
+ case UACPI_RESOURCE_TYPE_CLOCK_INPUT:
+ source = &resource->clock_input.source;
+ has_index = UACPI_FALSE;
+ break;
+ default:
+ return 0;
+ }
+
+ return aml_size_with_header(spec) +
+ size_for_aml_resource_source(source, has_index);
+}
+
+static uacpi_size extra_size_for_extended_irq(
+ const struct uacpi_resource_spec *spec, void *data, uacpi_size size
+)
+{
+ struct acpi_resource_extended_irq *irq = data;
+ uacpi_size extra_size = 0;
+
+ extra_size += irq->num_irqs * sizeof(uacpi_u32);
+ extra_size += extra_size_for_resource_source(
+ spec->aml_size, size - extra_size
+ );
+
+ return extra_size;
+}
+
+static uacpi_size size_for_aml_extended_irq(
+ const struct uacpi_resource_spec *spec, uacpi_resource *resource
+)
+{
+ uacpi_resource_extended_irq *irq = &resource->extended_irq;
+ uacpi_size size;
+
+ size = aml_size_with_header(spec);
+ size += irq->num_irqs * 4;
+ size += size_for_aml_resource_source(&irq->source, UACPI_TRUE);
+
+ return size;
+}
+
+static uacpi_size extra_size_for_native_gpio_or_pins(
+ const struct uacpi_resource_spec *spec, void *data, uacpi_size size
+)
+{
+ uacpi_size pin_table_offset;
+
+ /*
+ * These resources pretend to have variable layout by declaring "offset"
+ * fields, but the layout is hardcoded and mandated by the spec to be
+ * very specific. We can use the offset numbers here to calculate the final
+ * length.
+ *
+ * For example, the layout of GPIO connection _always_ looks as follows:
+ * [0...22] -> fixed data
+ * [23...<source name offset - 1>] -> pin table
+ * [<source name offset>...<vendor data offset - 1>] -> source name
+ * [<vendor data offset>...<data offset + data length>] -> vendor data
+ */
+ switch (spec->type) {
+ case UACPI_AML_RESOURCE_GPIO_CONNECTION: {
+ struct acpi_resource_gpio_connection *gpio = data;
+ pin_table_offset = gpio->pin_table_offset;
+ break;
+ }
+
+ case UACPI_AML_RESOURCE_PIN_FUNCTION: {
+ struct acpi_resource_pin_function *pin = data;
+ pin_table_offset = pin->pin_table_offset;
+ break;
+ }
+
+ case UACPI_AML_RESOURCE_PIN_CONFIGURATION: {
+ struct acpi_resource_pin_configuration *config = data;
+ pin_table_offset = config->pin_table_offset;
+ break;
+ }
+
+ case UACPI_AML_RESOURCE_PIN_GROUP: {
+ struct acpi_resource_pin_group *group = data;
+ pin_table_offset = group->pin_table_offset;
+ break;
+ }
+
+ default:
+ return 0;
+ }
+
+ /*
+ * The size we get passed here does not include the header size because
+ * that's how resources are encoded. Subtract it here so that we get the
+ * correct final length.
+ */
+ return size - (pin_table_offset - LARGE_ITEM_HEADER_SIZE);
+}
+
+static uacpi_size size_for_aml_gpio_or_pins(
+ const struct uacpi_resource_spec *spec, uacpi_resource *resource
+)
+{
+ uacpi_size source_length, vendor_length, pin_table_length, size;
+
+ size = aml_size_with_header(spec);
+ switch (spec->type) {
+ case UACPI_AML_RESOURCE_GPIO_CONNECTION: {
+ uacpi_resource_gpio_connection *res = &resource->gpio_connection;
+ source_length = res->source.length;
+ pin_table_length = res->pin_table_length;
+ vendor_length = res->vendor_data_length;
+ break;
+ }
+
+ case UACPI_AML_RESOURCE_PIN_FUNCTION: {
+ uacpi_resource_pin_function *res = &resource->pin_function;
+ source_length = res->source.length;
+ pin_table_length = res->pin_table_length;
+ vendor_length = res->vendor_data_length;
+ break;
+ }
+
+ case UACPI_AML_RESOURCE_PIN_CONFIGURATION: {
+ uacpi_resource_pin_configuration *res = &resource->pin_configuration;
+ source_length = res->source.length;
+ pin_table_length = res->pin_table_length;
+ vendor_length = res->vendor_data_length;
+ break;
+ }
+
+ case UACPI_AML_RESOURCE_PIN_GROUP: {
+ uacpi_resource_pin_group *res = &resource->pin_group;
+ source_length = res->label.length;
+ pin_table_length = res->pin_table_length;
+ vendor_length = res->vendor_data_length;
+ break;
+ }
+
+ default:
+ return 0;
+ }
+
+ size += source_length;
+ size += pin_table_length * 2;
+ size += vendor_length;
+
+ return size;
+}
+
+static uacpi_size extra_size_for_native_pin_group(
+ const struct uacpi_resource_spec *spec, void *data, uacpi_size size
+)
+{
+ uacpi_size source_offset;
+
+ switch (spec->type) {
+ case UACPI_AML_RESOURCE_PIN_GROUP_FUNCTION: {
+ struct acpi_resource_pin_group_function *func = data;
+ source_offset = func->source_offset;
+ break;
+ }
+
+ case UACPI_AML_RESOURCE_PIN_GROUP_CONFIGURATION: {
+ struct acpi_resource_pin_group_configuration *config = data;
+ source_offset = config->source_offset;
+ break;
+ }
+
+ default:
+ return 0;
+ }
+
+ // Same logic as extra_size_for_native_gpio_or_pins
+ return size - (source_offset - LARGE_ITEM_HEADER_SIZE);
+}
+
+static uacpi_size size_for_aml_pin_group(
+ const struct uacpi_resource_spec *spec, uacpi_resource *resource
+)
+{
+ uacpi_size source_length, label_length, vendor_length, size;
+
+ size = aml_size_with_header(spec);
+ switch (spec->type) {
+ case UACPI_AML_RESOURCE_PIN_GROUP_FUNCTION: {
+ uacpi_resource_pin_group_function *res = &resource->pin_group_function;
+ source_length = res->source.length;
+ label_length = res->label.length;
+ vendor_length = res->vendor_data_length;
+ break;
+ }
+
+ case UACPI_AML_RESOURCE_PIN_GROUP_CONFIGURATION: {
+ uacpi_resource_pin_group_configuration *res;
+ res = &resource->pin_group_configuration;
+ source_length = res->source.length;
+ label_length = res->label.length;
+ vendor_length = res->vendor_data_length;
+ break;
+ }
+
+ default:
+ return 0;
+ }
+
+ size += source_length;
+ size += label_length;
+ size += vendor_length;
+
+ return size;
+}
+
+#define AML_SERIAL_RESOURCE_EXTRA_SIZE(type) \
+ (sizeof(struct acpi_resource_serial_##type) \
+ - sizeof(struct acpi_resource_serial))
+
+#define NATIVE_SERIAL_RESOURCE_EXTRA_SIZE(type) \
+ (sizeof(uacpi_resource_##type##_connection) \
+ - sizeof(uacpi_resource_serial_bus_common))
+
+static const uacpi_u8 aml_serial_resource_to_extra_aml_size
+[ACPI_SERIAL_TYPE_MAX + 1] = {
+ [ACPI_SERIAL_TYPE_I2C] = AML_SERIAL_RESOURCE_EXTRA_SIZE(i2c),
+ [ACPI_SERIAL_TYPE_SPI] = AML_SERIAL_RESOURCE_EXTRA_SIZE(spi),
+ [ACPI_SERIAL_TYPE_UART] = AML_SERIAL_RESOURCE_EXTRA_SIZE(uart),
+ [ACPI_SERIAL_TYPE_CSI2] = AML_SERIAL_RESOURCE_EXTRA_SIZE(csi2),
+};
+
+static const uacpi_u8 aml_serial_resource_to_extra_native_size
+[ACPI_SERIAL_TYPE_MAX + 1] = {
+ [ACPI_SERIAL_TYPE_I2C] = NATIVE_SERIAL_RESOURCE_EXTRA_SIZE(i2c),
+ [ACPI_SERIAL_TYPE_SPI] = NATIVE_SERIAL_RESOURCE_EXTRA_SIZE(spi),
+ [ACPI_SERIAL_TYPE_UART] = NATIVE_SERIAL_RESOURCE_EXTRA_SIZE(uart),
+ [ACPI_SERIAL_TYPE_CSI2] = NATIVE_SERIAL_RESOURCE_EXTRA_SIZE(csi2),
+};
+
+static uacpi_size extra_size_for_serial_connection(
+ const struct uacpi_resource_spec *spec, void *data, uacpi_size size
+)
+{
+ struct acpi_resource_serial *serial = data;
+ uacpi_size extra_bytes = size;
+
+ extra_bytes -= spec->aml_size;
+ extra_bytes -= aml_serial_resource_to_extra_aml_size[serial->type];
+ extra_bytes += aml_serial_resource_to_extra_native_size[serial->type];
+
+ return extra_bytes;
+}
+
+static uacpi_size aml_size_for_serial_connection(
+ const struct uacpi_resource_spec *spec, uacpi_resource *resource
+)
+{
+ uacpi_size size;
+ uacpi_resource_serial_bus_common *serial_bus = &resource->serial_bus_common;
+
+ size = aml_size_with_header(spec);
+ size += aml_serial_resource_to_extra_aml_size[serial_bus->type];
+ size += serial_bus->vendor_data_length;
+ size += serial_bus->source.length;
+
+ return size;
+}
+
+#define OP(short_code, ...) \
+{ \
+ .code = UACPI_RESOURCE_CONVERT_OPCODE_##short_code, \
+ __VA_ARGS__ \
+}
+
+#define END() OP(END)
+
+#define AML_O(short_aml_name, field) \
+ uacpi_offsetof(struct acpi_resource_##short_aml_name, field)
+
+#define AML_F(short_aml_name, field) \
+ .f1.aml_offset = AML_O(short_aml_name, field)
+
+#define NATIVE_O(short_name, field) \
+ uacpi_offsetof(uacpi_resource_##short_name, field)
+
+#define NATIVE_F(short_native_name, field) \
+ .f2.native_offset = NATIVE_O(short_native_name, field)
+
+#define IMM(value) .f3.imm = value
+#define ARG0(value) .f1.arg0 = (value)
+#define ARG1(value) .f2.arg1 = (value)
+#define ARG2(value) .f3.arg2 = (value)
+
+
+static const struct uacpi_resource_convert_instruction convert_irq_to_native[] = {
+ OP(PACKED_ARRAY_16, AML_F(irq, irq_mask), NATIVE_F(irq, irqs),
+ ARG2(NATIVE_O(irq, num_irqs))),
+ OP(SKIP_IF_AML_SIZE_LESS_THAN, ARG0(3), IMM(6)),
+ OP(SET_TO_IMM, NATIVE_F(irq, length_kind),
+ IMM(UACPI_RESOURCE_LENGTH_KIND_FULL)),
+ OP(BIT_FIELD_1, AML_F(irq, flags), NATIVE_F(irq, triggering), IMM(0)),
+ OP(BIT_FIELD_1, AML_F(irq, flags), NATIVE_F(irq, polarity), IMM(3)),
+ OP(BIT_FIELD_1, AML_F(irq, flags), NATIVE_F(irq, sharing), IMM(4)),
+ OP(BIT_FIELD_1, AML_F(irq, flags), NATIVE_F(irq, wake_capability), IMM(5)),
+ END(),
+ OP(SET_TO_IMM, NATIVE_F(irq, length_kind),
+ IMM(UACPI_RESOURCE_LENGTH_KIND_ONE_LESS)),
+ OP(SET_TO_IMM, NATIVE_F(irq, triggering), IMM(UACPI_TRIGGERING_EDGE)),
+ END(),
+};
+
+const struct uacpi_resource_convert_instruction convert_irq_to_aml[] = {
+ OP(PACKED_ARRAY_16, AML_F(irq, irq_mask), NATIVE_F(irq, irqs),
+ ARG2(NATIVE_O(irq, num_irqs))),
+ OP(SKIP_IF_AML_SIZE_LESS_THAN, ARG0(3), IMM(4)),
+ OP(BIT_FIELD_1, AML_F(irq, flags), NATIVE_F(irq, triggering), IMM(0)),
+ OP(BIT_FIELD_1, AML_F(irq, flags), NATIVE_F(irq, polarity), IMM(3)),
+ OP(BIT_FIELD_1, AML_F(irq, flags), NATIVE_F(irq, sharing), IMM(4)),
+ OP(BIT_FIELD_1, AML_F(irq, flags), NATIVE_F(irq, wake_capability), IMM(5)),
+ END(),
+};
+
+static const struct uacpi_resource_convert_instruction convert_dma[] = {
+ OP(PACKED_ARRAY_8, AML_F(dma, channel_mask), NATIVE_F(dma, channels),
+ ARG2(NATIVE_O(dma, num_channels))),
+ OP(BIT_FIELD_2, AML_F(dma, flags), NATIVE_F(dma, transfer_type), IMM(0)),
+ OP(BIT_FIELD_1, AML_F(dma, flags), NATIVE_F(dma, bus_master_status), IMM(2)),
+ OP(BIT_FIELD_2, AML_F(dma, flags), NATIVE_F(dma, channel_speed), IMM(5)),
+ END(),
+};
+
+static const struct uacpi_resource_convert_instruction
+convert_start_dependent_to_native[] = {
+ OP(SKIP_IF_AML_SIZE_LESS_THAN, ARG0(1), IMM(4)),
+ OP(SET_TO_IMM, NATIVE_F(start_dependent, length_kind),
+ IMM(UACPI_RESOURCE_LENGTH_KIND_FULL)),
+ OP(BIT_FIELD_2, AML_F(start_dependent, flags),
+ NATIVE_F(start_dependent, compatibility), IMM(0)),
+ OP(BIT_FIELD_2, AML_F(start_dependent, flags),
+ NATIVE_F(start_dependent, performance), IMM(2)),
+ END(),
+ OP(SET_TO_IMM, NATIVE_F(start_dependent, length_kind),
+ IMM(UACPI_RESOURCE_LENGTH_KIND_ONE_LESS)),
+ OP(SET_TO_IMM, NATIVE_F(start_dependent, compatibility),
+ IMM(UACPI_ACCEPTABLE)),
+ OP(SET_TO_IMM, NATIVE_F(start_dependent, performance),
+ IMM(UACPI_ACCEPTABLE)),
+ END(),
+};
+
+static const struct uacpi_resource_convert_instruction
+convert_start_dependent_to_aml[] = {
+ OP(SKIP_IF_AML_SIZE_LESS_THAN, ARG0(1), IMM(1)),
+ OP(BIT_FIELD_2, AML_F(start_dependent, flags),
+ NATIVE_F(start_dependent, compatibility), IMM(0)),
+ OP(BIT_FIELD_2, AML_F(start_dependent, flags),
+ NATIVE_F(start_dependent, performance), IMM(2)),
+ END(),
+};
+
+static const struct uacpi_resource_convert_instruction convert_io[] = {
+ OP(BIT_FIELD_1, AML_F(io, information), NATIVE_F(io, decode_type)),
+ OP(FIELD_16, AML_F(io, minimum), NATIVE_F(io, minimum)),
+ OP(FIELD_16, AML_F(io, maximum), NATIVE_F(io, maximum)),
+ OP(FIELD_8, AML_F(io, alignment), NATIVE_F(io, alignment)),
+ OP(FIELD_8, AML_F(io, length), NATIVE_F(io, length)),
+ END(),
+};
+
+static const struct uacpi_resource_convert_instruction convert_fixed_io[] = {
+ OP(FIELD_16, AML_F(fixed_io, address), NATIVE_F(fixed_io, address)),
+ OP(FIELD_8, AML_F(fixed_io, length), NATIVE_F(fixed_io, length)),
+ END(),
+};
+
+static const struct uacpi_resource_convert_instruction convert_fixed_dma[] = {
+ OP(FIELD_16, AML_F(fixed_dma, request_line),
+ NATIVE_F(fixed_dma, request_line)),
+ OP(FIELD_16, AML_F(fixed_dma, channel), NATIVE_F(fixed_dma, channel)),
+ OP(FIELD_8, AML_F(fixed_dma, transfer_width),
+ NATIVE_F(fixed_dma, transfer_width)),
+ END(),
+};
+
+static const struct uacpi_resource_convert_instruction convert_vendor_type0[] = {
+ OP(LOAD_AML_SIZE_32, NATIVE_F(vendor, length)),
+ OP(FIELD_8, AML_F(vendor_defined_type0, byte_data), NATIVE_F(vendor, data)),
+ END(),
+};
+
+static const struct uacpi_resource_convert_instruction convert_vendor_type1[] = {
+ OP(LOAD_AML_SIZE_32, NATIVE_F(vendor, length)),
+ OP(FIELD_8, AML_F(vendor_defined_type1, byte_data), NATIVE_F(vendor, data)),
+ END(),
+};
+
+static const struct uacpi_resource_convert_instruction convert_memory24[] = {
+ OP(BIT_FIELD_1, AML_F(memory24, information),
+ NATIVE_F(memory24, write_status), IMM(0)),
+ OP(FIELD_16, AML_F(memory24, minimum), NATIVE_F(memory24, minimum), IMM(4)),
+ END(),
+};
+
+static const struct uacpi_resource_convert_instruction convert_memory32[] = {
+ OP(BIT_FIELD_1, AML_F(memory32, information),
+ NATIVE_F(memory32, write_status), IMM(0)),
+ OP(FIELD_32, AML_F(memory32, minimum), NATIVE_F(memory32, minimum), IMM(4)),
+ END(),
+};
+
+static const struct uacpi_resource_convert_instruction
+convert_fixed_memory32[] = {
+ OP(BIT_FIELD_1, AML_F(fixed_memory32, information),
+ NATIVE_F(fixed_memory32, write_status), IMM(0)),
+ OP(FIELD_32, AML_F(fixed_memory32, address),
+ NATIVE_F(fixed_memory32, address)),
+ OP(FIELD_32, AML_F(fixed_memory32, length),
+ NATIVE_F(fixed_memory32, length)),
+ END(),
+};
+
+static const struct uacpi_resource_convert_instruction
+convert_generic_register[] = {
+ OP(FIELD_8, AML_F(generic_register, address_space_id),
+ NATIVE_F(generic_register, address_space_id), IMM(4)),
+ OP(FIELD_64, AML_F(generic_register, address),
+ NATIVE_F(generic_register, address)),
+ END(),
+};
+
+#define CONVERT_TYPE_SPECIFIC_FLAGS(addr_type) \
+ OP(LOAD_8_STORE, AML_F(addr_type, common.type), \
+ NATIVE_F(addr_type, common.type)), \
+ OP(SKIP_IF_NOT_EQUALS, ARG0(UACPI_RANGE_MEMORY), IMM(5)), \
+ OP(BIT_FIELD_1, \
+ AML_F(addr_type, common.type_flags), \
+ NATIVE_F(addr_type, common.attribute.memory.write_status), IMM(0)), \
+ OP(BIT_FIELD_2, \
+ AML_F(addr_type, common.type_flags), \
+ NATIVE_F(addr_type, common.attribute.memory.caching), IMM(1)), \
+ OP(BIT_FIELD_2, \
+ AML_F(addr_type, common.type_flags), \
+ NATIVE_F(addr_type, common.attribute.memory.range_type), IMM(3)), \
+ OP(BIT_FIELD_1, \
+ AML_F(addr_type, common.type_flags), \
+ NATIVE_F(addr_type, common.attribute.memory.translation), IMM(5)), \
+ END(), \
+ OP(SKIP_IF_NOT_EQUALS, ARG0(UACPI_RANGE_IO), IMM(4)), \
+ OP(BIT_FIELD_2, \
+ AML_F(addr_type, common.type_flags), \
+ NATIVE_F(addr_type, common.attribute.io.range_type), IMM(0)), \
+ OP(BIT_FIELD_1, \
+ AML_F(addr_type, common.type_flags), \
+ NATIVE_F(addr_type, common.attribute.io.translation_type), IMM(4)), \
+ OP(BIT_FIELD_1, \
+ AML_F(addr_type, common.type_flags), \
+ NATIVE_F(addr_type, common.attribute.io.translation), IMM(5)), \
+ END(), \
+ /* Memory type that we don't know, just copy the byte */ \
+ OP(FIELD_8, AML_F(addr_type, common.type_flags), \
+ NATIVE_F(addr_type, common.attribute.type_specific), IMM(0xFF)), \
+ END()
+
+#define CONVERT_GENERAL_ADDRESS_FLAGS(addr_type) \
+ OP(BIT_FIELD_1, \
+ AML_F(addr_type, common.flags), \
+ NATIVE_F(addr_type, common.direction), IMM(0)), \
+ OP(BIT_FIELD_1, \
+ AML_F(addr_type, common.flags), \
+ NATIVE_F(addr_type, common.decode_type), IMM(1)), \
+ OP(BIT_FIELD_1, \
+ AML_F(addr_type, common.flags), \
+ NATIVE_F(addr_type, common.fixed_min_address), IMM(2)), \
+ OP(BIT_FIELD_1, \
+ AML_F(addr_type, common.flags), \
+ NATIVE_F(addr_type, common.fixed_max_address), IMM(3)) \
+
+#define DEFINE_ADDRESS_CONVERSION(width) \
+ static const struct uacpi_resource_convert_instruction \
+ convert_address##width[] = { \
+ CONVERT_GENERAL_ADDRESS_FLAGS(address##width), \
+ OP(FIELD_##width, AML_F(address##width, granularity), \
+ NATIVE_F(address##width, granularity), IMM(5)), \
+ OP(RESOURCE_SOURCE, NATIVE_F(address##width, source)), \
+ CONVERT_TYPE_SPECIFIC_FLAGS(address##width), \
+ };
+
+DEFINE_ADDRESS_CONVERSION(16)
+DEFINE_ADDRESS_CONVERSION(32)
+DEFINE_ADDRESS_CONVERSION(64)
+
+static const struct uacpi_resource_convert_instruction
+convert_address64_extended[] = {
+ CONVERT_GENERAL_ADDRESS_FLAGS(address64_extended),
+ OP(FIELD_8, AML_F(address64_extended, revision_id),
+ NATIVE_F(address64_extended, revision_id)),
+ OP(FIELD_64, AML_F(address64_extended, granularity),
+ NATIVE_F(address64_extended, granularity), IMM(6)),
+ CONVERT_TYPE_SPECIFIC_FLAGS(address64_extended),
+};
+
+static const struct uacpi_resource_convert_instruction
+convert_extended_irq[] = {
+ OP(BIT_FIELD_1, AML_F(extended_irq, flags),
+ NATIVE_F(extended_irq, direction), IMM(0)),
+ OP(BIT_FIELD_1, AML_F(extended_irq, flags),
+ NATIVE_F(extended_irq, triggering), IMM(1)),
+ OP(BIT_FIELD_1, AML_F(extended_irq, flags),
+ NATIVE_F(extended_irq, polarity), IMM(2)),
+ OP(BIT_FIELD_1, AML_F(extended_irq, flags),
+ NATIVE_F(extended_irq, sharing), IMM(3)),
+ OP(BIT_FIELD_1, AML_F(extended_irq, flags),
+ NATIVE_F(extended_irq, wake_capability), IMM(4)),
+ OP(LOAD_8_STORE, AML_F(extended_irq, num_irqs),
+ NATIVE_F(extended_irq, num_irqs), IMM(4)),
+ OP(RESOURCE_SOURCE, NATIVE_F(extended_irq, source)),
+
+ // Use FIELD_8 here since the accumulator has been multiplied by 4
+ OP(FIELD_8, AML_F(extended_irq, irqs), NATIVE_F(extended_irq, irqs)),
+ END(),
+};
+
+static const struct uacpi_resource_convert_instruction convert_clock_input[] = {
+ OP(FIELD_8, AML_F(clock_input, revision_id),
+ NATIVE_F(clock_input, revision_id)),
+ OP(BIT_FIELD_1, AML_F(clock_input, flags), NATIVE_F(clock_input, frequency),
+ IMM(0)),
+ OP(BIT_FIELD_2, AML_F(clock_input, flags), NATIVE_F(clock_input, scale),
+ IMM(1)),
+ OP(FIELD_16, AML_F(clock_input, divisor), NATIVE_F(clock_input, divisor)),
+ OP(FIELD_32, AML_F(clock_input, numerator), NATIVE_F(clock_input, numerator)),
+ OP(FIELD_8, AML_F(clock_input, source_index), NATIVE_F(clock_input, source.index)),
+ OP(RESOURCE_SOURCE_NO_INDEX, NATIVE_F(clock_input, source)),
+ END(),
+};
+
+#define DECODE_SOURCE_INDEX(short_aml_name) \
+ OP(FIELD_8, AML_F(short_aml_name, source_index), \
+ NATIVE_F(short_aml_name, source.index)) \
+
+#define DECODE_RES_PIN_TBL_AND_VENDOR_DATA( \
+ short_aml_name, res_opcode, offset_field, res_field \
+) \
+ OP(LOAD_PIN_TABLE_LENGTH, AML_F(short_aml_name, offset_field), \
+ NATIVE_F(short_aml_name, pin_table_length)), \
+ OP(RESOURCE_##res_opcode, NATIVE_F(short_aml_name, res_field), \
+ AML_F(short_aml_name, offset_field), \
+ ARG2(AML_O(short_aml_name, vendor_data_offset))), \
+ OP(PIN_TABLE, AML_F(short_aml_name, pin_table_offset), \
+ NATIVE_F(short_aml_name, pin_table_length), \
+ ARG2(NATIVE_O(short_aml_name, pin_table))), \
+ OP(VENDOR_DATA, AML_F(short_aml_name, vendor_data_offset), \
+ NATIVE_F(short_aml_name, vendor_data_length), \
+ ARG2(NATIVE_O(short_aml_name, vendor_data)))
+
+static const struct uacpi_resource_convert_instruction
+convert_gpio_connection[] = {
+ OP(FIELD_8, AML_F(gpio_connection, revision_id),
+ NATIVE_F(gpio_connection, revision_id)),
+ OP(BIT_FIELD_1, AML_F(gpio_connection, general_flags),
+ NATIVE_F(gpio_connection, direction)),
+ OP(FIELD_8, AML_F(gpio_connection, pull_configuration),
+ NATIVE_F(gpio_connection, pull_configuration)),
+ OP(FIELD_16, AML_F(gpio_connection, drive_strength),
+ NATIVE_F(gpio_connection, drive_strength), IMM(2)),
+ DECODE_SOURCE_INDEX(gpio_connection),
+ DECODE_RES_PIN_TBL_AND_VENDOR_DATA(
+ gpio_connection, SOURCE_NO_INDEX, source_offset, source
+ ),
+ OP(LOAD_8_STORE, AML_F(gpio_connection, type), NATIVE_F(gpio_connection, type)),
+ OP(SKIP_IF_NOT_EQUALS, ARG0(UACPI_GPIO_CONNECTION_INTERRUPT), IMM(5)),
+ OP(BIT_FIELD_1, AML_F(gpio_connection, connection_flags),
+ NATIVE_F(gpio_connection, intr.triggering), IMM(0)),
+ OP(BIT_FIELD_2, AML_F(gpio_connection, connection_flags),
+ NATIVE_F(gpio_connection, intr.polarity), IMM(1)),
+ OP(BIT_FIELD_1, AML_F(gpio_connection, connection_flags),
+ NATIVE_F(gpio_connection, intr.sharing), IMM(3)),
+ OP(BIT_FIELD_1, AML_F(gpio_connection, connection_flags),
+ NATIVE_F(gpio_connection, intr.wake_capability), IMM(4)),
+ END(),
+ OP(SKIP_IF_NOT_EQUALS, ARG0(UACPI_GPIO_CONNECTION_IO), IMM(3)),
+ OP(BIT_FIELD_2, AML_F(gpio_connection, connection_flags),
+ NATIVE_F(gpio_connection, io.restriction), IMM(0)),
+ OP(BIT_FIELD_1, AML_F(gpio_connection, connection_flags),
+ NATIVE_F(gpio_connection, io.sharing), IMM(3)),
+ END(),
+ OP(FIELD_16, AML_F(gpio_connection, connection_flags),
+ NATIVE_F(gpio_connection, type_specific), IMM(0xFF)),
+ END(),
+};
+
+static const struct uacpi_resource_convert_instruction
+convert_pin_function[] = {
+ OP(FIELD_8, AML_F(pin_function, revision_id),
+ NATIVE_F(pin_function, revision_id)),
+ OP(BIT_FIELD_1, AML_F(pin_function, flags),
+ NATIVE_F(pin_function, sharing), IMM(0)),
+ OP(FIELD_8, AML_F(pin_function, pull_configuration),
+ NATIVE_F(pin_function, pull_configuration)),
+ OP(FIELD_16, AML_F(pin_function, function_number),
+ NATIVE_F(pin_function, function_number)),
+ DECODE_SOURCE_INDEX(pin_function),
+ DECODE_RES_PIN_TBL_AND_VENDOR_DATA(
+ pin_function, SOURCE_NO_INDEX, source_offset, source
+ ),
+ END(),
+};
+
+static const struct uacpi_resource_convert_instruction
+convert_pin_configuration[] = {
+ OP(FIELD_8, AML_F(pin_configuration, revision_id),
+ NATIVE_F(pin_configuration, revision_id)),
+ OP(BIT_FIELD_1, AML_F(pin_configuration, flags),
+ NATIVE_F(pin_configuration, sharing), IMM(0)),
+ OP(BIT_FIELD_1, AML_F(pin_configuration, flags),
+ NATIVE_F(pin_configuration, direction), IMM(1)),
+ OP(FIELD_8, AML_F(pin_configuration, type),
+ NATIVE_F(pin_configuration, type)),
+ OP(FIELD_32, AML_F(pin_configuration, value),
+ NATIVE_F(pin_configuration, value)),
+ DECODE_SOURCE_INDEX(pin_configuration),
+ DECODE_RES_PIN_TBL_AND_VENDOR_DATA(
+ pin_configuration, SOURCE_NO_INDEX, source_offset, source
+ ),
+ END(),
+};
+
+static const struct uacpi_resource_convert_instruction convert_pin_group[] = {
+ OP(FIELD_8, AML_F(pin_group, revision_id),
+ NATIVE_F(pin_group, revision_id)),
+ OP(BIT_FIELD_1, AML_F(pin_group, flags),
+ NATIVE_F(pin_group, direction), IMM(0)),
+ DECODE_RES_PIN_TBL_AND_VENDOR_DATA(
+ pin_group, LABEL, source_lable_offset, label
+ ),
+ END(),
+};
+
+#define DECODE_PIN_GROUP_RES_SOURCES(postfix) \
+ DECODE_SOURCE_INDEX(pin_group_##postfix), \
+ OP(RESOURCE_SOURCE_NO_INDEX, NATIVE_F(pin_group_##postfix, source), \
+ AML_F(pin_group_##postfix, source_offset), \
+ ARG2(AML_O(pin_group_##postfix, source_lable_offset))), \
+ OP(LOAD_16_NATIVE, NATIVE_F(pin_group_##postfix, source.length)), \
+ OP(RESOURCE_LABEL, NATIVE_F(pin_group_##postfix, label), \
+ AML_F(pin_group_##postfix, source_lable_offset), \
+ ARG2(AML_O(pin_group_##postfix, vendor_data_offset))), \
+ OP(VENDOR_DATA, AML_F(pin_group_##postfix, vendor_data_offset), \
+ NATIVE_F(pin_group_##postfix, vendor_data_length), \
+ ARG2(NATIVE_O(pin_group_##postfix, vendor_data)))
+
+static const struct uacpi_resource_convert_instruction
+convert_pin_group_function[] = {
+ OP(FIELD_8, AML_F(pin_group_function, revision_id),
+ NATIVE_F(pin_group_function, revision_id)),
+ OP(BIT_FIELD_1, AML_F(pin_group_function, flags),
+ NATIVE_F(pin_group_function, sharing), IMM(0)),
+ OP(BIT_FIELD_1, AML_F(pin_group_function, flags),
+ NATIVE_F(pin_group_function, direction), IMM(1)),
+ OP(FIELD_16, AML_F(pin_group_function, function),
+ NATIVE_F(pin_group_function, function)),
+ DECODE_PIN_GROUP_RES_SOURCES(function),
+ END(),
+};
+
+static const struct uacpi_resource_convert_instruction
+convert_pin_group_configuration[] = {
+ OP(FIELD_8, AML_F(pin_group_configuration, revision_id),
+ NATIVE_F(pin_group_configuration, revision_id)),
+ OP(BIT_FIELD_1, AML_F(pin_group_configuration, flags),
+ NATIVE_F(pin_group_configuration, sharing), IMM(0)),
+ OP(BIT_FIELD_1, AML_F(pin_group_configuration, flags),
+ NATIVE_F(pin_group_configuration, direction), IMM(1)),
+ OP(FIELD_8, AML_F(pin_group_configuration, type),
+ NATIVE_F(pin_group_configuration, type)),
+ OP(FIELD_32, AML_F(pin_group_configuration, value),
+ NATIVE_F(pin_group_configuration, value)),
+ DECODE_PIN_GROUP_RES_SOURCES(configuration),
+ END(),
+};
+
+static const struct uacpi_resource_convert_instruction
+convert_generic_serial_bus[] = {
+ OP(FIELD_8, AML_F(serial, revision_id),
+ NATIVE_F(serial_bus_common, revision_id)),
+ OP(FIELD_8, AML_F(serial, type_specific_revision_id),
+ NATIVE_F(serial_bus_common, type_revision_id)),
+ OP(FIELD_8, AML_F(serial, source_index),
+ NATIVE_F(serial_bus_common, source.index)),
+ OP(FIELD_16, AML_F(serial, type_data_length),
+ NATIVE_F(serial_bus_common, type_data_length)),
+ OP(BIT_FIELD_1, AML_F(serial, flags),
+ NATIVE_F(serial_bus_common, mode), IMM(0)),
+ OP(BIT_FIELD_1, AML_F(serial, flags),
+ NATIVE_F(serial_bus_common, direction), IMM(1)),
+ OP(BIT_FIELD_1, AML_F(serial, flags),
+ NATIVE_F(serial_bus_common, sharing), IMM(2)),
+ OP(SERIAL_TYPE_SPECIFIC, AML_F(serial, type),
+ NATIVE_F(serial_bus_common, type)),
+ OP(RESOURCE_SOURCE_NO_INDEX, NATIVE_F(serial_bus_common, source)),
+ OP(LOAD_8_NATIVE, NATIVE_F(serial_bus_common, type)),
+ OP(SKIP_IF_NOT_EQUALS, ARG0(ACPI_SERIAL_TYPE_I2C), IMM(4)),
+ OP(BIT_FIELD_1, AML_F(serial, type_specific_flags),
+ NATIVE_F(i2c_connection, addressing_mode), IMM(0)),
+ OP(FIELD_32, AML_F(serial_i2c, connection_speed),
+ NATIVE_F(i2c_connection, connection_speed), IMM(0xFF)),
+ OP(FIELD_16, AML_F(serial_i2c, slave_address),
+ NATIVE_F(i2c_connection, slave_address)),
+ END(),
+ OP(SKIP_IF_NOT_EQUALS, ARG0(ACPI_SERIAL_TYPE_SPI), IMM(5)),
+ OP(BIT_FIELD_1, AML_F(serial, type_specific_flags),
+ NATIVE_F(spi_connection, wire_mode), IMM(0)),
+ OP(BIT_FIELD_1, AML_F(serial, type_specific_flags),
+ NATIVE_F(spi_connection, device_polarity), IMM(1)),
+ OP(FIELD_32, AML_F(serial_spi, connection_speed),
+ NATIVE_F(spi_connection, connection_speed), IMM(0xFF)),
+ OP(FIELD_8, AML_F(serial_spi, data_bit_length),
+ NATIVE_F(spi_connection, data_bit_length), IMM(5)),
+ END(),
+ OP(SKIP_IF_NOT_EQUALS, ARG0(ACPI_SERIAL_TYPE_UART), IMM(8)),
+ OP(BIT_FIELD_2, AML_F(serial, type_specific_flags),
+ NATIVE_F(uart_connection, flow_control), IMM(0)),
+ OP(BIT_FIELD_2, AML_F(serial, type_specific_flags),
+ NATIVE_F(uart_connection, stop_bits), IMM(2)),
+ OP(BIT_FIELD_3, AML_F(serial, type_specific_flags),
+ NATIVE_F(uart_connection, data_bits), IMM(4)),
+ OP(BIT_FIELD_1, AML_F(serial, type_specific_flags),
+ NATIVE_F(uart_connection, endianness), IMM(7)),
+ OP(FIELD_32, AML_F(serial_uart, baud_rate),
+ NATIVE_F(uart_connection, baud_rate), IMM(0xFF)),
+ OP(FIELD_16, AML_F(serial_uart, rx_fifo),
+ NATIVE_F(uart_connection, rx_fifo), IMM(2)),
+ OP(FIELD_8, AML_F(serial_uart, parity),
+ NATIVE_F(uart_connection, parity), IMM(2)),
+ END(),
+ OP(SKIP_IF_NOT_EQUALS, ARG0(ACPI_SERIAL_TYPE_CSI2), IMM(3)),
+ OP(BIT_FIELD_2, AML_F(serial, type_specific_flags),
+ NATIVE_F(csi2_connection, phy_type), IMM(0)),
+ OP(BIT_FIELD_6, AML_F(serial, type_specific_flags),
+ NATIVE_F(csi2_connection, local_port), IMM(2)),
+ END(),
+
+ /*
+ * Insert a trap to catch unimplemented types, this should be unreachable
+ * because of validation earlier.
+ */
+ OP(UNREACHABLE),
+};
+
+#define NATIVE_RESOURCE_HEADER_SIZE 8
+
+#define DEFINE_SMALL_AML_RESOURCE(aml_type_enum, native_type_enum, \
+ aml_struct, native_struct, ...) \
+ [aml_type_enum] = { \
+ .type = aml_type_enum, \
+ .native_type = native_type_enum, \
+ .resource_kind = UACPI_AML_RESOURCE_KIND_SMALL, \
+ .aml_size = sizeof(aml_struct) - SMALL_ITEM_HEADER_SIZE, \
+ .native_size = sizeof(native_struct) + NATIVE_RESOURCE_HEADER_SIZE, \
+ __VA_ARGS__ \
+ }
+
+#define DEFINE_SMALL_AML_RESOURCE_NO_NATIVE_REPR( \
+ aml_type_enum, native_type_enum, aml_struct, ... \
+) \
+ [aml_type_enum] = { \
+ .type = aml_type_enum, \
+ .native_type = native_type_enum, \
+ .resource_kind = UACPI_AML_RESOURCE_KIND_SMALL, \
+ .aml_size = sizeof(aml_struct) - SMALL_ITEM_HEADER_SIZE, \
+ .native_size = NATIVE_RESOURCE_HEADER_SIZE, \
+ __VA_ARGS__ \
+ }
+
+#define DEFINE_LARGE_AML_RESOURCE(aml_type_enum, native_type_enum, \
+ aml_struct, native_struct, ...) \
+ [aml_type_enum] = { \
+ .type = aml_type_enum, \
+ .native_type = native_type_enum, \
+ .resource_kind = UACPI_AML_RESOURCE_KIND_LARGE, \
+ .aml_size = sizeof(aml_struct) - LARGE_ITEM_HEADER_SIZE, \
+ .native_size = sizeof(native_struct) + NATIVE_RESOURCE_HEADER_SIZE, \
+ __VA_ARGS__ \
+ }
+
+const struct uacpi_resource_spec aml_resources[UACPI_AML_RESOURCE_MAX + 1] = {
+ DEFINE_SMALL_AML_RESOURCE(
+ UACPI_AML_RESOURCE_IRQ,
+ UACPI_RESOURCE_TYPE_IRQ,
+ struct acpi_resource_irq,
+ uacpi_resource_irq,
+ .size_kind = UACPI_AML_RESOURCE_SIZE_KIND_FIXED_OR_ONE_LESS,
+ .extra_size_for_native = extra_size_for_native_irq_or_dma,
+ .size_for_aml = size_for_aml_irq,
+ .to_native = convert_irq_to_native,
+ .to_aml = convert_irq_to_aml,
+ ),
+ DEFINE_SMALL_AML_RESOURCE(
+ UACPI_AML_RESOURCE_DMA,
+ UACPI_RESOURCE_TYPE_DMA,
+ struct acpi_resource_dma,
+ uacpi_resource_dma,
+ .size_kind = UACPI_AML_RESOURCE_SIZE_KIND_FIXED,
+ .extra_size_for_native = extra_size_for_native_irq_or_dma,
+ .to_native = convert_dma,
+ .to_aml = convert_dma,
+ ),
+ DEFINE_SMALL_AML_RESOURCE(
+ UACPI_AML_RESOURCE_START_DEPENDENT,
+ UACPI_RESOURCE_TYPE_START_DEPENDENT,
+ struct acpi_resource_start_dependent,
+ uacpi_resource_start_dependent,
+ .size_kind = UACPI_AML_RESOURCE_SIZE_KIND_FIXED_OR_ONE_LESS,
+ .size_for_aml = size_for_aml_start_dependent,
+ .to_native = convert_start_dependent_to_native,
+ .to_aml = convert_start_dependent_to_aml,
+ ),
+ DEFINE_SMALL_AML_RESOURCE_NO_NATIVE_REPR(
+ UACPI_AML_RESOURCE_END_DEPENDENT,
+ UACPI_RESOURCE_TYPE_END_DEPENDENT,
+ struct acpi_resource_end_dependent,
+ .size_kind = UACPI_AML_RESOURCE_SIZE_KIND_FIXED,
+ ),
+ DEFINE_SMALL_AML_RESOURCE(
+ UACPI_AML_RESOURCE_IO,
+ UACPI_RESOURCE_TYPE_IO,
+ struct acpi_resource_io,
+ uacpi_resource_io,
+ .size_kind = UACPI_AML_RESOURCE_SIZE_KIND_FIXED,
+ .to_native = convert_io,
+ .to_aml = convert_io,
+ ),
+ DEFINE_SMALL_AML_RESOURCE(
+ UACPI_AML_RESOURCE_FIXED_IO,
+ UACPI_RESOURCE_TYPE_FIXED_IO,
+ struct acpi_resource_fixed_io,
+ uacpi_resource_fixed_io,
+ .size_kind = UACPI_AML_RESOURCE_SIZE_KIND_FIXED,
+ .to_native = convert_fixed_io,
+ .to_aml = convert_fixed_io,
+ ),
+ DEFINE_SMALL_AML_RESOURCE(
+ UACPI_AML_RESOURCE_FIXED_DMA,
+ UACPI_RESOURCE_TYPE_FIXED_DMA,
+ struct acpi_resource_fixed_dma,
+ uacpi_resource_fixed_dma,
+ .size_kind = UACPI_AML_RESOURCE_SIZE_KIND_FIXED,
+ .to_native = convert_fixed_dma,
+ .to_aml = convert_fixed_dma,
+ ),
+ DEFINE_SMALL_AML_RESOURCE(
+ UACPI_AML_RESOURCE_VENDOR_TYPE0,
+ UACPI_RESOURCE_TYPE_VENDOR_SMALL,
+ struct acpi_resource_vendor_defined_type0,
+ uacpi_resource_vendor,
+ .size_kind = UACPI_AML_RESOURCE_SIZE_KIND_VARIABLE,
+ .size_for_aml = size_for_aml_vendor,
+ .extra_size_for_native = extra_size_for_native_vendor,
+ .to_native = convert_vendor_type0,
+ .to_aml = convert_vendor_type0,
+ ),
+ DEFINE_SMALL_AML_RESOURCE_NO_NATIVE_REPR(
+ UACPI_AML_RESOURCE_END_TAG,
+ UACPI_RESOURCE_TYPE_END_TAG,
+ struct acpi_resource_end_tag,
+ .size_kind = UACPI_AML_RESOURCE_SIZE_KIND_FIXED,
+ ),
+ DEFINE_LARGE_AML_RESOURCE(
+ UACPI_AML_RESOURCE_MEMORY24,
+ UACPI_RESOURCE_TYPE_MEMORY24,
+ struct acpi_resource_memory24,
+ uacpi_resource_memory24,
+ .size_kind = UACPI_AML_RESOURCE_SIZE_KIND_FIXED,
+ .to_native = convert_memory24,
+ .to_aml = convert_memory24,
+ ),
+ DEFINE_LARGE_AML_RESOURCE(
+ UACPI_AML_RESOURCE_GENERIC_REGISTER,
+ UACPI_RESOURCE_TYPE_GENERIC_REGISTER,
+ struct acpi_resource_generic_register,
+ uacpi_resource_generic_register,
+ .size_kind = UACPI_AML_RESOURCE_SIZE_KIND_FIXED,
+ .to_native = convert_generic_register,
+ .to_aml = convert_generic_register,
+ ),
+ DEFINE_LARGE_AML_RESOURCE(
+ UACPI_AML_RESOURCE_VENDOR_TYPE1,
+ UACPI_RESOURCE_TYPE_VENDOR_LARGE,
+ struct acpi_resource_vendor_defined_type1,
+ uacpi_resource_vendor,
+ .size_kind = UACPI_AML_RESOURCE_SIZE_KIND_VARIABLE,
+ .extra_size_for_native = extra_size_for_native_vendor,
+ .size_for_aml = size_for_aml_vendor,
+ .to_native = convert_vendor_type1,
+ .to_aml = convert_vendor_type1,
+ ),
+ DEFINE_LARGE_AML_RESOURCE(
+ UACPI_AML_RESOURCE_MEMORY32,
+ UACPI_RESOURCE_TYPE_MEMORY32,
+ struct acpi_resource_memory32,
+ uacpi_resource_memory32,
+ .size_kind = UACPI_AML_RESOURCE_SIZE_KIND_FIXED,
+ .to_native = convert_memory32,
+ .to_aml = convert_memory32,
+ ),
+ DEFINE_LARGE_AML_RESOURCE(
+ UACPI_AML_RESOURCE_FIXED_MEMORY32,
+ UACPI_RESOURCE_TYPE_FIXED_MEMORY32,
+ struct acpi_resource_fixed_memory32,
+ uacpi_resource_fixed_memory32,
+ .size_kind = UACPI_AML_RESOURCE_SIZE_KIND_FIXED,
+ .to_native = convert_fixed_memory32,
+ .to_aml = convert_fixed_memory32,
+ ),
+ DEFINE_LARGE_AML_RESOURCE(
+ UACPI_AML_RESOURCE_ADDRESS32,
+ UACPI_RESOURCE_TYPE_ADDRESS32,
+ struct acpi_resource_address32,
+ uacpi_resource_address32,
+ .size_kind = UACPI_AML_RESOURCE_SIZE_KIND_VARIABLE,
+ .extra_size_for_native = extra_size_for_native_address_or_clock_input,
+ .size_for_aml = size_for_aml_address_or_clock_input,
+ .to_native = convert_address32,
+ .to_aml = convert_address32,
+ ),
+ DEFINE_LARGE_AML_RESOURCE(
+ UACPI_AML_RESOURCE_ADDRESS16,
+ UACPI_RESOURCE_TYPE_ADDRESS16,
+ struct acpi_resource_address16,
+ uacpi_resource_address16,
+ .size_kind = UACPI_AML_RESOURCE_SIZE_KIND_VARIABLE,
+ .extra_size_for_native = extra_size_for_native_address_or_clock_input,
+ .size_for_aml = size_for_aml_address_or_clock_input,
+ .to_native = convert_address16,
+ .to_aml = convert_address16,
+ ),
+ DEFINE_LARGE_AML_RESOURCE(
+ UACPI_AML_RESOURCE_EXTENDED_IRQ,
+ UACPI_RESOURCE_TYPE_EXTENDED_IRQ,
+ struct acpi_resource_extended_irq,
+ uacpi_resource_extended_irq,
+ .size_kind = UACPI_AML_RESOURCE_SIZE_KIND_VARIABLE,
+ .extra_size_for_native = extra_size_for_extended_irq,
+ .size_for_aml = size_for_aml_extended_irq,
+ .to_native = convert_extended_irq,
+ .to_aml = convert_extended_irq,
+ ),
+ DEFINE_LARGE_AML_RESOURCE(
+ UACPI_AML_RESOURCE_ADDRESS64,
+ UACPI_RESOURCE_TYPE_ADDRESS64,
+ struct acpi_resource_address64,
+ uacpi_resource_address64,
+ .size_kind = UACPI_AML_RESOURCE_SIZE_KIND_VARIABLE,
+ .extra_size_for_native = extra_size_for_native_address_or_clock_input,
+ .size_for_aml = size_for_aml_address_or_clock_input,
+ .to_native = convert_address64,
+ .to_aml = convert_address64,
+ ),
+ DEFINE_LARGE_AML_RESOURCE(
+ UACPI_AML_RESOURCE_ADDRESS64_EXTENDED,
+ UACPI_RESOURCE_TYPE_ADDRESS64_EXTENDED,
+ struct acpi_resource_address64_extended,
+ uacpi_resource_address64_extended,
+ .size_kind = UACPI_AML_RESOURCE_SIZE_KIND_FIXED,
+ .to_native = convert_address64_extended,
+ .to_aml = convert_address64_extended,
+ ),
+ DEFINE_LARGE_AML_RESOURCE(
+ UACPI_AML_RESOURCE_GPIO_CONNECTION,
+ UACPI_RESOURCE_TYPE_GPIO_CONNECTION,
+ struct acpi_resource_gpio_connection,
+ uacpi_resource_gpio_connection,
+ .size_kind = UACPI_AML_RESOURCE_SIZE_KIND_VARIABLE,
+ .extra_size_for_native = extra_size_for_native_gpio_or_pins,
+ .size_for_aml = size_for_aml_gpio_or_pins,
+ .to_aml = convert_gpio_connection,
+ .to_native = convert_gpio_connection,
+ ),
+ DEFINE_LARGE_AML_RESOURCE(
+ UACPI_AML_RESOURCE_PIN_FUNCTION,
+ UACPI_RESOURCE_TYPE_PIN_FUNCTION,
+ struct acpi_resource_pin_function,
+ uacpi_resource_pin_function,
+ .size_kind = UACPI_AML_RESOURCE_SIZE_KIND_VARIABLE,
+ .extra_size_for_native = extra_size_for_native_gpio_or_pins,
+ .size_for_aml = size_for_aml_gpio_or_pins,
+ .to_aml = convert_pin_function,
+ .to_native = convert_pin_function,
+ ),
+ DEFINE_LARGE_AML_RESOURCE(
+ UACPI_AML_RESOURCE_SERIAL_CONNECTION,
+ 0, // the native type here is determined dynamically
+ struct acpi_resource_serial,
+ uacpi_resource_serial_bus_common,
+ .size_kind = UACPI_AML_RESOURCE_SIZE_KIND_VARIABLE,
+ .extra_size_for_native = extra_size_for_serial_connection,
+ .size_for_aml = aml_size_for_serial_connection,
+ .to_native = convert_generic_serial_bus,
+ .to_aml = convert_generic_serial_bus,
+ ),
+ DEFINE_LARGE_AML_RESOURCE(
+ UACPI_AML_RESOURCE_PIN_CONFIGURATION,
+ UACPI_RESOURCE_TYPE_PIN_CONFIGURATION,
+ struct acpi_resource_pin_configuration,
+ uacpi_resource_pin_configuration,
+ .size_kind = UACPI_AML_RESOURCE_SIZE_KIND_VARIABLE,
+ .extra_size_for_native = extra_size_for_native_gpio_or_pins,
+ .size_for_aml = size_for_aml_gpio_or_pins,
+ .to_native = convert_pin_configuration,
+ .to_aml = convert_pin_configuration,
+ ),
+ DEFINE_LARGE_AML_RESOURCE(
+ UACPI_AML_RESOURCE_PIN_GROUP,
+ UACPI_RESOURCE_TYPE_PIN_GROUP,
+ struct acpi_resource_pin_group,
+ uacpi_resource_pin_group,
+ .size_kind = UACPI_AML_RESOURCE_SIZE_KIND_VARIABLE,
+ .extra_size_for_native = extra_size_for_native_gpio_or_pins,
+ .size_for_aml = size_for_aml_gpio_or_pins,
+ .to_native = convert_pin_group,
+ .to_aml = convert_pin_group,
+ ),
+ DEFINE_LARGE_AML_RESOURCE(
+ UACPI_AML_RESOURCE_PIN_GROUP_FUNCTION,
+ UACPI_RESOURCE_TYPE_PIN_GROUP_FUNCTION,
+ struct acpi_resource_pin_group_function,
+ uacpi_resource_pin_group_function,
+ .size_kind = UACPI_AML_RESOURCE_SIZE_KIND_VARIABLE,
+ .extra_size_for_native = extra_size_for_native_pin_group,
+ .size_for_aml = size_for_aml_pin_group,
+ .to_native = convert_pin_group_function,
+ .to_aml = convert_pin_group_function,
+ ),
+ DEFINE_LARGE_AML_RESOURCE(
+ UACPI_AML_RESOURCE_PIN_GROUP_CONFIGURATION,
+ UACPI_RESOURCE_TYPE_PIN_GROUP_CONFIGURATION,
+ struct acpi_resource_pin_group_configuration,
+ uacpi_resource_pin_group_configuration,
+ .size_kind = UACPI_AML_RESOURCE_SIZE_KIND_VARIABLE,
+ .extra_size_for_native = extra_size_for_native_pin_group,
+ .size_for_aml = size_for_aml_pin_group,
+ .to_native = convert_pin_group_configuration,
+ .to_aml = convert_pin_group_configuration,
+ ),
+ DEFINE_LARGE_AML_RESOURCE(
+ UACPI_AML_RESOURCE_CLOCK_INPUT,
+ UACPI_RESOURCE_TYPE_CLOCK_INPUT,
+ struct acpi_resource_clock_input,
+ uacpi_resource_clock_input,
+ .size_kind = UACPI_AML_RESOURCE_SIZE_KIND_VARIABLE,
+ .extra_size_for_native = extra_size_for_native_address_or_clock_input,
+ .size_for_aml = size_for_aml_address_or_clock_input,
+ .to_native = convert_clock_input,
+ .to_aml = convert_clock_input,
+ ),
+};
+
+static enum uacpi_aml_resource get_aml_resource_type(uacpi_u8 raw_byte)
+{
+ if (raw_byte & ACPI_LARGE_ITEM) {
+ return aml_resource_to_type[
+ LARGE_RESOURCE_BASE + (raw_byte & ACPI_LARGE_ITEM_NAME_MASK)
+ ];
+ }
+
+ return aml_resource_to_type[
+ (raw_byte >> ACPI_SMALL_ITEM_NAME_IDX) & ACPI_SMALL_ITEM_NAME_MASK
+ ];
+}
+
+static uacpi_status get_aml_resource_size(
+ uacpi_u8 *data, uacpi_size bytes_left, uacpi_u16 *out_size
+)
+{
+ uacpi_u16 size;
+
+ /*
+ * Resource header is not included in size for both, so we subtract
+ * the header size from bytes_left to validate it.
+ */
+ if (*data & ACPI_LARGE_ITEM) {
+ if (uacpi_unlikely(bytes_left < 3))
+ return UACPI_STATUS_AML_INVALID_RESOURCE;
+
+ uacpi_memcpy(&size, data + 1, sizeof(size));
+ bytes_left -= aml_resource_kind_to_header_size[
+ UACPI_AML_RESOURCE_KIND_LARGE
+ ];
+ } else {
+ size = *data & ACPI_SMALL_ITEM_LENGTH_MASK;
+ bytes_left -= aml_resource_kind_to_header_size[
+ UACPI_AML_RESOURCE_KIND_SMALL
+ ];
+ }
+
+ if (uacpi_unlikely(size > bytes_left))
+ return UACPI_STATUS_AML_INVALID_RESOURCE;
+
+ *out_size = size;
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status validate_aml_serial_type(uacpi_u8 type)
+{
+ if (uacpi_unlikely(type < ACPI_SERIAL_TYPE_I2C ||
+ type > ACPI_SERIAL_TYPE_CSI2)) {
+ uacpi_error("invalid/unsupported serial connection type %d\n", type);
+ return UACPI_STATUS_AML_INVALID_RESOURCE;
+ }
+
+ return UACPI_STATUS_OK;
+}
+
+uacpi_status uacpi_for_each_aml_resource(
+ uacpi_data_view buffer, uacpi_aml_resource_iteration_callback cb, void *user
+)
+{
+ uacpi_status ret;
+ uacpi_iteration_decision decision;
+ uacpi_u8 *data;
+ uacpi_size bytes_left;
+ uacpi_u16 resource_size;
+ enum uacpi_aml_resource type;
+ const struct uacpi_resource_spec *spec;
+
+ bytes_left = buffer.length;
+ data = buffer.bytes;
+
+ while (bytes_left) {
+ type = get_aml_resource_type(*data);
+ if (uacpi_unlikely(type == UACPI_AML_RESOURCE_TYPE_INVALID))
+ return UACPI_STATUS_AML_INVALID_RESOURCE;
+
+ ret = get_aml_resource_size(data, bytes_left, &resource_size);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ spec = &aml_resources[type];
+ switch (spec->size_kind) {
+ case UACPI_AML_RESOURCE_SIZE_KIND_FIXED:
+ if (resource_size != spec->aml_size)
+ return UACPI_STATUS_AML_INVALID_RESOURCE;
+ break;
+ case UACPI_AML_RESOURCE_SIZE_KIND_VARIABLE:
+ if (resource_size < spec->aml_size)
+ return UACPI_STATUS_AML_INVALID_RESOURCE;
+ break;
+ case UACPI_AML_RESOURCE_SIZE_KIND_FIXED_OR_ONE_LESS:
+ if (resource_size != spec->aml_size &&
+ resource_size != (spec->aml_size - 1))
+ return UACPI_STATUS_AML_INVALID_RESOURCE;
+ break;
+ default:
+ return UACPI_STATUS_INTERNAL_ERROR;
+ }
+
+ if (spec->type == UACPI_AML_RESOURCE_SERIAL_CONNECTION) {
+ struct acpi_resource_serial *serial;
+
+ serial = (struct acpi_resource_serial*)data;
+
+ ret = validate_aml_serial_type(serial->type);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+ }
+
+ decision = cb(user, data, resource_size, spec);
+ switch (decision) {
+ case UACPI_ITERATION_DECISION_BREAK:
+ return UACPI_STATUS_OK;
+ case UACPI_ITERATION_DECISION_CONTINUE: {
+ uacpi_size total_size = resource_size;
+
+ total_size += aml_resource_kind_to_header_size[spec->resource_kind];
+ data += total_size;
+ bytes_left -= total_size;
+ break;
+ }
+ default:
+ return UACPI_STATUS_INTERNAL_ERROR;
+ }
+
+ if (type == UACPI_AML_RESOURCE_END_TAG)
+ return UACPI_STATUS_OK;
+ }
+
+ return UACPI_STATUS_NO_RESOURCE_END_TAG;
+}
+
+static uacpi_iteration_decision find_end(
+ void *opaque, uacpi_u8 *data, uacpi_u16 resource_size,
+ const struct uacpi_resource_spec *spec
+)
+{
+ uacpi_u8 **out_ptr = opaque;
+ UACPI_UNUSED(resource_size);
+
+ if (spec->type != UACPI_AML_RESOURCE_END_TAG)
+ return UACPI_ITERATION_DECISION_CONTINUE;
+
+ *out_ptr = data;
+ return UACPI_ITERATION_DECISION_BREAK;
+}
+
+static uacpi_size native_size_for_aml_resource(
+ uacpi_u8 *data, uacpi_u16 size, const struct uacpi_resource_spec *spec
+)
+{
+ uacpi_size final_size = spec->native_size;
+
+ if (spec->extra_size_for_native)
+ final_size += spec->extra_size_for_native(spec, data, size);
+
+ return UACPI_ALIGN_UP(final_size, sizeof(void*), uacpi_size);
+}
+
+uacpi_status uacpi_find_aml_resource_end_tag(
+ uacpi_data_view buffer, uacpi_size *out_offset
+)
+{
+ uacpi_u8 *end_tag_ptr = UACPI_NULL;
+ uacpi_status ret;
+
+ if (buffer.length == 0) {
+ *out_offset = 0;
+ return UACPI_STATUS_OK;
+ }
+
+ /*
+ * This returning UACPI_STATUS_OK guarantees that end_tag_ptr is set to
+ * a valid value because a missing end tag would produce a
+ * UACPI_STATUS_NO_RESOURCE_END_TAG error.
+ */
+ ret = uacpi_for_each_aml_resource(buffer, find_end, &end_tag_ptr);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ *out_offset = end_tag_ptr - buffer.bytes;
+ return UACPI_STATUS_OK;
+}
+
+struct resource_conversion_ctx {
+ union {
+ void *buf;
+ uacpi_u8 *byte_buf;
+ uacpi_size size;
+ };
+ uacpi_status st;
+ uacpi_bool just_one;
+};
+
+static uacpi_iteration_decision conditional_continue(
+ struct resource_conversion_ctx *ctx
+)
+{
+ return ctx->just_one ? UACPI_ITERATION_DECISION_BREAK :
+ UACPI_ITERATION_DECISION_CONTINUE;
+}
+
+// Opcodes that are the same for both AML->native and native->AML
+#define CONVERSION_OPCODES_COMMON(native_buf) \
+ case UACPI_RESOURCE_CONVERT_OPCODE_END: \
+ return conditional_continue(ctx); \
+ \
+ case UACPI_RESOURCE_CONVERT_OPCODE_FIELD_8: \
+ case UACPI_RESOURCE_CONVERT_OPCODE_FIELD_16: \
+ case UACPI_RESOURCE_CONVERT_OPCODE_FIELD_32: \
+ case UACPI_RESOURCE_CONVERT_OPCODE_FIELD_64: { \
+ uacpi_u8 bytes; \
+ \
+ bytes = 1 << (insn->code - UACPI_RESOURCE_CONVERT_OPCODE_FIELD_8); \
+ accumulator = insn->f3.imm == 0xFF ? 0 : accumulator + insn->f3.imm; \
+ \
+ uacpi_memcpy(dst, src, bytes * UACPI_MAX(1, accumulator)); \
+ accumulator = 0; \
+ break; \
+ } \
+ \
+ case UACPI_RESOURCE_CONVERT_OPCODE_SKIP_IF_AML_SIZE_LESS_THAN: \
+ if (aml_size < insn->f1.arg0) \
+ pc += insn->f3.imm; \
+ break; \
+ case UACPI_RESOURCE_CONVERT_OPCODE_SKIP_IF_NOT_EQUALS: \
+ if (insn->f1.arg0 != accumulator) \
+ pc += insn->f3.imm; \
+ break; \
+ \
+ case UACPI_RESOURCE_CONVERT_OPCODE_SET_TO_IMM: \
+ uacpi_memcpy(dst, &insn->f3.imm, sizeof(insn->f3.imm)); \
+ break; \
+ \
+ case UACPI_RESOURCE_CONVERT_OPCODE_LOAD_IMM: \
+ accumulator = insn->f3.imm; \
+ break; \
+ \
+ case UACPI_RESOURCE_CONVERT_OPCODE_LOAD_8_STORE: \
+ uacpi_memcpy_zerout(&accumulator, src, sizeof(accumulator), 1); \
+ uacpi_memcpy(dst, &accumulator, 1); \
+ \
+ if (insn->f3.imm) \
+ accumulator *= insn->f3.imm; \
+ break; \
+ \
+ case UACPI_RESOURCE_CONVERT_OPCODE_LOAD_8_NATIVE: \
+ case UACPI_RESOURCE_CONVERT_OPCODE_LOAD_16_NATIVE: { \
+ uacpi_u8 bytes; \
+ \
+ bytes = \
+ 1 << (insn->code - UACPI_RESOURCE_CONVERT_OPCODE_LOAD_8_NATIVE); \
+ uacpi_memcpy_zerout( \
+ &accumulator, native_buf, sizeof(accumulator), bytes \
+ ); \
+ break; \
+ } \
+ \
+ case UACPI_RESOURCE_CONVERT_OPCODE_UNREACHABLE: \
+ default: \
+ if (insn->code != UACPI_RESOURCE_CONVERT_OPCODE_UNREACHABLE) { \
+ uacpi_error("unhandled resource conversion opcode %d\n", \
+ insn->code); \
+ } else { \
+ uacpi_error("tried to execute unreachable conversion opcode\n"); \
+ } \
+ ctx->st = UACPI_STATUS_INTERNAL_ERROR; \
+ return UACPI_ITERATION_DECISION_BREAK;
+
+#define PTR_AT(ptr, offset) (void*)((uacpi_u8*)(ptr) + (offset))
+
+#define NATIVE_OFFSET(res, offset) \
+ PTR_AT(res, (offset) + (sizeof(uacpi_u32) * 2))
+
+#define NATIVE_FIELD(res, name, field) \
+ NATIVE_OFFSET(res, NATIVE_O(name, field))
+
+#define CHECK_AML_OOB(offset, prefix, what) \
+ if (uacpi_unlikely(offset > ((uacpi_u32)aml_size + header_size))) { \
+ uacpi_error(prefix what " is OOB: %zu > %u\n", \
+ (uacpi_size)offset, (uacpi_u32)aml_size + header_size); \
+ ctx->st = UACPI_STATUS_AML_INVALID_RESOURCE; \
+ return UACPI_ITERATION_DECISION_BREAK; \
+ }
+
+#define CHECK_AML_OFFSET_BASE(offset, what) \
+ if (uacpi_unlikely(offset < base_aml_size_with_header)) { \
+ uacpi_error( \
+ "invalid " what " offset: %zu, expected at least %u\n", \
+ (uacpi_size)offset, base_aml_size_with_header); \
+ ctx->st = UACPI_STATUS_AML_INVALID_RESOURCE; \
+ return UACPI_ITERATION_DECISION_BREAK; \
+ }
+
+#define CHECK_AML_OFFSET(offset, what) \
+ CHECK_AML_OOB(offset, "end of ", what) \
+ CHECK_AML_OFFSET_BASE(offset, what)
+
+static uacpi_resource_type aml_serial_to_native_type(
+ uacpi_u8 type
+)
+{
+ return (type - ACPI_SERIAL_TYPE_I2C) +
+ UACPI_RESOURCE_TYPE_SERIAL_I2C_CONNECTION;
+}
+
+static uacpi_iteration_decision do_aml_resource_to_native(
+ void *opaque, uacpi_u8 *data, uacpi_u16 aml_size,
+ const struct uacpi_resource_spec *spec
+)
+{
+ struct resource_conversion_ctx *ctx = opaque;
+ uacpi_resource *resource = ctx->buf;
+ const struct uacpi_resource_convert_instruction *insns, *insn;
+ uacpi_u8 header_size, pc = 0;
+ uacpi_u8 *src, *dst;
+ void *resource_end;
+ uacpi_u16 base_aml_size;
+ uacpi_u32 base_aml_size_with_header, accumulator = 0;
+
+ insns = spec->to_native;
+
+ header_size = aml_resource_kind_to_header_size[spec->resource_kind];
+ resource->type = spec->native_type;
+ resource->length = native_size_for_aml_resource(data, aml_size, spec);
+ resource_end = ctx->byte_buf + spec->native_size;
+ ctx->byte_buf += resource->length;
+
+ base_aml_size = base_aml_size_with_header = spec->aml_size;
+ base_aml_size_with_header += header_size;
+
+ if (insns == UACPI_NULL)
+ return conditional_continue(ctx);
+
+ for (;;) {
+ insn = &insns[pc++];
+
+ src = data + insn->f1.aml_offset;
+ dst = NATIVE_OFFSET(resource, insn->f2.native_offset);
+
+ switch (insn->code) {
+ case UACPI_RESOURCE_CONVERT_OPCODE_PACKED_ARRAY_8:
+ case UACPI_RESOURCE_CONVERT_OPCODE_PACKED_ARRAY_16: {
+ uacpi_size i, j, max_bit;
+ uacpi_u16 value;
+
+ if (insn->code == UACPI_RESOURCE_CONVERT_OPCODE_PACKED_ARRAY_16) {
+ max_bit = 16;
+ uacpi_memcpy(&value, src, sizeof(uacpi_u16));
+ } else {
+ max_bit = 8;
+ uacpi_memcpy_zerout(
+ &value, src, sizeof(value), sizeof(uacpi_u8)
+ );
+ }
+
+ for (i = 0, j = 0; i < max_bit; ++i) {
+ if (!(value & (1 << i)))
+ continue;
+
+ dst[j++] = i;
+ }
+
+ uacpi_memcpy(NATIVE_OFFSET(resource, insn->f3.arg2), &j, 1);
+ break;
+ }
+
+ case UACPI_RESOURCE_CONVERT_OPCODE_BIT_FIELD_1:
+ case UACPI_RESOURCE_CONVERT_OPCODE_BIT_FIELD_2:
+ case UACPI_RESOURCE_CONVERT_OPCODE_BIT_FIELD_3:
+ case UACPI_RESOURCE_CONVERT_OPCODE_BIT_FIELD_6:{
+ uacpi_u8 mask, value;
+
+ mask = (insn->code - UACPI_RESOURCE_CONVERT_OPCODE_BIT_FIELD_1) + 1;
+ mask = (1 << mask) - 1;
+
+ value = (*src >> insn->f3.imm) & mask;
+ uacpi_memcpy(dst, &value, sizeof(value));
+ break;
+ }
+
+ case UACPI_RESOURCE_CONVERT_OPCODE_LOAD_AML_SIZE_32:
+ accumulator = aml_size;
+ uacpi_memcpy(dst, &accumulator, 4);
+ break;
+
+ case UACPI_RESOURCE_CONVERT_OPCODE_RESOURCE_SOURCE:
+ case UACPI_RESOURCE_CONVERT_OPCODE_RESOURCE_SOURCE_NO_INDEX:
+ case UACPI_RESOURCE_CONVERT_OPCODE_RESOURCE_LABEL: {
+ uacpi_size offset = 0, max_offset, length = 0;
+ uacpi_char *src_string, *dst_string;
+ union {
+ void *ptr;
+ uacpi_resource_source *source;
+ uacpi_resource_label *label;
+ } dst_name = { 0 };
+
+ dst_name.ptr = dst;
+
+ /*
+ * Check if the string is bounded by anything at the top. If not, we
+ * just assume it ends at the end of the resource.
+ */
+ if (insn->f3.arg2) {
+ uacpi_memcpy_zerout(&max_offset, data + insn->f3.arg2,
+ sizeof(max_offset), sizeof(uacpi_u16));
+ CHECK_AML_OFFSET(max_offset, "resource source");
+ } else {
+ max_offset = aml_size + header_size;
+ }
+
+ offset += base_aml_size_with_header;
+ offset += accumulator;
+
+ if (insn->code != UACPI_RESOURCE_CONVERT_OPCODE_RESOURCE_LABEL)
+ dst_name.source->index_present = UACPI_TRUE;
+
+ if (offset >= max_offset) {
+ if (insn->code == UACPI_RESOURCE_CONVERT_OPCODE_RESOURCE_SOURCE)
+ dst_name.source->index_present = UACPI_FALSE;
+ break;
+ }
+
+ src_string = PTR_AT(data, offset);
+
+ if (insn->code == UACPI_RESOURCE_CONVERT_OPCODE_RESOURCE_SOURCE) {
+ uacpi_memcpy(&dst_name.source->index, src_string++, 1);
+ offset++;
+ }
+
+ if (offset == max_offset)
+ break;
+
+ while (offset++ < max_offset) {
+ if (src_string[length++] == '\0')
+ break;
+ }
+
+ if (src_string[length - 1] != '\0') {
+ uacpi_error("non-null-terminated resource source string\n");
+ ctx->st = UACPI_STATUS_AML_INVALID_RESOURCE;
+ return UACPI_ITERATION_DECISION_BREAK;
+ }
+
+ dst_string = PTR_AT(resource_end, accumulator);
+ uacpi_memcpy(dst_string, src_string, length);
+
+ if (insn->code == UACPI_RESOURCE_CONVERT_OPCODE_RESOURCE_LABEL) {
+ dst_name.label->length = length;
+ dst_name.label->string = dst_string;
+ } else {
+ dst_name.source->length = length;
+ dst_name.source->string = dst_string;
+ }
+
+ break;
+ }
+
+ case UACPI_RESOURCE_CONVERT_OPCODE_LOAD_PIN_TABLE_LENGTH:
+ uacpi_memcpy_zerout(&accumulator, src,
+ sizeof(accumulator), sizeof(uacpi_u16));
+ CHECK_AML_OFFSET(accumulator, "pin table");
+
+ accumulator -= base_aml_size_with_header;
+ break;
+
+ case UACPI_RESOURCE_CONVERT_OPCODE_PIN_TABLE: {
+ uacpi_u16 entry_count = accumulator / 2;
+
+ /*
+ * Pin table is stored right at the end of the resource buffer,
+ * copy the data there.
+ */
+ uacpi_memcpy(
+ resource_end,
+ data + base_aml_size_with_header,
+ accumulator
+ );
+
+ // Set pin_table_length
+ uacpi_memcpy(dst, &entry_count, sizeof(entry_count));
+
+ // Set pin_table pointer
+ uacpi_memcpy(NATIVE_OFFSET(resource, insn->f3.arg2),
+ &resource_end, sizeof(void*));
+ break;
+ }
+
+ case UACPI_RESOURCE_CONVERT_OPCODE_VENDOR_DATA: {
+ uacpi_size length;
+ uacpi_u16 data_offset, offset_from_end;
+ void *native_dst, *vendor_data;
+
+ uacpi_memcpy(&data_offset, src, sizeof(data_offset));
+ CHECK_AML_OFFSET(data_offset, "vendor data");
+
+ vendor_data = data + data_offset;
+
+ /*
+ * Rebase the offset to cut off the header as it's not included
+ * in the size fields.
+ */
+ data_offset -= header_size;
+
+ length = aml_size - data_offset;
+ if (length == 0)
+ break;
+
+ uacpi_memcpy(dst, &length, sizeof(uacpi_u16));
+
+ offset_from_end = data_offset - base_aml_size;
+ native_dst = PTR_AT(resource_end, offset_from_end);
+
+ uacpi_memcpy(native_dst, vendor_data, length);
+ uacpi_memcpy(NATIVE_OFFSET(resource, insn->f3.arg2),
+ &native_dst, sizeof(void*));
+ break;
+ }
+
+ case UACPI_RESOURCE_CONVERT_OPCODE_SERIAL_TYPE_SPECIFIC: {
+ uacpi_resource_serial_bus_common *serial_bus_common;
+ uacpi_u8 serial_type, extra_size, type_length;
+
+ serial_bus_common = &resource->serial_bus_common;
+ serial_type = *src;
+ serial_bus_common->type = serial_type;
+ resource->type = aml_serial_to_native_type(serial_type);
+
+ /*
+ * Now that we know the serial type rebase the end pointers and
+ * sizes.
+ */
+ resource_end = PTR_AT(
+ resource_end,
+ aml_serial_resource_to_extra_native_size[serial_type]
+ );
+ extra_size = aml_serial_resource_to_extra_aml_size[serial_type];
+ base_aml_size += extra_size;
+ base_aml_size_with_header += extra_size;
+
+ type_length = serial_bus_common->type_data_length;
+ if (uacpi_unlikely(type_length < extra_size)) {
+ uacpi_error(
+ "invalid type-specific data length: %d, "
+ "expected at least %d\n", type_length, extra_size
+ );
+ ctx->st = UACPI_STATUS_AML_INVALID_RESOURCE;
+ return UACPI_ITERATION_DECISION_BREAK;
+ }
+
+ /*
+ * Calculate the length of the vendor data. All the extra data
+ * beyond the end of type-specific size is considered vendor data.
+ */
+ accumulator = type_length - extra_size;
+ if (accumulator == 0)
+ break;
+
+ serial_bus_common->vendor_data_length = accumulator;
+ serial_bus_common->vendor_data = resource_end;
+ uacpi_memcpy(
+ resource_end,
+ data + base_aml_size_with_header,
+ accumulator
+ );
+ break;
+ }
+
+ CONVERSION_OPCODES_COMMON(dst)
+ }
+ }
+}
+
+static uacpi_iteration_decision accumulate_native_buffer_size(
+ void *opaque, uacpi_u8 *data, uacpi_u16 resource_size,
+ const struct uacpi_resource_spec *spec
+)
+{
+ struct resource_conversion_ctx *ctx = opaque;
+ uacpi_size size_for_this;
+
+ size_for_this = native_size_for_aml_resource(data, resource_size, spec);
+ if (size_for_this == 0 || (ctx->size + size_for_this) < ctx->size) {
+ uacpi_error("invalid native size for aml resource: %zu\n",
+ size_for_this);
+ ctx->st = UACPI_STATUS_AML_INVALID_RESOURCE;
+ return UACPI_ITERATION_DECISION_BREAK;
+ }
+
+ ctx->size += size_for_this;
+ return conditional_continue(ctx);
+}
+
+static uacpi_status eval_resource_helper(
+ uacpi_namespace_node *node, const uacpi_char *method,
+ uacpi_object **out_obj
+)
+{
+ uacpi_status ret;
+ uacpi_bool is_device;
+
+ ret = uacpi_namespace_node_is(node, UACPI_OBJECT_DEVICE, &is_device);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ if (uacpi_unlikely(!is_device))
+ return UACPI_STATUS_INVALID_ARGUMENT;
+
+ return uacpi_eval_simple_buffer(
+ node, method, out_obj
+ );
+}
+
+uacpi_status uacpi_native_resources_from_aml(
+ uacpi_data_view aml_buffer, uacpi_resources **out_resources
+)
+{
+ uacpi_status ret;
+ struct resource_conversion_ctx ctx = { 0 };
+ uacpi_resources *resources;
+
+ ret = uacpi_for_each_aml_resource(
+ aml_buffer, accumulate_native_buffer_size, &ctx
+ );
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ if (uacpi_unlikely_error(ctx.st))
+ return ctx.st;
+
+ // Realistically any resource buffer bigger than this is probably a bug
+ if (uacpi_unlikely(ctx.size > (5 * 1024u * 1024u))) {
+ uacpi_error("bug: bogus native resource buffer size %zu\n", ctx.size);
+ return UACPI_STATUS_INTERNAL_ERROR;
+ }
+
+ resources = uacpi_kernel_alloc_zeroed(ctx.size + sizeof(uacpi_resources));
+ if (uacpi_unlikely(resources == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+ resources->length = ctx.size;
+ resources->entries = UACPI_PTR_ADD(resources, sizeof(uacpi_resources));
+
+ uacpi_memzero(&ctx, sizeof(ctx));
+ ctx.buf = resources->entries;
+
+ ret = uacpi_for_each_aml_resource(aml_buffer, do_aml_resource_to_native, &ctx);
+ if (uacpi_unlikely_error(ret)) {
+ uacpi_free_resources(resources);
+ return ret;
+ }
+
+ *out_resources = resources;
+ return ret;
+}
+
+uacpi_status uacpi_get_resource_from_buffer(
+ uacpi_data_view aml_buffer, uacpi_resource **out_resource
+)
+{
+ uacpi_status ret;
+ struct resource_conversion_ctx ctx = {
+ .just_one = UACPI_TRUE,
+ };
+ uacpi_resource *resource;
+
+ ret = uacpi_for_each_aml_resource(
+ aml_buffer, accumulate_native_buffer_size, &ctx
+ );
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ resource = uacpi_kernel_alloc_zeroed(ctx.size);
+ if (uacpi_unlikely(resource == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ uacpi_memzero(&ctx, sizeof(ctx));
+ ctx.buf = resource;
+ ctx.just_one = UACPI_TRUE;
+
+ ret = uacpi_for_each_aml_resource(aml_buffer, do_aml_resource_to_native, &ctx);
+ if (uacpi_unlikely_error(ret)) {
+ uacpi_free_resource(resource);
+ return ret;
+ }
+
+ *out_resource = resource;
+ return ret;
+}
+
+void uacpi_free_resources(uacpi_resources *resources)
+{
+ if (resources == UACPI_NULL)
+ return;
+
+ uacpi_free(resources, sizeof(uacpi_resources) + resources->length);
+}
+
+void uacpi_free_resource(uacpi_resource *resource)
+{
+ if (resource == UACPI_NULL)
+ return;
+
+ uacpi_free(resource, resource->length);
+}
+
+static uacpi_status extract_native_resources_from_method(
+ uacpi_namespace_node *device, const uacpi_char *method,
+ uacpi_resources **out_resources
+)
+{
+ uacpi_status ret;
+ uacpi_object *obj;
+ uacpi_data_view buffer;
+
+ ret = eval_resource_helper(device, method, &obj);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ uacpi_buffer_to_view(obj->buffer, &buffer);
+
+ ret = uacpi_native_resources_from_aml(buffer, out_resources);
+ uacpi_object_unref(obj);
+
+ return ret;
+}
+
+uacpi_status uacpi_get_current_resources(
+ uacpi_namespace_node *device, uacpi_resources **out_resources
+)
+{
+ return extract_native_resources_from_method(device, "_CRS", out_resources);
+}
+
+uacpi_status uacpi_get_possible_resources(
+ uacpi_namespace_node *device, uacpi_resources **out_resources
+)
+{
+ return extract_native_resources_from_method(device, "_PRS", out_resources);
+}
+
+uacpi_status uacpi_get_device_resources(
+ uacpi_namespace_node *device, const uacpi_char *method,
+ uacpi_resources **out_resources
+)
+{
+ return extract_native_resources_from_method(device, method, out_resources);
+}
+
+uacpi_status uacpi_for_each_resource(
+ uacpi_resources *resources, uacpi_resource_iteration_callback cb, void *user
+)
+{
+ uacpi_size bytes_left = resources->length;
+ uacpi_resource *current = resources->entries;
+ uacpi_iteration_decision decision;
+
+ while (bytes_left) {
+ // At least the head bytes
+ if (uacpi_unlikely(bytes_left < 4)) {
+ uacpi_error("corrupted resource buffer %p length %zu\n",
+ resources, resources->length);
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+
+ if (uacpi_unlikely(current->type > UACPI_RESOURCE_TYPE_MAX)) {
+ uacpi_error("invalid resource type %d\n", current->type);
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+
+ if (uacpi_unlikely(current->length > bytes_left)) {
+ uacpi_error("corrupted resource@%p length %u (%zu bytes left)\n",
+ current, current->length, bytes_left);
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+
+ decision = cb(user, current);
+
+ if (decision == UACPI_ITERATION_DECISION_BREAK ||
+ current->type == UACPI_RESOURCE_TYPE_END_TAG)
+ return UACPI_STATUS_OK;
+
+ bytes_left -= current->length;
+ current = (uacpi_resource*)((uacpi_u8*)current + current->length);
+ }
+
+ return UACPI_STATUS_NO_RESOURCE_END_TAG;
+}
+
+uacpi_status uacpi_for_each_device_resource(
+ uacpi_namespace_node *device, const uacpi_char *method,
+ uacpi_resource_iteration_callback cb, void *user
+)
+{
+ uacpi_status ret;
+ uacpi_resources *resources;
+
+ ret = extract_native_resources_from_method(device, method, &resources);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ ret = uacpi_for_each_resource(resources, cb, user);
+ uacpi_free_resources(resources);
+
+ return ret;
+}
+
+static const struct uacpi_resource_spec *resource_spec_from_native(
+ uacpi_resource *resource
+)
+{
+ return &aml_resources[native_resource_to_type[resource->type]];
+}
+
+static uacpi_size aml_size_for_native_resource(
+ uacpi_resource *resource, const struct uacpi_resource_spec *spec
+)
+{
+ return spec->size_for_aml ?
+ spec->size_for_aml(spec, resource) :
+ aml_size_with_header(spec);
+}
+
+static uacpi_iteration_decision do_native_resource_to_aml(
+ void *opaque, uacpi_resource *resource
+)
+{
+ struct resource_conversion_ctx *ctx = opaque;
+ const struct uacpi_resource_spec *spec;
+ const struct uacpi_resource_convert_instruction *insns, *insn;
+ uacpi_u8 pc = 0;
+ uacpi_u8 *dst_base, *src, *dst;
+ uacpi_u32 aml_size, base_aml_size_with_header, accumulator = 0;
+ void *resource_end;
+
+ spec = resource_spec_from_native(resource);
+ aml_size = aml_size_for_native_resource(resource, spec);
+ insns = spec->to_aml;
+
+ dst_base = ctx->byte_buf;
+ ctx->byte_buf += aml_size;
+ aml_size -= aml_resource_kind_to_header_size[spec->resource_kind];
+
+ base_aml_size_with_header = spec->aml_size;
+ base_aml_size_with_header += aml_resource_kind_to_header_size[
+ spec->resource_kind
+ ];
+ resource_end = PTR_AT(resource, spec->native_size);
+
+ if (spec->resource_kind == UACPI_AML_RESOURCE_KIND_LARGE) {
+ *dst_base = ACPI_LARGE_ITEM | type_to_aml_resource[spec->type];
+ uacpi_memcpy(dst_base + 1, &aml_size, sizeof(uacpi_u16));
+ } else {
+ *dst_base = type_to_aml_resource[spec->type] << ACPI_SMALL_ITEM_NAME_IDX;
+ *dst_base |= aml_size;
+ }
+
+ if (insns == UACPI_NULL)
+ return UACPI_ITERATION_DECISION_CONTINUE;
+
+ for (;;) {
+ insn = &insns[pc++];
+
+ src = NATIVE_OFFSET(resource, insn->f2.native_offset);
+ dst = dst_base + insn->f1.aml_offset;
+
+ switch (insn->code) {
+ case UACPI_RESOURCE_CONVERT_OPCODE_PACKED_ARRAY_8:
+ case UACPI_RESOURCE_CONVERT_OPCODE_PACKED_ARRAY_16: {
+ uacpi_u8 i, *array_size, bytes = 1;
+ uacpi_u16 mask = 0;
+
+ array_size = NATIVE_OFFSET(resource, insn->f3.arg2);
+ for (i = 0; i < *array_size; ++i)
+ mask |= 1 << src[i];
+
+ if (insn->code == UACPI_RESOURCE_CONVERT_OPCODE_PACKED_ARRAY_16)
+ bytes = 2;
+
+ uacpi_memcpy(dst, &mask, bytes);
+ break;
+ }
+
+ case UACPI_RESOURCE_CONVERT_OPCODE_BIT_FIELD_1:
+ case UACPI_RESOURCE_CONVERT_OPCODE_BIT_FIELD_2:
+ case UACPI_RESOURCE_CONVERT_OPCODE_BIT_FIELD_3:
+ case UACPI_RESOURCE_CONVERT_OPCODE_BIT_FIELD_6:
+ *dst |= *src << insn->f3.imm;
+ break;
+
+ case UACPI_RESOURCE_CONVERT_OPCODE_LOAD_AML_SIZE_32:
+ accumulator = aml_size;
+ break;
+
+ case UACPI_RESOURCE_CONVERT_OPCODE_RESOURCE_SOURCE:
+ case UACPI_RESOURCE_CONVERT_OPCODE_RESOURCE_SOURCE_NO_INDEX:
+ case UACPI_RESOURCE_CONVERT_OPCODE_RESOURCE_LABEL: {
+ uacpi_size source_offset, length;
+ uacpi_u8 *dst_string;
+ const uacpi_char *src_string;
+ union {
+ void *ptr;
+ uacpi_resource_source *source;
+ uacpi_resource_label *label;
+ } src_name = { 0 };
+
+ src_name.ptr = src;
+
+ source_offset = base_aml_size_with_header + accumulator;
+ dst_string = dst_base + source_offset;
+
+ if (insn->f1.aml_offset)
+ uacpi_memcpy(dst, &source_offset, sizeof(uacpi_u16));
+
+ if (insn->code == UACPI_RESOURCE_CONVERT_OPCODE_RESOURCE_SOURCE &&
+ src_name.source->index_present)
+ uacpi_memcpy(dst_string++, &src_name.source->index, 1);
+
+ if (insn->code == UACPI_RESOURCE_CONVERT_OPCODE_RESOURCE_LABEL) {
+ length = src_name.label->length;
+ src_string = src_name.label->string;
+ } else {
+ length = src_name.source->length;
+ src_string = src_name.source->string;
+ }
+
+ if (length == 0)
+ break;
+
+ if (uacpi_unlikely(src_string == UACPI_NULL)) {
+ uacpi_error(
+ "source string length is %zu but the pointer is NULL\n",
+ length
+ );
+ ctx->st = UACPI_STATUS_INVALID_ARGUMENT;
+ return UACPI_ITERATION_DECISION_BREAK;
+ }
+
+ uacpi_memcpy(dst_string, src_string, length);
+ break;
+ }
+
+ case UACPI_RESOURCE_CONVERT_OPCODE_LOAD_PIN_TABLE_LENGTH:
+ uacpi_memcpy_zerout(&accumulator, src,
+ sizeof(accumulator), sizeof(uacpi_u16));
+ accumulator *= sizeof(uacpi_u16);
+ break;
+
+ case UACPI_RESOURCE_CONVERT_OPCODE_PIN_TABLE:
+ /*
+ * The pin table resides right at the end of the base resource,
+ * set the offset to it in the AML we're encoding.
+ */
+ uacpi_memcpy(dst, &base_aml_size_with_header, sizeof(uacpi_u16));
+
+ /*
+ * Copy the actual data. It also resides right at the end of the
+ * native base resource.
+ */
+ uacpi_memcpy(
+ dst_base + base_aml_size_with_header,
+ resource_end,
+ accumulator
+ );
+ break;
+
+ case UACPI_RESOURCE_CONVERT_OPCODE_VENDOR_DATA: {
+ uacpi_u16 vendor_data_length, data_offset, vendor_data_offset;
+ uacpi_u8 *vendor_data;
+
+ // Read the vendor_data pointer
+ uacpi_memcpy(&vendor_data, NATIVE_OFFSET(resource, insn->f3.arg2),
+ sizeof(void*));
+ uacpi_memcpy(&vendor_data_length, src, sizeof(uacpi_u16));
+
+ if (vendor_data == UACPI_NULL) {
+ uacpi_size full_aml_size;
+
+ if (uacpi_unlikely(vendor_data_length != 0)) {
+ uacpi_error(
+ "vendor_data_length is %d, but pointer is NULL\n",
+ vendor_data_length
+ );
+ ctx->st = UACPI_STATUS_INVALID_ARGUMENT;
+ return UACPI_ITERATION_DECISION_BREAK;
+ }
+
+ /*
+ * There's no vendor data. The specification still mandates
+ * that we fill the vendor data offset field correctly, meaning
+ * we set it to the total length of the resource.
+ */
+ full_aml_size = aml_size;
+ full_aml_size += aml_resource_kind_to_header_size[
+ spec->resource_kind
+ ];
+
+ uacpi_memcpy(dst, &full_aml_size, sizeof(uacpi_u16));
+ break;
+ }
+
+ /*
+ * Calculate the offset of vendor data from the end of the native
+ * resource and use it since it matches the offset from the end of
+ * the AML resource.
+ *
+ * Non-zero value means there's a source string in between.
+ */
+ data_offset = vendor_data - (uacpi_u8*)resource_end;
+ vendor_data_offset = data_offset + base_aml_size_with_header;
+
+ // Write vendor_data_offset
+ uacpi_memcpy(dst, &vendor_data_offset, sizeof(uacpi_u16));
+
+ /*
+ * Write vendor_data_length, this field is right after
+ * vendor_data_offset, and is completely redundant, but it exists
+ * nonetheless.
+ */
+ uacpi_memcpy(
+ dst + sizeof(uacpi_u16),
+ &vendor_data_length,
+ sizeof(vendor_data_length)
+ );
+
+ // Finally write the data itself
+ uacpi_memcpy(
+ dst_base + vendor_data_offset,
+ vendor_data,
+ vendor_data_length
+ );
+ break;
+ }
+
+ case UACPI_RESOURCE_CONVERT_OPCODE_SERIAL_TYPE_SPECIFIC: {
+ uacpi_u8 serial_type = *src;
+ *dst = serial_type;
+
+ ctx->st = validate_aml_serial_type(serial_type);
+ if (uacpi_unlikely_error(ctx->st))
+ return UACPI_ITERATION_DECISION_BREAK;
+
+ if (uacpi_unlikely(resource->type !=
+ aml_serial_to_native_type(serial_type))) {
+ uacpi_error(
+ "native serial resource type %d doesn't match expected %d\n",
+ resource->type, aml_serial_to_native_type(serial_type)
+ );
+ ctx->st = UACPI_STATUS_INVALID_ARGUMENT;
+ return UACPI_ITERATION_DECISION_BREAK;
+ }
+
+ // Rebase the end pointer & size now that we know the serial type
+ resource_end = PTR_AT(
+ resource_end,
+ aml_serial_resource_to_extra_native_size[serial_type]
+ );
+ base_aml_size_with_header += aml_serial_resource_to_extra_aml_size[
+ serial_type
+ ];
+
+ accumulator = resource->serial_bus_common.vendor_data_length;
+ if (accumulator == 0)
+ break;
+
+ // Copy vendor data
+ uacpi_memcpy(
+ dst_base + base_aml_size_with_header,
+ resource_end,
+ accumulator
+ );
+ break;
+ }
+
+ CONVERSION_OPCODES_COMMON(src)
+ }
+ }
+}
+
+static uacpi_status native_resources_to_aml(
+ uacpi_resources *native_resources, void *aml_buffer
+)
+{
+ uacpi_status ret;
+ struct resource_conversion_ctx ctx = { 0 };
+
+ ctx.buf = aml_buffer;
+
+ ret = uacpi_for_each_resource(
+ native_resources, do_native_resource_to_aml, &ctx
+ );
+ if (ret == UACPI_STATUS_NO_RESOURCE_END_TAG) {
+ // An end tag is always included
+ uacpi_resource end_tag = { .type = UACPI_RESOURCE_TYPE_END_TAG };
+
+ do_native_resource_to_aml(&ctx, &end_tag);
+ ret = UACPI_STATUS_OK;
+ }
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ return ctx.st;
+}
+
+static uacpi_iteration_decision accumulate_aml_buffer_size(
+ void *opaque, uacpi_resource *resource
+)
+{
+ struct resource_conversion_ctx *ctx = opaque;
+ const struct uacpi_resource_spec *spec;
+ uacpi_size size_for_this;
+
+ // resource->type is sanitized to be valid here by the iteration function
+ spec = resource_spec_from_native(resource);
+
+ size_for_this = aml_size_for_native_resource(resource, spec);
+ if (size_for_this == 0 || (ctx->size + size_for_this) < ctx->size) {
+ uacpi_error("invalid aml size for native resource: %zu\n",
+ size_for_this);
+ ctx->st = UACPI_STATUS_INVALID_ARGUMENT;
+ return UACPI_ITERATION_DECISION_BREAK;
+ }
+
+ ctx->size += size_for_this;
+ return UACPI_ITERATION_DECISION_CONTINUE;
+}
+
+uacpi_status uacpi_native_resources_to_aml(
+ uacpi_resources *resources, uacpi_object **out_template
+)
+{
+ uacpi_status ret;
+ uacpi_object *obj;
+ void *buffer;
+ struct resource_conversion_ctx ctx = { 0 };
+
+ ret = uacpi_for_each_resource(
+ resources, accumulate_aml_buffer_size, &ctx
+ );
+ if (ret == UACPI_STATUS_NO_RESOURCE_END_TAG) {
+ // An end tag is always included
+ uacpi_resource end_tag = { .type = UACPI_RESOURCE_TYPE_END_TAG };
+
+ accumulate_aml_buffer_size(&ctx, &end_tag);
+ ret = UACPI_STATUS_OK;
+ }
+ if (uacpi_unlikely_error(ret))
+ return ret;
+ if (uacpi_unlikely_error(ctx.st))
+ return ctx.st;
+
+ // Same reasoning as native_resource_from_aml
+ if (uacpi_unlikely(ctx.size > (5 * 1024u * 1024u))) {
+ uacpi_error("bug: bogus target aml resource buffer size %zu\n",
+ ctx.size);
+ return UACPI_STATUS_INTERNAL_ERROR;
+ }
+
+ buffer = uacpi_kernel_alloc_zeroed(ctx.size);
+ if (uacpi_unlikely(buffer == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ obj = uacpi_create_object(UACPI_OBJECT_BUFFER);
+ if (uacpi_unlikely(obj == UACPI_NULL)) {
+ uacpi_free(buffer, ctx.size);
+ return UACPI_STATUS_OUT_OF_MEMORY;
+ }
+
+ obj->buffer->data = buffer;
+ obj->buffer->size = ctx.size;
+
+ ret = native_resources_to_aml(resources, buffer);
+ if (uacpi_unlikely_error(ret))
+ uacpi_object_unref(obj);
+
+ if (ret == UACPI_STATUS_OK)
+ *out_template = obj;
+
+ return ret;
+}
+
+uacpi_status uacpi_set_resources(
+ uacpi_namespace_node *device, uacpi_resources *resources
+)
+{
+ uacpi_status ret;
+ uacpi_object *res_template;
+ uacpi_object_array args;
+
+ ret = uacpi_native_resources_to_aml(resources, &res_template);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ args.objects = &res_template;
+ args.count = 1;
+ ret = uacpi_eval(device, "_SRS", &args, UACPI_NULL);
+
+ uacpi_object_unref(res_template);
+ return ret;
+}
+
+#endif // !UACPI_BAREBONES_MODE
diff --git a/sys/dev/acpi/uacpi/shareable.c b/sys/dev/acpi/uacpi/shareable.c
new file mode 100644
index 0000000..b42660a
--- /dev/null
+++ b/sys/dev/acpi/uacpi/shareable.c
@@ -0,0 +1,71 @@
+#include <uacpi/internal/shareable.h>
+#include <uacpi/internal/stdlib.h>
+#include <uacpi/platform/atomic.h>
+
+#ifndef UACPI_BAREBONES_MODE
+
+#define BUGGED_REFCOUNT 0xFFFFFFFF
+
+void uacpi_shareable_init(uacpi_handle handle)
+{
+ struct uacpi_shareable *shareable = handle;
+ shareable->reference_count = 1;
+}
+
+uacpi_bool uacpi_bugged_shareable(uacpi_handle handle)
+{
+ struct uacpi_shareable *shareable = handle;
+
+ if (uacpi_unlikely(shareable->reference_count == 0))
+ uacpi_make_shareable_bugged(shareable);
+
+ return uacpi_atomic_load32(&shareable->reference_count) == BUGGED_REFCOUNT;
+}
+
+void uacpi_make_shareable_bugged(uacpi_handle handle)
+{
+ struct uacpi_shareable *shareable = handle;
+ uacpi_atomic_store32(&shareable->reference_count, BUGGED_REFCOUNT);
+}
+
+uacpi_u32 uacpi_shareable_ref(uacpi_handle handle)
+{
+ struct uacpi_shareable *shareable = handle;
+
+ if (uacpi_unlikely(uacpi_bugged_shareable(shareable)))
+ return BUGGED_REFCOUNT;
+
+ return uacpi_atomic_inc32(&shareable->reference_count) - 1;
+}
+
+uacpi_u32 uacpi_shareable_unref(uacpi_handle handle)
+{
+ struct uacpi_shareable *shareable = handle;
+
+ if (uacpi_unlikely(uacpi_bugged_shareable(shareable)))
+ return BUGGED_REFCOUNT;
+
+ return uacpi_atomic_dec32(&shareable->reference_count) + 1;
+}
+
+void uacpi_shareable_unref_and_delete_if_last(
+ uacpi_handle handle, void (*do_free)(uacpi_handle)
+)
+{
+ if (handle == UACPI_NULL)
+ return;
+
+ if (uacpi_unlikely(uacpi_bugged_shareable(handle)))
+ return;
+
+ if (uacpi_shareable_unref(handle) == 1)
+ do_free(handle);
+}
+
+uacpi_u32 uacpi_shareable_refcount(uacpi_handle handle)
+{
+ struct uacpi_shareable *shareable = handle;
+ return uacpi_atomic_load32(&shareable->reference_count);
+}
+
+#endif // !UACPI_BAREBONES_MODE
diff --git a/sys/dev/acpi/uacpi/sleep.c b/sys/dev/acpi/uacpi/sleep.c
new file mode 100644
index 0000000..4736324
--- /dev/null
+++ b/sys/dev/acpi/uacpi/sleep.c
@@ -0,0 +1,616 @@
+#include <uacpi/sleep.h>
+#include <uacpi/internal/context.h>
+#include <uacpi/internal/log.h>
+#include <uacpi/internal/io.h>
+#include <uacpi/internal/registers.h>
+#include <uacpi/internal/event.h>
+#include <uacpi/platform/arch_helpers.h>
+
+#ifndef UACPI_BAREBONES_MODE
+
+#ifndef UACPI_REDUCED_HARDWARE
+#define CALL_SLEEP_FN(name, state) \
+ (uacpi_is_hardware_reduced() ? \
+ name##_hw_reduced(state) : name##_hw_full(state))
+#else
+#define CALL_SLEEP_FN(name, state) name##_hw_reduced(state);
+#endif
+
+static uacpi_status eval_wak(uacpi_u8 state);
+static uacpi_status eval_sst(uacpi_u8 value);
+
+#ifndef UACPI_REDUCED_HARDWARE
+uacpi_status uacpi_set_waking_vector(
+ uacpi_phys_addr addr32, uacpi_phys_addr addr64
+)
+{
+ struct acpi_facs *facs = g_uacpi_rt_ctx.facs;
+
+ if (facs == UACPI_NULL)
+ return UACPI_STATUS_OK;
+
+ facs->firmware_waking_vector = addr32;
+
+ // The 64-bit wake vector doesn't exist, we're done
+ if (facs->length < 32)
+ return UACPI_STATUS_OK;
+
+ // Only allow 64-bit wake vector on 1.0 and above FACS
+ if (facs->version >= 1)
+ facs->x_firmware_waking_vector = addr64;
+ else
+ facs->x_firmware_waking_vector = 0;
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status enter_sleep_state_hw_full(uacpi_u8 state)
+{
+ uacpi_status ret;
+ uacpi_u64 wake_status, pm1a, pm1b;
+
+ ret = uacpi_write_register_field(
+ UACPI_REGISTER_FIELD_WAK_STS, ACPI_PM1_STS_CLEAR
+ );
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ ret = uacpi_disable_all_gpes();
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ ret = uacpi_clear_all_events();
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ ret = uacpi_enable_all_wake_gpes();
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ ret = uacpi_read_register(UACPI_REGISTER_PM1_CNT, &pm1a);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ pm1a &= ~((uacpi_u64)(ACPI_PM1_CNT_SLP_TYP_MASK | ACPI_PM1_CNT_SLP_EN_MASK));
+ pm1b = pm1a;
+
+ pm1a |= g_uacpi_rt_ctx.last_sleep_typ_a << ACPI_PM1_CNT_SLP_TYP_IDX;
+ pm1b |= g_uacpi_rt_ctx.last_sleep_typ_b << ACPI_PM1_CNT_SLP_TYP_IDX;
+
+ /*
+ * Just like ACPICA, split writing SLP_TYP and SLP_EN to work around
+ * buggy firmware that can't handle both written at the same time.
+ */
+ ret = uacpi_write_registers(UACPI_REGISTER_PM1_CNT, pm1a, pm1b);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ pm1a |= ACPI_PM1_CNT_SLP_EN_MASK;
+ pm1b |= ACPI_PM1_CNT_SLP_EN_MASK;
+
+ if (state < UACPI_SLEEP_STATE_S4)
+ UACPI_ARCH_FLUSH_CPU_CACHE();
+
+ ret = uacpi_write_registers(UACPI_REGISTER_PM1_CNT, pm1a, pm1b);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ if (state > UACPI_SLEEP_STATE_S3) {
+ /*
+ * We're still here, this is a bug or very slow firmware.
+ * Just try spinning for a bit.
+ */
+ uacpi_u64 stalled_time = 0;
+
+ // 10 seconds max
+ while (stalled_time < (10 * 1000 * 1000)) {
+ uacpi_kernel_stall(100);
+ stalled_time += 100;
+ }
+
+ // Try one more time
+ ret = uacpi_write_registers(UACPI_REGISTER_PM1_CNT, pm1a, pm1b);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ // Nothing we can do here, give up
+ return UACPI_STATUS_HARDWARE_TIMEOUT;
+ }
+
+ do {
+ ret = uacpi_read_register_field(
+ UACPI_REGISTER_FIELD_WAK_STS, &wake_status
+ );
+ if (uacpi_unlikely_error(ret))
+ return ret;
+ } while (wake_status != 1);
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status prepare_for_wake_from_sleep_state_hw_full(uacpi_u8 state)
+{
+ uacpi_status ret;
+ uacpi_u64 pm1a, pm1b;
+ UACPI_UNUSED(state);
+
+ /*
+ * Some hardware apparently relies on S0 values being written to the PM1
+ * control register on wake, so do this here.
+ */
+
+ if (g_uacpi_rt_ctx.s0_sleep_typ_a == UACPI_SLEEP_TYP_INVALID)
+ goto out;
+
+ ret = uacpi_read_register(UACPI_REGISTER_PM1_CNT, &pm1a);
+ if (uacpi_unlikely_error(ret))
+ goto out;
+
+ pm1a &= ~((uacpi_u64)(ACPI_PM1_CNT_SLP_TYP_MASK | ACPI_PM1_CNT_SLP_EN_MASK));
+ pm1b = pm1a;
+
+ pm1a |= g_uacpi_rt_ctx.s0_sleep_typ_a << ACPI_PM1_CNT_SLP_TYP_IDX;
+ pm1b |= g_uacpi_rt_ctx.s0_sleep_typ_b << ACPI_PM1_CNT_SLP_TYP_IDX;
+
+ uacpi_write_registers(UACPI_REGISTER_PM1_CNT, pm1a, pm1b);
+out:
+ // Errors ignored intentionally, we don't want to abort because of this
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status wake_from_sleep_state_hw_full(uacpi_u8 state)
+{
+ uacpi_status ret;
+ g_uacpi_rt_ctx.last_sleep_typ_a = UACPI_SLEEP_TYP_INVALID;
+ g_uacpi_rt_ctx.last_sleep_typ_b = UACPI_SLEEP_TYP_INVALID;
+
+ // Set the status to 2 (waking) while we execute the wake method.
+ eval_sst(2);
+
+ ret = uacpi_disable_all_gpes();
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ ret = uacpi_enable_all_runtime_gpes();
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ eval_wak(state);
+
+ // Apparently some BIOSes expect us to clear this, so do it
+ uacpi_write_register_field(
+ UACPI_REGISTER_FIELD_WAK_STS, ACPI_PM1_STS_CLEAR
+ );
+
+ // Now that we're awake set the status to 1 (running)
+ eval_sst(1);
+
+ return UACPI_STATUS_OK;
+}
+#endif
+
+static uacpi_status get_slp_type_for_state(
+ uacpi_u8 state, uacpi_u8 *a, uacpi_u8 *b
+)
+{
+ uacpi_char path[] = "_S0";
+ uacpi_status ret;
+ uacpi_object *obj0, *obj1, *ret_obj = UACPI_NULL;
+
+ path[2] += state;
+
+ ret = uacpi_eval_typed(
+ uacpi_namespace_root(), path, UACPI_NULL,
+ UACPI_OBJECT_PACKAGE_BIT, &ret_obj
+ );
+ if (ret != UACPI_STATUS_OK) {
+ if (uacpi_unlikely(ret != UACPI_STATUS_NOT_FOUND)) {
+ uacpi_warn("error while evaluating %s: %s\n", path,
+ uacpi_status_to_string(ret));
+ } else {
+ uacpi_trace("sleep state %d is not supported as %s was not found\n",
+ state, path);
+ }
+ goto out;
+ }
+
+ switch (ret_obj->package->count) {
+ case 0:
+ uacpi_error("empty package while evaluating %s!\n", path);
+ ret = UACPI_STATUS_AML_INCOMPATIBLE_OBJECT_TYPE;
+ goto out;
+
+ case 1:
+ obj0 = ret_obj->package->objects[0];
+ if (uacpi_unlikely(obj0->type != UACPI_OBJECT_INTEGER)) {
+ uacpi_error(
+ "invalid object type at pkg[0] => %s when evaluating %s\n",
+ uacpi_object_type_to_string(obj0->type), path
+ );
+ goto out;
+ }
+
+ *a = obj0->integer;
+ *b = obj0->integer >> 8;
+ break;
+
+ default:
+ obj0 = ret_obj->package->objects[0];
+ obj1 = ret_obj->package->objects[1];
+
+ if (uacpi_unlikely(obj0->type != UACPI_OBJECT_INTEGER ||
+ obj1->type != UACPI_OBJECT_INTEGER)) {
+ uacpi_error(
+ "invalid object type when evaluating %s: "
+ "pkg[0] => %s, pkg[1] => %s\n", path,
+ uacpi_object_type_to_string(obj0->type),
+ uacpi_object_type_to_string(obj1->type)
+ );
+ ret = UACPI_STATUS_AML_INCOMPATIBLE_OBJECT_TYPE;
+ goto out;
+ }
+
+ *a = obj0->integer;
+ *b = obj1->integer;
+ break;
+ }
+
+out:
+ if (ret != UACPI_STATUS_OK) {
+ *a = UACPI_SLEEP_TYP_INVALID;
+ *b = UACPI_SLEEP_TYP_INVALID;
+ }
+
+ uacpi_object_unref(ret_obj);
+ return ret;
+}
+
+static uacpi_status eval_sleep_helper(
+ uacpi_namespace_node *parent, const uacpi_char *path, uacpi_u8 value
+)
+{
+ uacpi_object *arg;
+ uacpi_object_array args;
+ uacpi_status ret;
+
+ arg = uacpi_create_object(UACPI_OBJECT_INTEGER);
+ if (uacpi_unlikely(arg == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ arg->integer = value;
+ args.objects = &arg;
+ args.count = 1;
+
+ ret = uacpi_eval(parent, path, &args, UACPI_NULL);
+ switch (ret) {
+ case UACPI_STATUS_OK:
+ break;
+ case UACPI_STATUS_NOT_FOUND:
+ ret = UACPI_STATUS_OK;
+ break;
+ default:
+ uacpi_error("error while evaluating %s: %s\n",
+ path, uacpi_status_to_string(ret));
+ break;
+ }
+
+ uacpi_object_unref(arg);
+ return ret;
+}
+
+static uacpi_status eval_pts(uacpi_u8 state)
+{
+ return eval_sleep_helper(uacpi_namespace_root(), "_PTS", state);
+}
+
+static uacpi_status eval_wak(uacpi_u8 state)
+{
+ return eval_sleep_helper(uacpi_namespace_root(), "_WAK", state);
+}
+
+static uacpi_status eval_sst(uacpi_u8 value)
+{
+ return eval_sleep_helper(
+ uacpi_namespace_get_predefined(UACPI_PREDEFINED_NAMESPACE_SI),
+ "_SST", value
+ );
+}
+
+static uacpi_status eval_sst_for_state(enum uacpi_sleep_state state)
+{
+ uacpi_u8 arg;
+
+ /*
+ * This optional object is a control method that OSPM invokes to set the
+ * system status indicator as desired.
+ * Arguments:(1)
+ * Arg0 - An Integer containing the system status indicator identifier:
+ * 0 - No system state indication. Indicator off
+ * 1 - Working
+ * 2 - Waking
+ * 3 - Sleeping. Used to indicate system state S1, S2, or S3
+ * 4 - Sleeping with context saved to non-volatile storage
+ */
+ switch (state) {
+ case UACPI_SLEEP_STATE_S0:
+ arg = 1;
+ break;
+ case UACPI_SLEEP_STATE_S1:
+ case UACPI_SLEEP_STATE_S2:
+ case UACPI_SLEEP_STATE_S3:
+ arg = 3;
+ break;
+ case UACPI_SLEEP_STATE_S4:
+ arg = 4;
+ break;
+ case UACPI_SLEEP_STATE_S5:
+ arg = 0;
+ break;
+ default:
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+
+ return eval_sst(arg);
+}
+
+uacpi_status uacpi_prepare_for_sleep_state(enum uacpi_sleep_state state_enum)
+{
+ uacpi_u8 state = state_enum;
+ uacpi_status ret;
+
+ UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_NAMESPACE_INITIALIZED);
+
+ if (uacpi_unlikely(state > UACPI_SLEEP_STATE_S5))
+ return UACPI_STATUS_INVALID_ARGUMENT;
+
+ ret = get_slp_type_for_state(
+ state,
+ &g_uacpi_rt_ctx.last_sleep_typ_a,
+ &g_uacpi_rt_ctx.last_sleep_typ_b
+ );
+ if (ret != UACPI_STATUS_OK)
+ return ret;
+
+ ret = get_slp_type_for_state(
+ 0,
+ &g_uacpi_rt_ctx.s0_sleep_typ_a,
+ &g_uacpi_rt_ctx.s0_sleep_typ_b
+ );
+
+ ret = eval_pts(state);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ eval_sst_for_state(state);
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_u8 make_hw_reduced_sleep_control(uacpi_u8 slp_typ)
+{
+ uacpi_u8 value;
+
+ value = (slp_typ << ACPI_SLP_CNT_SLP_TYP_IDX);
+ value &= ACPI_SLP_CNT_SLP_TYP_MASK;
+ value |= ACPI_SLP_CNT_SLP_EN_MASK;
+
+ return value;
+}
+
+static uacpi_status enter_sleep_state_hw_reduced(uacpi_u8 state)
+{
+ uacpi_status ret;
+ uacpi_u8 sleep_control;
+ uacpi_u64 wake_status;
+ struct acpi_fadt *fadt = &g_uacpi_rt_ctx.fadt;
+
+ if (!fadt->sleep_control_reg.address || !fadt->sleep_status_reg.address)
+ return UACPI_STATUS_NOT_FOUND;
+
+ ret = uacpi_write_register_field(
+ UACPI_REGISTER_FIELD_HWR_WAK_STS,
+ ACPI_SLP_STS_CLEAR
+ );
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ sleep_control = make_hw_reduced_sleep_control(
+ g_uacpi_rt_ctx.last_sleep_typ_a
+ );
+
+ if (state < UACPI_SLEEP_STATE_S4)
+ UACPI_ARCH_FLUSH_CPU_CACHE();
+
+ /*
+ * To put the system into a sleep state, software will write the HW-reduced
+ * Sleep Type value (obtained from the \_Sx object in the DSDT) and the
+ * SLP_EN bit to the sleep control register.
+ */
+ ret = uacpi_write_register(UACPI_REGISTER_SLP_CNT, sleep_control);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ /*
+ * The OSPM then polls the WAK_STS bit of the SLEEP_STATUS_REG waiting for
+ * it to be one (1), indicating that the system has been transitioned
+ * back to the Working state.
+ */
+ do {
+ ret = uacpi_read_register_field(
+ UACPI_REGISTER_FIELD_HWR_WAK_STS, &wake_status
+ );
+ if (uacpi_unlikely_error(ret))
+ return ret;
+ } while (wake_status != 1);
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status prepare_for_wake_from_sleep_state_hw_reduced(uacpi_u8 state)
+{
+ uacpi_u8 sleep_control;
+ UACPI_UNUSED(state);
+
+ if (g_uacpi_rt_ctx.s0_sleep_typ_a == UACPI_SLEEP_TYP_INVALID)
+ goto out;
+
+ sleep_control = make_hw_reduced_sleep_control(
+ g_uacpi_rt_ctx.s0_sleep_typ_a
+ );
+ uacpi_write_register(UACPI_REGISTER_SLP_CNT, sleep_control);
+
+out:
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status wake_from_sleep_state_hw_reduced(uacpi_u8 state)
+{
+ g_uacpi_rt_ctx.last_sleep_typ_a = UACPI_SLEEP_TYP_INVALID;
+ g_uacpi_rt_ctx.last_sleep_typ_b = UACPI_SLEEP_TYP_INVALID;
+
+ // Set the status to 2 (waking) while we execute the wake method.
+ eval_sst(2);
+
+ eval_wak(state);
+
+ // Apparently some BIOSes expect us to clear this, so do it
+ uacpi_write_register_field(
+ UACPI_REGISTER_FIELD_HWR_WAK_STS, ACPI_SLP_STS_CLEAR
+ );
+
+ // Now that we're awake set the status to 1 (running)
+ eval_sst(1);
+
+ return UACPI_STATUS_OK;
+}
+
+uacpi_status uacpi_enter_sleep_state(enum uacpi_sleep_state state_enum)
+{
+ uacpi_u8 state = state_enum;
+
+ UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_NAMESPACE_INITIALIZED);
+
+ if (uacpi_unlikely(state > UACPI_SLEEP_STATE_MAX))
+ return UACPI_STATUS_INVALID_ARGUMENT;
+
+ if (uacpi_unlikely(g_uacpi_rt_ctx.last_sleep_typ_a > ACPI_SLP_TYP_MAX ||
+ g_uacpi_rt_ctx.last_sleep_typ_b > ACPI_SLP_TYP_MAX)) {
+ uacpi_error("invalid SLP_TYP values: 0x%02X:0x%02X\n",
+ g_uacpi_rt_ctx.last_sleep_typ_a,
+ g_uacpi_rt_ctx.last_sleep_typ_b);
+ return UACPI_STATUS_AML_BAD_ENCODING;
+ }
+
+ return CALL_SLEEP_FN(enter_sleep_state, state);
+}
+
+uacpi_status uacpi_prepare_for_wake_from_sleep_state(
+ uacpi_sleep_state state_enum
+)
+{
+ uacpi_u8 state = state_enum;
+
+ UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_NAMESPACE_INITIALIZED);
+
+ if (uacpi_unlikely(state > UACPI_SLEEP_STATE_MAX))
+ return UACPI_STATUS_INVALID_ARGUMENT;
+
+ return CALL_SLEEP_FN(prepare_for_wake_from_sleep_state, state);
+}
+
+uacpi_status uacpi_wake_from_sleep_state(
+ uacpi_sleep_state state_enum
+)
+{
+ uacpi_u8 state = state_enum;
+
+ UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_NAMESPACE_INITIALIZED);
+
+ if (uacpi_unlikely(state > UACPI_SLEEP_STATE_MAX))
+ return UACPI_STATUS_INVALID_ARGUMENT;
+
+ return CALL_SLEEP_FN(wake_from_sleep_state, state);
+}
+
+uacpi_status uacpi_reboot(void)
+{
+ uacpi_status ret;
+ uacpi_handle pci_dev = UACPI_NULL, io_handle = UACPI_NULL;
+ struct acpi_fadt *fadt = &g_uacpi_rt_ctx.fadt;
+ struct acpi_gas *reset_reg = &fadt->reset_reg;
+
+ /*
+ * Allow restarting earlier than namespace load so that the kernel can
+ * use this in case of some initialization error.
+ */
+ UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_SUBSYSTEM_INITIALIZED);
+
+ if (!(fadt->flags & ACPI_RESET_REG_SUP) || !reset_reg->address)
+ return UACPI_STATUS_NOT_FOUND;
+
+ switch (reset_reg->address_space_id) {
+ case UACPI_ADDRESS_SPACE_SYSTEM_IO:
+ /*
+ * For SystemIO we don't do any checking, and we ignore bit width
+ * because that's what NT does.
+ */
+ ret = uacpi_kernel_io_map(reset_reg->address, 1, &io_handle);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ ret = uacpi_kernel_io_write8(io_handle, 0, fadt->reset_value);
+ break;
+ case UACPI_ADDRESS_SPACE_SYSTEM_MEMORY:
+ ret = uacpi_write_register(UACPI_REGISTER_RESET, fadt->reset_value);
+ break;
+ case UACPI_ADDRESS_SPACE_PCI_CONFIG: {
+ uacpi_pci_address address = { 0 };
+
+ // Bus is assumed to be 0 here
+ address.segment = 0;
+ address.bus = 0;
+ address.device = (reset_reg->address >> 32) & 0xFF;
+ address.function = (reset_reg->address >> 16) & 0xFF;
+
+ ret = uacpi_kernel_pci_device_open(address, &pci_dev);
+ if (uacpi_unlikely_error(ret))
+ break;
+
+ ret = uacpi_kernel_pci_write8(
+ pci_dev, reset_reg->address & 0xFFFF, fadt->reset_value
+ );
+ break;
+ }
+ default:
+ uacpi_warn(
+ "unable to perform a reset: unsupported address space '%s' (%d)\n",
+ uacpi_address_space_to_string(reset_reg->address_space_id),
+ reset_reg->address_space_id
+ );
+ ret = UACPI_STATUS_UNIMPLEMENTED;
+ }
+
+ if (ret == UACPI_STATUS_OK) {
+ /*
+ * This should've worked but we're still here.
+ * Spin for a bit then give up.
+ */
+ uacpi_u64 stalled_time = 0;
+
+ while (stalled_time < (1000 * 1000)) {
+ uacpi_kernel_stall(100);
+ stalled_time += 100;
+ }
+
+ uacpi_error("reset timeout\n");
+ ret = UACPI_STATUS_HARDWARE_TIMEOUT;
+ }
+
+ if (pci_dev != UACPI_NULL)
+ uacpi_kernel_pci_device_close(pci_dev);
+ if (io_handle != UACPI_NULL)
+ uacpi_kernel_io_unmap(io_handle);
+
+ return ret;
+}
+
+#endif // !UACPI_BAREBONES_MODE
diff --git a/sys/dev/acpi/uacpi/stdlib.c b/sys/dev/acpi/uacpi/stdlib.c
new file mode 100644
index 0000000..98344f1
--- /dev/null
+++ b/sys/dev/acpi/uacpi/stdlib.c
@@ -0,0 +1,728 @@
+#include <uacpi/internal/stdlib.h>
+#include <uacpi/internal/utilities.h>
+
+#ifdef UACPI_USE_BUILTIN_STRING
+
+#ifndef uacpi_memcpy
+void *uacpi_memcpy(void *dest, const void *src, uacpi_size count)
+{
+ uacpi_char *cd = dest;
+ const uacpi_char *cs = src;
+
+ while (count--)
+ *cd++ = *cs++;
+
+ return dest;
+}
+#endif
+
+#ifndef uacpi_memmove
+void *uacpi_memmove(void *dest, const void *src, uacpi_size count)
+{
+ uacpi_char *cd = dest;
+ const uacpi_char *cs = src;
+
+ if (src < dest) {
+ cs += count;
+ cd += count;
+
+ while (count--)
+ *--cd = *--cs;
+ } else {
+ while (count--)
+ *cd++ = *cs++;
+ }
+
+ return dest;
+}
+#endif
+
+#ifndef uacpi_memset
+void *uacpi_memset(void *dest, uacpi_i32 ch, uacpi_size count)
+{
+ uacpi_u8 fill = ch;
+ uacpi_u8 *cdest = dest;
+
+ while (count--)
+ *cdest++ = fill;
+
+ return dest;
+}
+#endif
+
+#ifndef uacpi_memcmp
+uacpi_i32 uacpi_memcmp(const void *lhs, const void *rhs, uacpi_size count)
+{
+ const uacpi_u8 *byte_lhs = lhs;
+ const uacpi_u8 *byte_rhs = rhs;
+ uacpi_size i;
+
+ for (i = 0; i < count; ++i) {
+ if (byte_lhs[i] != byte_rhs[i])
+ return byte_lhs[i] - byte_rhs[i];
+ }
+
+ return 0;
+}
+#endif
+
+#endif // UACPI_USE_BUILTIN_STRING
+
+#ifndef uacpi_strlen
+uacpi_size uacpi_strlen(const uacpi_char *str)
+{
+ const uacpi_char *str1;
+
+ for (str1 = str; *str1; str1++);
+
+ return str1 - str;
+}
+#endif
+
+#ifndef UACPI_BAREBONES_MODE
+
+#ifndef uacpi_strnlen
+uacpi_size uacpi_strnlen(const uacpi_char *str, uacpi_size max)
+{
+ const uacpi_char *str1;
+
+ for (str1 = str; max-- && *str1; str1++);
+
+ return str1 - str;
+}
+#endif
+
+#ifndef uacpi_strcmp
+uacpi_i32 uacpi_strcmp(const uacpi_char *lhs, const uacpi_char *rhs)
+{
+ uacpi_size i = 0;
+ typedef const uacpi_u8 *cucp;
+
+ while (lhs[i] && rhs[i]) {
+ if (lhs[i] != rhs[i])
+ return *(cucp)&lhs[i] - *(cucp)&rhs[i];
+
+ i++;
+ }
+
+ return *(cucp)&lhs[i] - *(cucp)&rhs[i];
+}
+#endif
+
+void uacpi_memcpy_zerout(void *dst, const void *src,
+ uacpi_size dst_size, uacpi_size src_size)
+{
+ uacpi_size bytes_to_copy = UACPI_MIN(src_size, dst_size);
+
+ if (bytes_to_copy)
+ uacpi_memcpy(dst, src, bytes_to_copy);
+
+ if (dst_size > bytes_to_copy)
+ uacpi_memzero((uacpi_u8 *)dst + bytes_to_copy, dst_size - bytes_to_copy);
+}
+
+uacpi_u8 uacpi_bit_scan_forward(uacpi_u64 value)
+{
+#if defined(_MSC_VER) && !defined(__clang__)
+ unsigned char ret;
+ unsigned long index;
+
+#ifdef _WIN64
+ ret = _BitScanForward64(&index, value);
+ if (ret == 0)
+ return 0;
+
+ return (uacpi_u8)index + 1;
+#else
+ ret = _BitScanForward(&index, value);
+ if (ret == 0) {
+ ret = _BitScanForward(&index, value >> 32);
+ if (ret == 0)
+ return 0;
+
+ return (uacpi_u8)index + 33;
+ }
+
+ return (uacpi_u8)index + 1;
+#endif
+
+#elif defined(__WATCOMC__)
+ // TODO: Use compiler intrinsics or inline ASM here
+ uacpi_u8 index;
+ uacpi_u64 mask = 1;
+
+ for (index = 1; index <= 64; index++, mask <<= 1) {
+ if (value & mask) {
+ return index;
+ }
+ }
+
+ return 0;
+#else
+ return __builtin_ffsll(value);
+#endif
+}
+
+uacpi_u8 uacpi_bit_scan_backward(uacpi_u64 value)
+{
+#if defined(_MSC_VER) && !defined(__clang__)
+ unsigned char ret;
+ unsigned long index;
+
+#ifdef _WIN64
+ ret = _BitScanReverse64(&index, value);
+ if (ret == 0)
+ return 0;
+
+ return (uacpi_u8)index + 1;
+#else
+ ret = _BitScanReverse(&index, value >> 32);
+ if (ret == 0) {
+ ret = _BitScanReverse(&index, value);
+ if (ret == 0)
+ return 0;
+
+ return (uacpi_u8)index + 1;
+ }
+
+ return (uacpi_u8)index + 33;
+#endif
+
+#elif defined(__WATCOMC__)
+ // TODO: Use compiler intrinsics or inline ASM here
+ uacpi_u8 index;
+ uacpi_u64 mask = (1ull << 63);
+
+ for (index = 64; index > 0; index--, mask >>= 1) {
+ if (value & mask) {
+ return index;
+ }
+ }
+
+ return 0;
+#else
+ if (value == 0)
+ return 0;
+
+ return 64 - __builtin_clzll(value);
+#endif
+}
+
+#ifndef UACPI_NATIVE_ALLOC_ZEROED
+void *uacpi_builtin_alloc_zeroed(uacpi_size size)
+{
+ void *ptr;
+
+ ptr = uacpi_kernel_alloc(size);
+ if (uacpi_unlikely(ptr == UACPI_NULL))
+ return ptr;
+
+ uacpi_memzero(ptr, size);
+ return ptr;
+}
+#endif
+
+#endif // !UACPI_BAREBONES_MODE
+
+#ifndef uacpi_vsnprintf
+struct fmt_buf_state {
+ uacpi_char *buffer;
+ uacpi_size capacity;
+ uacpi_size bytes_written;
+};
+
+struct fmt_spec {
+ uacpi_u8 is_signed : 1;
+ uacpi_u8 prepend : 1;
+ uacpi_u8 uppercase : 1;
+ uacpi_u8 left_justify : 1;
+ uacpi_u8 alternate_form : 1;
+ uacpi_u8 has_precision : 1;
+ uacpi_char pad_char;
+ uacpi_char prepend_char;
+ uacpi_u64 min_width;
+ uacpi_u64 precision;
+ uacpi_u32 base;
+};
+
+static void write_one(struct fmt_buf_state *fb_state, uacpi_char c)
+{
+ if (fb_state->bytes_written < fb_state->capacity)
+ fb_state->buffer[fb_state->bytes_written] = c;
+
+ fb_state->bytes_written++;
+}
+
+static void write_many(
+ struct fmt_buf_state *fb_state, const uacpi_char *string, uacpi_size count
+)
+{
+ if (fb_state->bytes_written < fb_state->capacity) {
+ uacpi_size count_to_write;
+
+ count_to_write = UACPI_MIN(
+ count, fb_state->capacity - fb_state->bytes_written
+ );
+ uacpi_memcpy(
+ &fb_state->buffer[fb_state->bytes_written], string, count_to_write
+ );
+ }
+
+ fb_state->bytes_written += count;
+}
+
+static uacpi_char hex_char(uacpi_bool upper, uacpi_u64 value)
+{
+ static const uacpi_char upper_hex[] = "0123456789ABCDEF";
+ static const uacpi_char lower_hex[] = "0123456789abcdef";
+
+ return (upper ? upper_hex : lower_hex)[value];
+}
+
+static void write_padding(
+ struct fmt_buf_state *fb_state, struct fmt_spec *fm, uacpi_size repr_size
+)
+{
+ uacpi_u64 mw = fm->min_width;
+
+ if (mw <= repr_size)
+ return;
+
+ mw -= repr_size;
+
+ while (mw--)
+ write_one(fb_state, fm->left_justify ? ' ' : fm->pad_char);
+}
+
+#define REPR_BUFFER_SIZE 32
+
+static void write_integer(
+ struct fmt_buf_state *fb_state, struct fmt_spec *fm, uacpi_u64 value
+)
+{
+ uacpi_char repr_buffer[REPR_BUFFER_SIZE];
+ uacpi_size index = REPR_BUFFER_SIZE;
+ uacpi_u64 remainder;
+ uacpi_char repr;
+ uacpi_bool negative = UACPI_FALSE;
+ uacpi_size repr_size;
+
+ if (fm->is_signed) {
+ uacpi_i64 as_ll = value;
+
+ if (as_ll < 0) {
+ value = -as_ll;
+ negative = UACPI_TRUE;
+ }
+ }
+
+ if (fm->prepend || negative)
+ write_one(fb_state, negative ? '-' : fm->prepend_char);
+
+ while (value) {
+ remainder = value % fm->base;
+ value /= fm->base;
+
+ if (fm->base == 16) {
+ repr = hex_char(fm->uppercase, remainder);
+ } else if (fm->base == 8 || fm->base == 10) {
+ repr = remainder + '0';
+ } else {
+ repr = '?';
+ }
+
+ repr_buffer[--index] = repr;
+ }
+ repr_size = REPR_BUFFER_SIZE - index;
+
+ if (repr_size == 0) {
+ repr_buffer[--index] = '0';
+ repr_size = 1;
+ }
+
+ if (fm->alternate_form) {
+ if (fm->base == 16) {
+ repr_buffer[--index] = fm->uppercase ? 'X' : 'x';
+ repr_buffer[--index] = '0';
+ repr_size += 2;
+ } else if (fm->base == 8) {
+ repr_buffer[--index] = '0';
+ repr_size += 1;
+ }
+ }
+
+ if (fm->left_justify) {
+ write_many(fb_state, &repr_buffer[index], repr_size);
+ write_padding(fb_state, fm, repr_size);
+ } else {
+ write_padding(fb_state, fm, repr_size);
+ write_many(fb_state, &repr_buffer[index], repr_size);
+ }
+}
+
+static uacpi_bool string_has_at_least(
+ const uacpi_char *string, uacpi_size characters
+)
+{
+ while (*string) {
+ if (--characters == 0)
+ return UACPI_TRUE;
+
+ string++;
+ }
+
+ return UACPI_FALSE;
+}
+
+static uacpi_bool consume_digits(
+ const uacpi_char **string, uacpi_size *out_size
+)
+{
+ uacpi_size size = 0;
+
+ for (;;) {
+ char c = **string;
+ if (c < '0' || c > '9')
+ break;
+
+ size++;
+ *string += 1;
+ }
+
+ if (size == 0)
+ return UACPI_FALSE;
+
+ *out_size = size;
+ return UACPI_TRUE;
+}
+
+enum parse_number_mode {
+ PARSE_NUMBER_MODE_MAYBE,
+ PARSE_NUMBER_MODE_MUST,
+};
+
+static uacpi_bool parse_number(
+ const uacpi_char **fmt, enum parse_number_mode mode, uacpi_u64 *out_value
+)
+{
+ uacpi_status ret;
+ uacpi_size num_digits;
+ const uacpi_char *digits = *fmt;
+
+ if (!consume_digits(fmt, &num_digits))
+ return mode != PARSE_NUMBER_MODE_MUST;
+
+ ret = uacpi_string_to_integer(digits, num_digits, UACPI_BASE_DEC, out_value);
+ return ret == UACPI_STATUS_OK;
+}
+
+static uacpi_bool consume(const uacpi_char **string, const uacpi_char *token)
+{
+ uacpi_size token_size;
+
+ token_size = uacpi_strlen(token);
+
+ if (!string_has_at_least(*string, token_size))
+ return UACPI_FALSE;
+
+ if (!uacpi_memcmp(*string, token, token_size)) {
+ *string += token_size;
+ return UACPI_TRUE;
+ }
+
+ return UACPI_FALSE;
+}
+
+static uacpi_bool is_one_of(uacpi_char c, const uacpi_char *list)
+{
+ for (; *list; list++) {
+ if (c == *list)
+ return UACPI_TRUE;
+ }
+
+ return UACPI_FALSE;
+}
+
+static uacpi_bool consume_one_of(
+ const uacpi_char **string, const uacpi_char *list, uacpi_char *consumed_char
+)
+{
+ uacpi_char c = **string;
+ if (!c)
+ return UACPI_FALSE;
+
+ if (is_one_of(c, list)) {
+ *consumed_char = c;
+ *string += 1;
+ return UACPI_TRUE;
+ }
+
+ return UACPI_FALSE;
+}
+
+static uacpi_u32 base_from_specifier(uacpi_char specifier)
+{
+ switch (specifier)
+ {
+ case 'x':
+ case 'X':
+ return 16;
+ case 'o':
+ return 8;
+ default:
+ return 10;
+ }
+}
+
+static uacpi_bool is_uppercase_specifier(uacpi_char specifier)
+{
+ return specifier == 'X';
+}
+
+static const uacpi_char *find_next_conversion(
+ const uacpi_char *fmt, uacpi_size *offset
+)
+{
+ *offset = 0;
+
+ while (*fmt) {
+ if (*fmt == '%')
+ return fmt;
+
+ fmt++;
+ *offset += 1;
+ }
+
+ return UACPI_NULL;
+}
+
+uacpi_i32 uacpi_vsnprintf(
+ uacpi_char *buffer, uacpi_size capacity, const uacpi_char *fmt,
+ uacpi_va_list vlist
+)
+{
+ struct fmt_buf_state fb_state = { 0 };
+ uacpi_u64 value;
+ const uacpi_char *next_conversion;
+ uacpi_size next_offset;
+ uacpi_char flag;
+
+ fb_state.buffer = buffer;
+ fb_state.capacity = capacity;
+ fb_state.bytes_written = 0;
+
+ while (*fmt) {
+ struct fmt_spec fm = {
+ .pad_char = ' ',
+ .base = 10,
+ };
+ next_conversion = find_next_conversion(fmt, &next_offset);
+
+ if (next_offset)
+ write_many(&fb_state, fmt, next_offset);
+
+ if (!next_conversion)
+ break;
+
+ fmt = next_conversion;
+ if (consume(&fmt, "%%")) {
+ write_one(&fb_state, '%');
+ continue;
+ }
+
+ // consume %
+ fmt++;
+
+ while (consume_one_of(&fmt, "+- 0#", &flag)) {
+ switch (flag) {
+ case '+':
+ case ' ':
+ fm.prepend = UACPI_TRUE;
+ fm.prepend_char = flag;
+ continue;
+ case '-':
+ fm.left_justify = UACPI_TRUE;
+ continue;
+ case '0':
+ fm.pad_char = '0';
+ continue;
+ case '#':
+ fm.alternate_form = UACPI_TRUE;
+ continue;
+ default:
+ return -1;
+ }
+ }
+
+ if (consume(&fmt, "*")) {
+ fm.min_width = uacpi_va_arg(vlist, int);
+ } else if (!parse_number(&fmt, PARSE_NUMBER_MODE_MAYBE, &fm.min_width)) {
+ return -1;
+ }
+
+ if (consume(&fmt, ".")) {
+ fm.has_precision = UACPI_TRUE;
+
+ if (consume(&fmt, "*")) {
+ fm.precision = uacpi_va_arg(vlist, int);
+ } else {
+ if (!parse_number(&fmt, PARSE_NUMBER_MODE_MUST, &fm.precision))
+ return -1;
+ }
+ }
+
+ flag = 0;
+
+ if (consume(&fmt, "c")) {
+ uacpi_char c = uacpi_va_arg(vlist, int);
+ write_one(&fb_state, c);
+ continue;
+ }
+
+ if (consume(&fmt, "s")) {
+ const uacpi_char *string = uacpi_va_arg(vlist, uacpi_char*);
+ uacpi_size i;
+
+ if (uacpi_unlikely(string == UACPI_NULL))
+ string = "<null>";
+
+ for (i = 0; (!fm.has_precision || i < fm.precision) && string[i]; ++i)
+ write_one(&fb_state, string[i]);
+ while (i++ < fm.min_width)
+ write_one(&fb_state, ' ');
+ continue;
+ }
+
+ if (consume(&fmt, "p")) {
+ value = (uacpi_uintptr)uacpi_va_arg(vlist, void*);
+ fm.base = 16;
+ fm.min_width = UACPI_POINTER_SIZE * 2;
+ fm.pad_char = '0';
+ goto write_int;
+ }
+
+ if (consume(&fmt, "hh")) {
+ if (consume(&fmt, "d") || consume(&fmt, "i")) {
+ value = (signed char)uacpi_va_arg(vlist, int);
+ fm.is_signed = UACPI_TRUE;
+ } else if (consume_one_of(&fmt, "oxXu", &flag)) {
+ value = (unsigned char)uacpi_va_arg(vlist, int);
+ } else {
+ return -1;
+ }
+ goto write_int;
+ }
+
+ if (consume(&fmt, "h")) {
+ if (consume(&fmt, "d") || consume(&fmt, "i")) {
+ value = (signed short)uacpi_va_arg(vlist, int);
+ fm.is_signed = UACPI_TRUE;
+ } else if (consume_one_of(&fmt, "oxXu", &flag)) {
+ value = (unsigned short)uacpi_va_arg(vlist, int);
+ } else {
+ return -1;
+ }
+ goto write_int;
+ }
+
+ if (consume(&fmt, "ll") ||
+ (sizeof(uacpi_size) == sizeof(long long) && consume(&fmt, "z"))) {
+ if (consume(&fmt, "d") || consume(&fmt, "i")) {
+ value = uacpi_va_arg(vlist, long long);
+ fm.is_signed = UACPI_TRUE;
+ } else if (consume_one_of(&fmt, "oxXu", &flag)) {
+ value = uacpi_va_arg(vlist, unsigned long long);
+ } else {
+ return -1;
+ }
+ goto write_int;
+ }
+
+ if (consume(&fmt, "l") ||
+ (sizeof(uacpi_size) == sizeof(long) && consume(&fmt, "z"))) {
+ if (consume(&fmt, "d") || consume(&fmt, "i")) {
+ value = uacpi_va_arg(vlist, long);
+ fm.is_signed = UACPI_TRUE;
+ } else if (consume_one_of(&fmt, "oxXu", &flag)) {
+ value = uacpi_va_arg(vlist, unsigned long);
+ } else {
+ return -1;
+ }
+ goto write_int;
+ }
+
+ if (consume(&fmt, "d") || consume(&fmt, "i")) {
+ value = uacpi_va_arg(vlist, uacpi_i32);
+ fm.is_signed = UACPI_TRUE;
+ } else if (consume_one_of(&fmt, "oxXu", &flag)) {
+ value = uacpi_va_arg(vlist, uacpi_u32);
+ } else {
+ return -1;
+ }
+
+ write_int:
+ if (flag != 0) {
+ fm.base = base_from_specifier(flag);
+ fm.uppercase = is_uppercase_specifier(flag);
+ }
+
+ write_integer(&fb_state, &fm, value);
+ }
+
+ if (fb_state.capacity) {
+ uacpi_size last_char;
+
+ last_char = UACPI_MIN(fb_state.bytes_written, fb_state.capacity - 1);
+ fb_state.buffer[last_char] = '\0';
+ }
+
+ return fb_state.bytes_written;
+}
+#endif
+
+#ifndef uacpi_snprintf
+uacpi_i32 uacpi_snprintf(
+ uacpi_char *buffer, uacpi_size capacity, const uacpi_char *fmt, ...
+)
+{
+ uacpi_va_list vlist;
+ uacpi_i32 ret;
+
+ uacpi_va_start(vlist, fmt);
+ ret = uacpi_vsnprintf(buffer, capacity, fmt, vlist);
+ uacpi_va_end(vlist);
+
+ return ret;
+}
+#endif
+
+#ifndef UACPI_FORMATTED_LOGGING
+void uacpi_log(uacpi_log_level lvl, const uacpi_char *str, ...)
+{
+ uacpi_char buf[UACPI_PLAIN_LOG_BUFFER_SIZE];
+ int ret;
+
+ uacpi_va_list vlist;
+ uacpi_va_start(vlist, str);
+
+ ret = uacpi_vsnprintf(buf, sizeof(buf), str, vlist);
+ if (uacpi_unlikely(ret < 0))
+ return;
+
+ /*
+ * If this log message is too large for the configured buffer size, cut off
+ * the end and transform into "...\n" to indicate that it didn't fit and
+ * prevent the newline from being truncated.
+ */
+ if (uacpi_unlikely(ret >= UACPI_PLAIN_LOG_BUFFER_SIZE)) {
+ buf[UACPI_PLAIN_LOG_BUFFER_SIZE - 5] = '.';
+ buf[UACPI_PLAIN_LOG_BUFFER_SIZE - 4] = '.';
+ buf[UACPI_PLAIN_LOG_BUFFER_SIZE - 3] = '.';
+ buf[UACPI_PLAIN_LOG_BUFFER_SIZE - 2] = '\n';
+ }
+
+ uacpi_kernel_log(lvl, buf);
+
+ uacpi_va_end(vlist);
+}
+#endif
diff --git a/sys/dev/acpi/uacpi/tables.c b/sys/dev/acpi/uacpi/tables.c
new file mode 100644
index 0000000..df7d7b9
--- /dev/null
+++ b/sys/dev/acpi/uacpi/tables.c
@@ -0,0 +1,1399 @@
+#include <uacpi/internal/tables.h>
+#include <uacpi/internal/utilities.h>
+#include <uacpi/internal/stdlib.h>
+#include <uacpi/internal/interpreter.h>
+#include <uacpi/internal/mutex.h>
+
+DYNAMIC_ARRAY_WITH_INLINE_STORAGE(
+ table_array, struct uacpi_installed_table, UACPI_STATIC_TABLE_ARRAY_LEN
+)
+DYNAMIC_ARRAY_WITH_INLINE_STORAGE_IMPL(
+ table_array, struct uacpi_installed_table, static
+)
+
+static struct table_array tables;
+static uacpi_bool early_table_access;
+static uacpi_table_installation_handler installation_handler;
+
+#ifndef UACPI_BAREBONES_MODE
+
+static uacpi_handle table_mutex;
+
+#define ENSURE_TABLES_ONLINE() \
+ do { \
+ if (!early_table_access) \
+ UACPI_ENSURE_INIT_LEVEL_AT_LEAST( \
+ UACPI_INIT_LEVEL_SUBSYSTEM_INITIALIZED \
+ ); \
+ } while (0)
+
+#else
+
+/*
+ * Use a dummy function instead of a macro to prevent the following error:
+ * error: statement with no effect [-Werror=unused-value]
+ */
+static inline uacpi_status dummy_mutex_acquire_release(uacpi_handle mtx)
+{
+ UACPI_UNUSED(mtx);
+ return UACPI_STATUS_OK;
+}
+
+#define table_mutex UACPI_NULL
+#define uacpi_acquire_native_mutex_may_be_null dummy_mutex_acquire_release
+#define uacpi_release_native_mutex_may_be_null dummy_mutex_acquire_release
+
+#define ENSURE_TABLES_ONLINE() \
+ do { \
+ if (!early_table_access) \
+ return UACPI_STATUS_INIT_LEVEL_MISMATCH; \
+ } while (0)
+
+#endif // !UACPI_BAREBONES_MODE
+
+static uacpi_status table_install_physical_with_origin_unlocked(
+ uacpi_phys_addr phys, enum uacpi_table_origin origin,
+ const uacpi_char *expected_signature, uacpi_table *out_table
+);
+static uacpi_status table_install_with_origin_unlocked(
+ void *virt, enum uacpi_table_origin origin, uacpi_table *out_table
+);
+
+UACPI_PACKED(struct uacpi_rxsdt {
+ struct acpi_sdt_hdr hdr;
+ uacpi_u8 ptr_bytes[];
+})
+
+static void dump_table_header(
+ uacpi_phys_addr phys_addr, void *hdr
+)
+{
+ struct acpi_sdt_hdr *sdt = hdr;
+
+ if (uacpi_signatures_match(hdr, ACPI_FACS_SIGNATURE)) {
+ uacpi_info(
+ "FACS 0x%016"UACPI_PRIX64" %08X\n", UACPI_FMT64(phys_addr),
+ sdt->length
+ );
+ return;
+ }
+
+ if (!uacpi_memcmp(hdr, ACPI_RSDP_SIGNATURE, sizeof(ACPI_RSDP_SIGNATURE) - 1)) {
+ struct acpi_rsdp *rsdp = hdr;
+
+ uacpi_info(
+ "RSDP 0x%016"UACPI_PRIX64" %08X v%02X (%6.6s)\n",
+ UACPI_FMT64(phys_addr), rsdp->revision >= 2 ? rsdp->length : 20,
+ rsdp->revision, rsdp->oemid
+ );
+ return;
+ }
+
+ uacpi_info(
+ "%.4s 0x%016"UACPI_PRIX64" %08X v%02X (%6.6s %8.8s)\n",
+ sdt->signature, UACPI_FMT64(phys_addr), sdt->length, sdt->revision,
+ sdt->oemid, sdt->oem_table_id
+ );
+}
+
+static uacpi_status initialize_from_rxsdt(uacpi_phys_addr rxsdt_addr,
+ uacpi_size entry_size)
+{
+ struct uacpi_rxsdt *rxsdt;
+ uacpi_size i, entry_bytes, map_len = sizeof(*rxsdt);
+ uacpi_phys_addr entry_addr;
+ uacpi_status ret;
+
+ rxsdt = uacpi_kernel_map(rxsdt_addr, map_len);
+ if (rxsdt == UACPI_NULL)
+ return UACPI_STATUS_MAPPING_FAILED;
+
+ dump_table_header(rxsdt_addr, rxsdt);
+
+ ret = uacpi_check_table_signature(rxsdt,
+ entry_size == 8 ? ACPI_XSDT_SIGNATURE : ACPI_RSDT_SIGNATURE);
+ if (uacpi_unlikely_error(ret))
+ goto error_out;
+
+ map_len = rxsdt->hdr.length;
+ uacpi_kernel_unmap(rxsdt, sizeof(*rxsdt));
+
+ if (uacpi_unlikely(map_len < (sizeof(*rxsdt) + entry_size)))
+ return UACPI_STATUS_INVALID_TABLE_LENGTH;
+
+ // Make sure length is aligned to entry size so we don't OOB
+ entry_bytes = map_len - sizeof(*rxsdt);
+ entry_bytes &= ~(entry_size - 1);
+
+ rxsdt = uacpi_kernel_map(rxsdt_addr, map_len);
+ if (uacpi_unlikely(rxsdt == UACPI_NULL))
+ return UACPI_STATUS_MAPPING_FAILED;
+
+ ret = uacpi_verify_table_checksum(rxsdt, map_len);
+ if (uacpi_unlikely_error(ret))
+ goto error_out;
+
+ for (i = 0; i < entry_bytes; i += entry_size) {
+ uacpi_u64 entry_phys_addr_large = 0;
+ uacpi_memcpy(&entry_phys_addr_large, &rxsdt->ptr_bytes[i], entry_size);
+
+ if (!entry_phys_addr_large)
+ continue;
+
+ entry_addr = uacpi_truncate_phys_addr_with_warn(entry_phys_addr_large);
+ ret = uacpi_table_install_physical_with_origin(
+ entry_addr, UACPI_TABLE_ORIGIN_FIRMWARE_PHYSICAL, UACPI_NULL
+ );
+ if (uacpi_unlikely(ret != UACPI_STATUS_OK &&
+ ret != UACPI_STATUS_OVERRIDDEN))
+ goto error_out;
+ }
+
+ ret = UACPI_STATUS_OK;
+
+error_out:
+ uacpi_kernel_unmap(rxsdt, map_len);
+ return ret;
+}
+
+static uacpi_status initialize_from_rsdp(void)
+{
+ uacpi_status ret;
+ uacpi_phys_addr rsdp_phys;
+ struct acpi_rsdp *rsdp;
+ uacpi_phys_addr rxsdt;
+ uacpi_size rxsdt_entry_size;
+
+ g_uacpi_rt_ctx.is_rev1 = UACPI_TRUE;
+
+ ret = uacpi_kernel_get_rsdp(&rsdp_phys);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ rsdp = uacpi_kernel_map(rsdp_phys, sizeof(struct acpi_rsdp));
+ if (rsdp == UACPI_NULL)
+ return UACPI_STATUS_MAPPING_FAILED;
+
+ dump_table_header(rsdp_phys, rsdp);
+
+ if (rsdp->revision > 1 && rsdp->xsdt_addr &&
+ !uacpi_check_flag(UACPI_FLAG_BAD_XSDT))
+ {
+ rxsdt = uacpi_truncate_phys_addr_with_warn(rsdp->xsdt_addr);
+ rxsdt_entry_size = 8;
+ } else {
+ rxsdt = (uacpi_phys_addr)rsdp->rsdt_addr;
+ rxsdt_entry_size = 4;
+ }
+
+ uacpi_kernel_unmap(rsdp, sizeof(struct acpi_rsdp));
+
+ if (!rxsdt) {
+ uacpi_error("both RSDT & XSDT tables are NULL!\n");
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+
+ return initialize_from_rxsdt(rxsdt, rxsdt_entry_size);
+}
+
+uacpi_status uacpi_setup_early_table_access(
+ void *temporary_buffer, uacpi_size buffer_size
+)
+{
+ uacpi_status ret;
+
+#ifndef UACPI_BAREBONES_MODE
+ UACPI_ENSURE_INIT_LEVEL_IS(UACPI_INIT_LEVEL_EARLY);
+#endif
+ if (uacpi_unlikely(early_table_access))
+ return UACPI_STATUS_INIT_LEVEL_MISMATCH;
+
+ if (uacpi_unlikely(buffer_size < sizeof(struct uacpi_installed_table)))
+ return UACPI_STATUS_INVALID_ARGUMENT;
+
+ uacpi_logger_initialize();
+
+ tables.dynamic_storage = temporary_buffer;
+ tables.dynamic_capacity = buffer_size / sizeof(struct uacpi_installed_table);
+ early_table_access = UACPI_TRUE;
+
+ ret = initialize_from_rsdp();
+ if (uacpi_unlikely_error(ret))
+ uacpi_deinitialize_tables();
+
+ return ret;
+}
+
+#ifndef UACPI_BAREBONES_MODE
+static uacpi_iteration_decision warn_if_early_referenced(
+ void *user, struct uacpi_installed_table *tbl, uacpi_size idx
+)
+{
+ UACPI_UNUSED(user);
+
+ if (uacpi_unlikely(tbl->reference_count != 0)) {
+ uacpi_warn(
+ "table "UACPI_PRI_TBL_HDR" (%zu) still has %d early reference(s)!\n",
+ UACPI_FMT_TBL_HDR(&tbl->hdr), idx, tbl->reference_count
+ );
+ }
+
+ return UACPI_ITERATION_DECISION_CONTINUE;
+}
+
+uacpi_status uacpi_initialize_tables(void)
+{
+ if (early_table_access) {
+ uacpi_size num_tables;
+
+ uacpi_for_each_table(0, warn_if_early_referenced, UACPI_NULL);
+
+ // Reallocate the user buffer into a normal heap array
+ num_tables = table_array_size(&tables);
+ if (num_tables > table_array_inline_capacity(&tables)) {
+ void *new_buf;
+
+ /*
+ * Allocate a new buffer with size equal to exactly the number of
+ * dynamic tables (that live in the user provided temporary buffer).
+ */
+ num_tables -= table_array_inline_capacity(&tables);
+ new_buf = uacpi_kernel_alloc(
+ sizeof(struct uacpi_installed_table) * num_tables
+ );
+ if (uacpi_unlikely(new_buf == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ uacpi_memcpy(new_buf, tables.dynamic_storage,
+ sizeof(struct uacpi_installed_table) * num_tables);
+ tables.dynamic_storage = new_buf;
+ tables.dynamic_capacity = num_tables;
+ } else {
+ /*
+ * User-provided temporary buffer was not used at all, just remove
+ * any references to it.
+ */
+ tables.dynamic_storage = UACPI_NULL;
+ tables.dynamic_capacity = 0;
+ }
+
+ early_table_access = UACPI_FALSE;
+ } else {
+ uacpi_status ret;
+
+ ret = initialize_from_rsdp();
+ if (uacpi_unlikely_error(ret))
+ return ret;
+ }
+
+ if (!uacpi_is_hardware_reduced()) {
+ struct acpi_fadt *fadt = &g_uacpi_rt_ctx.fadt;
+ uacpi_table tbl;
+
+ if (fadt->x_firmware_ctrl) {
+ uacpi_status ret;
+
+ ret = table_install_physical_with_origin_unlocked(
+ fadt->x_firmware_ctrl, UACPI_TABLE_ORIGIN_FIRMWARE_PHYSICAL,
+ ACPI_FACS_SIGNATURE, &tbl
+ );
+ if (uacpi_unlikely(ret != UACPI_STATUS_OK &&
+ ret != UACPI_STATUS_OVERRIDDEN))
+ return ret;
+
+ g_uacpi_rt_ctx.facs = tbl.ptr;
+ }
+ }
+
+ table_mutex = uacpi_kernel_create_mutex();
+ if (uacpi_unlikely(table_mutex == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ return UACPI_STATUS_OK;
+}
+#endif // !UACPI_BAREBONES_MODE
+
+void uacpi_deinitialize_tables(void)
+{
+ uacpi_size i;
+
+ for (i = 0; i < table_array_size(&tables); ++i) {
+ struct uacpi_installed_table *tbl = table_array_at(&tables, i);
+
+ switch (tbl->origin) {
+#ifndef UACPI_BAREBONES_MODE
+ case UACPI_TABLE_ORIGIN_FIRMWARE_VIRTUAL:
+ uacpi_free(tbl->ptr, tbl->hdr.length);
+ break;
+#endif
+ case UACPI_TABLE_ORIGIN_FIRMWARE_PHYSICAL:
+ case UACPI_TABLE_ORIGIN_HOST_PHYSICAL:
+ if (tbl->reference_count != 0)
+ uacpi_kernel_unmap(tbl->ptr, tbl->hdr.length);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (early_table_access) {
+ uacpi_memzero(&tables, sizeof(tables));
+ early_table_access = UACPI_FALSE;
+ } else {
+ table_array_clear(&tables);
+ }
+
+ installation_handler = UACPI_NULL;
+
+#ifndef UACPI_BAREBONES_MODE
+ if (table_mutex)
+ uacpi_kernel_free_mutex(table_mutex);
+
+ table_mutex = UACPI_NULL;
+#endif
+}
+
+uacpi_status uacpi_set_table_installation_handler(
+ uacpi_table_installation_handler handler
+)
+{
+ uacpi_status ret;
+
+ ret = uacpi_acquire_native_mutex_may_be_null(table_mutex);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ if (installation_handler != UACPI_NULL && handler != UACPI_NULL)
+ goto out;
+
+ installation_handler = handler;
+
+out:
+ uacpi_release_native_mutex_may_be_null(table_mutex);
+ return ret;
+}
+
+static uacpi_status initialize_fadt(const void*);
+
+static uacpi_u8 table_checksum(void *table, uacpi_size size)
+{
+ uacpi_u8 *bytes = table;
+ uacpi_u8 csum = 0;
+ uacpi_size i;
+
+ for (i = 0; i < size; ++i)
+ csum += bytes[i];
+
+ return csum;
+}
+
+uacpi_status uacpi_verify_table_checksum(void *table, uacpi_size size)
+{
+ uacpi_status ret = UACPI_STATUS_OK;
+ uacpi_u8 csum;
+
+ csum = table_checksum(table, size);
+
+ if (uacpi_unlikely(csum != 0)) {
+ enum uacpi_log_level lvl = UACPI_LOG_WARN;
+ struct acpi_sdt_hdr *hdr = table;
+
+ if (uacpi_check_flag(UACPI_FLAG_BAD_CSUM_FATAL)) {
+ ret = UACPI_STATUS_BAD_CHECKSUM;
+ lvl = UACPI_LOG_ERROR;
+ }
+
+ uacpi_log_lvl(
+ lvl, "invalid table "UACPI_PRI_TBL_HDR" checksum %d!\n",
+ UACPI_FMT_TBL_HDR(hdr), csum
+ );
+ }
+
+ return ret;
+}
+
+uacpi_bool uacpi_signatures_match(const void *const lhs, const void *const rhs)
+{
+ return uacpi_memcmp(lhs, rhs, sizeof(uacpi_object_name)) == 0;
+}
+
+uacpi_status uacpi_check_table_signature(void *table, const uacpi_char *expect)
+{
+ uacpi_status ret = UACPI_STATUS_OK;
+
+ if (!uacpi_signatures_match(table, expect)) {
+ enum uacpi_log_level lvl = UACPI_LOG_WARN;
+ struct acpi_sdt_hdr *hdr = table;
+
+ if (uacpi_check_flag(UACPI_FLAG_BAD_TBL_SIGNATURE_FATAL)) {
+ ret = UACPI_STATUS_INVALID_SIGNATURE;
+ lvl = UACPI_LOG_ERROR;
+ }
+
+ uacpi_log_lvl(
+ lvl,
+ "invalid table "UACPI_PRI_TBL_HDR" signature (expected '%.4s')\n",
+ UACPI_FMT_TBL_HDR(hdr), expect
+ );
+ }
+
+ return ret;
+}
+
+static uacpi_status table_alloc(
+ struct uacpi_installed_table **out_tbl, uacpi_size *out_idx
+)
+{
+ struct uacpi_installed_table *tbl;
+
+ if (early_table_access &&
+ table_array_size(&tables) == table_array_capacity(&tables)) {
+ uacpi_warn("early table access buffer capacity exhausted!\n");
+ return UACPI_STATUS_OUT_OF_MEMORY;
+ }
+
+ tbl = table_array_alloc(&tables);
+ if (uacpi_unlikely(tbl == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ *out_tbl = tbl;
+ *out_idx = table_array_size(&tables) - 1;
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status get_external_table_header(
+ uacpi_phys_addr phys_addr, struct acpi_sdt_hdr *out_hdr
+)
+{
+ void *virt;
+
+ virt = uacpi_kernel_map(phys_addr, sizeof(*out_hdr));
+ if (uacpi_unlikely(virt == UACPI_NULL))
+ return UACPI_STATUS_MAPPING_FAILED;
+
+ uacpi_memcpy(out_hdr, virt, sizeof(*out_hdr));
+
+ uacpi_kernel_unmap(virt, sizeof(*out_hdr));
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status table_ref_unlocked(struct uacpi_installed_table *tbl)
+{
+ switch (tbl->reference_count) {
+ case 0: {
+ uacpi_status ret;
+
+ if (tbl->flags & UACPI_TABLE_INVALID)
+ return UACPI_STATUS_INVALID_ARGUMENT;
+
+ if (tbl->origin != UACPI_TABLE_ORIGIN_HOST_PHYSICAL &&
+ tbl->origin != UACPI_TABLE_ORIGIN_FIRMWARE_PHYSICAL)
+ break;
+
+ tbl->ptr = uacpi_kernel_map(tbl->phys_addr, tbl->hdr.length);
+ if (uacpi_unlikely(tbl->ptr == UACPI_NULL))
+ return UACPI_STATUS_MAPPING_FAILED;
+
+ if (!(tbl->flags & UACPI_TABLE_CSUM_VERIFIED)) {
+ ret = uacpi_verify_table_checksum(tbl->ptr, tbl->hdr.length);
+ if (uacpi_unlikely_error(ret)) {
+ uacpi_kernel_unmap(tbl->ptr, tbl->hdr.length);
+ tbl->flags |= UACPI_TABLE_INVALID;
+ tbl->ptr = UACPI_NULL;
+ return ret;
+ }
+
+ tbl->flags |= UACPI_TABLE_CSUM_VERIFIED;
+ }
+ break;
+ }
+ case 0xFFFF - 1:
+ uacpi_warn(
+ "too many references for "UACPI_PRI_TBL_HDR
+ ", mapping permanently\n", UACPI_FMT_TBL_HDR(&tbl->hdr)
+ );
+ break;
+ default:
+ break;
+ }
+
+ if (uacpi_likely(tbl->reference_count != 0xFFFF))
+ tbl->reference_count++;
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status table_unref_unlocked(struct uacpi_installed_table *tbl)
+{
+ switch (tbl->reference_count) {
+ case 0:
+ uacpi_warn(
+ "tried to unref table "UACPI_PRI_TBL_HDR" with no references\n",
+ UACPI_FMT_TBL_HDR(&tbl->hdr)
+ );
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ case 1:
+ if (tbl->origin != UACPI_TABLE_ORIGIN_HOST_PHYSICAL &&
+ tbl->origin != UACPI_TABLE_ORIGIN_FIRMWARE_PHYSICAL)
+ break;
+
+ uacpi_kernel_unmap(tbl->ptr, tbl->hdr.length);
+ tbl->ptr = UACPI_NULL;
+ break;
+ case 0xFFFF:
+ /*
+ * Consider the reference count (overflow) of 0xFFFF to be a permanently
+ * mapped table as we don't know the actual number of references.
+ */
+ return UACPI_STATUS_OK;
+ default:
+ break;
+ }
+
+ tbl->reference_count--;
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status verify_and_install_table(
+ struct acpi_sdt_hdr *hdr, uacpi_phys_addr phys_addr, void *virt_addr,
+ enum uacpi_table_origin origin, uacpi_table *out_table
+)
+{
+ uacpi_status ret;
+ struct uacpi_installed_table *table;
+ uacpi_bool is_fadt;
+ uacpi_size idx;
+ uacpi_u8 flags = 0;
+
+ is_fadt = uacpi_signatures_match(hdr->signature, ACPI_FADT_SIGNATURE);
+
+ /*
+ * FACS is the only(?) table without a checksum because it has OSPM
+ * writable fields. Don't try to validate it here.
+ */
+ if (uacpi_signatures_match(hdr->signature, ACPI_FACS_SIGNATURE)) {
+ flags |= UACPI_TABLE_CSUM_VERIFIED;
+ } else if (uacpi_check_flag(UACPI_FLAG_PROACTIVE_TBL_CSUM) || is_fadt ||
+ out_table != UACPI_NULL) {
+ void *mapping = virt_addr;
+
+ // We may already have a valid mapping, reuse it if we do
+ if (mapping == UACPI_NULL)
+ mapping = uacpi_kernel_map(phys_addr, hdr->length);
+ if (uacpi_unlikely(mapping == UACPI_NULL))
+ return UACPI_STATUS_MAPPING_FAILED;
+
+ ret = uacpi_verify_table_checksum(mapping, hdr->length);
+ if (uacpi_likely_success(ret)) {
+ if (is_fadt)
+ ret = initialize_fadt(mapping);
+ flags |= UACPI_TABLE_CSUM_VERIFIED;
+ }
+
+ if (virt_addr == UACPI_NULL)
+ uacpi_kernel_unmap(mapping, hdr->length);
+
+ if (uacpi_unlikely_error(ret))
+ return ret;
+ }
+
+ if (uacpi_signatures_match(hdr->signature, ACPI_DSDT_SIGNATURE))
+ g_uacpi_rt_ctx.is_rev1 = hdr->revision < 2;
+
+ ret = table_alloc(&table, &idx);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ dump_table_header(phys_addr, hdr);
+
+ uacpi_memcpy(&table->hdr, hdr, sizeof(*hdr));
+ table->reference_count = 0;
+ table->phys_addr = phys_addr;
+ table->ptr = virt_addr;
+ table->flags = flags;
+ table->origin = origin;
+
+ if (out_table == UACPI_NULL)
+ return UACPI_STATUS_OK;
+
+ table->reference_count++;
+ out_table->ptr = virt_addr;
+ out_table->index = idx;
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status handle_table_override(
+ uacpi_table_installation_disposition disposition, uacpi_u64 address,
+ uacpi_table *out_table
+)
+{
+ uacpi_status ret;
+
+ switch (disposition) {
+ case UACPI_TABLE_INSTALLATION_DISPOSITON_VIRTUAL_OVERRIDE:
+ ret = table_install_with_origin_unlocked(
+ UACPI_VIRT_ADDR_TO_PTR((uacpi_virt_addr)address),
+ UACPI_TABLE_ORIGIN_HOST_VIRTUAL,
+ out_table
+ );
+ return ret;
+ case UACPI_TABLE_INSTALLATION_DISPOSITON_PHYSICAL_OVERRIDE:
+ return table_install_physical_with_origin_unlocked(
+ (uacpi_phys_addr)address,
+ UACPI_TABLE_ORIGIN_HOST_PHYSICAL,
+ UACPI_NULL,
+ out_table
+ );
+ default:
+ uacpi_error("invalid table installation disposition %d\n", disposition);
+ return UACPI_STATUS_INTERNAL_ERROR;
+ }
+}
+
+static uacpi_status table_install_physical_with_origin_unlocked(
+ uacpi_phys_addr phys, enum uacpi_table_origin origin,
+ const uacpi_char *expected_signature, uacpi_table *out_table
+)
+{
+ struct acpi_sdt_hdr hdr;
+ void *virt = UACPI_NULL;
+ uacpi_status ret;
+
+ ret = get_external_table_header(phys, &hdr);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ if (uacpi_unlikely(hdr.length < sizeof(struct acpi_sdt_hdr))) {
+ uacpi_error("invalid table '%.4s' (0x%016"UACPI_PRIX64") size: %u\n",
+ hdr.signature, UACPI_FMT64(phys), hdr.length);
+ return UACPI_STATUS_INVALID_TABLE_LENGTH;
+ }
+
+ if (expected_signature != UACPI_NULL) {
+ ret = uacpi_check_table_signature(&hdr, expected_signature);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+ }
+
+ if (installation_handler != UACPI_NULL || out_table != UACPI_NULL) {
+ virt = uacpi_kernel_map(phys, hdr.length);
+ if (uacpi_unlikely(!virt))
+ return UACPI_STATUS_MAPPING_FAILED;
+ }
+
+ if (origin == UACPI_TABLE_ORIGIN_FIRMWARE_PHYSICAL &&
+ installation_handler != UACPI_NULL) {
+ uacpi_u64 override;
+ uacpi_table_installation_disposition disposition;
+
+ disposition = installation_handler(virt, &override);
+
+ switch (disposition) {
+ case UACPI_TABLE_INSTALLATION_DISPOSITON_ALLOW:
+ break;
+ case UACPI_TABLE_INSTALLATION_DISPOSITON_DENY:
+ uacpi_info(
+ "table '%.4s' (0x%016"UACPI_PRIX64") installation denied "
+ "by host\n", hdr.signature, UACPI_FMT64(phys)
+ );
+ ret = UACPI_STATUS_DENIED;
+ goto out;
+
+ default:
+ uacpi_info(
+ "table '%.4s' (0x%016"UACPI_PRIX64") installation "
+ "overridden by host\n", hdr.signature, UACPI_FMT64(phys)
+ );
+
+ ret = handle_table_override(disposition, override, out_table);
+ if (uacpi_likely_success(ret))
+ ret = UACPI_STATUS_OVERRIDDEN;
+
+ goto out;
+ }
+ }
+
+ ret = verify_and_install_table(&hdr, phys, virt, origin, out_table);
+out:
+ // We don't unmap only in this case
+ if (ret == UACPI_STATUS_OK && out_table != UACPI_NULL)
+ return ret;
+
+ if (virt != UACPI_NULL)
+ uacpi_kernel_unmap(virt, hdr.length);
+ return UACPI_STATUS_OK;
+}
+
+uacpi_status uacpi_table_install_physical_with_origin(
+ uacpi_phys_addr phys, enum uacpi_table_origin origin, uacpi_table *out_table
+)
+{
+ uacpi_status ret;
+
+ ret = uacpi_acquire_native_mutex_may_be_null(table_mutex);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ ret = table_install_physical_with_origin_unlocked(
+ phys, origin, UACPI_NULL, out_table
+ );
+ uacpi_release_native_mutex_may_be_null(table_mutex);
+
+ return ret;
+}
+
+static uacpi_status table_install_with_origin_unlocked(
+ void *virt, enum uacpi_table_origin origin, uacpi_table *out_table
+)
+{
+ struct acpi_sdt_hdr *hdr = virt;
+
+ if (uacpi_unlikely(hdr->length < sizeof(struct acpi_sdt_hdr))) {
+ uacpi_error("invalid table '%.4s' (%p) size: %u\n",
+ hdr->signature, virt, hdr->length);
+ return UACPI_STATUS_INVALID_TABLE_LENGTH;
+ }
+
+#ifndef UACPI_BAREBONES_MODE
+ if (origin == UACPI_TABLE_ORIGIN_FIRMWARE_VIRTUAL &&
+ installation_handler != UACPI_NULL) {
+ uacpi_u64 override;
+ uacpi_table_installation_disposition disposition;
+
+ disposition = installation_handler(virt, &override);
+
+ switch (disposition) {
+ case UACPI_TABLE_INSTALLATION_DISPOSITON_ALLOW:
+ break;
+ case UACPI_TABLE_INSTALLATION_DISPOSITON_DENY:
+ uacpi_info(
+ "table "UACPI_PRI_TBL_HDR" installation denied by host\n",
+ UACPI_FMT_TBL_HDR(hdr)
+ );
+ return UACPI_STATUS_DENIED;
+
+ default: {
+ uacpi_status ret;
+ uacpi_info(
+ "table "UACPI_PRI_TBL_HDR" installation overridden by host\n",
+ UACPI_FMT_TBL_HDR(hdr)
+ );
+
+ ret = handle_table_override(disposition, override, out_table);
+ if (uacpi_likely_success(ret))
+ ret = UACPI_STATUS_OVERRIDDEN;
+
+ return ret;
+ }
+ }
+ }
+#endif
+
+ return verify_and_install_table(
+ hdr, 0, virt, origin, out_table
+ );
+}
+
+uacpi_status uacpi_table_install_with_origin(
+ void *virt, enum uacpi_table_origin origin, uacpi_table *out_table
+)
+{
+ uacpi_status ret;
+
+ ret = uacpi_acquire_native_mutex_may_be_null(table_mutex);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ ret = table_install_with_origin_unlocked(virt, origin, out_table);
+
+ uacpi_release_native_mutex_may_be_null(table_mutex);
+ return ret;
+}
+
+uacpi_status uacpi_table_install(void *virt, uacpi_table *out_table)
+{
+ ENSURE_TABLES_ONLINE();
+
+ return uacpi_table_install_with_origin(
+ virt, UACPI_TABLE_ORIGIN_HOST_VIRTUAL, out_table
+ );
+}
+
+uacpi_status uacpi_table_install_physical(
+ uacpi_phys_addr addr, uacpi_table *out_table
+)
+{
+ ENSURE_TABLES_ONLINE();
+
+ return uacpi_table_install_physical_with_origin(
+ addr, UACPI_TABLE_ORIGIN_HOST_PHYSICAL, out_table
+ );
+}
+
+uacpi_status uacpi_for_each_table(
+ uacpi_size base_idx, uacpi_table_iteration_callback cb, void *user
+)
+{
+ uacpi_status ret;
+ uacpi_size idx;
+ struct uacpi_installed_table *tbl;
+ uacpi_iteration_decision dec;
+
+ ENSURE_TABLES_ONLINE();
+
+ ret = uacpi_acquire_native_mutex_may_be_null(table_mutex);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ for (idx = base_idx; idx < table_array_size(&tables); ++idx) {
+ tbl = table_array_at(&tables, idx);
+
+ if (tbl->flags & UACPI_TABLE_INVALID)
+ continue;
+
+ dec = cb(user, tbl, idx);
+ if (dec == UACPI_ITERATION_DECISION_BREAK)
+ break;
+ }
+
+ uacpi_release_native_mutex_may_be_null(table_mutex);
+ return ret;
+}
+
+enum search_type {
+ SEARCH_TYPE_BY_ID,
+ SEARCH_TYPE_MATCH,
+};
+
+struct table_search_ctx {
+ union {
+ const uacpi_table_identifiers *id;
+ uacpi_table_match_callback match_cb;
+ };
+
+ uacpi_table *out_table;
+ uacpi_u8 search_type;
+ uacpi_status status;
+};
+
+static uacpi_iteration_decision do_search_tables(
+ void *user, struct uacpi_installed_table *tbl, uacpi_size idx
+)
+{
+ struct table_search_ctx *ctx = user;
+ uacpi_table *out_table;
+ uacpi_status ret;
+
+ switch (ctx->search_type) {
+ case SEARCH_TYPE_BY_ID: {
+ const uacpi_table_identifiers *id = ctx->id;
+
+ if (!uacpi_signatures_match(&id->signature, tbl->hdr.signature))
+ return UACPI_ITERATION_DECISION_CONTINUE;
+ if (id->oemid[0] != '\0' &&
+ uacpi_memcmp(id->oemid, tbl->hdr.oemid, sizeof(id->oemid)) != 0)
+ return UACPI_ITERATION_DECISION_CONTINUE;
+
+ if (id->oem_table_id[0] != '\0' &&
+ uacpi_memcmp(id->oem_table_id, tbl->hdr.oem_table_id,
+ sizeof(id->oem_table_id)) != 0)
+ return UACPI_ITERATION_DECISION_CONTINUE;
+
+ break;
+ }
+
+ case SEARCH_TYPE_MATCH:
+ if (!ctx->match_cb(tbl))
+ return UACPI_ITERATION_DECISION_CONTINUE;
+ break;
+
+ default:
+ ctx->status = UACPI_STATUS_INVALID_ARGUMENT;
+ return UACPI_ITERATION_DECISION_BREAK;
+ }
+
+ ret = table_ref_unlocked(tbl);
+ if (uacpi_likely_success(ret)) {
+ out_table = ctx->out_table;
+ out_table->ptr = tbl->ptr;
+ out_table->index = idx;
+ ctx->status = ret;
+ return UACPI_ITERATION_DECISION_BREAK;
+ }
+
+ /*
+ * Don't abort nor propagate bad checksums, just pretend this table never
+ * existed and go on with the search.
+ */
+ if (ret == UACPI_STATUS_BAD_CHECKSUM)
+ return UACPI_ITERATION_DECISION_CONTINUE;
+
+ ctx->status = ret;
+ return UACPI_ITERATION_DECISION_BREAK;
+}
+
+#ifndef UACPI_BAREBONES_MODE
+uacpi_status uacpi_table_match(
+ uacpi_size base_idx, uacpi_table_match_callback cb, uacpi_table *out_table
+)
+{
+ uacpi_status ret;
+ struct table_search_ctx ctx = { 0 };
+
+ ctx.match_cb = cb;
+ ctx.search_type = SEARCH_TYPE_MATCH;
+ ctx.out_table = out_table;
+ ctx.status = UACPI_STATUS_NOT_FOUND;
+
+ ret = uacpi_for_each_table(base_idx, do_search_tables, &ctx);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ return ctx.status;
+}
+#endif
+
+static uacpi_status find_table(
+ uacpi_size base_idx, const uacpi_table_identifiers *id,
+ uacpi_table *out_table
+)
+{
+ uacpi_status ret;
+ struct table_search_ctx ctx = { 0 };
+
+ ctx.id = id;
+ ctx.out_table = out_table;
+ ctx.search_type = SEARCH_TYPE_BY_ID;
+ ctx.status = UACPI_STATUS_NOT_FOUND;
+
+ ret = uacpi_for_each_table(base_idx, do_search_tables, &ctx);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ return ctx.status;
+}
+
+uacpi_status uacpi_table_find_by_signature(
+ const uacpi_char *signature_string, struct uacpi_table *out_table
+)
+{
+ struct uacpi_table_identifiers id = { 0 };
+
+ id.signature.text[0] = signature_string[0];
+ id.signature.text[1] = signature_string[1];
+ id.signature.text[2] = signature_string[2];
+ id.signature.text[3] = signature_string[3];
+
+ ENSURE_TABLES_ONLINE();
+
+ return find_table(0, &id, out_table);
+}
+
+uacpi_status uacpi_table_find_next_with_same_signature(
+ uacpi_table *in_out_table
+)
+{
+ struct uacpi_table_identifiers id = { 0 };
+
+ ENSURE_TABLES_ONLINE();
+
+ if (uacpi_unlikely(in_out_table->ptr == UACPI_NULL))
+ return UACPI_STATUS_INVALID_ARGUMENT;
+
+ uacpi_memcpy(&id.signature, in_out_table->hdr->signature,
+ sizeof(id.signature));
+ uacpi_table_unref(in_out_table);
+
+ return find_table(in_out_table->index + 1, &id, in_out_table);
+}
+
+uacpi_status uacpi_table_find(
+ const uacpi_table_identifiers *id, uacpi_table *out_table
+)
+{
+ ENSURE_TABLES_ONLINE();
+
+ return find_table(0, id, out_table);
+}
+
+#define TABLE_CTL_SET_FLAGS (1 << 0)
+#define TABLE_CTL_CLEAR_FLAGS (1 << 1)
+#define TABLE_CTL_VALIDATE_SET_FLAGS (1 << 2)
+#define TABLE_CTL_VALIDATE_CLEAR_FLAGS (1 << 3)
+#define TABLE_CTL_GET (1 << 4)
+#define TABLE_CTL_PUT (1 << 5)
+
+struct table_ctl_request {
+ uacpi_u8 type;
+
+ uacpi_u8 expect_set;
+ uacpi_u8 expect_clear;
+ uacpi_u8 set;
+ uacpi_u8 clear;
+
+ void *out_tbl;
+};
+
+static uacpi_status table_ctl(uacpi_size idx, struct table_ctl_request *req)
+{
+ uacpi_status ret;
+ struct uacpi_installed_table *tbl;
+
+ ENSURE_TABLES_ONLINE();
+
+ ret = uacpi_acquire_native_mutex_may_be_null(table_mutex);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ if (uacpi_unlikely(table_array_size(&tables) <= idx)) {
+ uacpi_error(
+ "requested invalid table index %zu (%zu tables installed)\n",
+ idx, table_array_size(&tables)
+ );
+ ret = UACPI_STATUS_INVALID_ARGUMENT;
+ goto out;
+ }
+
+ tbl = table_array_at(&tables, idx);
+ if (uacpi_unlikely(tbl->flags & UACPI_TABLE_INVALID))
+ return UACPI_STATUS_INVALID_ARGUMENT;
+
+ if (req->type & TABLE_CTL_VALIDATE_SET_FLAGS) {
+ uacpi_u8 mask = req->expect_set;
+
+ if (uacpi_unlikely((tbl->flags & mask) != mask)) {
+ uacpi_error(
+ "unexpected table '%.4s' flags %02X, expected %02X to be set\n",
+ tbl->hdr.signature, tbl->flags, mask
+ );
+ ret = UACPI_STATUS_INVALID_ARGUMENT;
+ goto out;
+ }
+ }
+
+ if (req->type & TABLE_CTL_VALIDATE_CLEAR_FLAGS) {
+ uacpi_u8 mask = req->expect_clear;
+
+ if (uacpi_unlikely((tbl->flags & mask) != 0)) {
+ uacpi_error(
+ "unexpected table '%.4s' flags %02X, expected %02X "
+ "to be clear\n", tbl->hdr.signature, tbl->flags, mask
+ );
+ ret = UACPI_STATUS_ALREADY_EXISTS;
+ goto out;
+ }
+ }
+
+ if (req->type & TABLE_CTL_GET) {
+ ret = table_ref_unlocked(tbl);
+ if (uacpi_unlikely_error(ret))
+ goto out;
+
+ req->out_tbl = tbl->ptr;
+ }
+
+ if (req->type & TABLE_CTL_PUT) {
+ ret = table_unref_unlocked(tbl);
+ if (uacpi_unlikely_error(ret))
+ goto out;
+ }
+
+ if (req->type & TABLE_CTL_SET_FLAGS)
+ tbl->flags |= req->set;
+ if (req->type & TABLE_CTL_CLEAR_FLAGS)
+ tbl->flags &= ~req->clear;
+
+out:
+ uacpi_release_native_mutex_may_be_null(table_mutex);
+ return ret;
+}
+
+#ifndef UACPI_BAREBONES_MODE
+uacpi_status uacpi_table_load_with_cause(
+ uacpi_size idx, enum uacpi_table_load_cause cause
+)
+{
+ uacpi_status ret;
+ struct table_ctl_request req = {
+ .type = TABLE_CTL_SET_FLAGS | TABLE_CTL_VALIDATE_CLEAR_FLAGS |
+ TABLE_CTL_GET,
+ .set = UACPI_TABLE_LOADED,
+ .expect_clear = UACPI_TABLE_LOADED,
+ };
+
+ ret = table_ctl(idx, &req);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ ret = uacpi_execute_table(req.out_tbl, cause);
+
+ req.type = TABLE_CTL_PUT;
+ table_ctl(idx, &req);
+ return ret;
+}
+
+uacpi_status uacpi_table_load(uacpi_size idx)
+{
+ return uacpi_table_load_with_cause(idx, UACPI_TABLE_LOAD_CAUSE_HOST);
+}
+
+void uacpi_table_mark_as_loaded(uacpi_size idx)
+{
+ struct table_ctl_request req = {
+ .type = TABLE_CTL_SET_FLAGS, .set = UACPI_TABLE_LOADED
+ };
+
+ table_ctl(idx, &req);
+}
+#endif // !UACPI_BAREBONES_MODE
+
+uacpi_status uacpi_table_ref(uacpi_table *tbl)
+{
+ struct table_ctl_request req = {
+ .type = TABLE_CTL_GET
+ };
+
+ return table_ctl(tbl->index, &req);
+}
+
+uacpi_status uacpi_table_unref(uacpi_table *tbl)
+{
+ struct table_ctl_request req = {
+ .type = TABLE_CTL_PUT
+ };
+
+ return table_ctl(tbl->index, &req);
+}
+
+uacpi_u16 fadt_version_sizes[] = {
+ 116, 132, 244, 244, 268, 276
+};
+
+static void fadt_ensure_correct_revision(struct acpi_fadt *fadt)
+{
+ uacpi_size current_rev, rev;
+
+ current_rev = fadt->hdr.revision;
+
+ for (rev = 0; rev < UACPI_ARRAY_SIZE(fadt_version_sizes); ++rev) {
+ if (fadt->hdr.length <= fadt_version_sizes[rev])
+ break;
+ }
+
+ if (rev == UACPI_ARRAY_SIZE(fadt_version_sizes)) {
+ uacpi_trace(
+ "FADT revision (%zu) is likely greater than the last "
+ "supported, reducing to %zu\n", current_rev, rev
+ );
+ fadt->hdr.revision = rev;
+ return;
+ }
+
+ rev++;
+
+ if (current_rev != rev && !(rev == 3 && current_rev == 4)) {
+ uacpi_warn(
+ "FADT length %u doesn't match expected for revision %zu, "
+ "assuming version %zu\n", fadt->hdr.length, current_rev,
+ rev
+ );
+ fadt->hdr.revision = rev;
+ }
+}
+
+static void gas_init_system_io(
+ struct acpi_gas *gas, uacpi_u64 address, uacpi_u8 byte_size
+)
+{
+ gas->address = address;
+ gas->address_space_id = UACPI_ADDRESS_SPACE_SYSTEM_IO;
+ gas->register_bit_width = UACPI_MIN(255, byte_size * 8);
+ gas->register_bit_offset = 0;
+ gas->access_size = 0;
+}
+
+
+struct register_description {
+ uacpi_size offset, xoffset;
+ uacpi_size length_offset;
+};
+
+#define fadt_offset(field) uacpi_offsetof(struct acpi_fadt, field)
+
+/*
+ * We convert all the legacy registers into GAS format and write them into
+ * the x_* fields for convenience and faster access at runtime.
+ */
+static struct register_description fadt_registers[] = {
+ {
+ .offset = fadt_offset(pm1a_evt_blk),
+ .xoffset = fadt_offset(x_pm1a_evt_blk),
+ .length_offset = fadt_offset(pm1_evt_len),
+ },
+ {
+ .offset = fadt_offset(pm1b_evt_blk),
+ .xoffset = fadt_offset(x_pm1b_evt_blk),
+ .length_offset = fadt_offset(pm1_evt_len),
+ },
+ {
+ .offset = fadt_offset(pm1a_cnt_blk),
+ .xoffset = fadt_offset(x_pm1a_cnt_blk),
+ .length_offset = fadt_offset(pm1_cnt_len),
+ },
+ {
+ .offset = fadt_offset(pm1b_cnt_blk),
+ .xoffset = fadt_offset(x_pm1b_cnt_blk),
+ .length_offset = fadt_offset(pm1_cnt_len),
+ },
+ {
+ .offset = fadt_offset(pm2_cnt_blk),
+ .xoffset = fadt_offset(x_pm2_cnt_blk),
+ .length_offset = fadt_offset(pm2_cnt_len),
+ },
+ {
+ .offset = fadt_offset(pm_tmr_blk),
+ .xoffset = fadt_offset(x_pm_tmr_blk),
+ .length_offset = fadt_offset(pm_tmr_len),
+ },
+ {
+ .offset = fadt_offset(gpe0_blk),
+ .xoffset = fadt_offset(x_gpe0_blk),
+ .length_offset = fadt_offset(gpe0_blk_len),
+ },
+ {
+ .offset = fadt_offset(gpe1_blk),
+ .xoffset = fadt_offset(x_gpe1_blk),
+ .length_offset = fadt_offset(gpe1_blk_len),
+ },
+};
+
+static void *fadt_relative(uacpi_size offset)
+{
+ return ((uacpi_u8*)&g_uacpi_rt_ctx.fadt) + offset;
+}
+
+static void convert_registers_to_gas(void)
+{
+ uacpi_size i;
+ struct register_description *desc;
+ struct acpi_gas *gas;
+ uacpi_u32 legacy_addr;
+ uacpi_u8 length;
+
+ for (i = 0; i < UACPI_ARRAY_SIZE(fadt_registers); ++i) {
+ desc = &fadt_registers[i];
+
+ legacy_addr = *(uacpi_u32*)fadt_relative(desc->offset);
+ length = *(uacpi_u8*)fadt_relative(desc->length_offset);
+ gas = fadt_relative(desc->xoffset);
+
+ if (gas->address)
+ continue;
+
+ gas_init_system_io(gas, legacy_addr, length);
+ }
+}
+
+#ifndef UACPI_BAREBONES_MODE
+static void split_one_block(
+ struct acpi_gas *src, struct acpi_gas *dst0, struct acpi_gas *dst1
+)
+{
+ uacpi_size byte_length;
+
+ if (src->address == 0)
+ return;
+
+ byte_length = src->register_bit_width / 8;
+ byte_length /= 2;
+
+ gas_init_system_io(dst0, src->address, byte_length);
+ gas_init_system_io(dst1, src->address + byte_length, byte_length);
+}
+
+static void split_event_blocks(void)
+{
+ split_one_block(
+ &g_uacpi_rt_ctx.fadt.x_pm1a_evt_blk,
+ &g_uacpi_rt_ctx.pm1a_status_blk,
+ &g_uacpi_rt_ctx.pm1a_enable_blk
+ );
+ split_one_block(
+ &g_uacpi_rt_ctx.fadt.x_pm1b_evt_blk,
+ &g_uacpi_rt_ctx.pm1b_status_blk,
+ &g_uacpi_rt_ctx.pm1b_enable_blk
+ );
+}
+#endif // !UACPI_BAREBONES_MODE
+
+static uacpi_status initialize_fadt(const void *virt)
+{
+ uacpi_status ret;
+ struct acpi_fadt *fadt = &g_uacpi_rt_ctx.fadt;
+ const struct acpi_sdt_hdr *hdr = virt;
+
+ /*
+ * Here we (roughly) follow ACPICA initialization sequence to make sure we
+ * handle potential BIOS quirks with garbage inside FADT correctly.
+ */
+
+ uacpi_memcpy(fadt, hdr, UACPI_MIN(sizeof(*fadt), hdr->length));
+
+#if !defined(UACPI_REDUCED_HARDWARE) && !defined(UACPI_BAREBONES_MODE)
+ g_uacpi_rt_ctx.is_hardware_reduced = fadt->flags & ACPI_HW_REDUCED_ACPI;
+#endif
+
+ fadt_ensure_correct_revision(fadt);
+
+ /*
+ * These are reserved prior to version 3, so zero them out to work around
+ * BIOS implementations that might dirty these.
+ */
+ if (fadt->hdr.revision <= 2) {
+ fadt->preferred_pm_profile = 0;
+ fadt->pstate_cnt = 0;
+ fadt->cst_cnt = 0;
+ fadt->iapc_boot_arch = 0;
+ }
+
+ if (!fadt->x_dsdt)
+ fadt->x_dsdt = fadt->dsdt;
+
+ if (fadt->x_dsdt) {
+ ret = table_install_physical_with_origin_unlocked(
+ fadt->x_dsdt, UACPI_TABLE_ORIGIN_FIRMWARE_PHYSICAL,
+ ACPI_DSDT_SIGNATURE, UACPI_NULL
+ );
+ if (uacpi_unlikely(ret != UACPI_STATUS_OK &&
+ ret != UACPI_STATUS_OVERRIDDEN))
+ return ret;
+ }
+
+ /*
+ * Unconditionally use 32 bit FACS if it exists, as 64 bit FACS is known
+ * to cause issues on some firmware:
+ * https://bugzilla.kernel.org/show_bug.cgi?id=74021
+ *
+ * Note that we don't install it here as FACS needs permanent mapping, which
+ * we might not be able to obtain at this point in case of early table
+ * access.
+ */
+ if (fadt->firmware_ctrl)
+ fadt->x_firmware_ctrl = fadt->firmware_ctrl;
+
+ if (!uacpi_is_hardware_reduced()) {
+ convert_registers_to_gas();
+#ifndef UACPI_BAREBONES_MODE
+ split_event_blocks();
+#endif
+ }
+
+ return UACPI_STATUS_OK;
+}
+
+uacpi_status uacpi_table_fadt(struct acpi_fadt **out_fadt)
+{
+ ENSURE_TABLES_ONLINE();
+
+ *out_fadt = &g_uacpi_rt_ctx.fadt;
+ return UACPI_STATUS_OK;
+}
diff --git a/sys/dev/acpi/uacpi/types.c b/sys/dev/acpi/uacpi/types.c
new file mode 100644
index 0000000..840d3ef
--- /dev/null
+++ b/sys/dev/acpi/uacpi/types.c
@@ -0,0 +1,1489 @@
+#include <uacpi/types.h>
+#include <uacpi/internal/types.h>
+#include <uacpi/internal/stdlib.h>
+#include <uacpi/internal/shareable.h>
+#include <uacpi/internal/dynamic_array.h>
+#include <uacpi/internal/log.h>
+#include <uacpi/internal/namespace.h>
+#include <uacpi/internal/tables.h>
+#include <uacpi/kernel_api.h>
+
+const uacpi_char *uacpi_address_space_to_string(
+ enum uacpi_address_space space
+)
+{
+ switch (space) {
+ case UACPI_ADDRESS_SPACE_SYSTEM_MEMORY:
+ return "SystemMemory";
+ case UACPI_ADDRESS_SPACE_SYSTEM_IO:
+ return "SystemIO";
+ case UACPI_ADDRESS_SPACE_PCI_CONFIG:
+ return "PCI_Config";
+ case UACPI_ADDRESS_SPACE_EMBEDDED_CONTROLLER:
+ return "EmbeddedControl";
+ case UACPI_ADDRESS_SPACE_SMBUS:
+ return "SMBus";
+ case UACPI_ADDRESS_SPACE_SYSTEM_CMOS:
+ return "SystemCMOS";
+ case UACPI_ADDRESS_SPACE_PCI_BAR_TARGET:
+ return "PciBarTarget";
+ case UACPI_ADDRESS_SPACE_IPMI:
+ return "IPMI";
+ case UACPI_ADDRESS_SPACE_GENERAL_PURPOSE_IO:
+ return "GeneralPurposeIO";
+ case UACPI_ADDRESS_SPACE_GENERIC_SERIAL_BUS:
+ return "GenericSerialBus";
+ case UACPI_ADDRESS_SPACE_PCC:
+ return "PCC";
+ case UACPI_ADDRESS_SPACE_PRM:
+ return "PlatformRtMechanism";
+ case UACPI_ADDRESS_SPACE_FFIXEDHW:
+ return "FFixedHW";
+ case UACPI_ADDRESS_SPACE_TABLE_DATA:
+ return "TableData";
+ default:
+ return "<vendor specific>";
+ }
+}
+
+#ifndef UACPI_BAREBONES_MODE
+
+const uacpi_char *uacpi_object_type_to_string(uacpi_object_type type)
+{
+ switch (type) {
+ case UACPI_OBJECT_UNINITIALIZED:
+ return "Uninitialized";
+ case UACPI_OBJECT_INTEGER:
+ return "Integer";
+ case UACPI_OBJECT_STRING:
+ return "String";
+ case UACPI_OBJECT_BUFFER:
+ return "Buffer";
+ case UACPI_OBJECT_PACKAGE:
+ return "Package";
+ case UACPI_OBJECT_FIELD_UNIT:
+ return "Field Unit";
+ case UACPI_OBJECT_DEVICE:
+ return "Device";
+ case UACPI_OBJECT_EVENT:
+ return "Event";
+ case UACPI_OBJECT_REFERENCE:
+ return "Reference";
+ case UACPI_OBJECT_BUFFER_INDEX:
+ return "Buffer Index";
+ case UACPI_OBJECT_METHOD:
+ return "Method";
+ case UACPI_OBJECT_MUTEX:
+ return "Mutex";
+ case UACPI_OBJECT_OPERATION_REGION:
+ return "Operation Region";
+ case UACPI_OBJECT_POWER_RESOURCE:
+ return "Power Resource";
+ case UACPI_OBJECT_PROCESSOR:
+ return "Processor";
+ case UACPI_OBJECT_THERMAL_ZONE:
+ return "Thermal Zone";
+ case UACPI_OBJECT_BUFFER_FIELD:
+ return "Buffer Field";
+ case UACPI_OBJECT_DEBUG:
+ return "Debug";
+ default:
+ return "<Invalid type>";
+ }
+}
+
+static uacpi_bool buffer_alloc(uacpi_object *obj, uacpi_size initial_size)
+{
+ uacpi_buffer *buf;
+
+ buf = uacpi_kernel_alloc_zeroed(sizeof(uacpi_buffer));
+ if (uacpi_unlikely(buf == UACPI_NULL))
+ return UACPI_FALSE;
+
+ uacpi_shareable_init(buf);
+
+ if (initial_size) {
+ buf->data = uacpi_kernel_alloc(initial_size);
+ if (uacpi_unlikely(buf->data == UACPI_NULL)) {
+ uacpi_free(buf, sizeof(*buf));
+ return UACPI_FALSE;
+ }
+
+ buf->size = initial_size;
+ }
+
+ obj->buffer = buf;
+ return UACPI_TRUE;
+}
+
+static uacpi_bool empty_buffer_or_string_alloc(uacpi_object *object)
+{
+ return buffer_alloc(object, 0);
+}
+
+uacpi_bool uacpi_package_fill(
+ uacpi_package *pkg, uacpi_size num_elements,
+ enum uacpi_prealloc_objects prealloc_objects
+)
+{
+ uacpi_size i;
+
+ if (uacpi_unlikely(num_elements == 0))
+ return UACPI_TRUE;
+
+ pkg->objects = uacpi_kernel_alloc_zeroed(
+ num_elements * sizeof(uacpi_handle)
+ );
+ if (uacpi_unlikely(pkg->objects == UACPI_NULL))
+ return UACPI_FALSE;
+
+ pkg->count = num_elements;
+
+ if (prealloc_objects == UACPI_PREALLOC_OBJECTS_YES) {
+ for (i = 0; i < num_elements; ++i) {
+ pkg->objects[i] = uacpi_create_object(UACPI_OBJECT_UNINITIALIZED);
+
+ if (uacpi_unlikely(pkg->objects[i] == UACPI_NULL))
+ return UACPI_FALSE;
+ }
+ }
+
+ return UACPI_TRUE;
+}
+
+static uacpi_bool package_alloc(
+ uacpi_object *obj, uacpi_size initial_size,
+ enum uacpi_prealloc_objects prealloc
+)
+{
+ uacpi_package *pkg;
+
+ pkg = uacpi_kernel_alloc_zeroed(sizeof(uacpi_package));
+ if (uacpi_unlikely(pkg == UACPI_NULL))
+ return UACPI_FALSE;
+
+ uacpi_shareable_init(pkg);
+
+ if (uacpi_unlikely(!uacpi_package_fill(pkg, initial_size, prealloc))) {
+ uacpi_free(pkg, sizeof(*pkg));
+ return UACPI_FALSE;
+ }
+
+ obj->package = pkg;
+ return UACPI_TRUE;
+}
+
+static uacpi_bool empty_package_alloc(uacpi_object *object)
+{
+ return package_alloc(object, 0, UACPI_PREALLOC_OBJECTS_NO);
+}
+
+uacpi_mutex *uacpi_create_mutex(void)
+{
+ uacpi_mutex *mutex;
+
+ mutex = uacpi_kernel_alloc_zeroed(sizeof(uacpi_mutex));
+ if (uacpi_unlikely(mutex == UACPI_NULL))
+ return UACPI_NULL;
+
+ mutex->owner = UACPI_THREAD_ID_NONE;
+
+ mutex->handle = uacpi_kernel_create_mutex();
+ if (mutex->handle == UACPI_NULL) {
+ uacpi_free(mutex, sizeof(*mutex));
+ return UACPI_NULL;
+ }
+
+ uacpi_shareable_init(mutex);
+ return mutex;
+}
+
+static uacpi_bool mutex_alloc(uacpi_object *obj)
+{
+ obj->mutex = uacpi_create_mutex();
+ return obj->mutex != UACPI_NULL;
+}
+
+static uacpi_bool event_alloc(uacpi_object *obj)
+{
+ uacpi_event *event;
+
+ event = uacpi_kernel_alloc_zeroed(sizeof(uacpi_event));
+ if (uacpi_unlikely(event == UACPI_NULL))
+ return UACPI_FALSE;
+
+ event->handle = uacpi_kernel_create_event();
+ if (event->handle == UACPI_NULL) {
+ uacpi_free(event, sizeof(*event));
+ return UACPI_FALSE;
+ }
+
+ uacpi_shareable_init(event);
+ obj->event = event;
+
+ return UACPI_TRUE;
+}
+
+static uacpi_bool method_alloc(uacpi_object *obj)
+{
+ uacpi_control_method *method;
+
+ method = uacpi_kernel_alloc_zeroed(sizeof(*method));
+ if (uacpi_unlikely(method == UACPI_NULL))
+ return UACPI_FALSE;
+
+ uacpi_shareable_init(method);
+ obj->method = method;
+
+ return UACPI_TRUE;
+}
+
+static uacpi_bool op_region_alloc(uacpi_object *obj)
+{
+ uacpi_operation_region *op_region;
+
+ op_region = uacpi_kernel_alloc_zeroed(sizeof(*op_region));
+ if (uacpi_unlikely(op_region == UACPI_NULL))
+ return UACPI_FALSE;
+
+ uacpi_shareable_init(op_region);
+ obj->op_region = op_region;
+
+ return UACPI_TRUE;
+}
+
+static uacpi_bool field_unit_alloc(uacpi_object *obj)
+{
+ uacpi_field_unit *field_unit;
+
+ field_unit = uacpi_kernel_alloc_zeroed(sizeof(*field_unit));
+ if (uacpi_unlikely(field_unit == UACPI_NULL))
+ return UACPI_FALSE;
+
+ uacpi_shareable_init(field_unit);
+ obj->field_unit = field_unit;
+
+ return UACPI_TRUE;
+}
+
+static uacpi_bool processor_alloc(uacpi_object *obj)
+{
+ uacpi_processor *processor;
+
+ processor = uacpi_kernel_alloc_zeroed(sizeof(*processor));
+ if (uacpi_unlikely(processor == UACPI_NULL))
+ return UACPI_FALSE;
+
+ uacpi_shareable_init(processor);
+ obj->processor = processor;
+
+ return UACPI_TRUE;
+}
+
+static uacpi_bool device_alloc(uacpi_object *obj)
+{
+ uacpi_device *device;
+
+ device = uacpi_kernel_alloc_zeroed(sizeof(*device));
+ if (uacpi_unlikely(device == UACPI_NULL))
+ return UACPI_FALSE;
+
+ uacpi_shareable_init(device);
+ obj->device = device;
+
+ return UACPI_TRUE;
+}
+
+static uacpi_bool thermal_zone_alloc(uacpi_object *obj)
+{
+ uacpi_thermal_zone *thermal_zone;
+
+ thermal_zone = uacpi_kernel_alloc_zeroed(sizeof(*thermal_zone));
+ if (uacpi_unlikely(thermal_zone == UACPI_NULL))
+ return UACPI_FALSE;
+
+ uacpi_shareable_init(thermal_zone);
+ obj->thermal_zone = thermal_zone;
+
+ return UACPI_TRUE;
+}
+
+typedef uacpi_bool (*object_ctor)(uacpi_object *obj);
+
+static object_ctor object_constructor_table[UACPI_OBJECT_MAX_TYPE_VALUE + 1] = {
+ [UACPI_OBJECT_STRING] = empty_buffer_or_string_alloc,
+ [UACPI_OBJECT_BUFFER] = empty_buffer_or_string_alloc,
+ [UACPI_OBJECT_PACKAGE] = empty_package_alloc,
+ [UACPI_OBJECT_FIELD_UNIT] = field_unit_alloc,
+ [UACPI_OBJECT_MUTEX] = mutex_alloc,
+ [UACPI_OBJECT_EVENT] = event_alloc,
+ [UACPI_OBJECT_OPERATION_REGION] = op_region_alloc,
+ [UACPI_OBJECT_METHOD] = method_alloc,
+ [UACPI_OBJECT_PROCESSOR] = processor_alloc,
+ [UACPI_OBJECT_DEVICE] = device_alloc,
+ [UACPI_OBJECT_THERMAL_ZONE] = thermal_zone_alloc,
+};
+
+uacpi_object *uacpi_create_object(uacpi_object_type type)
+{
+ uacpi_object *ret;
+ object_ctor ctor;
+
+ ret = uacpi_kernel_alloc_zeroed(sizeof(*ret));
+ if (uacpi_unlikely(ret == UACPI_NULL))
+ return ret;
+
+ uacpi_shareable_init(ret);
+ ret->type = type;
+
+ ctor = object_constructor_table[type];
+ if (ctor == UACPI_NULL)
+ return ret;
+
+ if (uacpi_unlikely(!ctor(ret))) {
+ uacpi_free(ret, sizeof(*ret));
+ return UACPI_NULL;
+ }
+
+ return ret;
+}
+
+static void free_buffer(uacpi_handle handle)
+{
+ uacpi_buffer *buf = handle;
+
+ if (buf->data != UACPI_NULL)
+ /*
+ * If buffer has a size of 0 but a valid data pointer it's probably an
+ * "empty" buffer allocated by the interpreter in make_null_buffer
+ * and its real size is actually 1.
+ */
+ uacpi_free(buf->data, UACPI_MAX(buf->size, 1));
+
+ uacpi_free(buf, sizeof(*buf));
+}
+
+DYNAMIC_ARRAY_WITH_INLINE_STORAGE(free_queue, uacpi_package*, 4)
+DYNAMIC_ARRAY_WITH_INLINE_STORAGE_IMPL(free_queue, uacpi_package*, static)
+
+static uacpi_bool free_queue_push(struct free_queue *queue, uacpi_package *pkg)
+{
+ uacpi_package **slot;
+
+ slot = free_queue_alloc(queue);
+ if (uacpi_unlikely(slot == UACPI_NULL))
+ return UACPI_FALSE;
+
+ *slot = pkg;
+ return UACPI_TRUE;
+}
+
+static void free_object(uacpi_object *obj);
+
+// No references allowed here, only plain objects
+static void free_plain_no_recurse(uacpi_object *obj, struct free_queue *queue)
+{
+ switch (obj->type) {
+ case UACPI_OBJECT_PACKAGE:
+ if (uacpi_shareable_unref(obj->package) > 1)
+ break;
+
+ if (uacpi_unlikely(!free_queue_push(queue,
+ obj->package))) {
+ uacpi_warn(
+ "unable to free nested package @%p: not enough memory\n",
+ obj->package
+ );
+ }
+
+ // Don't call free_object here as that will recurse
+ uacpi_free(obj, sizeof(*obj));
+ break;
+ default:
+ /*
+ * This call is guaranteed to not recurse further as we handle
+ * recursive cases elsewhere explicitly.
+ */
+ free_object(obj);
+ }
+}
+
+static void unref_plain_no_recurse(uacpi_object *obj, struct free_queue *queue)
+{
+ if (uacpi_shareable_unref(obj) > 1)
+ return;
+
+ free_plain_no_recurse(obj, queue);
+}
+
+static void unref_chain_no_recurse(uacpi_object *obj, struct free_queue *queue)
+{
+ uacpi_object *next_obj = UACPI_NULL;
+
+ while (obj) {
+ if (obj->type == UACPI_OBJECT_REFERENCE)
+ next_obj = obj->inner_object;
+
+ if (uacpi_shareable_unref(obj) > 1)
+ goto do_next;
+
+ if (obj->type == UACPI_OBJECT_REFERENCE) {
+ uacpi_free(obj, sizeof(*obj));
+ } else {
+ free_plain_no_recurse(obj, queue);
+ }
+
+ do_next:
+ obj = next_obj;
+ next_obj = UACPI_NULL;
+ }
+}
+
+static void unref_object_no_recurse(uacpi_object *obj, struct free_queue *queue)
+{
+ if (obj->type == UACPI_OBJECT_REFERENCE) {
+ unref_chain_no_recurse(obj, queue);
+ return;
+ }
+
+ unref_plain_no_recurse(obj, queue);
+}
+
+static void free_package(uacpi_handle handle)
+{
+ struct free_queue queue = { 0 };
+ uacpi_package *pkg = handle;
+ uacpi_object *obj;
+ uacpi_size i;
+
+ free_queue_push(&queue, pkg);
+
+ while (free_queue_size(&queue) != 0) {
+ pkg = *free_queue_last(&queue);
+ free_queue_pop(&queue);
+
+ /*
+ * 1. Unref/free every object in the package. Note that this might add
+ * even more packages into the free queue.
+ */
+ for (i = 0; i < pkg->count; ++i) {
+ obj = pkg->objects[i];
+ unref_object_no_recurse(obj, &queue);
+ }
+
+ // 2. Release the object array
+ uacpi_free(pkg->objects, sizeof(*pkg->objects) * pkg->count);
+
+ // 3. Release the package itself
+ uacpi_free(pkg, sizeof(*pkg));
+ }
+
+ free_queue_clear(&queue);
+}
+
+static void free_mutex(uacpi_handle handle)
+{
+ uacpi_mutex *mutex = handle;
+
+ uacpi_kernel_free_mutex(mutex->handle);
+ uacpi_free(mutex, sizeof(*mutex));
+}
+
+void uacpi_mutex_unref(uacpi_mutex *mutex)
+{
+ if (mutex == UACPI_NULL)
+ return;
+
+ uacpi_shareable_unref_and_delete_if_last(mutex, free_mutex);
+}
+
+static void free_event(uacpi_handle handle)
+{
+ uacpi_event *event = handle;
+
+ uacpi_kernel_free_event(event->handle);
+ uacpi_free(event, sizeof(*event));
+}
+
+static void free_address_space_handler(uacpi_handle handle)
+{
+ uacpi_address_space_handler *handler = handle;
+ uacpi_free(handler, sizeof(*handler));
+}
+
+static void free_address_space_handlers(
+ uacpi_address_space_handler *handler
+)
+{
+ uacpi_address_space_handler *next_handler;
+
+ while (handler) {
+ next_handler = handler->next;
+ uacpi_shareable_unref_and_delete_if_last(
+ handler, free_address_space_handler
+ );
+ handler = next_handler;
+ }
+}
+
+static void free_device_notify_handlers(uacpi_device_notify_handler *handler)
+{
+ uacpi_device_notify_handler *next_handler;
+
+ while (handler) {
+ next_handler = handler->next;
+ uacpi_free(handler, sizeof(*handler));
+ handler = next_handler;
+ }
+}
+
+static void free_handlers(uacpi_handle handle)
+{
+ uacpi_handlers *handlers = handle;
+
+ free_address_space_handlers(handlers->address_space_head);
+ free_device_notify_handlers(handlers->notify_head);
+}
+
+void uacpi_address_space_handler_unref(uacpi_address_space_handler *handler)
+{
+ uacpi_shareable_unref_and_delete_if_last(
+ handler, free_address_space_handler
+ );
+}
+
+static void free_op_region(uacpi_handle handle)
+{
+ uacpi_operation_region *op_region = handle;
+
+ if (uacpi_unlikely(op_region->handler != UACPI_NULL)) {
+ uacpi_warn(
+ "BUG: attempting to free an opregion@%p with a handler attached\n",
+ op_region
+ );
+ }
+
+ switch (op_region->space) {
+ case UACPI_ADDRESS_SPACE_PCC:
+ uacpi_free(op_region->internal_buffer, op_region->length);
+ break;
+ case UACPI_ADDRESS_SPACE_TABLE_DATA: {
+ struct uacpi_table table = { 0 };
+
+ table.index = op_region->table_idx;
+ uacpi_table_unref(
+ &table
+ );
+ break;
+ }
+ default:
+ break;
+ }
+
+ uacpi_free(op_region, sizeof(*op_region));
+}
+
+static void free_device(uacpi_handle handle)
+{
+ uacpi_device *device = handle;
+ free_handlers(device);
+ uacpi_free(device, sizeof(*device));
+}
+
+static void free_processor(uacpi_handle handle)
+{
+ uacpi_processor *processor = handle;
+ free_handlers(processor);
+ uacpi_free(processor, sizeof(*processor));
+}
+
+static void free_thermal_zone(uacpi_handle handle)
+{
+ uacpi_thermal_zone *thermal_zone = handle;
+ free_handlers(thermal_zone);
+ uacpi_free(thermal_zone, sizeof(*thermal_zone));
+}
+
+static void free_field_unit(uacpi_handle handle)
+{
+ uacpi_field_unit *field_unit = handle;
+
+ if (field_unit->connection)
+ uacpi_object_unref(field_unit->connection);
+
+ switch (field_unit->kind) {
+ case UACPI_FIELD_UNIT_KIND_NORMAL:
+ uacpi_namespace_node_unref(field_unit->region);
+ break;
+ case UACPI_FIELD_UNIT_KIND_BANK:
+ uacpi_namespace_node_unref(field_unit->bank_region);
+ uacpi_shareable_unref_and_delete_if_last(
+ field_unit->bank_selection, free_field_unit
+ );
+ break;
+ case UACPI_FIELD_UNIT_KIND_INDEX:
+ uacpi_shareable_unref_and_delete_if_last(
+ field_unit->index, free_field_unit
+ );
+ uacpi_shareable_unref_and_delete_if_last(
+ field_unit->data, free_field_unit
+ );
+ break;
+ default:
+ break;
+ }
+
+ uacpi_free(field_unit, sizeof(*field_unit));
+}
+
+static void free_method(uacpi_handle handle)
+{
+ uacpi_control_method *method = handle;
+
+ uacpi_shareable_unref_and_delete_if_last(
+ method->mutex, free_mutex
+ );
+
+ if (!method->native_call && method->owns_code)
+ uacpi_free(method->code, method->size);
+ uacpi_free(method, sizeof(*method));
+}
+
+void uacpi_method_unref(uacpi_control_method *method)
+{
+ uacpi_shareable_unref_and_delete_if_last(method, free_method);
+}
+
+static void free_object_storage(uacpi_object *obj)
+{
+ switch (obj->type) {
+ case UACPI_OBJECT_STRING:
+ case UACPI_OBJECT_BUFFER:
+ uacpi_shareable_unref_and_delete_if_last(obj->buffer, free_buffer);
+ break;
+ case UACPI_OBJECT_BUFFER_FIELD:
+ uacpi_shareable_unref_and_delete_if_last(obj->buffer_field.backing,
+ free_buffer);
+ break;
+ case UACPI_OBJECT_BUFFER_INDEX:
+ uacpi_shareable_unref_and_delete_if_last(obj->buffer_index.buffer,
+ free_buffer);
+ break;
+ case UACPI_OBJECT_METHOD:
+ uacpi_method_unref(obj->method);
+ break;
+ case UACPI_OBJECT_PACKAGE:
+ uacpi_shareable_unref_and_delete_if_last(obj->package,
+ free_package);
+ break;
+ case UACPI_OBJECT_FIELD_UNIT:
+ uacpi_shareable_unref_and_delete_if_last(obj->field_unit,
+ free_field_unit);
+ break;
+ case UACPI_OBJECT_MUTEX:
+ uacpi_mutex_unref(obj->mutex);
+ break;
+ case UACPI_OBJECT_EVENT:
+ uacpi_shareable_unref_and_delete_if_last(obj->event,
+ free_event);
+ break;
+ case UACPI_OBJECT_OPERATION_REGION:
+ uacpi_shareable_unref_and_delete_if_last(obj->op_region,
+ free_op_region);
+ break;
+ case UACPI_OBJECT_PROCESSOR:
+ uacpi_shareable_unref_and_delete_if_last(obj->processor,
+ free_processor);
+ break;
+ case UACPI_OBJECT_DEVICE:
+ uacpi_shareable_unref_and_delete_if_last(obj->device,
+ free_device);
+ break;
+ case UACPI_OBJECT_THERMAL_ZONE:
+ uacpi_shareable_unref_and_delete_if_last(obj->thermal_zone,
+ free_thermal_zone);
+ break;
+ default:
+ break;
+ }
+}
+
+static void free_object(uacpi_object *obj)
+{
+ free_object_storage(obj);
+ uacpi_free(obj, sizeof(*obj));
+}
+
+static void make_chain_bugged(uacpi_object *obj)
+{
+ uacpi_warn("object refcount bug, marking chain @%p as bugged\n", obj);
+
+ while (obj) {
+ uacpi_make_shareable_bugged(obj);
+
+ if (obj->type == UACPI_OBJECT_REFERENCE)
+ obj = obj->inner_object;
+ else
+ obj = UACPI_NULL;
+ }
+}
+
+void uacpi_object_ref(uacpi_object *obj)
+{
+ while (obj) {
+ uacpi_shareable_ref(obj);
+
+ if (obj->type == UACPI_OBJECT_REFERENCE)
+ obj = obj->inner_object;
+ else
+ obj = UACPI_NULL;
+ }
+}
+
+static void free_chain(uacpi_object *obj)
+{
+ uacpi_object *next_obj = UACPI_NULL;
+
+ while (obj) {
+ if (obj->type == UACPI_OBJECT_REFERENCE)
+ next_obj = obj->inner_object;
+
+ if (uacpi_shareable_refcount(obj) == 0)
+ free_object(obj);
+
+ obj = next_obj;
+ next_obj = UACPI_NULL;
+ }
+}
+
+void uacpi_object_unref(uacpi_object *obj)
+{
+ uacpi_object *this_obj = obj;
+
+ if (!obj)
+ return;
+
+ while (obj) {
+ if (uacpi_unlikely(uacpi_bugged_shareable(obj)))
+ return;
+
+ uacpi_shareable_unref(obj);
+
+ if (obj->type == UACPI_OBJECT_REFERENCE) {
+ obj = obj->inner_object;
+ } else {
+ obj = UACPI_NULL;
+ }
+ }
+
+ if (uacpi_shareable_refcount(this_obj) == 0)
+ free_chain(this_obj);
+}
+
+static uacpi_status buffer_alloc_and_store(
+ uacpi_object *obj, uacpi_size buf_size,
+ const void *src, uacpi_size src_size
+)
+{
+ if (uacpi_unlikely(!buffer_alloc(obj, buf_size)))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ uacpi_memcpy_zerout(obj->buffer->data, src, buf_size, src_size);
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status assign_buffer(uacpi_object *dst, uacpi_object *src,
+ enum uacpi_assign_behavior behavior)
+{
+ if (behavior == UACPI_ASSIGN_BEHAVIOR_SHALLOW_COPY) {
+ dst->buffer = src->buffer;
+ uacpi_shareable_ref(dst->buffer);
+ return UACPI_STATUS_OK;
+ }
+
+ return buffer_alloc_and_store(dst, src->buffer->size,
+ src->buffer->data, src->buffer->size);
+}
+
+struct pkg_copy_req {
+ uacpi_object *dst;
+ uacpi_package *src;
+};
+
+DYNAMIC_ARRAY_WITH_INLINE_STORAGE(pkg_copy_reqs, struct pkg_copy_req, 2)
+DYNAMIC_ARRAY_WITH_INLINE_STORAGE_IMPL(
+ pkg_copy_reqs, struct pkg_copy_req, static
+)
+
+static uacpi_bool pkg_copy_reqs_push(
+ struct pkg_copy_reqs *reqs,
+ uacpi_object *dst, uacpi_package *pkg
+)
+{
+ struct pkg_copy_req *req;
+
+ req = pkg_copy_reqs_alloc(reqs);
+ if (uacpi_unlikely(req == UACPI_NULL))
+ return UACPI_FALSE;
+
+ req->dst = dst;
+ req->src = pkg;
+
+ return UACPI_TRUE;
+}
+
+static uacpi_status deep_copy_package_no_recurse(
+ uacpi_object *dst, uacpi_package *src,
+ struct pkg_copy_reqs *reqs
+)
+{
+ uacpi_size i;
+ uacpi_package *dst_package;
+
+ if (uacpi_unlikely(!package_alloc(dst, src->count,
+ UACPI_PREALLOC_OBJECTS_YES)))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ dst->type = UACPI_OBJECT_PACKAGE;
+ dst_package = dst->package;
+
+ for (i = 0; i < src->count; ++i) {
+ uacpi_status st;
+ uacpi_object *src_obj = src->objects[i];
+ uacpi_object *dst_obj = dst_package->objects[i];
+
+ // Don't copy the internal package index reference
+ if (src_obj->type == UACPI_OBJECT_REFERENCE &&
+ src_obj->flags == UACPI_REFERENCE_KIND_PKG_INDEX)
+ src_obj = src_obj->inner_object;
+
+ if (src_obj->type == UACPI_OBJECT_PACKAGE) {
+ uacpi_bool ret;
+
+ ret = pkg_copy_reqs_push(reqs, dst_obj, src_obj->package);
+ if (uacpi_unlikely(!ret))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ continue;
+ }
+
+ st = uacpi_object_assign(dst_obj, src_obj,
+ UACPI_ASSIGN_BEHAVIOR_DEEP_COPY);
+ if (uacpi_unlikely_error(st))
+ return st;
+ }
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status deep_copy_package(uacpi_object *dst, uacpi_object *src)
+{
+ uacpi_status ret = UACPI_STATUS_OK;
+ struct pkg_copy_reqs reqs = { 0 };
+
+ pkg_copy_reqs_push(&reqs, dst, src->package);
+
+ while (pkg_copy_reqs_size(&reqs) != 0) {
+ struct pkg_copy_req req;
+
+ req = *pkg_copy_reqs_last(&reqs);
+ pkg_copy_reqs_pop(&reqs);
+
+ ret = deep_copy_package_no_recurse(req.dst, req.src, &reqs);
+ if (uacpi_unlikely_error(ret))
+ break;
+ }
+
+ pkg_copy_reqs_clear(&reqs);
+ return ret;
+}
+
+static uacpi_status assign_mutex(uacpi_object *dst, uacpi_object *src,
+ enum uacpi_assign_behavior behavior)
+{
+ if (behavior == UACPI_ASSIGN_BEHAVIOR_DEEP_COPY) {
+ if (uacpi_likely(mutex_alloc(dst))) {
+ dst->mutex->sync_level = src->mutex->sync_level;
+ return UACPI_STATUS_OK;
+ }
+
+ return UACPI_STATUS_OUT_OF_MEMORY;
+ }
+
+ dst->mutex = src->mutex;
+ uacpi_shareable_ref(dst->mutex);
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status assign_event(uacpi_object *dst, uacpi_object *src,
+ enum uacpi_assign_behavior behavior)
+{
+ if (behavior == UACPI_ASSIGN_BEHAVIOR_DEEP_COPY) {
+ if (uacpi_likely(event_alloc(dst)))
+ return UACPI_STATUS_OK;
+
+ return UACPI_STATUS_OUT_OF_MEMORY;
+ }
+
+ dst->event = src->event;
+ uacpi_shareable_ref(dst->event);
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status assign_package(uacpi_object *dst, uacpi_object *src,
+ enum uacpi_assign_behavior behavior)
+{
+ if (behavior == UACPI_ASSIGN_BEHAVIOR_SHALLOW_COPY) {
+ dst->package = src->package;
+ uacpi_shareable_ref(dst->package);
+ return UACPI_STATUS_OK;
+ }
+
+ return deep_copy_package(dst, src);
+}
+
+void uacpi_object_attach_child(uacpi_object *parent, uacpi_object *child)
+{
+ uacpi_u32 refs_to_add;
+
+ parent->inner_object = child;
+
+ if (uacpi_unlikely(uacpi_bugged_shareable(parent))) {
+ make_chain_bugged(child);
+ return;
+ }
+
+ refs_to_add = uacpi_shareable_refcount(parent);
+ while (refs_to_add--)
+ uacpi_object_ref(child);
+}
+
+void uacpi_object_detach_child(uacpi_object *parent)
+{
+ uacpi_u32 refs_to_remove;
+ uacpi_object *child;
+
+ child = parent->inner_object;
+ parent->inner_object = UACPI_NULL;
+
+ if (uacpi_unlikely(uacpi_bugged_shareable(parent)))
+ return;
+
+ refs_to_remove = uacpi_shareable_refcount(parent);
+ while (refs_to_remove--)
+ uacpi_object_unref(child);
+}
+
+uacpi_object_type uacpi_object_get_type(uacpi_object *obj)
+{
+ return obj->type;
+}
+
+uacpi_object_type_bits uacpi_object_get_type_bit(uacpi_object *obj)
+{
+ return (1u << obj->type);
+}
+
+uacpi_bool uacpi_object_is(uacpi_object *obj, uacpi_object_type type)
+{
+ return obj->type == type;
+}
+
+uacpi_bool uacpi_object_is_one_of(
+ uacpi_object *obj, uacpi_object_type_bits type_mask
+)
+{
+ return (uacpi_object_get_type_bit(obj) & type_mask) != 0;
+}
+
+#define TYPE_CHECK_USER_OBJ_RET(obj, type_bits, ret) \
+ do { \
+ if (uacpi_unlikely(obj == UACPI_NULL || \
+ !uacpi_object_is_one_of(obj, type_bits))) \
+ return ret; \
+ } while (0)
+
+#define TYPE_CHECK_USER_OBJ(obj, type_bits) \
+ TYPE_CHECK_USER_OBJ_RET(obj, type_bits, UACPI_STATUS_INVALID_ARGUMENT)
+
+#define ENSURE_VALID_USER_OBJ_RET(obj, ret) \
+ do { \
+ if (uacpi_unlikely(obj == UACPI_NULL)) \
+ return ret; \
+ } while (0)
+
+#define ENSURE_VALID_USER_OBJ(obj) \
+ ENSURE_VALID_USER_OBJ_RET(obj, UACPI_STATUS_INVALID_ARGUMENT)
+
+uacpi_status uacpi_object_get_integer(uacpi_object *obj, uacpi_u64 *out)
+{
+ TYPE_CHECK_USER_OBJ(obj, UACPI_OBJECT_INTEGER_BIT);
+
+ *out = obj->integer;
+ return UACPI_STATUS_OK;
+}
+
+uacpi_status uacpi_object_assign_integer(uacpi_object *obj, uacpi_u64 value)
+{
+ uacpi_object object = { 0 };
+
+ ENSURE_VALID_USER_OBJ(obj);
+
+ object.type = UACPI_OBJECT_INTEGER;
+ object.integer = value;
+
+ return uacpi_object_assign(obj, &object, UACPI_ASSIGN_BEHAVIOR_DEEP_COPY);
+}
+
+void uacpi_buffer_to_view(uacpi_buffer *buf, uacpi_data_view *out_view)
+{
+ out_view->bytes = buf->byte_data;
+ out_view->length = buf->size;
+}
+
+static uacpi_status uacpi_object_do_get_string_or_buffer(
+ uacpi_object *obj, uacpi_data_view *out, uacpi_u32 mask
+)
+{
+ TYPE_CHECK_USER_OBJ(obj, mask);
+
+ uacpi_buffer_to_view(obj->buffer, out);
+ return UACPI_STATUS_OK;
+}
+
+uacpi_status uacpi_object_get_string_or_buffer(
+ uacpi_object *obj, uacpi_data_view *out
+)
+{
+ return uacpi_object_do_get_string_or_buffer(
+ obj, out, UACPI_OBJECT_STRING_BIT | UACPI_OBJECT_BUFFER_BIT
+ );
+}
+
+uacpi_status uacpi_object_get_string(uacpi_object *obj, uacpi_data_view *out)
+{
+ return uacpi_object_do_get_string_or_buffer(
+ obj, out, UACPI_OBJECT_STRING_BIT
+ );
+}
+
+uacpi_status uacpi_object_get_buffer(uacpi_object *obj, uacpi_data_view *out)
+{
+ return uacpi_object_do_get_string_or_buffer(
+ obj, out, UACPI_OBJECT_BUFFER_BIT
+ );
+}
+
+uacpi_bool uacpi_object_is_aml_namepath(uacpi_object *obj)
+{
+ TYPE_CHECK_USER_OBJ_RET(obj, UACPI_OBJECT_STRING_BIT, UACPI_FALSE);
+ return obj->flags == UACPI_STRING_KIND_PATH;
+}
+
+uacpi_status uacpi_object_resolve_as_aml_namepath(
+ uacpi_object *obj, uacpi_namespace_node *scope,
+ uacpi_namespace_node **out_node
+)
+{
+ uacpi_status ret;
+ uacpi_namespace_node *node;
+
+ if (!uacpi_object_is_aml_namepath(obj))
+ return UACPI_STATUS_INVALID_ARGUMENT;
+
+ ret = uacpi_namespace_node_resolve_from_aml_namepath(
+ scope, obj->buffer->text, &node
+ );
+ if (uacpi_likely_success(ret))
+ *out_node = node;
+ return ret;
+}
+
+static uacpi_status uacpi_object_do_assign_buffer(
+ uacpi_object *obj, uacpi_data_view in, uacpi_object_type type
+)
+{
+ uacpi_status ret;
+ uacpi_object tmp_obj = { 0 };
+ uacpi_size dst_buf_size = in.length;
+
+ tmp_obj.type = type;
+
+ ENSURE_VALID_USER_OBJ(obj);
+
+ if (type == UACPI_OBJECT_STRING && (in.length == 0 ||
+ in.const_bytes[in.length - 1] != 0x00))
+ dst_buf_size++;
+
+ ret = buffer_alloc_and_store(
+ &tmp_obj, dst_buf_size, in.const_bytes, in.length
+ );
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ ret = uacpi_object_assign(
+ obj, &tmp_obj, UACPI_ASSIGN_BEHAVIOR_SHALLOW_COPY
+ );
+ uacpi_shareable_unref_and_delete_if_last(tmp_obj.buffer, free_buffer);
+
+ return ret;
+}
+
+uacpi_status uacpi_object_assign_string(uacpi_object *obj, uacpi_data_view in)
+{
+ return uacpi_object_do_assign_buffer(obj, in, UACPI_OBJECT_STRING);
+}
+
+uacpi_status uacpi_object_assign_buffer(uacpi_object *obj, uacpi_data_view in)
+{
+ return uacpi_object_do_assign_buffer(obj, in, UACPI_OBJECT_BUFFER);
+}
+
+uacpi_object *uacpi_object_create_uninitialized(void)
+{
+ return uacpi_create_object(UACPI_OBJECT_UNINITIALIZED);
+}
+
+uacpi_status uacpi_object_create_integer_safe(
+ uacpi_u64 value, uacpi_overflow_behavior behavior, uacpi_object **out_obj
+)
+{
+ uacpi_status ret;
+ uacpi_u8 bitness;
+ uacpi_object *obj;
+
+ ret = uacpi_get_aml_bitness(&bitness);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ switch (behavior) {
+ case UACPI_OVERFLOW_TRUNCATE:
+ case UACPI_OVERFLOW_DISALLOW:
+ if (bitness == 32 && value > 0xFFFFFFFF) {
+ if (behavior == UACPI_OVERFLOW_DISALLOW)
+ return UACPI_STATUS_INVALID_ARGUMENT;
+
+ value &= 0xFFFFFFFF;
+ }
+ UACPI_FALLTHROUGH;
+ case UACPI_OVERFLOW_ALLOW:
+ obj = uacpi_object_create_integer(value);
+ if (uacpi_unlikely(obj == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ *out_obj = obj;
+ return ret;
+ default:
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+}
+
+uacpi_object *uacpi_object_create_integer(uacpi_u64 value)
+{
+ uacpi_object *obj;
+
+ obj = uacpi_create_object(UACPI_OBJECT_INTEGER);
+ if (uacpi_unlikely(obj == UACPI_NULL))
+ return obj;
+
+ obj->integer = value;
+ return obj;
+}
+
+static uacpi_object *uacpi_object_do_create_string_or_buffer(
+ uacpi_data_view view, uacpi_object_type type
+)
+{
+ uacpi_status ret;
+ uacpi_object *obj;
+
+ obj = uacpi_create_object(UACPI_OBJECT_UNINITIALIZED);
+ if (uacpi_unlikely(obj == UACPI_NULL))
+ return UACPI_NULL;
+
+ ret = uacpi_object_do_assign_buffer(obj, view, type);
+ if (uacpi_unlikely_error(ret)) {
+ uacpi_object_unref(obj);
+ return UACPI_NULL;
+ }
+
+ return obj;
+}
+
+uacpi_object *uacpi_object_create_string(uacpi_data_view view)
+{
+ return uacpi_object_do_create_string_or_buffer(view, UACPI_OBJECT_STRING);
+}
+
+uacpi_object *uacpi_object_create_buffer(uacpi_data_view view)
+{
+ return uacpi_object_do_create_string_or_buffer(view, UACPI_OBJECT_BUFFER);
+}
+
+uacpi_object *uacpi_object_create_cstring(const uacpi_char *str)
+{
+ uacpi_data_view data_view = { 0 };
+
+ data_view.const_text = str;
+ data_view.length = uacpi_strlen(str) + 1;
+ return uacpi_object_create_string(data_view);
+}
+
+uacpi_status uacpi_object_get_package(
+ uacpi_object *obj, uacpi_object_array *out
+)
+{
+ TYPE_CHECK_USER_OBJ(obj, UACPI_OBJECT_PACKAGE_BIT);
+
+ out->objects = obj->package->objects;
+ out->count = obj->package->count;
+ return UACPI_STATUS_OK;
+}
+
+uacpi_object *uacpi_object_create_reference(uacpi_object *child)
+{
+ uacpi_object *obj;
+
+ ENSURE_VALID_USER_OBJ_RET(child, UACPI_NULL);
+
+ obj = uacpi_create_object(UACPI_OBJECT_REFERENCE);
+ if (uacpi_unlikely(obj == UACPI_NULL))
+ return UACPI_NULL;
+
+ uacpi_object_attach_child(obj, child);
+ obj->flags = UACPI_REFERENCE_KIND_ARG;
+
+ return obj;
+}
+
+uacpi_status uacpi_object_assign_reference(
+ uacpi_object *obj, uacpi_object *child
+)
+{
+ uacpi_status ret;
+ uacpi_object object = { 0 };
+
+ ENSURE_VALID_USER_OBJ(obj);
+ ENSURE_VALID_USER_OBJ(child);
+
+ // First clear out the object
+ object.type = UACPI_OBJECT_UNINITIALIZED;
+ ret = uacpi_object_assign(
+ obj, &object,
+ UACPI_ASSIGN_BEHAVIOR_DEEP_COPY
+ );
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ obj->type = UACPI_OBJECT_REFERENCE;
+ uacpi_object_attach_child(obj, child);
+ obj->flags = UACPI_REFERENCE_KIND_ARG;
+
+ return UACPI_STATUS_OK;
+}
+
+uacpi_status uacpi_object_get_dereferenced(
+ uacpi_object *obj, uacpi_object **out
+)
+{
+ TYPE_CHECK_USER_OBJ(obj, UACPI_OBJECT_REFERENCE_BIT);
+
+ *out = obj->inner_object;
+ uacpi_shareable_ref(*out);
+
+ return UACPI_STATUS_OK;
+}
+
+uacpi_status uacpi_object_get_processor_info(
+ uacpi_object *obj, uacpi_processor_info *out
+)
+{
+ TYPE_CHECK_USER_OBJ(obj, UACPI_OBJECT_PROCESSOR_BIT);
+
+ out->id = obj->processor->id;
+ out->block_address = obj->processor->block_address;
+ out->block_length = obj->processor->block_length;
+ return UACPI_STATUS_OK;
+}
+
+uacpi_status uacpi_object_get_power_resource_info(
+ uacpi_object *obj, uacpi_power_resource_info *out
+)
+{
+ TYPE_CHECK_USER_OBJ(obj, UACPI_OBJECT_POWER_RESOURCE_BIT);
+
+ out->system_level = obj->power_resource.system_level;
+ out->resource_order = obj->power_resource.resource_order;
+ return UACPI_STATUS_OK;
+}
+
+uacpi_status uacpi_object_assign_package(
+ uacpi_object *obj, uacpi_object_array in
+)
+{
+ uacpi_status ret;
+ uacpi_size i;
+ uacpi_object tmp_obj = {
+ .type = UACPI_OBJECT_PACKAGE,
+ };
+
+ ENSURE_VALID_USER_OBJ(obj);
+
+ if (uacpi_unlikely(!package_alloc(&tmp_obj, in.count,
+ UACPI_PREALLOC_OBJECTS_NO)))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ obj->type = UACPI_OBJECT_PACKAGE;
+
+ for (i = 0; i < in.count; ++i) {
+ tmp_obj.package->objects[i] = in.objects[i];
+ uacpi_object_ref(tmp_obj.package->objects[i]);
+ }
+
+ ret = uacpi_object_assign(obj, &tmp_obj, UACPI_ASSIGN_BEHAVIOR_SHALLOW_COPY);
+ uacpi_shareable_unref_and_delete_if_last(tmp_obj.package, free_package);
+
+ return ret;
+}
+
+uacpi_object *uacpi_object_create_package(uacpi_object_array in)
+{
+ uacpi_status ret;
+ uacpi_object *obj;
+
+ obj = uacpi_object_create_uninitialized();
+ if (uacpi_unlikely(obj == UACPI_NULL))
+ return obj;
+
+ ret = uacpi_object_assign_package(obj, in);
+ if (uacpi_unlikely_error(ret)) {
+ uacpi_object_unref(obj);
+ return UACPI_NULL;
+ }
+
+ return obj;
+}
+
+uacpi_status uacpi_object_assign(uacpi_object *dst, uacpi_object *src,
+ enum uacpi_assign_behavior behavior)
+{
+ uacpi_status ret = UACPI_STATUS_OK;
+
+ if (src == dst)
+ return ret;
+
+ switch (dst->type) {
+ case UACPI_OBJECT_REFERENCE:
+ uacpi_object_detach_child(dst);
+ break;
+ case UACPI_OBJECT_STRING:
+ case UACPI_OBJECT_BUFFER:
+ case UACPI_OBJECT_METHOD:
+ case UACPI_OBJECT_PACKAGE:
+ case UACPI_OBJECT_MUTEX:
+ case UACPI_OBJECT_EVENT:
+ case UACPI_OBJECT_PROCESSOR:
+ case UACPI_OBJECT_DEVICE:
+ case UACPI_OBJECT_THERMAL_ZONE:
+ free_object_storage(dst);
+ break;
+ default:
+ break;
+ }
+
+ switch (src->type) {
+ case UACPI_OBJECT_UNINITIALIZED:
+ case UACPI_OBJECT_DEBUG:
+ break;
+ case UACPI_OBJECT_BUFFER:
+ case UACPI_OBJECT_STRING:
+ dst->flags = src->flags;
+ ret = assign_buffer(dst, src, behavior);
+ break;
+ case UACPI_OBJECT_BUFFER_FIELD:
+ dst->buffer_field = src->buffer_field;
+ uacpi_shareable_ref(dst->buffer_field.backing);
+ break;
+ case UACPI_OBJECT_BUFFER_INDEX:
+ dst->buffer_index = src->buffer_index;
+ uacpi_shareable_ref(dst->buffer_index.buffer);
+ break;
+ case UACPI_OBJECT_INTEGER:
+ dst->integer = src->integer;
+ break;
+ case UACPI_OBJECT_METHOD:
+ dst->method = src->method;
+ uacpi_shareable_ref(dst->method);
+ break;
+ case UACPI_OBJECT_MUTEX:
+ ret = assign_mutex(dst, src, behavior);
+ break;
+ case UACPI_OBJECT_EVENT:
+ ret = assign_event(dst, src, behavior);
+ break;
+ case UACPI_OBJECT_OPERATION_REGION:
+ dst->op_region = src->op_region;
+ uacpi_shareable_ref(dst->op_region);
+ break;
+ case UACPI_OBJECT_PACKAGE:
+ ret = assign_package(dst, src, behavior);
+ break;
+ case UACPI_OBJECT_FIELD_UNIT:
+ dst->field_unit = src->field_unit;
+ uacpi_shareable_ref(dst->field_unit);
+ break;
+ case UACPI_OBJECT_REFERENCE:
+ uacpi_object_attach_child(dst, src->inner_object);
+ break;
+ case UACPI_OBJECT_PROCESSOR:
+ dst->processor = src->processor;
+ uacpi_shareable_ref(dst->processor);
+ break;
+ case UACPI_OBJECT_DEVICE:
+ dst->device = src->device;
+ uacpi_shareable_ref(dst->device);
+ break;
+ case UACPI_OBJECT_THERMAL_ZONE:
+ dst->thermal_zone = src->thermal_zone;
+ uacpi_shareable_ref(dst->thermal_zone);
+ break;
+ default:
+ ret = UACPI_STATUS_UNIMPLEMENTED;
+ }
+
+ if (ret == UACPI_STATUS_OK)
+ dst->type = src->type;
+
+ return ret;
+}
+
+struct uacpi_object *uacpi_create_internal_reference(
+ enum uacpi_reference_kind kind, uacpi_object *child
+)
+{
+ uacpi_object *ret;
+
+ ret = uacpi_create_object(UACPI_OBJECT_REFERENCE);
+ if (uacpi_unlikely(ret == UACPI_NULL))
+ return ret;
+
+ ret->flags = kind;
+ uacpi_object_attach_child(ret, child);
+ return ret;
+}
+
+uacpi_object *uacpi_unwrap_internal_reference(uacpi_object *object)
+{
+ for (;;) {
+ if (object->type != UACPI_OBJECT_REFERENCE ||
+ (object->flags == UACPI_REFERENCE_KIND_REFOF ||
+ object->flags == UACPI_REFERENCE_KIND_PKG_INDEX))
+ return object;
+
+ object = object->inner_object;
+ }
+}
+
+#endif // !UACPI_BAREBONES_MODE
diff --git a/sys/dev/acpi/uacpi/uacpi.c b/sys/dev/acpi/uacpi/uacpi.c
new file mode 100644
index 0000000..c6c569f
--- /dev/null
+++ b/sys/dev/acpi/uacpi/uacpi.c
@@ -0,0 +1,998 @@
+#include <uacpi/uacpi.h>
+#include <uacpi/acpi.h>
+
+#include <uacpi/internal/log.h>
+#include <uacpi/internal/context.h>
+#include <uacpi/internal/utilities.h>
+#include <uacpi/internal/tables.h>
+#include <uacpi/internal/interpreter.h>
+#include <uacpi/internal/namespace.h>
+#include <uacpi/internal/opregion.h>
+#include <uacpi/internal/registers.h>
+#include <uacpi/internal/event.h>
+#include <uacpi/internal/notify.h>
+#include <uacpi/internal/osi.h>
+#include <uacpi/internal/registers.h>
+
+struct uacpi_runtime_context g_uacpi_rt_ctx = { 0 };
+
+void uacpi_context_set_log_level(uacpi_log_level lvl)
+{
+ if (lvl == 0)
+ lvl = UACPI_DEFAULT_LOG_LEVEL;
+
+ g_uacpi_rt_ctx.log_level = lvl;
+}
+
+void uacpi_logger_initialize(void)
+{
+ static uacpi_bool version_printed = UACPI_FALSE;
+
+ if (g_uacpi_rt_ctx.log_level == 0)
+ uacpi_context_set_log_level(UACPI_DEFAULT_LOG_LEVEL);
+
+ if (!version_printed) {
+ version_printed = UACPI_TRUE;
+ uacpi_info(
+ "starting uACPI, version %d.%d.%d\n",
+ UACPI_MAJOR, UACPI_MINOR, UACPI_PATCH
+ );
+ }
+}
+
+void uacpi_context_set_proactive_table_checksum(uacpi_bool setting)
+{
+ if (setting)
+ g_uacpi_rt_ctx.flags |= UACPI_FLAG_PROACTIVE_TBL_CSUM;
+ else
+ g_uacpi_rt_ctx.flags &= ~UACPI_FLAG_PROACTIVE_TBL_CSUM;
+}
+
+const uacpi_char *uacpi_status_to_string(uacpi_status st)
+{
+ switch (st) {
+ case UACPI_STATUS_OK:
+ return "no error";
+ case UACPI_STATUS_MAPPING_FAILED:
+ return "failed to map memory";
+ case UACPI_STATUS_OUT_OF_MEMORY:
+ return "out of memory";
+ case UACPI_STATUS_BAD_CHECKSUM:
+ return "bad table checksum";
+ case UACPI_STATUS_INVALID_SIGNATURE:
+ return "invalid table signature";
+ case UACPI_STATUS_INVALID_TABLE_LENGTH:
+ return "invalid table length";
+ case UACPI_STATUS_NOT_FOUND:
+ return "not found";
+ case UACPI_STATUS_INVALID_ARGUMENT:
+ return "invalid argument";
+ case UACPI_STATUS_UNIMPLEMENTED:
+ return "unimplemented";
+ case UACPI_STATUS_ALREADY_EXISTS:
+ return "already exists";
+ case UACPI_STATUS_INTERNAL_ERROR:
+ return "internal error";
+ case UACPI_STATUS_TYPE_MISMATCH:
+ return "object type mismatch";
+ case UACPI_STATUS_INIT_LEVEL_MISMATCH:
+ return "init level too low/high for this action";
+ case UACPI_STATUS_NAMESPACE_NODE_DANGLING:
+ return "attempting to use a dangling namespace node";
+ case UACPI_STATUS_NO_HANDLER:
+ return "no handler found";
+ case UACPI_STATUS_NO_RESOURCE_END_TAG:
+ return "resource template without an end tag";
+ case UACPI_STATUS_COMPILED_OUT:
+ return "this functionality has been compiled out of this build";
+ case UACPI_STATUS_HARDWARE_TIMEOUT:
+ return "timed out waiting for hardware response";
+ case UACPI_STATUS_TIMEOUT:
+ return "wait timed out";
+ case UACPI_STATUS_OVERRIDDEN:
+ return "the requested action has been overridden";
+ case UACPI_STATUS_DENIED:
+ return "the requested action has been denied";
+
+ case UACPI_STATUS_AML_UNDEFINED_REFERENCE:
+ return "AML referenced an undefined object";
+ case UACPI_STATUS_AML_INVALID_NAMESTRING:
+ return "invalid AML name string";
+ case UACPI_STATUS_AML_OBJECT_ALREADY_EXISTS:
+ return "object already exists";
+ case UACPI_STATUS_AML_INVALID_OPCODE:
+ return "invalid AML opcode";
+ case UACPI_STATUS_AML_INCOMPATIBLE_OBJECT_TYPE:
+ return "incompatible AML object type";
+ case UACPI_STATUS_AML_BAD_ENCODING:
+ return "bad AML instruction encoding";
+ case UACPI_STATUS_AML_OUT_OF_BOUNDS_INDEX:
+ return "out of bounds AML index";
+ case UACPI_STATUS_AML_SYNC_LEVEL_TOO_HIGH:
+ return "AML attempted to acquire a mutex with a lower sync level";
+ case UACPI_STATUS_AML_INVALID_RESOURCE:
+ return "invalid resource template encoding or type";
+ case UACPI_STATUS_AML_LOOP_TIMEOUT:
+ return "hanging AML while loop";
+ case UACPI_STATUS_AML_CALL_STACK_DEPTH_LIMIT:
+ return "reached maximum AML call stack depth";
+ default:
+ return "<invalid status>";
+ }
+}
+
+void uacpi_state_reset(void)
+{
+#ifndef UACPI_BAREBONES_MODE
+ uacpi_deinitialize_namespace();
+ uacpi_deinitialize_interfaces();
+ uacpi_deinitialize_events();
+ uacpi_deinitialize_notify();
+ uacpi_deinitialize_opregion();
+#endif
+
+ uacpi_deinitialize_tables();
+
+#ifndef UACPI_BAREBONES_MODE
+
+#ifndef UACPI_REDUCED_HARDWARE
+ if (g_uacpi_rt_ctx.was_in_legacy_mode)
+ uacpi_leave_acpi_mode();
+#endif
+
+ uacpi_deinitialize_registers();
+
+#ifndef UACPI_REDUCED_HARDWARE
+ if (g_uacpi_rt_ctx.global_lock_event)
+ uacpi_kernel_free_event(g_uacpi_rt_ctx.global_lock_event);
+ if (g_uacpi_rt_ctx.global_lock_spinlock)
+ uacpi_kernel_free_spinlock(g_uacpi_rt_ctx.global_lock_spinlock);
+#endif
+
+#endif // !UACPI_BAREBONES_MODE
+
+ uacpi_memzero(&g_uacpi_rt_ctx, sizeof(g_uacpi_rt_ctx));
+
+#if defined(UACPI_KERNEL_INITIALIZATION) && !defined(UACPI_BAREBONES_MODE)
+ uacpi_kernel_deinitialize();
+#endif
+}
+
+#ifndef UACPI_BAREBONES_MODE
+
+void uacpi_context_set_loop_timeout(uacpi_u32 seconds)
+{
+ if (seconds == 0)
+ seconds = UACPI_DEFAULT_LOOP_TIMEOUT_SECONDS;
+
+ g_uacpi_rt_ctx.loop_timeout_seconds = seconds;
+}
+
+void uacpi_context_set_max_call_stack_depth(uacpi_u32 depth)
+{
+ if (depth == 0)
+ depth = UACPI_DEFAULT_MAX_CALL_STACK_DEPTH;
+
+ g_uacpi_rt_ctx.max_call_stack_depth = depth;
+}
+
+uacpi_u32 uacpi_context_get_loop_timeout(void)
+{
+ return g_uacpi_rt_ctx.loop_timeout_seconds;
+}
+
+#ifndef UACPI_REDUCED_HARDWARE
+enum hw_mode {
+ HW_MODE_ACPI = 0,
+ HW_MODE_LEGACY = 1,
+};
+
+static enum hw_mode read_mode(void)
+{
+ uacpi_status ret;
+ uacpi_u64 raw_value;
+ struct acpi_fadt *fadt = &g_uacpi_rt_ctx.fadt;
+
+ if (!fadt->smi_cmd)
+ return HW_MODE_ACPI;
+
+ ret = uacpi_read_register_field(UACPI_REGISTER_FIELD_SCI_EN, &raw_value);
+ if (uacpi_unlikely_error(ret))
+ return HW_MODE_LEGACY;
+
+ return raw_value ? HW_MODE_ACPI : HW_MODE_LEGACY;
+}
+
+static uacpi_status set_mode(enum hw_mode mode)
+{
+ uacpi_status ret;
+ uacpi_u64 raw_value, stalled_time = 0;
+ struct acpi_fadt *fadt = &g_uacpi_rt_ctx.fadt;
+
+ if (uacpi_unlikely(!fadt->smi_cmd)) {
+ uacpi_error("SMI_CMD is not implemented by the firmware\n");
+ return UACPI_STATUS_NOT_FOUND;
+ }
+
+ if (uacpi_unlikely(!fadt->acpi_enable && !fadt->acpi_disable)) {
+ uacpi_error("mode transition is not implemented by the hardware\n");
+ return UACPI_STATUS_NOT_FOUND;
+ }
+
+ switch (mode) {
+ case HW_MODE_ACPI:
+ raw_value = fadt->acpi_enable;
+ break;
+ case HW_MODE_LEGACY:
+ raw_value = fadt->acpi_disable;
+ break;
+ default:
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+
+ ret = uacpi_write_register(UACPI_REGISTER_SMI_CMD, raw_value);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ // Allow up to 5 seconds for the hardware to enter the desired mode
+ while (stalled_time < (5 * 1000 * 1000)) {
+ if (read_mode() == mode)
+ return UACPI_STATUS_OK;
+
+ uacpi_kernel_stall(100);
+ stalled_time += 100;
+ }
+
+ uacpi_error("hardware time out while changing modes\n");
+ return UACPI_STATUS_HARDWARE_TIMEOUT;
+}
+
+static uacpi_status enter_mode(enum hw_mode mode, uacpi_bool *did_change)
+{
+ uacpi_status ret;
+ const uacpi_char *mode_str;
+
+ UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_SUBSYSTEM_INITIALIZED);
+
+ if (uacpi_is_hardware_reduced())
+ return UACPI_STATUS_OK;
+
+ mode_str = mode == HW_MODE_LEGACY ? "legacy" : "acpi";
+
+ if (read_mode() == mode) {
+ uacpi_trace("%s mode already enabled\n", mode_str);
+ return UACPI_STATUS_OK;
+ }
+
+ ret = set_mode(mode);
+ if (uacpi_unlikely_error(ret)) {
+ uacpi_warn(
+ "unable to enter %s mode: %s\n",
+ mode_str, uacpi_status_to_string(ret)
+ );
+ return ret;
+ }
+
+ uacpi_trace("entered %s mode\n", mode_str);
+ if (did_change != UACPI_NULL)
+ *did_change = UACPI_TRUE;
+
+ return ret;
+}
+
+uacpi_status uacpi_enter_acpi_mode(void)
+{
+ return enter_mode(HW_MODE_ACPI, UACPI_NULL);
+}
+
+uacpi_status uacpi_leave_acpi_mode(void)
+{
+ return enter_mode(HW_MODE_LEGACY, UACPI_NULL);
+}
+
+static void enter_acpi_mode_initial(void)
+{
+ enter_mode(HW_MODE_ACPI, &g_uacpi_rt_ctx.was_in_legacy_mode);
+}
+#else
+static void enter_acpi_mode_initial(void) { }
+#endif
+
+uacpi_init_level uacpi_get_current_init_level(void)
+{
+ return g_uacpi_rt_ctx.init_level;
+}
+
+uacpi_status uacpi_initialize(uacpi_u64 flags)
+{
+ uacpi_status ret;
+
+ UACPI_ENSURE_INIT_LEVEL_IS(UACPI_INIT_LEVEL_EARLY);
+
+#ifdef UACPI_KERNEL_INITIALIZATION
+ ret = uacpi_kernel_initialize(UACPI_INIT_LEVEL_EARLY);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+#endif
+
+ g_uacpi_rt_ctx.init_level = UACPI_INIT_LEVEL_SUBSYSTEM_INITIALIZED;
+ g_uacpi_rt_ctx.last_sleep_typ_a = UACPI_SLEEP_TYP_INVALID;
+ g_uacpi_rt_ctx.last_sleep_typ_b = UACPI_SLEEP_TYP_INVALID;
+ g_uacpi_rt_ctx.s0_sleep_typ_a = UACPI_SLEEP_TYP_INVALID;
+ g_uacpi_rt_ctx.s0_sleep_typ_b = UACPI_SLEEP_TYP_INVALID;
+ g_uacpi_rt_ctx.flags = flags;
+
+ uacpi_logger_initialize();
+
+ if (g_uacpi_rt_ctx.loop_timeout_seconds == 0)
+ uacpi_context_set_loop_timeout(UACPI_DEFAULT_LOOP_TIMEOUT_SECONDS);
+ if (g_uacpi_rt_ctx.max_call_stack_depth == 0)
+ uacpi_context_set_max_call_stack_depth(UACPI_DEFAULT_MAX_CALL_STACK_DEPTH);
+
+ ret = uacpi_initialize_tables();
+ if (uacpi_unlikely_error(ret))
+ goto out_fatal_error;
+
+ ret = uacpi_initialize_registers();
+ if (uacpi_unlikely_error(ret))
+ goto out_fatal_error;
+
+ ret = uacpi_initialize_events_early();
+ if (uacpi_unlikely_error(ret))
+ goto out_fatal_error;
+
+ ret = uacpi_initialize_opregion();
+ if (uacpi_unlikely_error(ret))
+ goto out_fatal_error;
+
+ ret = uacpi_initialize_interfaces();
+ if (uacpi_unlikely_error(ret))
+ goto out_fatal_error;
+
+ ret = uacpi_initialize_namespace();
+ if (uacpi_unlikely_error(ret))
+ goto out_fatal_error;
+
+ ret = uacpi_initialize_notify();
+ if (uacpi_unlikely_error(ret))
+ goto out_fatal_error;
+
+ uacpi_install_default_address_space_handlers();
+
+ if (!uacpi_check_flag(UACPI_FLAG_NO_ACPI_MODE))
+ enter_acpi_mode_initial();
+
+ return UACPI_STATUS_OK;
+
+out_fatal_error:
+ uacpi_state_reset();
+ return ret;
+}
+
+struct table_load_stats {
+ uacpi_u32 load_counter;
+ uacpi_u32 failure_counter;
+};
+
+static void trace_table_load_failure(
+ struct acpi_sdt_hdr *tbl, uacpi_log_level lvl, uacpi_status ret
+)
+{
+ uacpi_log_lvl(
+ lvl,
+ "failed to load "UACPI_PRI_TBL_HDR": %s\n",
+ UACPI_FMT_TBL_HDR(tbl), uacpi_status_to_string(ret)
+ );
+}
+
+static uacpi_bool match_ssdt_or_psdt(struct uacpi_installed_table *tbl)
+{
+ if (tbl->flags & UACPI_TABLE_LOADED)
+ return UACPI_FALSE;
+
+ return uacpi_signatures_match(tbl->hdr.signature, ACPI_SSDT_SIGNATURE) ||
+ uacpi_signatures_match(tbl->hdr.signature, ACPI_PSDT_SIGNATURE);
+}
+
+static uacpi_u64 elapsed_ms(uacpi_u64 begin_ns, uacpi_u64 end_ns)
+{
+ return (end_ns - begin_ns) / (1000ull * 1000ull);
+}
+
+static uacpi_bool warn_on_bad_timesource(uacpi_u64 begin_ts, uacpi_u64 end_ts)
+{
+ const uacpi_char *reason;
+
+ if (uacpi_unlikely(begin_ts == 0 && end_ts == 0)) {
+ reason = "uacpi_kernel_get_nanoseconds_since_boot() appears to be a stub";
+ goto out_bad_timesource;
+ }
+
+ if (uacpi_unlikely(begin_ts == end_ts)) {
+ reason = "poor time source precision detected";
+ goto out_bad_timesource;
+ }
+
+ if (uacpi_unlikely(end_ts < begin_ts)) {
+ reason = "time source backwards drift detected";
+ goto out_bad_timesource;
+ }
+
+ return UACPI_FALSE;
+
+out_bad_timesource:
+ uacpi_warn("%s, this may cause problems\n", reason);
+ return UACPI_TRUE;
+}
+
+uacpi_status uacpi_namespace_load(void)
+{
+ struct uacpi_table tbl;
+ uacpi_status ret;
+ uacpi_u64 begin_ts, end_ts;
+ struct table_load_stats st = { 0 };
+ uacpi_size cur_index;
+
+ UACPI_ENSURE_INIT_LEVEL_IS(UACPI_INIT_LEVEL_SUBSYSTEM_INITIALIZED);
+
+#ifdef UACPI_KERNEL_INITIALIZATION
+ ret = uacpi_kernel_initialize(UACPI_INIT_LEVEL_SUBSYSTEM_INITIALIZED);
+ if (uacpi_unlikely_error(ret))
+ goto out_fatal_error;
+#endif
+
+ begin_ts = uacpi_kernel_get_nanoseconds_since_boot();
+
+ ret = uacpi_table_find_by_signature(ACPI_DSDT_SIGNATURE, &tbl);
+ if (uacpi_unlikely_error(ret)) {
+ uacpi_error("unable to find DSDT: %s\n", uacpi_status_to_string(ret));
+ goto out_fatal_error;
+ }
+
+ ret = uacpi_table_load_with_cause(tbl.index, UACPI_TABLE_LOAD_CAUSE_INIT);
+ if (uacpi_unlikely_error(ret)) {
+ trace_table_load_failure(tbl.hdr, UACPI_LOG_ERROR, ret);
+ st.failure_counter++;
+ }
+ st.load_counter++;
+ uacpi_table_unref(&tbl);
+
+ for (cur_index = 0;; cur_index = tbl.index + 1) {
+ ret = uacpi_table_match(cur_index, match_ssdt_or_psdt, &tbl);
+ if (ret != UACPI_STATUS_OK) {
+ if (uacpi_unlikely(ret != UACPI_STATUS_NOT_FOUND))
+ goto out_fatal_error;
+
+ break;
+ }
+
+ ret = uacpi_table_load_with_cause(tbl.index, UACPI_TABLE_LOAD_CAUSE_INIT);
+ if (uacpi_unlikely_error(ret)) {
+ trace_table_load_failure(tbl.hdr, UACPI_LOG_WARN, ret);
+ st.failure_counter++;
+ }
+ st.load_counter++;
+ uacpi_table_unref(&tbl);
+ }
+
+ end_ts = uacpi_kernel_get_nanoseconds_since_boot();
+ g_uacpi_rt_ctx.bad_timesource = warn_on_bad_timesource(begin_ts, end_ts);
+
+ if (uacpi_unlikely(st.failure_counter != 0 || g_uacpi_rt_ctx.bad_timesource)) {
+ uacpi_info(
+ "loaded %u AML blob%s (%u error%s)\n",
+ st.load_counter, st.load_counter > 1 ? "s" : "", st.failure_counter,
+ st.failure_counter == 1 ? "" : "s"
+ );
+ } else {
+ uacpi_u64 ops = g_uacpi_rt_ctx.opcodes_executed;
+ uacpi_u64 ops_per_sec = ops * UACPI_NANOSECONDS_PER_SEC;
+
+ ops_per_sec /= end_ts - begin_ts;
+
+ uacpi_info(
+ "successfully loaded %u AML blob%s, %"UACPI_PRIu64" ops in "
+ "%"UACPI_PRIu64"ms (avg %"UACPI_PRIu64"/s)\n",
+ st.load_counter, st.load_counter > 1 ? "s" : "",
+ UACPI_FMT64(ops), UACPI_FMT64(elapsed_ms(begin_ts, end_ts)),
+ UACPI_FMT64(ops_per_sec)
+ );
+ }
+
+ ret = uacpi_initialize_events();
+ if (uacpi_unlikely_error(ret)) {
+ uacpi_error("event initialization failed: %s\n",
+ uacpi_status_to_string(ret));
+ goto out_fatal_error;
+ }
+
+ g_uacpi_rt_ctx.init_level = UACPI_INIT_LEVEL_NAMESPACE_LOADED;
+ return UACPI_STATUS_OK;
+
+out_fatal_error:
+ uacpi_state_reset();
+ return ret;
+}
+
+struct ns_init_context {
+ uacpi_size ini_executed;
+ uacpi_size ini_errors;
+ uacpi_size sta_executed;
+ uacpi_size sta_errors;
+ uacpi_size devices;
+ uacpi_size thermal_zones;
+};
+
+static void ini_eval(struct ns_init_context *ctx, uacpi_namespace_node *node)
+{
+ uacpi_status ret;
+
+ ret = uacpi_eval(node, "_INI", UACPI_NULL, UACPI_NULL);
+ if (ret == UACPI_STATUS_NOT_FOUND)
+ return;
+
+ ctx->ini_executed++;
+ if (uacpi_unlikely_error(ret))
+ ctx->ini_errors++;
+}
+
+static uacpi_status sta_eval(
+ struct ns_init_context *ctx, uacpi_namespace_node *node,
+ uacpi_u32 *value
+)
+{
+ uacpi_status ret;
+
+ ret = uacpi_eval_sta(node, value);
+ if (*value == 0xFFFFFFFF)
+ return ret;
+
+ ctx->sta_executed++;
+ if (uacpi_unlikely_error(ret))
+ ctx->sta_errors++;
+
+ return ret;
+}
+
+static uacpi_iteration_decision do_sta_ini(
+ void *opaque, uacpi_namespace_node *node, uacpi_u32 depth
+)
+{
+ struct ns_init_context *ctx = opaque;
+ uacpi_status ret;
+ uacpi_object_type type = UACPI_OBJECT_UNINITIALIZED;
+ uacpi_u32 sta_ret;
+
+ UACPI_UNUSED(depth);
+
+ // We don't care about aliases
+ if (uacpi_namespace_node_is_alias(node))
+ return UACPI_ITERATION_DECISION_NEXT_PEER;
+
+ ret = uacpi_namespace_node_type(node, &type);
+ switch (type) {
+ case UACPI_OBJECT_DEVICE:
+ case UACPI_OBJECT_PROCESSOR:
+ ctx->devices++;
+ break;
+ case UACPI_OBJECT_THERMAL_ZONE:
+ ctx->thermal_zones++;
+ break;
+ default:
+ if (node != uacpi_namespace_get_predefined(UACPI_PREDEFINED_NAMESPACE_TZ))
+ return UACPI_ITERATION_DECISION_CONTINUE;
+ }
+
+ ret = sta_eval(ctx, node, &sta_ret);
+ if (uacpi_unlikely_error(ret))
+ return UACPI_ITERATION_DECISION_CONTINUE;
+
+ if (!(sta_ret & ACPI_STA_RESULT_DEVICE_PRESENT)) {
+ if (!(sta_ret & ACPI_STA_RESULT_DEVICE_FUNCTIONING))
+ return UACPI_ITERATION_DECISION_NEXT_PEER;
+
+ /*
+ * ACPI 6.5 specification:
+ * _STA may return bit 0 clear (not present) with bit [3] set (device
+ * is functional). This case is used to indicate a valid device for
+ * which no device driver should be loaded (for example, a bridge
+ * device.) Children of this device may be present and valid. OSPM
+ * should continue enumeration below a device whose _STA returns this
+ * bit combination.
+ */
+ return UACPI_ITERATION_DECISION_CONTINUE;
+ }
+
+ ini_eval(ctx, node);
+
+ return UACPI_ITERATION_DECISION_CONTINUE;
+}
+
+uacpi_status uacpi_namespace_initialize(void)
+{
+ struct ns_init_context ctx = { 0 };
+ uacpi_namespace_node *root;
+ uacpi_u64 begin_ts, end_ts;
+ uacpi_address_space_handlers *handlers;
+ uacpi_address_space_handler *handler;
+ uacpi_status ret = UACPI_STATUS_OK;
+
+ UACPI_ENSURE_INIT_LEVEL_IS(UACPI_INIT_LEVEL_NAMESPACE_LOADED);
+
+#ifdef UACPI_KERNEL_INITIALIZATION
+ ret = uacpi_kernel_initialize(UACPI_INIT_LEVEL_NAMESPACE_LOADED);
+ if (uacpi_unlikely_error(ret))
+ goto out;
+#endif
+
+ /*
+ * Initialization order here is identical to ACPICA because ACPI
+ * specification doesn't really have any detailed steps that explain
+ * how to do it.
+ */
+
+ root = uacpi_namespace_root();
+
+ begin_ts = uacpi_kernel_get_nanoseconds_since_boot();
+
+ // Step 1 - Execute \_INI
+ ini_eval(&ctx, root);
+
+ // Step 2 - Execute \_SB._INI
+ ini_eval(
+ &ctx, uacpi_namespace_get_predefined(UACPI_PREDEFINED_NAMESPACE_SB)
+ );
+
+ /*
+ * Step 3 - Run _REG methods for all globally installed
+ * address space handlers.
+ */
+ handlers = uacpi_node_get_address_space_handlers(root);
+ if (handlers) {
+ handler = handlers->head;
+
+ while (handler) {
+ if (uacpi_address_space_handler_is_default(handler))
+ uacpi_reg_all_opregions(root, handler->space);
+
+ handler = handler->next;
+ }
+ }
+
+ // Step 4 - Run all other _STA and _INI methods
+ uacpi_namespace_for_each_child(
+ root, do_sta_ini, UACPI_NULL,
+ UACPI_OBJECT_ANY_BIT, UACPI_MAX_DEPTH_ANY, &ctx
+ );
+
+ end_ts = uacpi_kernel_get_nanoseconds_since_boot();
+
+ if (uacpi_likely(!g_uacpi_rt_ctx.bad_timesource)) {
+ uacpi_info(
+ "namespace initialization done in %"UACPI_PRIu64"ms: "
+ "%zu devices, %zu thermal zones\n",
+ UACPI_FMT64(elapsed_ms(begin_ts, end_ts)),
+ ctx.devices, ctx.thermal_zones
+ );
+ } else {
+ uacpi_info(
+ "namespace initialization done: %zu devices, %zu thermal zones\n",
+ ctx.devices, ctx.thermal_zones
+ );
+ }
+
+ uacpi_trace(
+ "_STA calls: %zu (%zu errors), _INI calls: %zu (%zu errors)\n",
+ ctx.sta_executed, ctx.sta_errors, ctx.ini_executed,
+ ctx.ini_errors
+ );
+
+ g_uacpi_rt_ctx.init_level = UACPI_INIT_LEVEL_NAMESPACE_INITIALIZED;
+#ifdef UACPI_KERNEL_INITIALIZATION
+ ret = uacpi_kernel_initialize(UACPI_INIT_LEVEL_NAMESPACE_INITIALIZED);
+out:
+ if (uacpi_unlikely_error(ret))
+ uacpi_state_reset();
+#endif
+ return ret;
+}
+
+uacpi_status uacpi_eval(
+ uacpi_namespace_node *parent, const uacpi_char *path,
+ const uacpi_object_array *args, uacpi_object **out_obj
+)
+{
+ struct uacpi_namespace_node *node;
+ uacpi_control_method *method;
+ uacpi_object *obj;
+ uacpi_status ret = UACPI_STATUS_INVALID_ARGUMENT;
+
+ if (uacpi_unlikely(parent == UACPI_NULL && path == UACPI_NULL))
+ return ret;
+
+ ret = uacpi_namespace_read_lock();
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ if (path != UACPI_NULL) {
+ ret = uacpi_namespace_node_resolve(
+ parent, path, UACPI_SHOULD_LOCK_NO,
+ UACPI_MAY_SEARCH_ABOVE_PARENT_NO, UACPI_PERMANENT_ONLY_YES,
+ &node
+ );
+ if (uacpi_unlikely_error(ret))
+ goto out_read_unlock;
+ } else {
+ node = parent;
+ }
+
+ obj = uacpi_namespace_node_get_object(node);
+ if (uacpi_unlikely(obj == UACPI_NULL)) {
+ ret = UACPI_STATUS_INVALID_ARGUMENT;
+ goto out_read_unlock;
+ }
+
+ if (obj->type != UACPI_OBJECT_METHOD) {
+ uacpi_object *new_obj;
+
+ if (uacpi_unlikely(out_obj == UACPI_NULL))
+ goto out_read_unlock;
+
+ new_obj = uacpi_create_object(UACPI_OBJECT_UNINITIALIZED);
+ if (uacpi_unlikely(new_obj == UACPI_NULL)) {
+ ret = UACPI_STATUS_OUT_OF_MEMORY;
+ goto out_read_unlock;
+ }
+
+ ret = uacpi_object_assign(
+ new_obj, obj, UACPI_ASSIGN_BEHAVIOR_DEEP_COPY
+ );
+ if (uacpi_unlikely_error(ret)) {
+ uacpi_object_unref(new_obj);
+ goto out_read_unlock;
+ }
+ *out_obj = new_obj;
+
+ out_read_unlock:
+ uacpi_namespace_read_unlock();
+ return ret;
+ }
+
+ method = obj->method;
+ uacpi_shareable_ref(method);
+ uacpi_namespace_read_unlock();
+
+ // Upgrade to a write-lock since we're about to run a method
+ ret = uacpi_namespace_write_lock();
+ if (uacpi_unlikely_error(ret))
+ goto out_no_write_lock;
+
+ ret = uacpi_execute_control_method(node, method, args, out_obj);
+ uacpi_namespace_write_unlock();
+
+out_no_write_lock:
+ uacpi_method_unref(method);
+ return ret;
+}
+
+uacpi_status uacpi_eval_simple(
+ uacpi_namespace_node *parent, const uacpi_char *path, uacpi_object **ret
+)
+{
+ return uacpi_eval(parent, path, UACPI_NULL, ret);
+}
+
+uacpi_status uacpi_execute(
+ uacpi_namespace_node *parent, const uacpi_char *path,
+ const uacpi_object_array *args
+)
+{
+ return uacpi_eval(parent, path, args, UACPI_NULL);
+}
+
+uacpi_status uacpi_execute_simple(
+ uacpi_namespace_node *parent, const uacpi_char *path
+)
+{
+ return uacpi_eval(parent, path, UACPI_NULL, UACPI_NULL);
+}
+
+#define TRACE_BAD_RET(path_fmt, type, ...) \
+ uacpi_warn( \
+ "unexpected '%s' object returned by method "path_fmt \
+ ", expected type mask: %08X\n", uacpi_object_type_to_string(type), \
+ __VA_ARGS__ \
+ )
+
+#define TRACE_NO_RET(path_fmt, ...) \
+ uacpi_warn( \
+ "no value returned from method "path_fmt", expected type mask: " \
+ "%08X\n", __VA_ARGS__ \
+ )
+
+static void trace_invalid_return_type(
+ uacpi_namespace_node *parent, const uacpi_char *path,
+ uacpi_object_type_bits expected_mask, uacpi_object_type actual_type
+)
+{
+ const uacpi_char *abs_path;
+ uacpi_bool dynamic_abs_path = UACPI_FALSE;
+
+ if (parent == UACPI_NULL || (path != UACPI_NULL && path[0] == '\\')) {
+ abs_path = path;
+ } else {
+ abs_path = uacpi_namespace_node_generate_absolute_path(parent);
+ dynamic_abs_path = UACPI_TRUE;
+ }
+
+ if (dynamic_abs_path && path != UACPI_NULL) {
+ if (actual_type == UACPI_OBJECT_UNINITIALIZED)
+ TRACE_NO_RET("%s.%s", abs_path, path, expected_mask);
+ else
+ TRACE_BAD_RET("%s.%s", actual_type, abs_path, path, expected_mask);
+ } else {
+ if (actual_type == UACPI_OBJECT_UNINITIALIZED) {
+ TRACE_NO_RET("%s", abs_path, expected_mask);
+ } else {
+ TRACE_BAD_RET("%s", actual_type, abs_path, expected_mask);
+ }
+ }
+
+ if (dynamic_abs_path)
+ uacpi_free_dynamic_string(abs_path);
+}
+
+uacpi_status uacpi_eval_typed(
+ uacpi_namespace_node *parent, const uacpi_char *path,
+ const uacpi_object_array *args, uacpi_object_type_bits ret_mask,
+ uacpi_object **out_obj
+)
+{
+ uacpi_status ret;
+ uacpi_object *obj;
+ uacpi_object_type returned_type = UACPI_OBJECT_UNINITIALIZED;
+
+ if (uacpi_unlikely(out_obj == UACPI_NULL))
+ return UACPI_STATUS_INVALID_ARGUMENT;
+
+ ret = uacpi_eval(parent, path, args, &obj);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ if (obj != UACPI_NULL)
+ returned_type = obj->type;
+
+ if (ret_mask && (ret_mask & (1 << returned_type)) == 0) {
+ trace_invalid_return_type(parent, path, ret_mask, returned_type);
+ uacpi_object_unref(obj);
+ return UACPI_STATUS_TYPE_MISMATCH;
+ }
+
+ *out_obj = obj;
+ return UACPI_STATUS_OK;
+}
+
+uacpi_status uacpi_eval_simple_typed(
+ uacpi_namespace_node *parent, const uacpi_char *path,
+ uacpi_object_type_bits ret_mask, uacpi_object **ret
+)
+{
+ return uacpi_eval_typed(parent, path, UACPI_NULL, ret_mask, ret);
+}
+
+uacpi_status uacpi_eval_integer(
+ uacpi_namespace_node *parent, const uacpi_char *path,
+ const uacpi_object_array *args, uacpi_u64 *out_value
+)
+{
+ uacpi_object *int_obj;
+ uacpi_status ret;
+
+ ret = uacpi_eval_typed(
+ parent, path, args, UACPI_OBJECT_INTEGER_BIT, &int_obj
+ );
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ *out_value = int_obj->integer;
+ uacpi_object_unref(int_obj);
+
+ return UACPI_STATUS_OK;
+}
+
+uacpi_status uacpi_eval_simple_integer(
+ uacpi_namespace_node *parent, const uacpi_char *path, uacpi_u64 *out_value
+)
+{
+ return uacpi_eval_integer(parent, path, UACPI_NULL, out_value);
+}
+
+uacpi_status uacpi_eval_buffer_or_string(
+ uacpi_namespace_node *parent, const uacpi_char *path,
+ const uacpi_object_array *args, uacpi_object **ret
+)
+{
+ return uacpi_eval_typed(
+ parent, path, args,
+ UACPI_OBJECT_BUFFER_BIT | UACPI_OBJECT_STRING_BIT,
+ ret
+ );
+}
+
+uacpi_status uacpi_eval_simple_buffer_or_string(
+ uacpi_namespace_node *parent, const uacpi_char *path, uacpi_object **ret
+)
+{
+ return uacpi_eval_typed(
+ parent, path, UACPI_NULL,
+ UACPI_OBJECT_BUFFER_BIT | UACPI_OBJECT_STRING_BIT,
+ ret
+ );
+}
+
+uacpi_status uacpi_eval_string(
+ uacpi_namespace_node *parent, const uacpi_char *path,
+ const uacpi_object_array *args, uacpi_object **ret
+)
+{
+ return uacpi_eval_typed(
+ parent, path, args, UACPI_OBJECT_STRING_BIT, ret
+ );
+}
+
+uacpi_status uacpi_eval_simple_string(
+ uacpi_namespace_node *parent, const uacpi_char *path, uacpi_object **ret
+)
+{
+ return uacpi_eval_typed(
+ parent, path, UACPI_NULL, UACPI_OBJECT_STRING_BIT, ret
+ );
+}
+
+uacpi_status uacpi_eval_buffer(
+ uacpi_namespace_node *parent, const uacpi_char *path,
+ const uacpi_object_array *args, uacpi_object **ret
+)
+{
+ return uacpi_eval_typed(
+ parent, path, args, UACPI_OBJECT_BUFFER_BIT, ret
+ );
+}
+
+uacpi_status uacpi_eval_simple_buffer(
+ uacpi_namespace_node *parent, const uacpi_char *path, uacpi_object **ret
+)
+{
+ return uacpi_eval_typed(
+ parent, path, UACPI_NULL, UACPI_OBJECT_BUFFER_BIT, ret
+ );
+}
+
+uacpi_status uacpi_eval_package(
+ uacpi_namespace_node *parent, const uacpi_char *path,
+ const uacpi_object_array *args, uacpi_object **ret
+)
+{
+ return uacpi_eval_typed(
+ parent, path, args, UACPI_OBJECT_PACKAGE_BIT, ret
+ );
+}
+
+uacpi_status uacpi_eval_simple_package(
+ uacpi_namespace_node *parent, const uacpi_char *path, uacpi_object **ret
+)
+{
+ return uacpi_eval_typed(
+ parent, path, UACPI_NULL, UACPI_OBJECT_PACKAGE_BIT, ret
+ );
+}
+
+uacpi_status uacpi_get_aml_bitness(uacpi_u8 *out_bitness)
+{
+ UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_SUBSYSTEM_INITIALIZED);
+
+ *out_bitness = g_uacpi_rt_ctx.is_rev1 ? 32 : 64;
+ return UACPI_STATUS_OK;
+}
+
+#endif // !UACPI_BAREBONES_MODE
diff --git a/sys/dev/acpi/uacpi/utilities.c b/sys/dev/acpi/uacpi/utilities.c
new file mode 100644
index 0000000..c7ca20a
--- /dev/null
+++ b/sys/dev/acpi/uacpi/utilities.c
@@ -0,0 +1,1156 @@
+#include <uacpi/types.h>
+#include <uacpi/status.h>
+#include <uacpi/uacpi.h>
+
+#include <uacpi/internal/context.h>
+#include <uacpi/internal/utilities.h>
+#include <uacpi/internal/log.h>
+#include <uacpi/internal/namespace.h>
+
+enum char_type {
+ CHAR_TYPE_CONTROL = 1 << 0,
+ CHAR_TYPE_SPACE = 1 << 1,
+ CHAR_TYPE_BLANK = 1 << 2,
+ CHAR_TYPE_PUNCTUATION = 1 << 3,
+ CHAR_TYPE_LOWER = 1 << 4,
+ CHAR_TYPE_UPPER = 1 << 5,
+ CHAR_TYPE_DIGIT = 1 << 6,
+ CHAR_TYPE_HEX_DIGIT = 1 << 7,
+ CHAR_TYPE_ALPHA = CHAR_TYPE_LOWER | CHAR_TYPE_UPPER,
+ CHAR_TYPE_ALHEX = CHAR_TYPE_ALPHA | CHAR_TYPE_HEX_DIGIT,
+ CHAR_TYPE_ALNUM = CHAR_TYPE_ALPHA | CHAR_TYPE_DIGIT,
+};
+
+static const uacpi_u8 ascii_map[256] = {
+ CHAR_TYPE_CONTROL, // 0
+ CHAR_TYPE_CONTROL, // 1
+ CHAR_TYPE_CONTROL, // 2
+ CHAR_TYPE_CONTROL, // 3
+ CHAR_TYPE_CONTROL, // 4
+ CHAR_TYPE_CONTROL, // 5
+ CHAR_TYPE_CONTROL, // 6
+ CHAR_TYPE_CONTROL, // 7
+ CHAR_TYPE_CONTROL, // -> 8 control codes
+
+ CHAR_TYPE_CONTROL | CHAR_TYPE_SPACE | CHAR_TYPE_BLANK, // 9 tab
+
+ CHAR_TYPE_CONTROL | CHAR_TYPE_SPACE, // 10
+ CHAR_TYPE_CONTROL | CHAR_TYPE_SPACE, // 11
+ CHAR_TYPE_CONTROL | CHAR_TYPE_SPACE, // 12
+ CHAR_TYPE_CONTROL | CHAR_TYPE_SPACE, // -> 13 whitespaces
+
+ CHAR_TYPE_CONTROL, // 14
+ CHAR_TYPE_CONTROL, // 15
+ CHAR_TYPE_CONTROL, // 16
+ CHAR_TYPE_CONTROL, // 17
+ CHAR_TYPE_CONTROL, // 18
+ CHAR_TYPE_CONTROL, // 19
+ CHAR_TYPE_CONTROL, // 20
+ CHAR_TYPE_CONTROL, // 21
+ CHAR_TYPE_CONTROL, // 22
+ CHAR_TYPE_CONTROL, // 23
+ CHAR_TYPE_CONTROL, // 24
+ CHAR_TYPE_CONTROL, // 25
+ CHAR_TYPE_CONTROL, // 26
+ CHAR_TYPE_CONTROL, // 27
+ CHAR_TYPE_CONTROL, // 28
+ CHAR_TYPE_CONTROL, // 29
+ CHAR_TYPE_CONTROL, // 30
+ CHAR_TYPE_CONTROL, // -> 31 control codes
+
+ CHAR_TYPE_SPACE | CHAR_TYPE_BLANK, // 32 space
+
+ CHAR_TYPE_PUNCTUATION, // 33
+ CHAR_TYPE_PUNCTUATION, // 34
+ CHAR_TYPE_PUNCTUATION, // 35
+ CHAR_TYPE_PUNCTUATION, // 36
+ CHAR_TYPE_PUNCTUATION, // 37
+ CHAR_TYPE_PUNCTUATION, // 38
+ CHAR_TYPE_PUNCTUATION, // 39
+ CHAR_TYPE_PUNCTUATION, // 40
+ CHAR_TYPE_PUNCTUATION, // 41
+ CHAR_TYPE_PUNCTUATION, // 42
+ CHAR_TYPE_PUNCTUATION, // 43
+ CHAR_TYPE_PUNCTUATION, // 44
+ CHAR_TYPE_PUNCTUATION, // 45
+ CHAR_TYPE_PUNCTUATION, // 46
+ CHAR_TYPE_PUNCTUATION, // -> 47 punctuation
+
+ CHAR_TYPE_DIGIT | CHAR_TYPE_HEX_DIGIT, // 48
+ CHAR_TYPE_DIGIT | CHAR_TYPE_HEX_DIGIT, // 49
+ CHAR_TYPE_DIGIT | CHAR_TYPE_HEX_DIGIT, // 50
+ CHAR_TYPE_DIGIT | CHAR_TYPE_HEX_DIGIT, // 51
+ CHAR_TYPE_DIGIT | CHAR_TYPE_HEX_DIGIT, // 52
+ CHAR_TYPE_DIGIT | CHAR_TYPE_HEX_DIGIT, // 53
+ CHAR_TYPE_DIGIT | CHAR_TYPE_HEX_DIGIT, // 54
+ CHAR_TYPE_DIGIT | CHAR_TYPE_HEX_DIGIT, // 55
+ CHAR_TYPE_DIGIT | CHAR_TYPE_HEX_DIGIT, // 56
+ CHAR_TYPE_DIGIT | CHAR_TYPE_HEX_DIGIT, // -> 57 digits
+
+ CHAR_TYPE_PUNCTUATION, // 58
+ CHAR_TYPE_PUNCTUATION, // 59
+ CHAR_TYPE_PUNCTUATION, // 60
+ CHAR_TYPE_PUNCTUATION, // 61
+ CHAR_TYPE_PUNCTUATION, // 62
+ CHAR_TYPE_PUNCTUATION, // 63
+ CHAR_TYPE_PUNCTUATION, // -> 64 punctuation
+
+ CHAR_TYPE_UPPER | CHAR_TYPE_HEX_DIGIT, // 65
+ CHAR_TYPE_UPPER | CHAR_TYPE_HEX_DIGIT, // 66
+ CHAR_TYPE_UPPER | CHAR_TYPE_HEX_DIGIT, // 67
+ CHAR_TYPE_UPPER | CHAR_TYPE_HEX_DIGIT, // 68
+ CHAR_TYPE_UPPER | CHAR_TYPE_HEX_DIGIT, // 69
+ CHAR_TYPE_UPPER | CHAR_TYPE_HEX_DIGIT, // -> 70 ABCDEF
+
+ CHAR_TYPE_UPPER, // 71
+ CHAR_TYPE_UPPER, // 72
+ CHAR_TYPE_UPPER, // 73
+ CHAR_TYPE_UPPER, // 74
+ CHAR_TYPE_UPPER, // 75
+ CHAR_TYPE_UPPER, // 76
+ CHAR_TYPE_UPPER, // 77
+ CHAR_TYPE_UPPER, // 78
+ CHAR_TYPE_UPPER, // 79
+ CHAR_TYPE_UPPER, // 80
+ CHAR_TYPE_UPPER, // 81
+ CHAR_TYPE_UPPER, // 82
+ CHAR_TYPE_UPPER, // 83
+ CHAR_TYPE_UPPER, // 84
+ CHAR_TYPE_UPPER, // 85
+ CHAR_TYPE_UPPER, // 86
+ CHAR_TYPE_UPPER, // 87
+ CHAR_TYPE_UPPER, // 88
+ CHAR_TYPE_UPPER, // 89
+ CHAR_TYPE_UPPER, // -> 90 the rest of UPPERCASE alphabet
+
+ CHAR_TYPE_PUNCTUATION, // 91
+ CHAR_TYPE_PUNCTUATION, // 92
+ CHAR_TYPE_PUNCTUATION, // 93
+ CHAR_TYPE_PUNCTUATION, // 94
+ CHAR_TYPE_PUNCTUATION, // 95
+ CHAR_TYPE_PUNCTUATION, // -> 96 punctuation
+
+ CHAR_TYPE_LOWER | CHAR_TYPE_HEX_DIGIT, // 97
+ CHAR_TYPE_LOWER | CHAR_TYPE_HEX_DIGIT, // 98
+ CHAR_TYPE_LOWER | CHAR_TYPE_HEX_DIGIT, // 99
+ CHAR_TYPE_LOWER | CHAR_TYPE_HEX_DIGIT, // 100
+ CHAR_TYPE_LOWER | CHAR_TYPE_HEX_DIGIT, // 101
+ CHAR_TYPE_LOWER | CHAR_TYPE_HEX_DIGIT, // -> 102 abcdef
+
+ CHAR_TYPE_LOWER, // 103
+ CHAR_TYPE_LOWER, // 104
+ CHAR_TYPE_LOWER, // 105
+ CHAR_TYPE_LOWER, // 106
+ CHAR_TYPE_LOWER, // 107
+ CHAR_TYPE_LOWER, // 108
+ CHAR_TYPE_LOWER, // 109
+ CHAR_TYPE_LOWER, // 110
+ CHAR_TYPE_LOWER, // 111
+ CHAR_TYPE_LOWER, // 112
+ CHAR_TYPE_LOWER, // 113
+ CHAR_TYPE_LOWER, // 114
+ CHAR_TYPE_LOWER, // 115
+ CHAR_TYPE_LOWER, // 116
+ CHAR_TYPE_LOWER, // 117
+ CHAR_TYPE_LOWER, // 118
+ CHAR_TYPE_LOWER, // 119
+ CHAR_TYPE_LOWER, // 120
+ CHAR_TYPE_LOWER, // 121
+ CHAR_TYPE_LOWER, // -> 122 the rest of UPPERCASE alphabet
+
+ CHAR_TYPE_PUNCTUATION, // 123
+ CHAR_TYPE_PUNCTUATION, // 124
+ CHAR_TYPE_PUNCTUATION, // 125
+ CHAR_TYPE_PUNCTUATION, // -> 126 punctuation
+
+ CHAR_TYPE_CONTROL // 127 backspace
+};
+
+static uacpi_bool is_char(uacpi_char c, enum char_type type)
+{
+ return (ascii_map[(uacpi_u8)c] & type) == type;
+}
+
+static uacpi_char to_lower(uacpi_char c)
+{
+ if (is_char(c, CHAR_TYPE_UPPER))
+ return c + ('a' - 'A');
+
+ return c;
+}
+
+static uacpi_bool peek_one(
+ const uacpi_char **str, const uacpi_size *size, uacpi_char *out_char
+)
+{
+ if (*size == 0)
+ return UACPI_FALSE;
+
+ *out_char = **str;
+ return UACPI_TRUE;
+}
+
+static uacpi_bool consume_one(
+ const uacpi_char **str, uacpi_size *size, uacpi_char *out_char
+)
+{
+ if (!peek_one(str, size, out_char))
+ return UACPI_FALSE;
+
+ *str += 1;
+ *size -= 1;
+ return UACPI_TRUE;
+}
+
+static uacpi_bool consume_if(
+ const uacpi_char **str, uacpi_size *size, enum char_type type
+)
+{
+ uacpi_char c;
+
+ if (!peek_one(str, size, &c) || !is_char(c, type))
+ return UACPI_FALSE;
+
+ *str += 1;
+ *size -= 1;
+ return UACPI_TRUE;
+}
+
+static uacpi_bool consume_if_equals(
+ const uacpi_char **str, uacpi_size *size, uacpi_char c
+)
+{
+ uacpi_char c1;
+
+ if (!peek_one(str, size, &c1) || to_lower(c1) != c)
+ return UACPI_FALSE;
+
+ *str += 1;
+ *size -= 1;
+ return UACPI_TRUE;
+}
+
+uacpi_status uacpi_string_to_integer(
+ const uacpi_char *str, uacpi_size max_chars, enum uacpi_base base,
+ uacpi_u64 *out_value
+)
+{
+ uacpi_status ret = UACPI_STATUS_INVALID_ARGUMENT;
+ uacpi_bool negative = UACPI_FALSE;
+ uacpi_u64 next, value = 0;
+ uacpi_char c = '\0';
+
+ while (consume_if(&str, &max_chars, CHAR_TYPE_SPACE));
+
+ if (consume_if_equals(&str, &max_chars, '-'))
+ negative = UACPI_TRUE;
+ else
+ consume_if_equals(&str, &max_chars, '+');
+
+ if (base == UACPI_BASE_AUTO) {
+ base = UACPI_BASE_DEC;
+
+ if (consume_if_equals(&str, &max_chars, '0')) {
+ base = UACPI_BASE_OCT;
+ if (consume_if_equals(&str, &max_chars, 'x'))
+ base = UACPI_BASE_HEX;
+ }
+ }
+
+ while (consume_one(&str, &max_chars, &c)) {
+ switch (ascii_map[(uacpi_u8)c] & (CHAR_TYPE_DIGIT | CHAR_TYPE_ALHEX)) {
+ case CHAR_TYPE_DIGIT | CHAR_TYPE_HEX_DIGIT:
+ next = c - '0';
+ if (base == UACPI_BASE_OCT && next > 7)
+ goto out;
+ break;
+ case CHAR_TYPE_LOWER | CHAR_TYPE_HEX_DIGIT:
+ case CHAR_TYPE_UPPER | CHAR_TYPE_HEX_DIGIT:
+ if (base != UACPI_BASE_HEX)
+ goto out;
+ next = 10 + (to_lower(c) - 'a');
+ break;
+ default:
+ goto out;
+ }
+
+ next = (value * base) + next;
+ if ((next / base) != value) {
+ value = 0xFFFFFFFFFFFFFFFF;
+ goto out;
+ }
+
+ value = next;
+ }
+
+out:
+ if (negative)
+ value = -((uacpi_i64)value);
+
+ *out_value = value;
+ if (max_chars == 0 || c == '\0')
+ ret = UACPI_STATUS_OK;
+
+ return ret;
+}
+
+#ifndef UACPI_BAREBONES_MODE
+
+static inline uacpi_bool is_valid_name_byte(uacpi_u8 c)
+{
+ // ‘_’ := 0x5F
+ if (c == 0x5F)
+ return UACPI_TRUE;
+
+ /*
+ * LeadNameChar := ‘A’-‘Z’ | ‘_’
+ * DigitChar := ‘0’ - ‘9’
+ * NameChar := DigitChar | LeadNameChar
+ * ‘A’-‘Z’ := 0x41 - 0x5A
+ * ‘0’-‘9’ := 0x30 - 0x39
+ */
+ return (ascii_map[c] & (CHAR_TYPE_DIGIT | CHAR_TYPE_UPPER)) != 0;
+}
+
+uacpi_bool uacpi_is_valid_nameseg(uacpi_u8 *nameseg)
+{
+ return is_valid_name_byte(nameseg[0]) &&
+ is_valid_name_byte(nameseg[1]) &&
+ is_valid_name_byte(nameseg[2]) &&
+ is_valid_name_byte(nameseg[3]);
+}
+
+void uacpi_eisa_id_to_string(uacpi_u32 id, uacpi_char *out_string)
+{
+ static uacpi_char hex_to_ascii[16] = {
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ 'A', 'B', 'C', 'D', 'E', 'F'
+ };
+
+ /*
+ * For whatever reason bits are encoded upper to lower here, swap
+ * them around so that we don't have to do ridiculous bit shifts
+ * everywhere.
+ */
+ union {
+ uacpi_u8 bytes[4];
+ uacpi_u32 dword;
+ } orig, swapped;
+
+ orig.dword = id;
+ swapped.bytes[0] = orig.bytes[3];
+ swapped.bytes[1] = orig.bytes[2];
+ swapped.bytes[2] = orig.bytes[1];
+ swapped.bytes[3] = orig.bytes[0];
+
+ /*
+ * Bit 16 - 20: 3rd character (- 0x40) of mfg code
+ * Bit 21 - 25: 2nd character (- 0x40) of mfg code
+ * Bit 26 - 30: 1st character (- 0x40) of mfg code
+ */
+ out_string[0] = (uacpi_char)(0x40 + ((swapped.dword >> 26) & 0x1F));
+ out_string[1] = (uacpi_char)(0x40 + ((swapped.dword >> 21) & 0x1F));
+ out_string[2] = (uacpi_char)(0x40 + ((swapped.dword >> 16) & 0x1F));
+
+ /*
+ * Bit 0 - 3 : 4th hex digit of product number
+ * Bit 4 - 7 : 3rd hex digit of product number
+ * Bit 8 - 11: 2nd hex digit of product number
+ * Bit 12 - 15: 1st hex digit of product number
+ */
+ out_string[3] = hex_to_ascii[(swapped.dword >> 12) & 0x0F];
+ out_string[4] = hex_to_ascii[(swapped.dword >> 8 ) & 0x0F];
+ out_string[5] = hex_to_ascii[(swapped.dword >> 4 ) & 0x0F];
+ out_string[6] = hex_to_ascii[(swapped.dword >> 0 ) & 0x0F];
+
+ out_string[7] = '\0';
+}
+
+#define PNP_ID_LENGTH 8
+
+uacpi_status uacpi_eval_hid(uacpi_namespace_node *node, uacpi_id_string **out_id)
+{
+ uacpi_status ret;
+ uacpi_object *hid_ret;
+ uacpi_id_string *id = UACPI_NULL;
+ uacpi_u32 size;
+
+ ret = uacpi_eval_typed(
+ node, "_HID", UACPI_NULL,
+ UACPI_OBJECT_INTEGER_BIT | UACPI_OBJECT_STRING_BIT,
+ &hid_ret
+ );
+ if (ret != UACPI_STATUS_OK)
+ return ret;
+
+ size = sizeof(uacpi_id_string);
+
+ switch (hid_ret->type) {
+ case UACPI_OBJECT_STRING: {
+ uacpi_buffer *buf = hid_ret->buffer;
+
+ size += buf->size;
+ if (uacpi_unlikely(buf->size == 0 || size < buf->size)) {
+ uacpi_object_name name = uacpi_namespace_node_name(node);
+
+ uacpi_error(
+ "%.4s._HID: empty/invalid EISA ID string (%zu bytes)\n",
+ name.text, buf->size
+ );
+ ret = UACPI_STATUS_AML_BAD_ENCODING;
+ break;
+ }
+
+ id = uacpi_kernel_alloc(size);
+ if (uacpi_unlikely(id == UACPI_NULL)) {
+ ret = UACPI_STATUS_OUT_OF_MEMORY;
+ break;
+ }
+ id->size = buf->size;
+ id->value = UACPI_PTR_ADD(id, sizeof(uacpi_id_string));
+
+ uacpi_memcpy(id->value, buf->text, buf->size);
+ id->value[buf->size - 1] = '\0';
+ break;
+ }
+
+ case UACPI_OBJECT_INTEGER:
+ size += PNP_ID_LENGTH;
+
+ id = uacpi_kernel_alloc(size);
+ if (uacpi_unlikely(id == UACPI_NULL)) {
+ ret = UACPI_STATUS_OUT_OF_MEMORY;
+ break;
+ }
+ id->size = PNP_ID_LENGTH;
+ id->value = UACPI_PTR_ADD(id, sizeof(uacpi_id_string));
+
+ uacpi_eisa_id_to_string(hid_ret->integer, id->value);
+ break;
+ }
+
+ uacpi_object_unref(hid_ret);
+ if (uacpi_likely_success(ret))
+ *out_id = id;
+ return ret;
+}
+
+void uacpi_free_id_string(uacpi_id_string *id)
+{
+ if (id == UACPI_NULL)
+ return;
+
+ uacpi_free(id, sizeof(uacpi_id_string) + id->size);
+}
+
+uacpi_status uacpi_eval_cid(
+ uacpi_namespace_node *node, uacpi_pnp_id_list **out_list
+)
+{
+ uacpi_status ret;
+ uacpi_object *object, *cid_ret;
+ uacpi_object **objects;
+ uacpi_size num_ids, i;
+ uacpi_u32 size;
+ uacpi_id_string *id;
+ uacpi_char *id_buffer;
+ uacpi_pnp_id_list *list;
+
+ ret = uacpi_eval_typed(
+ node, "_CID", UACPI_NULL,
+ UACPI_OBJECT_INTEGER_BIT | UACPI_OBJECT_STRING_BIT |
+ UACPI_OBJECT_PACKAGE_BIT,
+ &cid_ret
+ );
+ if (ret != UACPI_STATUS_OK)
+ return ret;
+
+ switch (cid_ret->type) {
+ case UACPI_OBJECT_PACKAGE:
+ objects = cid_ret->package->objects;
+ num_ids = cid_ret->package->count;
+ break;
+ default:
+ objects = &cid_ret;
+ num_ids = 1;
+ break;
+ }
+
+ size = sizeof(uacpi_pnp_id_list);
+ size += num_ids * sizeof(uacpi_id_string);
+
+ for (i = 0; i < num_ids; ++i) {
+ object = objects[i];
+
+ switch (object->type) {
+ case UACPI_OBJECT_STRING: {
+ uacpi_size buf_size = object->buffer->size;
+
+ if (uacpi_unlikely(buf_size == 0)) {
+ uacpi_object_name name = uacpi_namespace_node_name(node);
+
+ uacpi_error(
+ "%.4s._CID: empty EISA ID string (sub-object %zu)\n",
+ name.text, i
+ );
+ return UACPI_STATUS_AML_INCOMPATIBLE_OBJECT_TYPE;
+ }
+
+ size += buf_size;
+ if (uacpi_unlikely(size < buf_size)) {
+ uacpi_object_name name = uacpi_namespace_node_name(node);
+
+ uacpi_error(
+ "%.4s._CID: buffer size overflow (+ %zu)\n",
+ name.text, buf_size
+ );
+ return UACPI_STATUS_AML_BAD_ENCODING;
+ }
+
+ break;
+ }
+
+ case UACPI_OBJECT_INTEGER:
+ size += PNP_ID_LENGTH;
+ break;
+ default: {
+ uacpi_object_name name = uacpi_namespace_node_name(node);
+
+ uacpi_error(
+ "%.4s._CID: invalid package sub-object %zu type: %s\n",
+ name.text, i,
+ uacpi_object_type_to_string(object->type)
+ );
+ return UACPI_STATUS_AML_INCOMPATIBLE_OBJECT_TYPE;
+ }
+ }
+ }
+
+ list = uacpi_kernel_alloc(size);
+ if (uacpi_unlikely(list == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+ list->num_ids = num_ids;
+ list->size = size - sizeof(uacpi_pnp_id_list);
+
+ id_buffer = UACPI_PTR_ADD(list, sizeof(uacpi_pnp_id_list));
+ id_buffer += num_ids * sizeof(uacpi_id_string);
+
+ for (i = 0; i < num_ids; ++i) {
+ object = objects[i];
+ id = &list->ids[i];
+
+ switch (object->type) {
+ case UACPI_OBJECT_STRING: {
+ uacpi_buffer *buf = object->buffer;
+
+ id->size = buf->size;
+ id->value = id_buffer;
+
+ uacpi_memcpy(id->value, buf->text, id->size);
+ id->value[id->size - 1] = '\0';
+ break;
+ }
+
+ case UACPI_OBJECT_INTEGER:
+ id->size = PNP_ID_LENGTH;
+ id->value = id_buffer;
+ uacpi_eisa_id_to_string(object->integer, id_buffer);
+ break;
+ }
+
+ id_buffer += id->size;
+ }
+
+ uacpi_object_unref(cid_ret);
+ *out_list = list;
+ return ret;
+}
+
+void uacpi_free_pnp_id_list(uacpi_pnp_id_list *list)
+{
+ if (list == UACPI_NULL)
+ return;
+
+ uacpi_free(list, sizeof(uacpi_pnp_id_list) + list->size);
+}
+
+uacpi_status uacpi_eval_sta(uacpi_namespace_node *node, uacpi_u32 *flags)
+{
+ uacpi_status ret;
+ uacpi_u64 value = 0;
+
+ ret = uacpi_eval_integer(node, "_STA", UACPI_NULL, &value);
+
+ /*
+ * ACPI 6.5 specification:
+ * If a device object (including the processor object) does not have
+ * an _STA object, then OSPM assumes that all of the above bits are
+ * set (i.e., the device is present, enabled, shown in the UI,
+ * and functioning).
+ */
+ if (ret == UACPI_STATUS_NOT_FOUND) {
+ value = 0xFFFFFFFF;
+ ret = UACPI_STATUS_OK;
+ }
+
+ *flags = value;
+ return ret;
+}
+
+uacpi_status uacpi_eval_adr(uacpi_namespace_node *node, uacpi_u64 *out)
+{
+ return uacpi_eval_integer(node, "_ADR", UACPI_NULL, out);
+}
+
+#define CLS_REPR_SIZE 7
+
+static uacpi_u8 extract_package_byte_or_zero(uacpi_package *pkg, uacpi_size i)
+{
+ uacpi_object *obj;
+
+ if (uacpi_unlikely(pkg->count <= i))
+ return 0;
+
+ obj = pkg->objects[i];
+ if (uacpi_unlikely(obj->type != UACPI_OBJECT_INTEGER))
+ return 0;
+
+ return obj->integer;
+}
+
+uacpi_status uacpi_eval_cls(
+ uacpi_namespace_node *node, uacpi_id_string **out_id
+)
+{
+ uacpi_status ret;
+ uacpi_object *obj;
+ uacpi_package *pkg;
+ uacpi_u8 class_codes[3];
+ uacpi_id_string *id_string;
+
+ ret = uacpi_eval_typed(
+ node, "_CLS", UACPI_NULL, UACPI_OBJECT_PACKAGE_BIT, &obj
+ );
+ if (ret != UACPI_STATUS_OK)
+ return ret;
+
+ pkg = obj->package;
+ class_codes[0] = extract_package_byte_or_zero(pkg, 0);
+ class_codes[1] = extract_package_byte_or_zero(pkg, 1);
+ class_codes[2] = extract_package_byte_or_zero(pkg, 2);
+
+ id_string = uacpi_kernel_alloc(sizeof(uacpi_id_string) + CLS_REPR_SIZE);
+ if (uacpi_unlikely(id_string == UACPI_NULL)) {
+ ret = UACPI_STATUS_OUT_OF_MEMORY;
+ goto out;
+ }
+
+ id_string->size = CLS_REPR_SIZE;
+ id_string->value = UACPI_PTR_ADD(id_string, sizeof(uacpi_id_string));
+
+ uacpi_snprintf(
+ id_string->value, CLS_REPR_SIZE, "%02X%02X%02X",
+ class_codes[0], class_codes[1], class_codes[2]
+ );
+
+out:
+ if (uacpi_likely_success(ret))
+ *out_id = id_string;
+
+ uacpi_object_unref(obj);
+ return ret;
+}
+
+uacpi_status uacpi_eval_uid(
+ uacpi_namespace_node *node, uacpi_id_string **out_uid
+)
+{
+ uacpi_status ret;
+ uacpi_object *obj;
+ uacpi_id_string *id_string;
+ uacpi_u32 size;
+
+ ret = uacpi_eval_typed(
+ node, "_UID", UACPI_NULL,
+ UACPI_OBJECT_INTEGER_BIT | UACPI_OBJECT_STRING_BIT,
+ &obj
+ );
+ if (ret != UACPI_STATUS_OK)
+ return ret;
+
+ if (obj->type == UACPI_OBJECT_STRING) {
+ size = obj->buffer->size;
+ if (uacpi_unlikely(size == 0 || size > 0xE0000000)) {
+ uacpi_object_name name = uacpi_namespace_node_name(node);
+
+ uacpi_error(
+ "invalid %.4s._UID string size: %u\n",
+ name.text, size
+ );
+ ret = UACPI_STATUS_AML_BAD_ENCODING;
+ goto out;
+ }
+ } else {
+ size = uacpi_snprintf(
+ UACPI_NULL, 0, "%"UACPI_PRIu64, UACPI_FMT64(obj->integer)
+ ) + 1;
+ }
+
+ id_string = uacpi_kernel_alloc(sizeof(uacpi_id_string) + size);
+ if (uacpi_unlikely(id_string == UACPI_NULL)) {
+ ret = UACPI_STATUS_OUT_OF_MEMORY;
+ goto out;
+ }
+
+ id_string->value = UACPI_PTR_ADD(id_string, sizeof(uacpi_id_string));
+ id_string->size = size;
+
+ if (obj->type == UACPI_OBJECT_STRING) {
+ uacpi_memcpy(id_string->value, obj->buffer->text, size);
+ id_string->value[size - 1] = '\0';
+ } else {
+ uacpi_snprintf(
+ id_string->value, id_string->size, "%"UACPI_PRIu64,
+ UACPI_FMT64(obj->integer)
+ );
+ }
+
+out:
+ if (uacpi_likely_success(ret))
+ *out_uid = id_string;
+
+ uacpi_object_unref(obj);
+ return ret;
+}
+
+static uacpi_bool matches_any(
+ uacpi_id_string *id, const uacpi_char *const *ids
+)
+{
+ uacpi_size i;
+
+ for (i = 0; ids[i]; ++i) {
+ if (uacpi_strcmp(id->value, ids[i]) == 0)
+ return UACPI_TRUE;
+ }
+
+ return UACPI_FALSE;
+}
+
+static uacpi_status uacpi_eval_dstate_method_template(
+ uacpi_namespace_node *parent, uacpi_char *template, uacpi_u8 num_methods,
+ uacpi_u8 *out_values
+)
+{
+ uacpi_u8 i;
+ uacpi_status ret = UACPI_STATUS_NOT_FOUND, eval_ret;
+ uacpi_object *obj;
+
+ // We expect either _SxD or _SxW, so increment template[2]
+ for (i = 0; i < num_methods; ++i, template[2]++) {
+ eval_ret = uacpi_eval_typed(
+ parent, template, UACPI_NULL, UACPI_OBJECT_INTEGER_BIT, &obj
+ );
+ if (eval_ret == UACPI_STATUS_OK) {
+ ret = UACPI_STATUS_OK;
+ out_values[i] = obj->integer;
+ uacpi_object_unref(obj);
+ continue;
+ }
+
+ out_values[i] = 0xFF;
+ if (uacpi_unlikely(eval_ret != UACPI_STATUS_NOT_FOUND)) {
+ const char *path;
+
+ path = uacpi_namespace_node_generate_absolute_path(parent);
+ uacpi_warn(
+ "failed to evaluate %s.%s: %s\n",
+ path, template, uacpi_status_to_string(eval_ret)
+ );
+ uacpi_free_dynamic_string(path);
+ }
+ }
+
+ return ret;
+}
+
+#define NODE_INFO_EVAL_ADD_ID(name) \
+ if (uacpi_eval_##name(node, &name) == UACPI_STATUS_OK) { \
+ size += name->size; \
+ if (uacpi_unlikely(size < name->size)) { \
+ ret = UACPI_STATUS_AML_BAD_ENCODING; \
+ goto out; \
+ } \
+ }
+
+#define NODE_INFO_COPY_ID(name, flag) \
+ if (name != UACPI_NULL) { \
+ flags |= UACPI_NS_NODE_INFO_HAS_##flag; \
+ info->name.value = cursor; \
+ info->name.size = name->size; \
+ uacpi_memcpy(cursor, name->value, name->size); \
+ cursor += name->size; \
+ } else { \
+ uacpi_memzero(&info->name, sizeof(*name)); \
+ } \
+
+uacpi_status uacpi_get_namespace_node_info(
+ uacpi_namespace_node *node, uacpi_namespace_node_info **out_info
+)
+{
+ uacpi_status ret = UACPI_STATUS_OK;
+ uacpi_u32 size = sizeof(uacpi_namespace_node_info);
+ uacpi_object *obj;
+ uacpi_namespace_node_info *info;
+ uacpi_id_string *hid = UACPI_NULL, *uid = UACPI_NULL, *cls = UACPI_NULL;
+ uacpi_pnp_id_list *cid = UACPI_NULL;
+ uacpi_char *cursor;
+ uacpi_u64 adr = 0;
+ uacpi_u8 flags = 0;
+ uacpi_u8 sxd[4], sxw[5];
+
+ obj = uacpi_namespace_node_get_object(node);
+ if (uacpi_unlikely(obj == UACPI_NULL))
+ return UACPI_STATUS_INVALID_ARGUMENT;
+
+ if (obj->type == UACPI_OBJECT_DEVICE ||
+ obj->type == UACPI_OBJECT_PROCESSOR) {
+ char dstate_method_template[5] = { '_', 'S', '1', 'D', '\0' };
+
+ NODE_INFO_EVAL_ADD_ID(hid)
+ NODE_INFO_EVAL_ADD_ID(uid)
+ NODE_INFO_EVAL_ADD_ID(cls)
+ NODE_INFO_EVAL_ADD_ID(cid)
+
+ if (uacpi_eval_adr(node, &adr) == UACPI_STATUS_OK)
+ flags |= UACPI_NS_NODE_INFO_HAS_ADR;
+
+ if (uacpi_eval_dstate_method_template(
+ node, dstate_method_template, sizeof(sxd), sxd
+ ) == UACPI_STATUS_OK)
+ flags |= UACPI_NS_NODE_INFO_HAS_SXD;
+
+ dstate_method_template[2] = '0';
+ dstate_method_template[3] = 'W';
+
+ if (uacpi_eval_dstate_method_template(
+ node, dstate_method_template, sizeof(sxw), sxw
+ ) == UACPI_STATUS_OK)
+ flags |= UACPI_NS_NODE_INFO_HAS_SXW;
+ }
+
+ info = uacpi_kernel_alloc(size);
+ if (uacpi_unlikely(info == UACPI_NULL)) {
+ ret = UACPI_STATUS_OUT_OF_MEMORY;
+ goto out;
+ }
+ info->size = size;
+ cursor = UACPI_PTR_ADD(info, sizeof(uacpi_namespace_node_info));
+ info->name = uacpi_namespace_node_name(node);
+ info->type = obj->type;
+ info->num_params = info->type == UACPI_OBJECT_METHOD ? obj->method->args : 0;
+
+ info->adr = adr;
+ if (flags & UACPI_NS_NODE_INFO_HAS_SXD)
+ uacpi_memcpy(info->sxd, sxd, sizeof(sxd));
+ else
+ uacpi_memzero(info->sxd, sizeof(info->sxd));
+
+ if (flags & UACPI_NS_NODE_INFO_HAS_SXW)
+ uacpi_memcpy(info->sxw, sxw, sizeof(sxw));
+ else
+ uacpi_memzero(info->sxw, sizeof(info->sxw));
+
+ if (cid != UACPI_NULL) {
+ uacpi_u32 i;
+
+ uacpi_memcpy(&info->cid, cid, cid->size + sizeof(*cid));
+ cursor += cid->num_ids * sizeof(uacpi_id_string);
+
+ for (i = 0; i < cid->num_ids; ++i) {
+ info->cid.ids[i].value = cursor;
+ cursor += info->cid.ids[i].size;
+ }
+
+ flags |= UACPI_NS_NODE_INFO_HAS_CID;
+ } else {
+ uacpi_memzero(&info->cid, sizeof(*cid));
+ }
+
+ NODE_INFO_COPY_ID(hid, HID)
+ NODE_INFO_COPY_ID(uid, UID)
+ NODE_INFO_COPY_ID(cls, CLS)
+
+out:
+ if (uacpi_likely_success(ret)) {
+ info->flags = flags;
+ *out_info = info;
+ }
+
+ uacpi_free_id_string(hid);
+ uacpi_free_id_string(uid);
+ uacpi_free_id_string(cls);
+ uacpi_free_pnp_id_list(cid);
+ return ret;
+}
+
+void uacpi_free_namespace_node_info(uacpi_namespace_node_info *info)
+{
+ if (info == UACPI_NULL)
+ return;
+
+ uacpi_free(info, info->size);
+}
+
+uacpi_bool uacpi_device_matches_pnp_id(
+ uacpi_namespace_node *node, const uacpi_char *const *ids
+)
+{
+ uacpi_status st;
+ uacpi_bool ret = UACPI_FALSE;
+ uacpi_id_string *id = UACPI_NULL;
+ uacpi_pnp_id_list *id_list = UACPI_NULL;
+
+ st = uacpi_eval_hid(node, &id);
+ if (st == UACPI_STATUS_OK && matches_any(id, ids)) {
+ ret = UACPI_TRUE;
+ goto out;
+ }
+
+ st = uacpi_eval_cid(node, &id_list);
+ if (st == UACPI_STATUS_OK) {
+ uacpi_size i;
+
+ for (i = 0; i < id_list->num_ids; ++i) {
+ if (matches_any(&id_list->ids[i], ids)) {
+ ret = UACPI_TRUE;
+ goto out;
+ }
+ }
+ }
+
+out:
+ uacpi_free_id_string(id);
+ uacpi_free_pnp_id_list(id_list);
+ return ret;
+}
+
+struct device_find_ctx {
+ const uacpi_char *const *target_hids;
+ void *user;
+ uacpi_iteration_callback cb;
+};
+
+static uacpi_iteration_decision find_one_device(
+ void *opaque, uacpi_namespace_node *node, uacpi_u32 depth
+)
+{
+ struct device_find_ctx *ctx = opaque;
+ uacpi_status ret;
+ uacpi_u32 flags;
+
+ if (!uacpi_device_matches_pnp_id(node, ctx->target_hids))
+ return UACPI_ITERATION_DECISION_CONTINUE;
+
+ ret = uacpi_eval_sta(node, &flags);
+ if (uacpi_unlikely_error(ret))
+ return UACPI_ITERATION_DECISION_NEXT_PEER;
+
+ if (!(flags & ACPI_STA_RESULT_DEVICE_PRESENT) &&
+ !(flags & ACPI_STA_RESULT_DEVICE_FUNCTIONING))
+ return UACPI_ITERATION_DECISION_NEXT_PEER;
+
+ return ctx->cb(ctx->user, node, depth);
+}
+
+
+uacpi_status uacpi_find_devices_at(
+ uacpi_namespace_node *parent, const uacpi_char *const *hids,
+ uacpi_iteration_callback cb, void *user
+)
+{
+ struct device_find_ctx ctx = { 0 };
+
+ UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_NAMESPACE_LOADED);
+
+ ctx.target_hids = hids;
+ ctx.user = user;
+ ctx.cb = cb;
+
+ return uacpi_namespace_for_each_child(
+ parent, find_one_device, UACPI_NULL, UACPI_OBJECT_DEVICE_BIT,
+ UACPI_MAX_DEPTH_ANY, &ctx
+ );
+}
+
+uacpi_status uacpi_find_devices(
+ const uacpi_char *hid, uacpi_iteration_callback cb, void *user
+)
+{
+ const uacpi_char *hids[2] = {
+ UACPI_NULL, UACPI_NULL
+ };
+
+ hids[0] = hid;
+
+ return uacpi_find_devices_at(uacpi_namespace_root(), hids, cb, user);
+}
+
+uacpi_status uacpi_set_interrupt_model(uacpi_interrupt_model model)
+{
+ uacpi_status ret;
+ uacpi_object *arg;
+ uacpi_object_array args;
+
+ UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_NAMESPACE_LOADED);
+
+ arg = uacpi_create_object(UACPI_OBJECT_INTEGER);
+ if (uacpi_unlikely(arg == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ arg->integer = model;
+ args.objects = &arg;
+ args.count = 1;
+
+ ret = uacpi_eval(uacpi_namespace_root(), "_PIC", &args, UACPI_NULL);
+ uacpi_object_unref(arg);
+
+ if (ret == UACPI_STATUS_NOT_FOUND)
+ ret = UACPI_STATUS_OK;
+
+ return ret;
+}
+
+uacpi_status uacpi_get_pci_routing_table(
+ uacpi_namespace_node *parent, uacpi_pci_routing_table **out_table
+)
+{
+ uacpi_status ret;
+ uacpi_object *obj, *entry_obj, *elem_obj;
+ uacpi_package *table_pkg, *entry_pkg;
+ uacpi_pci_routing_table_entry *entry;
+ uacpi_pci_routing_table *table;
+ uacpi_size size, i;
+
+ UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_NAMESPACE_LOADED);
+
+ obj = uacpi_namespace_node_get_object(parent);
+ if (uacpi_unlikely(obj == UACPI_NULL || obj->type != UACPI_OBJECT_DEVICE))
+ return UACPI_STATUS_INVALID_ARGUMENT;
+
+ ret = uacpi_eval_typed(
+ parent, "_PRT", UACPI_NULL, UACPI_OBJECT_PACKAGE_BIT, &obj
+ );
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ table_pkg = obj->package;
+ if (uacpi_unlikely(table_pkg->count == 0 || table_pkg->count > 1024)) {
+ uacpi_error("invalid number of _PRT entries: %zu\n", table_pkg->count);
+ uacpi_object_unref(obj);
+ return UACPI_STATUS_AML_BAD_ENCODING;
+ }
+
+ size = table_pkg->count * sizeof(uacpi_pci_routing_table_entry);
+ table = uacpi_kernel_alloc(sizeof(uacpi_pci_routing_table) + size);
+ if (uacpi_unlikely(table == UACPI_NULL)) {
+ uacpi_object_unref(obj);
+ return UACPI_STATUS_OUT_OF_MEMORY;
+ }
+ table->num_entries = table_pkg->count;
+
+ for (i = 0; i < table_pkg->count; ++i) {
+ entry_obj = table_pkg->objects[i];
+
+ if (uacpi_unlikely(entry_obj->type != UACPI_OBJECT_PACKAGE)) {
+ uacpi_error("_PRT sub-object %zu is not a package: %s\n",
+ i, uacpi_object_type_to_string(entry_obj->type));
+ goto out_bad_encoding;
+ }
+
+ entry_pkg = entry_obj->package;
+ if (uacpi_unlikely(entry_pkg->count != 4)) {
+ uacpi_error("invalid _PRT sub-package entry count %zu\n",
+ entry_pkg->count);
+ goto out_bad_encoding;
+ }
+
+ entry = &table->entries[i];
+
+ elem_obj = entry_pkg->objects[0];
+ if (uacpi_unlikely(elem_obj->type != UACPI_OBJECT_INTEGER)) {
+ uacpi_error("invalid _PRT sub-package %zu address type: %s\n",
+ i, uacpi_object_type_to_string(elem_obj->type));
+ goto out_bad_encoding;
+ }
+ entry->address = elem_obj->integer;
+
+ elem_obj = entry_pkg->objects[1];
+ if (uacpi_unlikely(elem_obj->type != UACPI_OBJECT_INTEGER)) {
+ uacpi_error("invalid _PRT sub-package %zu pin type: %s\n",
+ i, uacpi_object_type_to_string(elem_obj->type));
+ goto out_bad_encoding;
+ }
+ entry->pin = elem_obj->integer;
+
+ elem_obj = entry_pkg->objects[2];
+ switch (elem_obj->type) {
+ case UACPI_OBJECT_STRING:
+ ret = uacpi_object_resolve_as_aml_namepath(
+ elem_obj, parent, &entry->source
+ );
+ if (uacpi_unlikely_error(ret)) {
+ uacpi_error("unable to lookup _PRT source %s: %s\n",
+ elem_obj->buffer->text, uacpi_status_to_string(ret));
+ goto out_bad_encoding;
+ }
+ break;
+ case UACPI_OBJECT_INTEGER:
+ entry->source = UACPI_NULL;
+ break;
+ default:
+ uacpi_error("invalid _PRT sub-package %zu source type: %s\n",
+ i, uacpi_object_type_to_string(elem_obj->type));
+ goto out_bad_encoding;
+ }
+
+ elem_obj = entry_pkg->objects[3];
+ if (uacpi_unlikely(elem_obj->type != UACPI_OBJECT_INTEGER)) {
+ uacpi_error("invalid _PRT sub-package %zu source index type: %s\n",
+ i, uacpi_object_type_to_string(elem_obj->type));
+ goto out_bad_encoding;
+ }
+ entry->index = elem_obj->integer;
+ }
+
+ uacpi_object_unref(obj);
+ *out_table = table;
+ return UACPI_STATUS_OK;
+
+out_bad_encoding:
+ uacpi_object_unref(obj);
+ uacpi_free_pci_routing_table(table);
+ return UACPI_STATUS_AML_BAD_ENCODING;
+}
+
+void uacpi_free_pci_routing_table(uacpi_pci_routing_table *table)
+{
+ if (table == UACPI_NULL)
+ return;
+
+ uacpi_free(
+ table,
+ sizeof(uacpi_pci_routing_table) +
+ table->num_entries * sizeof(uacpi_pci_routing_table_entry)
+ );
+}
+
+void uacpi_free_dynamic_string(const uacpi_char *str)
+{
+ if (str == UACPI_NULL)
+ return;
+
+ uacpi_free((void*)str, uacpi_strlen(str) + 1);
+}
+
+#endif // !UACPI_BAREBONES_MODE
diff --git a/sys/dev/cons/cons.c b/sys/dev/cons/cons.c
index b89727f..d9d727a 100644
--- a/sys/dev/cons/cons.c
+++ b/sys/dev/cons/cons.c
@@ -37,6 +37,7 @@
#include <dev/cons/font.h>
#include <dev/cons/cons.h>
#include <fs/devfs.h>
+#include <fs/ctlfs.h>
#include <vm/dynalloc.h>
#include <string.h>
@@ -44,7 +45,7 @@
cons_draw_cursor((SCR), (SCR)->bg)
#define SHOW_CURSOR(SCR) \
- cons_draw_cursor((SCR), (SCR)->fg)
+ cons_draw_cursor((SCR), rgb_invert((SCR)->bg))
/* Console background from kconf */
#if defined(__CONSOLE_BG)
@@ -62,10 +63,28 @@
struct cons_screen g_root_scr = {0};
static struct cdevsw cons_cdevsw;
+static struct ctlops cons_feat_ctl;
+static struct ctlops cons_attr_ctl;
static void cons_draw_cursor(struct cons_screen *scr, uint32_t color);
static int cons_handle_special(struct cons_screen *scr, char c);
-static void cons_clear_scr(struct cons_screen *scr, uint32_t bg);
+
+static uint32_t
+rgb_invert(uint32_t rgb)
+{
+ uint8_t r, g, b;
+ uint32_t ret;
+
+ r = (rgb >> 16) & 0xFF;
+ g = (rgb >> 8) & 0xFF;
+ b = rgb & 0xFF;
+
+ ret = (255 - r) << 16;
+ ret |= (255 - g) << 8;
+ ret |= 255 - b;
+ return ret;
+}
+
/*
* Render a character onto the screen.
@@ -91,9 +110,9 @@ cons_draw_char(struct cons_screen *scr, struct cons_char ch)
y = ch.y;
for (uint32_t cy = 0; cy < FONT_HEIGHT; ++cy) {
+ idx = fbdev_get_index(&scr->fbdev, x + (FONT_WIDTH - 1), y + cy);
for (uint32_t cx = 0; cx < FONT_WIDTH; ++cx) {
- idx = fbdev_get_index(&scr->fbdev, x + (FONT_WIDTH - 1) - cx, y + cy);
- scr->fb_mem[idx] = ISSET(glyph[cy], BIT(cx)) ? ch.fg : ch.bg;
+ scr->fb_mem[idx--] = ISSET(glyph[cy], BIT(cx)) ? ch.fg : ch.bg;
}
}
}
@@ -158,6 +177,17 @@ cons_handle_special(struct cons_screen *scr, char c)
}
switch (c) {
+ case ASCII_HT:
+ HIDE_CURSOR(scr);
+ scr->curs_col += 4;
+ scr->ch_col += 4;
+ if (scr->ch_col >= scr->ncols - 1) {
+ cons_handle_special(scr, '\n');
+ }
+ SHOW_CURSOR(scr);
+ return 0;
+ case ASCII_NUL:
+ return 0;
case ASCII_BS:
bp = scr->ob[scr->ch_row];
if (bp->head > bp->tail) {
@@ -165,27 +195,21 @@ cons_handle_special(struct cons_screen *scr, char c)
}
HIDE_CURSOR(scr);
- --scr->ch_col;
- --scr->curs_col;
+ if (scr->ch_col > 0 && scr->curs_col > 0) {
+ --scr->ch_col;
+ --scr->curs_col;
+ }
SHOW_CURSOR(scr);
return 0;
case ASCII_LF:
- HIDE_CURSOR(scr);
-
/* Are we past screen width? */
if (scr->ch_row >= scr->nrows - 1) {
cons_clear_scr(scr, scr->bg);
- cons_flush(scr);
- scr->ch_col = 0;
- scr->ch_row = 0;
-
- /* Update cursor */
- scr->curs_row = 0;
- scr->curs_col = 0;
- SHOW_CURSOR(scr);
return 0;
}
+ HIDE_CURSOR(scr);
+
/* Make a newline */
cons_flush(scr);
++scr->ch_row;
@@ -212,8 +236,14 @@ cons_handle_special(struct cons_screen *scr, char c)
static void
cons_draw_cursor(struct cons_screen *scr, uint32_t color)
{
+ struct console_feat *featp;
size_t idx;
+ featp = &scr->feat;
+ if (!featp->show_curs) {
+ color = scr->bg;
+ }
+
/* Past screen width? */
if (scr->curs_col >= scr->ncols) {
scr->curs_col = 0;
@@ -221,9 +251,9 @@ cons_draw_cursor(struct cons_screen *scr, uint32_t color)
}
for (uint32_t cy = 0; cy < FONT_HEIGHT; ++cy) {
+ idx = fbdev_get_index(&scr->fbdev, scr->curs_col * FONT_WIDTH, (scr->curs_row * FONT_HEIGHT) + cy);
for (uint32_t cx = 0; cx < FONT_WIDTH; ++cx) {
- idx = fbdev_get_index(&scr->fbdev, (scr->curs_col * FONT_WIDTH) + cx, (scr->curs_row * FONT_HEIGHT) + cy);
- scr->fb_mem[idx] = color;
+ scr->fb_mem[idx++] = color;
}
}
}
@@ -234,34 +264,88 @@ cons_draw_cursor(struct cons_screen *scr, uint32_t color)
* @scr: Screen to clear.
* @bg: Color to clear it to.
*/
-static void
+void
cons_clear_scr(struct cons_screen *scr, uint32_t bg)
{
struct fbdev fbdev = scr->fbdev;
- struct cons_buf *bp;
+
+ cons_flush(scr);
+ HIDE_CURSOR(scr);
+
+ scr->ch_col = 0;
+ scr->ch_row = 0;
+ scr->curs_col = 0;
+ scr->curs_row = 0;
for (size_t i = 0; i < fbdev.height * fbdev.pitch; ++i) {
scr->fb_mem[i] = bg;
}
- bp = scr->ob[scr->nrows - 1];
- bp->flags |= CONS_BUF_CLEAN;
+ SHOW_CURSOR(scr);
+
}
/*
- * Character device function.
+ * Quickly put a character on the screen.
+ * XXX: Does not acquire the screen's lock or show/hide the cursor.
+ *
+ * @scr: Screen.
+ * @c: Character to draw.
*/
-static int
-dev_write(dev_t dev, struct sio_txn *sio, int flags)
+static void
+cons_fast_putch(struct cons_screen *scr, char c)
{
- char *p;
+ struct cons_char cc;
+ struct cons_buf *bp;
+ int ansi;
+
+ ansi = ansi_feed(&scr->ansi_s, c);
+ if (ansi > 0) {
+ c = ASCII_NUL;
+ } else if (ansi < 0) {
+ c = ASCII_NUL;
+ }
+
+ /* Handle specials */
+ if (cons_handle_special(scr, c) == 0) {
+ return;
+ }
+
+ /* Create a new character */
+ cc.c = c;
+ cc.fg = scr->fg;
+ cc.bg = scr->bg;
+ cc.x = scr->ch_col * FONT_WIDTH;
+ cc.y = scr->ch_row * FONT_HEIGHT;
+
+ /* Push our new character */
+ bp = scr->ob[scr->ch_row];
+ bp->flags &= ~CONS_BUF_CLEAN;
+ cons_obuf_push(bp, cc);
+ ++scr->ch_col;
- p = sio->buf;
+ /* Check screen bounds */
+ if (cc.x >= (scr->ncols * FONT_WIDTH) - 1) {
+ scr->ch_col = 0;
+ ++scr->ch_row;
+ }
- for (size_t i = 0; i < sio->len; ++i) {
- cons_putch(&g_root_scr, p[i]);
+ ++scr->curs_col;
+ if (scr->curs_col > scr->ncols - 1) {
+ scr->curs_col = 0;
+ if (scr->curs_row < scr->nrows)
+ ++scr->curs_row;
}
+}
+/*
+ * Character device function.
+ */
+static int
+dev_write(dev_t dev, struct sio_txn *sio, int flags)
+{
+ cons_attach();
+ cons_putstr(&g_root_scr, sio->buf, sio->len);
cons_flush(&g_root_scr);
return sio->len;
}
@@ -272,6 +356,7 @@ dev_write(dev_t dev, struct sio_txn *sio, int flags)
static int
dev_read(dev_t dev, struct sio_txn *sio, int flags)
{
+ struct cons_screen *scr = &g_root_scr;
struct cons_input input;
uint8_t *p;
int retval;
@@ -285,12 +370,12 @@ dev_read(dev_t dev, struct sio_txn *sio, int flags)
return -EFAULT;
}
- retval = cons_ibuf_pop(&g_root_scr, &input);
+ retval = cons_ibuf_pop(scr, &input);
if (retval < 0) {
return -EAGAIN;
}
- spinlock_acquire(&g_root_scr.lock);
+ cons_attach();
for (;;) {
/* Buffer too small */
if (n == 0) {
@@ -302,12 +387,11 @@ dev_read(dev_t dev, struct sio_txn *sio, int flags)
n -= 2;
/* Try to get the next byte */
- retval = cons_ibuf_pop(&g_root_scr, &input);
+ retval = cons_ibuf_pop(scr, &input);
if (retval < 0) {
break;
}
}
- spinlock_release(&g_root_scr.lock);
return sio->len;
}
@@ -338,6 +422,195 @@ cons_init_bufs(struct cons_screen *scr)
return 0;
}
+static int
+ctl_feat_read(struct ctlfs_dev *cdp, struct sio_txn *sio)
+{
+ struct cons_screen *scr = &g_root_scr;
+ struct console_feat *featp;
+
+ if (sio->buf == NULL || sio->len == 0) {
+ return -EINVAL;
+ }
+
+ featp = &scr->feat;
+ if (sio->len > sizeof(*featp)) {
+ sio->len = sizeof(*featp);
+ }
+
+ memcpy(sio->buf, featp, sio->len);
+ return sio->len;
+}
+
+static int
+ctl_feat_write(struct ctlfs_dev *cdp, struct sio_txn *sio)
+{
+ struct cons_screen *scr = &g_root_scr;
+ struct console_feat *featp, oldfeat;
+
+ featp = &scr->feat;
+ oldfeat = *featp;
+ if (sio->len > sizeof(*featp)) {
+ sio->len = sizeof(*featp);
+ }
+
+ memcpy(featp, sio->buf, sio->len);
+
+ /*
+ * If we are suddenly trying to reset the cursor
+ * status, redraw it.
+ */
+ if (featp->show_curs != oldfeat.show_curs) {
+ if (featp->show_curs == 0) {
+ HIDE_CURSOR(scr);
+ } else {
+ SHOW_CURSOR(scr);
+ }
+ }
+ return sio->len;
+}
+
+static int
+ctl_attr_read(struct ctlfs_dev *cdp, struct sio_txn *sio)
+{
+ struct cons_screen *scr = &g_root_scr;
+ struct console_attr *attrp;
+
+ if (sio->buf == NULL || sio->len == 0) {
+ return -EINVAL;
+ }
+
+ attrp = &scr->attr;
+ if (sio->len > sizeof(*attrp)) {
+ sio->len = sizeof(*attrp);
+ }
+
+ memcpy(sio->buf, attrp, sio->len);
+ return sio->len;
+}
+
+static int
+ctl_attr_write(struct ctlfs_dev *cdp, struct sio_txn *sio)
+{
+ struct cons_screen *scr = &g_root_scr;
+ struct console_attr *attrp;
+
+ attrp = &scr->attr;
+ if (sio->len > sizeof(*attrp)) {
+ sio->len = sizeof(*attrp);
+ }
+
+ spinlock_acquire(&scr->lock);
+ HIDE_CURSOR(scr);
+ memcpy(attrp, sio->buf, sio->len);
+
+ /* Clip the x/y positions */
+ if (attrp->cursor_x >= scr->ncols)
+ attrp->cursor_x = scr->ncols - FONT_WIDTH;
+ if (attrp->cursor_y >= scr->nrows)
+ attrp->cursor_y = scr->nrows - FONT_HEIGHT;
+
+ /* Update cursor */
+ scr->curs_col = attrp->cursor_x;
+ scr->curs_row = attrp->cursor_y;
+ scr->ch_col = attrp->cursor_x;
+ scr->ch_row = attrp->cursor_y;
+ SHOW_CURSOR(scr);
+
+ spinlock_release(&scr->lock);
+ return sio->len;
+}
+
+/*
+ * Detach the currently running process from the
+ * console.
+ */
+int
+cons_detach(void)
+{
+ struct cons_screen *scr;
+
+ scr = &g_root_scr;
+ if (scr->atproc == NULL) {
+ return 0;
+ }
+ if (scr->atproc_lock == NULL) {
+ return 0;
+ }
+
+ scr = &g_root_scr;
+ scr->atproc = NULL;
+ mutex_release(scr->atproc_lock);
+ return 0;
+}
+
+/*
+ * Attach the current process to the
+ * console.
+ */
+int
+cons_attach(void)
+{
+ struct cons_screen *scr;
+ struct proc *td, *atproc;
+
+ td = this_td();
+ if (td == NULL) {
+ return -1;
+ }
+
+ scr = &g_root_scr;
+ if (scr->atproc_lock == NULL) {
+ return 0;
+ }
+
+ scr = &g_root_scr;
+ atproc = scr->atproc;
+
+ if (atproc != NULL) {
+ if (atproc->pid == td->pid) {
+ return 0;
+ }
+
+ /*
+ * Do not release this here as we want
+ * any other process that tries to attach
+ * to wait.
+ */
+ mutex_acquire(scr->atproc_lock, 0);
+ }
+
+ scr->atproc = td;
+ return 0;
+}
+
+/*
+ * Reset console color.
+ */
+void
+cons_reset_color(struct cons_screen *scr)
+{
+ g_root_scr.fg = CONSOLE_FG;
+ g_root_scr.bg = CONSOLE_BG;
+}
+
+void
+cons_update_color(struct cons_screen *scr, uint32_t fg, uint32_t bg)
+{
+ scr->fg = fg;
+ scr->bg = bg;
+}
+
+void
+cons_reset_cursor(struct cons_screen *scr)
+{
+ HIDE_CURSOR(scr);
+ scr->ch_col = 0;
+ scr->ch_row = 0;
+ scr->curs_col = 0;
+ scr->curs_row = 0;
+ SHOW_CURSOR(scr);
+}
+
/*
* Put a character on the screen.
*
@@ -347,47 +620,37 @@ cons_init_bufs(struct cons_screen *scr)
int
cons_putch(struct cons_screen *scr, char c)
{
- struct cons_buf *bp;
- struct cons_char cc;
- size_t max_width;
-
spinlock_acquire(&scr->lock);
+ HIDE_CURSOR(scr);
- /* Handle specials */
- if (cons_handle_special(scr, c) == 0) {
- goto done;
- }
+ cons_fast_putch(scr, c);
- HIDE_CURSOR(scr);
+ SHOW_CURSOR(scr);
+ spinlock_release(&scr->lock);
+ return 0;
+}
- /* Create a new character */
- cc.c = c;
- cc.fg = scr->fg;
- cc.bg = scr->bg;
- cc.x = scr->ch_col * FONT_WIDTH;
- cc.y = scr->ch_row * FONT_HEIGHT;
+/*
+ * Put a string on the screen.
+ *
+ * @scr: Screen.
+ * @s: String to draw.
+ * @l: Length of s.
+ */
+int
+cons_putstr(struct cons_screen *scr, const char *s, size_t len)
+{
+ const char *p = s;
- /* Push our new character */
- bp = scr->ob[scr->ch_row];
- bp->flags &= ~CONS_BUF_CLEAN;
- cons_obuf_push(bp, cc);
- ++scr->ch_col;
+ spinlock_acquire(&scr->lock);
+ HIDE_CURSOR(scr);
- /* Check screen bounds */
- max_width = scr->ncols * FONT_WIDTH;
- if (cc.x >= max_width - 1) {
- scr->ch_col = 0;
- ++scr->ch_row;
+ while (len--) {
+ cons_fast_putch(scr, *p);
+ ++p;
}
- ++scr->curs_col;
- if (scr->curs_col > scr->ncols - 1) {
- scr->curs_col = 0;
- if (scr->curs_row < scr->nrows)
- ++scr->curs_row;
- }
SHOW_CURSOR(scr);
-done:
spinlock_release(&scr->lock);
return 0;
}
@@ -396,7 +659,11 @@ void
cons_init(void)
{
struct fbdev fbdev = fbdev_get();
+ struct console_feat *featp;
+ featp = &g_root_scr.feat;
+ featp->ansi_esc = 1;
+ featp->show_curs = 1;
g_root_scr.ch_col = 0;
g_root_scr.ch_row = 0;
g_root_scr.fg = CONSOLE_FG;
@@ -405,6 +672,8 @@ cons_init(void)
g_root_scr.nrows = fbdev.height / FONT_HEIGHT;
g_root_scr.ncols = fbdev.width / FONT_WIDTH;
g_root_scr.fbdev = fbdev;
+ g_root_scr.atproc = NULL;
+ g_root_scr.atproc_lock = NULL;
memset(&g_root_scr.lock, 0, sizeof(g_root_scr.lock));
cons_init_bufs(&g_root_scr);
SHOW_CURSOR(&g_root_scr);
@@ -417,6 +686,7 @@ void
cons_expose(void)
{
static int once = 0;
+ struct ctlfs_dev ctl;
char devname[] = "console";
devmajor_t major;
dev_t dev;
@@ -426,11 +696,28 @@ cons_expose(void)
return;
}
+ /* Init the attached proc mutex lock */
+ g_root_scr.atproc_lock = mutex_new("console0");
+
/* Register the device here */
major = dev_alloc_major();
dev = dev_alloc(major);
dev_register(major, dev, &cons_cdevsw);
devfs_create_entry(devname, major, dev, 0444);
+
+ /* Register feat ctl */
+ ctl.mode = 0666;
+ ctlfs_create_node(devname, &ctl);
+ ctl.devname = devname;
+ ctl.ops = &cons_feat_ctl;
+ ctlfs_create_entry("feat", &ctl);
+
+ /* Register attr ctl */
+ ctl.mode = 0666;
+ ctlfs_create_node(devname, &ctl);
+ ctl.devname = devname;
+ ctl.ops = &cons_attr_ctl;
+ ctlfs_create_entry("attr", &ctl);
once ^= 1;
}
@@ -438,3 +725,13 @@ static struct cdevsw cons_cdevsw = {
.read = dev_read,
.write = dev_write
};
+
+static struct ctlops cons_feat_ctl = {
+ .read = ctl_feat_read,
+ .write = ctl_feat_write
+};
+
+static struct ctlops cons_attr_ctl = {
+ .read = ctl_attr_read,
+ .write = ctl_attr_write
+};
diff --git a/sys/dev/cons/cons_ansi.c b/sys/dev/cons/cons_ansi.c
new file mode 100644
index 0000000..bd78d9a
--- /dev/null
+++ b/sys/dev/cons/cons_ansi.c
@@ -0,0 +1,189 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/cdefs.h>
+#include <sys/console.h>
+#include <dev/cons/cons.h>
+#include <dev/cons/ansi.h>
+#include <string.h>
+
+__always_inline static inline bool
+is_valid_color(int c)
+{
+ return c >= '0' && c <= '7';
+}
+
+static inline void
+ansi_reset(struct ansi_state *statep)
+{
+ memset(statep, 0, sizeof(*statep));
+}
+
+/*
+ * Feed a byte into the ANSI escape sequence
+ * state machine.
+ *
+ * @statep: State machine pointer.
+ * @c: Byte to feed.
+ *
+ * On success, `c' is returned. On failure,
+ * 0 is returned. Values less than 0 indicate
+ * success with console attributes updated
+ * (ANSI_UPDATE_*).
+ */
+int
+ansi_feed(struct ansi_state *statep, char c)
+{
+ struct cons_screen *scr = &g_root_scr;
+ struct console_feat *featp;
+
+ /* Standard colors */
+ static uint32_t colortab[] = {
+ ANSI_BLACK, ANSI_RED,
+ ANSI_GREEN, ANSI_YELLOW,
+ ANSI_BLUE, ANSI_MAGENTA,
+ ANSI_CYAN, ANSI_WHITE
+ };
+
+ featp = &scr->feat;
+ if (!featp->ansi_esc) {
+ return 0;
+ }
+
+ /*
+ * Handle the control sequence introducer
+ * bytes.
+ */
+ switch (statep->csi) {
+ case 0: /* '\033' */
+ if (c != '\033') {
+ return 0;
+ }
+ statep->csi = 1;
+ statep->prev = c;
+ return c;
+ case 1: /* '[' */
+ if (c != '[') {
+ ansi_reset(statep);
+ return 0;
+ }
+ statep->csi = 2;
+ statep->prev = c;
+ return c;
+ case 2:
+ if (c == '2') {
+ statep->csi = 3;
+ statep->prev = c;
+ return c;
+ }
+ break;
+ case 3:
+ /* Did we get '\033[2J' ? */
+ if (statep->prev == '2' && c == 'J') {
+ cons_clear_scr(scr, g_root_scr.bg);
+ ansi_reset(statep);
+ return ANSI_UPDATE_CURSOR;
+ }
+ break;
+ }
+
+ if (!statep->set_fg && !statep->set_bg) {
+ /* Reset attributes? */
+ if (statep->reset_color) {
+ ansi_reset(statep);
+ cons_reset_color(scr);
+ return ANSI_UPDATE_COLOR;
+ }
+
+ /* Mark attributes to be reset? */
+ if (c == '0') {
+ statep->reset_color = 1;
+ statep->prev = c;
+ return c;
+ }
+
+ /* Expect foreground */
+ if (c != '3') {
+ ansi_reset(statep);
+ return 0;
+ }
+ statep->set_fg = 1;
+ statep->prev = c;
+ return c;
+ }
+
+ if (statep->set_fg && c != ';') {
+ /* Make sure this is valid */
+ if (!is_valid_color(c)) {
+ ansi_reset(statep);
+ return 0;
+ }
+
+ /* Set the foreground */
+ statep->fg = colortab[c - '0'];
+ statep->set_bg = 1;
+ statep->set_fg = 0;
+ statep->prev = c;
+ return c;
+ }
+
+ if (statep->set_bg) {
+ if (c == ';') {
+ statep->prev = c;
+ return c;
+ }
+
+ /* Expect '4' after ';' */
+ if (statep->prev == ';' && c != '4') {
+ ansi_reset(statep);
+ return 0;
+ }
+
+ if (c == 'm') {
+ cons_update_color(scr, statep->fg, statep->bg);
+ ansi_reset(statep);
+ return ANSI_UPDATE_COLOR;
+ }
+
+ /* Make sure this is valid */
+ if (!is_valid_color(c)) {
+ ansi_reset(statep);
+ return 0;
+ }
+
+ /* Set the background */
+ statep->bg = colortab[c - '0'];
+ statep->prev = c;
+ return c;
+ }
+
+ ansi_reset(statep);
+ return 0;
+}
diff --git a/sys/dev/cons/cons_buf.c b/sys/dev/cons/cons_buf.c
index 3bc45a1..84a38ce 100644
--- a/sys/dev/cons/cons_buf.c
+++ b/sys/dev/cons/cons_buf.c
@@ -81,20 +81,26 @@ int
cons_obuf_push(struct cons_buf *bp, struct cons_char c)
{
uint8_t next;
+ int retval = 0;
if (bp == NULL) {
return -EINVAL;
}
+ spinlock_acquire(&bp->lock);
__assert(bp->type == CONS_BUF_OUTPUT);
next = bp->head + 1;
if (next > bp->len) {
- return -ENOSPC;
+ retval = -ENOSPC;
+ goto done;
}
bp->obuf[bp->head] = c;
bp->head = next;
- return 0;
+
+done:
+ spinlock_release(&bp->lock);
+ return retval;
}
/*
@@ -108,18 +114,21 @@ int
cons_obuf_pop(struct cons_buf *bp, struct cons_char *res)
{
uint8_t next;
+ int retval = 0;
if (bp == NULL || res == NULL) {
return -EINVAL;
}
__assert(bp->type == CONS_BUF_OUTPUT);
+ spinlock_acquire(&bp->lock);
/* Do we have any data left? */
if (bp->head == bp->tail) {
bp->head = 0;
bp->tail = 0;
- return -EAGAIN;
+ retval = -EAGAIN;
+ goto done;
}
next = bp->tail + 1;
@@ -129,7 +138,10 @@ cons_obuf_pop(struct cons_buf *bp, struct cons_char *res)
*res = bp->obuf[bp->tail];
bp->tail = next;
- return 0;
+
+done:
+ spinlock_release(&bp->lock);
+ return retval;
}
int
@@ -137,22 +149,28 @@ cons_ibuf_push(struct cons_screen *scr, struct cons_input in)
{
struct cons_buf *bp;
uint8_t head_next;
+ int retval = 0;
if (scr == NULL) {
return -EINVAL;
}
bp = scr->ib;
+ spinlock_acquire(&bp->lock);
__assert(bp->type == CONS_BUF_INPUT);
head_next = bp->head + 1;
if (head_next > bp->len) {
- return -ENOSPC;
+ retval = -ENOSPC;
+ goto done;
}
bp->ibuf[bp->head] = in;
bp->head = head_next;
- return 0;
+
+done:
+ spinlock_release(&bp->lock);
+ return retval;
}
int
@@ -160,6 +178,7 @@ cons_ibuf_pop(struct cons_screen *scr, struct cons_input *res)
{
uint8_t tail_next;
struct cons_buf *bp;
+ int retval = 0;
if (scr == NULL || res == NULL) {
return -EINVAL;
@@ -167,12 +186,14 @@ cons_ibuf_pop(struct cons_screen *scr, struct cons_input *res)
bp = scr->ib;
__assert(bp->type == CONS_BUF_INPUT);
+ spinlock_acquire(&bp->lock);
/* Do we have any data left? */
if (bp->head == bp->tail) {
bp->head = 0;
bp->tail = 0;
- return -EAGAIN;
+ retval = -EAGAIN;
+ goto done;
}
tail_next = bp->tail + 1;
@@ -182,5 +203,8 @@ cons_ibuf_pop(struct cons_screen *scr, struct cons_input *res)
*res = bp->ibuf[bp->tail];
bp->tail = tail_next;
- return 0;
+
+done:
+ spinlock_release(&bp->lock);
+ return retval;
}
diff --git a/sys/dev/dcdr/cache.c b/sys/dev/dcdr/cache.c
index c44c8ea..33f977e 100644
--- a/sys/dev/dcdr/cache.c
+++ b/sys/dev/dcdr/cache.c
@@ -126,6 +126,20 @@ struct dcd *
dcdr_cachein(struct dcdr *dcdr, void *block, off_t lba)
{
struct dcd *dcd, *tmp;
+ struct dcdr_lookup check;
+ int status;
+
+ /*
+ * If there is already a block within this
+ * DCDR, then we simply need to copy the
+ * new data into the old DCD.
+ */
+ status = dcdr_lookup(dcdr, lba, &check);
+ if (status == 0) {
+ dcd = check.dcd_res;
+ memcpy(dcd->block, block, dcdr->bsize);
+ return dcd;
+ }
dcd = dynalloc(sizeof(*dcd));
if (dcd == NULL) {
diff --git a/sys/dev/dmi/dmi.c b/sys/dev/dmi/dmi.c
new file mode 100644
index 0000000..73a9ab7
--- /dev/null
+++ b/sys/dev/dmi/dmi.c
@@ -0,0 +1,306 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/limine.h>
+#include <sys/errno.h>
+#include <sys/param.h>
+#include <sys/driver.h>
+#include <sys/cdefs.h>
+#include <sys/syslog.h>
+#include <dev/dmi/dmi.h>
+#include <dev/dmi/dmivar.h>
+#include <dev/acpi/tables.h>
+#include <fs/ctlfs.h>
+#include <string.h>
+
+#define DMI_BIOS_INFO 0
+#define DMI_SYSTEM_INFO 1
+#define DMI_PROCESSOR_INFO 4
+#define DMI_END_OF_TABLE 127
+
+/* String offsets */
+#define BIOSINFO_VENDOR 0x01
+#define SYSINFO_PRODUCT 0x02
+#define SYSINFO_VERSION 0x03
+#define SYSINFO_FAMILY 0x06
+#define PROCINFO_MANUFACT 0x02
+#define PROCINFO_VERSION 0x03
+#define PROCINFO_PARTNO 0x06
+
+static struct limine_smbios_request smbios_req = {
+ .id = LIMINE_SMBIOS_REQUEST,
+ .revision = 0
+};
+
+/* DMI/SMBIOS structure header */
+struct __packed dmi_shdr {
+ uint8_t type;
+ uint8_t length;
+ uint16_t handle;
+} *hdrs[DMI_END_OF_TABLE + 1];
+
+/*
+ * Grab a structure header from a type
+ *
+ * @type: A DMI structure type to find
+ *
+ * Returns NULL if not found.
+ */
+static inline struct dmi_shdr *
+dmi_shdr(uint8_t type)
+{
+ struct dmi_shdr *hdr;
+
+ hdr = hdrs[type];
+ if (hdr == NULL) {
+ return NULL;
+ }
+
+ return hdr;
+}
+
+/*
+ * Grab a string from the DMI/SMBIOS formatted
+ * section.
+ *
+ * @hdr: DMI header to lookup string index
+ * @index: 1-based string index
+ *
+ * See section 6.1.3 of the DTMF SMBIOS Reference
+ * Specification
+ */
+static const char *
+dmi_str_index(struct dmi_shdr *hdr, uint8_t index)
+{
+ const char *strdata = PTR_OFFSET(hdr, hdr->length);
+
+ for (uint8_t i = 1; *strdata != '\0'; ++i) {
+ if (i == index) {
+ return strdata;
+ }
+
+ strdata += strlen(strdata) + 1;
+ }
+
+ return NULL;
+}
+
+/*
+ * Get the DMI/SMBIOS structure size from a
+ * header.
+ */
+static size_t
+dmi_struct_size(struct dmi_shdr *hdr)
+{
+ const char *strdata;
+ size_t i = 1;
+
+ strdata = PTR_OFFSET(hdr, hdr->length);
+ while (strdata[i - 1] != '\0' || strdata[i] != '\0') {
+ ++i;
+ }
+
+ return hdr->length + i + 1;
+}
+
+/*
+ * Get the vendor string from the DMI/SMBIOS BIOS
+ * info structure
+ *
+ * Returns NULL if not found.
+ */
+const char *
+dmi_vendor(void)
+{
+ struct dmi_shdr *hdr;
+
+ if ((hdr = dmi_shdr(DMI_BIOS_INFO)) == NULL) {
+ return NULL;
+ }
+
+ return dmi_str_index(hdr, BIOSINFO_VENDOR);
+}
+
+/*
+ * Return the product string from the DMI/SMBIOS System
+ * Info structure
+ *
+ * Returns NULL if not found.
+ */
+const char *
+dmi_product(void)
+{
+ struct dmi_shdr *hdr;
+
+ if ((hdr = dmi_shdr(DMI_SYSTEM_INFO)) == NULL) {
+ return NULL;
+ }
+
+ return dmi_str_index(hdr, SYSINFO_PRODUCT);
+}
+
+/*
+ * Return the product version from the DMI/SMBIOS
+ * System Info structure
+ *
+ * Returns NULL if not found
+ */
+const char *
+dmi_prodver(void)
+{
+ struct dmi_shdr *hdr;
+
+ if ((hdr = dmi_shdr(DMI_SYSTEM_INFO)) == NULL) {
+ return NULL;
+ }
+
+ return dmi_str_index(hdr, SYSINFO_VERSION);
+}
+
+/*
+ * Return the product family from the DMI/SMBIOS
+ * System Info structure
+ *
+ * Returns NULL if not found
+ */
+const char *
+dmi_prodfam(void)
+{
+ struct dmi_shdr *hdr;
+
+ if ((hdr = dmi_shdr(DMI_SYSTEM_INFO)) == NULL) {
+ return NULL;
+ }
+
+ return dmi_str_index(hdr, SYSINFO_FAMILY);
+}
+
+/*
+ * Return the CPU manufacturer string from the
+ * DMI/SMBIOS Processor Info structure
+ *
+ * Returns NULL if not found
+ */
+const char *
+dmi_cpu_manufact(void)
+{
+ struct dmi_shdr *hdr;
+
+ if ((hdr = dmi_shdr(DMI_PROCESSOR_INFO)) == NULL) {
+ return NULL;
+ }
+
+ return dmi_str_index(hdr, PROCINFO_MANUFACT);
+}
+
+/*
+ * Return the CPU version string from the
+ * DMI/SMBIOS Processor Info structure
+ *
+ * Returns NULL if not found
+ */
+const char *
+dmi_cpu_version(void)
+{
+ struct dmi_shdr *hdr;
+
+ if ((hdr = dmi_shdr(DMI_PROCESSOR_INFO)) == NULL) {
+ return NULL;
+ }
+
+ return dmi_str_index(hdr, PROCINFO_VERSION);
+}
+
+static void
+dmi_init_ctl(void)
+{
+ struct ctlfs_dev ctl;
+ char ctlname[] = "dmi";
+
+ /* Create '/ctl/dmi/board' */
+ ctl.mode = 0444;
+ ctlfs_create_node(ctlname, &ctl);
+ ctl.devname = ctlname;
+ ctl.ops = &g_ctl_board_ident;
+ ctlfs_create_entry("board", &ctl);
+}
+
+static int
+dmi_init(void)
+{
+ struct dmi_entry32 *entry32 = NULL;
+ struct limine_smbios_response *resp = smbios_req.response;
+ struct dmi_entry64 *entry64 = NULL;
+ struct dmi_shdr *hdr = NULL;
+ size_t scount = 0, smax_len = 0;
+ size_t nbytes = 0, cur_nbytes = 0;
+
+ if (resp == NULL) {
+ return -ENODEV;
+ }
+ if (resp->entry_32 == 0 && resp->entry_64 == 0) {
+ return -ENODEV;
+ }
+
+ if (resp->entry_64 != 0) {
+ entry64 = (void *)resp->entry_64;
+ hdr = PHYS_TO_VIRT(entry64->addr);
+ smax_len = entry64->max_size;
+ } else if (resp->entry_32 != 0) {
+ entry32 = (void *)(uint64_t)resp->entry_32;
+ hdr = PHYS_TO_VIRT((uint64_t)entry32->addr);
+ scount = entry32->nstruct;
+ } else {
+ return -ENODEV;
+ }
+
+ memset(hdrs, 0, sizeof(hdrs));
+ for (size_t i = 0; i < scount; ++i) {
+ if (hdr->type == DMI_END_OF_TABLE) {
+ break;
+ }
+
+ if (hdr->type < NELEM(hdrs)) {
+ hdrs[hdr->type] = hdr;
+ }
+ cur_nbytes = dmi_struct_size(hdr);
+ if (smax_len > 0 && (nbytes + cur_nbytes) >= smax_len) {
+ break;
+ }
+
+ nbytes += cur_nbytes;
+ hdr = PTR_OFFSET(hdr, cur_nbytes);
+ }
+
+ dmi_init_ctl();
+ return 0;
+}
+
+DRIVER_EXPORT(dmi_init, "dmi");
diff --git a/sys/dev/dmi/dmi_board.c b/sys/dev/dmi/dmi_board.c
new file mode 100644
index 0000000..23709bd
--- /dev/null
+++ b/sys/dev/dmi/dmi_board.c
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/dmi.h>
+#include <dev/dmi/dmi.h>
+#include <dev/dmi/dmivar.h>
+#include <fs/ctlfs.h>
+#include <string.h>
+
+extern struct ctlops ctl_cpu_ident;
+
+static int
+board_ctl_read(struct ctlfs_dev *cdp, struct sio_txn *sio)
+{
+ struct dmi_board board;
+ const char *cpu_manuf, *prodver;
+ const char *product, *vendor;
+ const char *cpu_ver, *p;
+ size_t len;
+
+ if (cdp == NULL || sio == NULL) {
+ return -EINVAL;
+ }
+ /* Cannot copy zero bytes */
+ if (sio->len == 0) {
+ return -EINVAL;
+ }
+
+ /* Check offset and clamp length */
+ if (sio->offset >= sizeof(board)) {
+ return 0;
+ }
+ if ((sio->offset + sio->len) > sizeof(board)) {
+ sio->len = sizeof(board);
+ }
+
+ memset(&board, 0, sizeof(board));
+ cpu_ver = dmi_cpu_version();
+ if (cpu_ver != NULL) {
+ len = strlen(cpu_ver);
+ memcpy(board.cpu_version, cpu_ver, len);
+ }
+
+ prodver = dmi_prodver();
+ if (prodver != NULL) {
+ len = strlen(prodver);
+ memcpy(board.version, prodver, len);
+ }
+
+ cpu_manuf = dmi_cpu_manufact();
+ if (cpu_manuf != NULL) {
+ len = strlen(cpu_manuf);
+ memcpy(board.cpu_manuf, cpu_manuf, len);
+ }
+
+ product = dmi_product();
+ if (product != NULL) {
+ len = strlen(product);
+ memcpy(board.product, product, len);
+ }
+
+ vendor = dmi_vendor();
+ if (vendor != NULL) {
+ len = strlen(vendor);
+ memcpy(board.vendor, vendor, len);
+ }
+
+ p = (char *)&board;
+ memcpy(sio->buf, &p[sio->offset], sio->len);
+ return sio->len;
+}
+
+struct ctlops g_ctl_board_ident = {
+ .read = board_ctl_read,
+ .write = NULL
+};
diff --git a/sys/dev/ic/ahci.c b/sys/dev/ic/ahci.c
index b483e7a..d994ef1 100644
--- a/sys/dev/ic/ahci.c
+++ b/sys/dev/ic/ahci.c
@@ -29,19 +29,49 @@
#include <sys/types.h>
#include <sys/driver.h>
+#include <sys/device.h>
#include <sys/errno.h>
#include <sys/syslog.h>
+#include <sys/sio.h>
+#include <sys/param.h>
+#include <sys/bitops.h>
#include <sys/mmio.h>
+#include <sys/disk.h>
#include <dev/pci/pci.h>
+#include <dev/pci/pciregs.h>
#include <dev/timer.h>
#include <dev/ic/ahcivar.h>
#include <dev/ic/ahciregs.h>
+#include <dev/dcdr/cache.h>
+#include <fs/devfs.h>
+#include <fs/ctlfs.h>
+#include <vm/dynalloc.h>
+#include <vm/physmem.h>
+#include <machine/cdefs.h>
+#include <string.h>
#define pr_trace(fmt, ...) kprintf("ahci: " fmt, ##__VA_ARGS__)
#define pr_error(...) pr_trace(__VA_ARGS__)
+static uint32_t devs_max = 0;
+static struct bdevsw ahci_bdevsw;
+static struct hba_device *devs;
static struct pci_device *ahci_dev;
static struct timer tmr;
+static struct ahci_hba g_hba;
+static struct driver_var __driver_var;
+
+#define MODEL_LEN 40 /* Model number length */
+#define SERIAL_LEN 20 /* Serial number length */
+
+/*
+ * Simplified structure containing certain
+ * information from device identity.
+ */
+struct dev_info {
+ char model[MODEL_LEN];
+ char serial[SERIAL_LEN];
+};
/*
* Poll register to have 'bits' set/unset.
@@ -78,7 +108,54 @@ ahci_poll_reg(volatile uint32_t *reg, uint32_t bits, bool pollset)
}
}
- return val;
+ return 0;
+}
+
+static struct hba_device *
+ahci_get_dev(dev_t dev)
+{
+ for (int i = 0; i < devs_max; ++i) {
+ if (devs[i].dev == dev) {
+ return &devs[i];
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * Allocate a command slot for a port on
+ * the HBA.
+ */
+static int
+ahci_alloc_cmdslot(struct ahci_hba *hba, struct hba_port *port)
+{
+ uint32_t slotlist;
+
+ slotlist = mmio_read32(&port->ci);
+ slotlist |= mmio_read32(&port->sact);
+
+ for (int i = 0; i < hba->nslots; ++i) {
+ if (!ISSET(slotlist, i)) {
+ return i;
+ }
+ }
+
+ return -EAGAIN;
+}
+
+/*
+ * Get the command list base.
+ */
+static paddr_t
+ahci_cmdbase(struct hba_port *port)
+{
+ paddr_t basel, baseh, base;
+
+ basel = mmio_read32(&port->clb);
+ baseh = mmio_read32(&port->clbu);
+ base = COMBINE32(baseh, basel);
+ return base;
}
static int
@@ -114,12 +191,789 @@ ahci_hba_reset(struct ahci_hba *hba)
return 0;
}
+/*
+ * Dump identify structure for debugging
+ * purposes.
+ *
+ * Returns a pointer to a 'dev_info' structure
+ * on success, otherwise a value of NULL is returned
+ * on failure.
+ */
+static int
+ahci_dump_identity(struct ata_identity *identity, struct dev_info *res)
+{
+ char tmp;
+
+ if (res == NULL) {
+ return -EINVAL;
+ }
+
+ /* Copy the data, might be big endian */
+ memcpy(
+ res->serial,
+ identity->serial_number,
+ SERIAL_LEN
+ );
+ memcpy(
+ res->model,
+ identity->model_number,
+ sizeof(res->model)
+ );
+
+ res->serial[SERIAL_LEN - 1] = '\0';
+ res->model[MODEL_LEN - 1] = '\0';
+
+ /* Fixup endianess for serial number */
+ for (size_t i = 0; i < SERIAL_LEN; i += 2) {
+ tmp = res->serial[i];
+ res->serial[i] = res->serial[i + 1];
+ res->serial[i + 1] = tmp;
+ }
+
+ /* Fixup endianess for model number */
+ for (size_t i = 0; i < MODEL_LEN; i += 2) {
+ tmp = res->model[i];
+ res->model[i] = res->model[i + 1];
+ res->model[i + 1] = tmp;
+ }
+
+ return 0;
+}
+
+/*
+ * Stop an HBA port's command list and FIS
+ * engine.
+ */
+static int
+hba_port_stop(struct hba_port *port)
+{
+ const uint32_t RUN_MASK = (AHCI_PXCMD_FR | AHCI_PXCMD_CR);
+ uint32_t cmd, tmp;
+
+ /* Ensure the port is running */
+ cmd = mmio_read32(&port->cmd);
+ if (!ISSET(cmd, RUN_MASK)) {
+ return 0;
+ }
+
+ cmd &= ~(AHCI_PXCMD_ST | AHCI_PXCMD_FRE);
+ mmio_write32(&port->cmd, cmd);
+
+ /*
+ * The spec states that once the port is stopped,
+ * PxCMD.CR and PxCMD.FR become unset
+ */
+ tmp = AHCI_PXCMD_FR | AHCI_PXCMD_CR;
+ if (ahci_poll_reg(&port->cmd, tmp, false) < 0) {
+ return -EAGAIN;
+ }
+
+ return 0;
+}
+
+/*
+ * Bring up an HBA port's command list
+ * and FIS engine.
+ */
+static int
+hba_port_start(struct hba_port *port)
+{
+ const uint32_t RUN_MASK = (AHCI_PXCMD_FR | AHCI_PXCMD_CR);
+ uint32_t cmd, tmp;
+
+ /* Ensure the port is not running */
+ cmd = mmio_read32(&port->cmd);
+ if (ISSET(cmd, RUN_MASK)) {
+ return 0;
+ }
+
+ /* Bring up the port */
+ cmd |= AHCI_PXCMD_ST | AHCI_PXCMD_FRE;
+ mmio_write32(&port->cmd, cmd);
+
+ tmp = AHCI_PXCMD_FR | AHCI_PXCMD_CR;
+ if (ahci_poll_reg(&port->cmd, tmp, true) < 0) {
+ return -EAGAIN;
+ }
+
+ return 0;
+}
+
+/*
+ * Check for interface errors, returns
+ * 0 on success (i.e., no errors), otherwise
+ * the "ERR" word of PxSERR.
+ */
+static int
+hba_port_chkerr(struct hba_port *port)
+{
+ uint32_t serr;
+ uint16_t err;
+ uint8_t critical = 0;
+
+ serr = mmio_read32(&port->serr);
+ err = serr & 0xFFFF;
+ if (err == 0) {
+ return 0;
+ }
+
+ if (ISSET(err, AHCI_SERR_I)) {
+ pr_error("recovered data integrity error\n");
+ }
+ if (ISSET(err, AHCI_SERR_M)) {
+ pr_error("recovered comms error\n");
+ }
+ if (ISSET(err, AHCI_SERR_T)) {
+ pr_error("transient data integrity error\n");
+ }
+ if (ISSET(err, AHCI_SERR_C)) {
+ pr_error("persistent comms error\n");
+ critical = 1;
+ }
+ if (ISSET(err, AHCI_SERR_P)) {
+ pr_error("protocol error\n");
+ critical = 1;
+ }
+ if (ISSET(err, AHCI_SERR_E)) {
+ pr_error("internal hba error\n");
+ critical = 1;
+ }
+ if (critical) {
+ pr_error("CRITICAL - DISABLING PORT **\n");
+ hba_port_stop(port);
+ return err;
+ }
+
+ mmio_write32(&port->serr, 0xFFFFFFFF);
+ return err;
+
+}
+
+/*
+ * Reset a port on the HBA
+ *
+ * XXX: This function stops the port once the
+ * COMRESET is complete.
+ */
+static int
+hba_port_reset(struct ahci_hba *hba, struct hba_port *port)
+{
+ uint32_t sctl, ssts, cmd;
+ uint8_t det, ipm, spd;
+ uint32_t elapsed = 0;
+
+ sctl = mmio_read32(&port->sctl);
+
+ /*
+ * Transmit a COMRESET to the device. If the HBA
+ * supports staggered spin-up, we'll need to set
+ * the PxCMD.SUD bit as well.
+ */
+ sctl = (sctl & ~0x0F) | AHCI_DET_COMRESET;
+ mmio_write32(&port->sctl, sctl);
+ if (hba->sss) {
+ cmd = mmio_read32(&port->cmd);
+ cmd |= AHCI_PXCMD_SUD;
+ mmio_write32(&port->cmd, cmd);
+ }
+
+ /*
+ * Wait for the link to become reestablished
+ * between the port and the HBA.
+ */
+ tmr.msleep(8);
+ sctl &= ~AHCI_DET_COMRESET;
+ mmio_write32(&port->sctl, sctl);
+
+ for (;;) {
+ if (elapsed >= AHCI_TIMEOUT) {
+ break;
+ }
+ ssts = mmio_read32(&port->ssts);
+ det = AHCI_PXSSTS_DET(ssts);
+ if (det == AHCI_DET_COMM) {
+ break;
+ }
+
+ tmr.msleep(10);
+ elapsed += 10;
+ }
+
+ ipm = AHCI_PXSSTS_IPM(ssts);
+ spd = AHCI_PXSSTS_SPD(ssts);
+
+ if (det == AHCI_DET_PRESENT) {
+ pr_error("SATA link timeout\n");
+ return -EAGAIN;
+ }
+ if (det != AHCI_DET_COMM) {
+ return -EAGAIN;
+ }
+
+ /*
+ * Ensure the interface is in an active
+ * state.
+ */
+ if (ipm != AHCI_IPM_ACTIVE) {
+ pr_error("device interface not active\n");
+ return -EAGAIN;
+ }
+
+ switch (spd) {
+ case AHCI_SPD_GEN1:
+ pr_trace("SATA link rate @ ~1.5 Gb/s\n");
+ break;
+ case AHCI_SPD_GEN2:
+ pr_trace("SATA link rate @ ~3 Gb/s\n");
+ break;
+ case AHCI_SPD_GEN3:
+ pr_trace("SATA link rate @ ~6 Gb/s\n");
+ break;
+ }
+
+ return 0;
+}
+
+static int
+ahci_submit_cmd(struct ahci_hba *hba, struct hba_port *port, uint8_t slot)
+{
+ const uint32_t BUSY_BITS = (AHCI_PXTFD_BSY | AHCI_PXTFD_DRQ);
+ const uint8_t MAX_ATTEMPTS = 3;
+ uint32_t ci;
+ uint8_t attempts = 0;
+ int status = 0;
+
+ /*
+ * Spin on `TFD.BSY` and `TFD.DRQ` to ensure
+ * that the port is not busy before we send
+ * any commands.
+ */
+ if (ahci_poll_reg(&port->tfd, BUSY_BITS, false) < 0) {
+ pr_trace("cmd failed, port busy (slot=%d)\n", slot);
+ return -EBUSY;
+ }
+
+ /*
+ * Submit and wait for completion, this may take
+ * a bit so give it several attempts.
+ */
+ ci = mmio_read32(&port->ci);
+ mmio_write32(&port->ci, ci | BIT(slot));
+ while ((attempts++) < MAX_ATTEMPTS) {
+ status = ahci_poll_reg(&port->ci, BIT(slot), false);
+ if (status == 0) {
+ break;
+ }
+ }
+ if (status != 0) {
+ return status;
+ }
+
+ return hba_port_chkerr(port);
+}
+
+/*
+ * Send an ATA IDENTIFY command to a
+ * SATA device.
+ */
+static int
+ahci_identify(struct ahci_hba *hba, struct hba_device *dp)
+{
+ paddr_t base, buf;
+ struct dev_info dev_info;
+ struct hba_port *port;
+ struct ahci_cmd_hdr *cmdhdr;
+ struct ahci_cmdtab *cmdtbl;
+ struct ahci_fis_h2d *fis;
+ uint16_t *p;
+ int cmdslot, status;
+
+ buf = vm_alloc_frame(1);
+ if (buf == 0) {
+ pr_trace("failed to alloc frame\n");
+ return -ENOMEM;
+ }
+
+ port = dp->io;
+ cmdslot = ahci_alloc_cmdslot(hba, port);
+ if (cmdslot < 0) {
+ pr_trace("failed to alloc cmdslot\n");
+ vm_free_frame(buf, 1);
+ return cmdslot;
+ }
+
+ base = ahci_cmdbase(port);
+ base += cmdslot * sizeof(*cmdhdr);
+
+ /* Setup the command header */
+ cmdhdr = PHYS_TO_VIRT(base);
+ cmdhdr->w = 0;
+ cmdhdr->cfl = sizeof(struct ahci_fis_h2d) / 4;
+ cmdhdr->prdtl = 1;
+
+ cmdtbl = PHYS_TO_VIRT(cmdhdr->ctba);
+ cmdtbl->prdt[0].dba = buf;
+ cmdtbl->prdt[0].dbc = 511;
+ cmdtbl->prdt[0].i = 0;
+
+ fis = (void *)&cmdtbl->cfis;
+ fis->command = ATA_CMD_IDENTIFY;
+ fis->c = 1;
+ fis->type = FIS_TYPE_H2D;
+
+ if ((status = ahci_submit_cmd(hba, port, cmdslot)) != 0) {
+ goto done;
+ }
+
+ ahci_dump_identity(PHYS_TO_VIRT(buf), &dev_info);
+ p = (uint16_t *)PHYS_TO_VIRT(buf);
+ dp->nlba = (p[61] << 16) | p[60];
+
+ pr_trace("max block size: %d\n", dp->nlba);
+ pr_trace("model number: %s\n", dev_info.model);
+ pr_trace("serial number: %s\n", dev_info.serial);
+done:
+ vm_free_frame(buf, 1);
+ return status;
+}
+
+/*
+ * Send a read/write command to a SATA drive
+ *
+ * @hba: Host bus adapter of target port
+ * @dev: Device to send over
+ * @sio: System I/O descriptor
+ * @write: If true, data pointed to by `sio` will be written
+ *
+ * XXX: - The `len` field in `sio` is block relative, in other words,
+ * set to 1 to read one block (512 bytes per block), etc.
+ *
+ * - The `offset` field in `sio` is the LBA address.
+ */
+static int
+ahci_sata_rw(struct ahci_hba *hba, struct hba_device *dev, struct sio_txn *sio,
+ bool write)
+{
+ paddr_t base, buf;
+ char *p, *dest;
+ bool dcdr_hit = false;
+ struct hba_port *port;
+ struct dcdr_lookup dcd_lookup;
+ struct dcd *dcd;
+ struct ahci_cmd_hdr *cmdhdr;
+ struct ahci_cmdtab *cmdtbl;
+ struct ahci_fis_h2d *fis;
+ int cmdslot, status;
+ size_t nblocks, cur_lba;
+ size_t len;
+
+ if (sio == NULL) {
+ return -EINVAL;
+ }
+ if (sio->len == 0 || sio->buf == NULL) {
+ return -EINVAL;
+ }
+
+ port = dev->io;
+
+ /*
+ * Compute how many blocks can be cached.
+ *
+ * XXX: We do not want to fill the entire DCDR
+ * with a single drive read to reduce the
+ * frequency of DCDR evictions.
+ *
+ * TODO: We should also take advantage of logical
+ * block coalescing.
+ */
+ nblocks = sio->len;
+ if (nblocks >= AHCI_DCDR_CAP) {
+ nblocks = AHCI_DCDR_CAP / 2;
+ }
+
+ /*
+ * If we are reading the drive, see if we have
+ * anything in the cache.
+ *
+ * XXX: If there is a break in the cache and we
+ * have a miss inbetween, other DCDs are
+ * ignored. Wonder how we can mitigate
+ * fragmentation.
+ */
+ cur_lba = sio->offset;
+ len = sio->len;
+ for (size_t i = 0; i < nblocks && !write; ++i) {
+ status = dcdr_lookup(dev->dcdr, cur_lba, &dcd_lookup);
+ if (status != 0) {
+ break;
+ }
+ if (len == 0) {
+ break;
+ }
+
+ dcdr_hit = true;
+ dcd = dcd_lookup.dcd_res;
+
+ /* Hit, copy the cached data */
+ dest = &((char *)sio->buf)[i * 512];
+ p = dcd->block;
+ memcpy(dest, p, 512);
+
+ ++cur_lba;
+ --len;
+ }
+
+ /* Did we get everything already? */
+ if (len == 0) {
+ return 0;
+ }
+
+ buf = VIRT_TO_PHYS(sio->buf);
+ cmdslot = ahci_alloc_cmdslot(hba, port);
+ if (cmdslot < 0) {
+ pr_trace("failed to alloc cmdslot\n");
+ return cmdslot;
+ }
+
+ base = ahci_cmdbase(port);
+ base += cmdslot * sizeof(*cmdhdr);
+
+ /* Setup the command header */
+ cmdhdr = PHYS_TO_VIRT(base);
+ cmdhdr->w = write;
+ cmdhdr->cfl = sizeof(struct ahci_fis_h2d) / 4;
+ cmdhdr->prdtl = 1;
+
+ cmdtbl = PHYS_TO_VIRT(cmdhdr->ctba);
+ cmdtbl->prdt[0].dba = buf;
+ cmdtbl->prdt[0].dbc = (sio->len << 9) - 1;
+ cmdtbl->prdt[0].i = 0;
+
+ fis = (void *)&cmdtbl->cfis;
+ fis->command = write ? ATA_CMD_WRITE_DMA : ATA_CMD_READ_DMA;
+ fis->c = 1;
+ fis->type = FIS_TYPE_H2D;
+ fis->device = (1 << 6); /* LBA */
+
+ /* Setup LBA */
+ fis->lba0 = cur_lba & 0xFF;
+ fis->lba1 = (cur_lba >> 8) & 0xFF;
+ fis->lba2 = (cur_lba >> 16) & 0xFF;
+ fis->lba3 = (cur_lba >> 24) & 0xFF;
+ fis->lba4 = (cur_lba >> 32) & 0xFF;
+ fis->lba5 = (cur_lba >> 40) & 0xFF;
+
+ /* Setup count */
+ fis->countl = len & 0xFF;
+ fis->counth = (len >> 8) & 0xFF;
+
+ if ((status = ahci_submit_cmd(hba, port, cmdslot)) != 0) {
+ return status;
+ }
+
+ /* Don't cache again on hit */
+ if (!write && dcdr_hit) {
+ return 0;
+ }
+
+ /* Cache our read */
+ for (size_t i = 0; i < nblocks; ++i) {
+ cur_lba = sio->offset + i;
+ p = sio->buf;
+ dcdr_cachein(dev->dcdr, &p[i * 512], cur_lba);
+ }
+ return 0;
+}
+
+static int
+sata_dev_rw(dev_t dev, struct sio_txn *sio, bool write)
+{
+ const size_t BSIZE = 512;
+ struct sio_txn wr_sio;
+ struct hba_device *devp;
+ size_t block_count, len;
+ off_t block_off, read_off;
+ char *buf;
+ int status;
+
+ if (sio == NULL) {
+ return -EINVAL;
+ }
+ if (sio->len == 0 || sio->buf == NULL) {
+ return -EINVAL;
+ }
+ if (dev > devs_max) {
+ return -ENODEV;
+ }
+
+ devp = ahci_get_dev(dev);
+ if (__unlikely(devp == NULL)) {
+ return -ENODEV;
+ }
+
+ /* Compute block count and offset */
+ block_count = ALIGN_UP(sio->len, BSIZE);
+ block_count /= BSIZE;
+ block_off = sio->offset / BSIZE;
+
+ /* Allocate internal buffer */
+ len = block_count * BSIZE;
+ buf = dynalloc_memalign(len, 0x1000);
+ if (buf == NULL) {
+ return -ENOMEM;
+ }
+
+ /* Copy SIO buffer if write */
+ if (write) {
+ memset(buf, 0, len);
+ memcpy(buf, sio->buf, sio->len);
+ }
+
+ /*
+ * Perform the r/w operation and copy internal buffer
+ * out if this is a read operation.
+ */
+ wr_sio.buf = buf;
+ wr_sio.len = block_count;
+ wr_sio.offset = block_off;
+ status = ahci_sata_rw(&g_hba, devp, &wr_sio, write);
+ if (status == 0 && !write) {
+ read_off = sio->offset & (BSIZE - 1);
+ memcpy(sio->buf, buf + read_off, sio->len);
+ }
+
+ dynfree(buf);
+ return sio->len;
+}
+
+/*
+ * Device interface read
+ */
+static int
+ahci_dev_read(dev_t dev, struct sio_txn *sio, int flags)
+{
+ while (DRIVER_DEFERRED()) {
+ md_pause();
+ }
+
+ return sata_dev_rw(dev, sio, false);
+}
+
+/*
+ * Device interface write
+ */
+static int
+ahci_dev_write(dev_t dev, struct sio_txn *sio, int flags)
+{
+ while (DRIVER_DEFERRED()) {
+ md_pause();
+ }
+
+ return sata_dev_rw(dev, sio, true);
+}
+
+/*
+ * Device interface number of blocks
+ */
+static int
+ahci_dev_bsize(dev_t dev)
+{
+ struct hba_device *dp;
+
+ while (DRIVER_DEFERRED()) {
+ md_pause();
+ }
+
+ if ((dp = ahci_get_dev(dev)) == NULL) {
+ return -ENODEV;
+ }
+
+ return dp->nlba;
+}
+
+/*
+ * Register a block device connected to an HBA port
+ * to the rest of the system.
+ *
+ * @dp: Device pointer
+ * @hba: HBA this device belongs to
+ *
+ * Returns zero on success, otherwise a less than
+ * zero value is returned.
+ */
+static int
+ahci_register(struct hba_device *dp, struct ahci_hba *hba)
+{
+ struct ctlfs_dev dev;
+ char devname[128];
+ int error;
+
+ if (hba->major == 0) {
+ hba->major = dev_alloc_major();
+ }
+
+ dp->dev = dev_alloc(hba->major);
+ snprintf(devname, sizeof(devname), "sd%d", dp->dev);
+
+ /* Register the device */
+ dev_register(hba->major, dp->dev, &ahci_bdevsw);
+ pr_trace("drive @ /dev/%s\n", devname);
+
+ /* Register a control node */
+ dev.mode = 0444;
+ ctlfs_create_node(devname, &dev);
+ pr_trace("drive control @ /ctl/%s/\n", devname);
+
+ /* Register control files */
+ dev.devname = devname;
+ dev.ops = &g_sata_bsize_ops;
+ ctlfs_create_entry("bsize", &dev);
+
+ error = devfs_create_entry(devname, hba->major, dp->dev, 060444);
+ if (error < 0) {
+ pr_error("failed to create devfs entry\n");
+ return error;
+ }
+
+ snprintf(devname, sizeof(devname), "SATA drive %d", dp->dev);
+ error = disk_add(devname, dp->dev, &ahci_bdevsw, 0);
+ if (error < 0) {
+ pr_error("failed to add disk \"%s\"\n", devname);
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Initialize a drive on an HBA port
+ *
+ * @hba: HBA descriptor
+ * @portno: Port number
+ */
+static int
+ahci_init_port(struct ahci_hba *hba, uint32_t portno)
+{
+ struct hba_memspace *abar = hba->io;
+ struct hba_port *port;
+ struct hba_device *dp;
+ size_t clen, pagesz;
+ uint32_t lo, hi, sig;
+ paddr_t fra, cmdlist, tmp;
+ int error;
+
+ pagesz = DEFAULT_PAGESIZE;
+ port = &abar->ports[portno];
+
+ if ((error = hba_port_reset(hba, port)) < 0) {
+ return error;
+ }
+ sig = mmio_read32(&port->sig);
+ if (sig == ATAPI_SIG) {
+ return -ENOTSUP;
+ }
+
+ pr_trace("found device @ port %d\n", portno);
+ dp = &devs[portno];
+ dp->io = port;
+ dp->hba = hba;
+ dp->dev = portno;
+
+ dp->dcdr = dcdr_alloc(512, AHCI_DCDR_CAP);
+ if (dp->dcdr == NULL) {
+ pr_error("failed to alloc dcdr\n");
+ return -ENOMEM;
+ }
+
+ /* Allocate a command list */
+ clen = ALIGN_UP(hba->nslots * AHCI_CMDENTRY_SIZE, pagesz);
+ clen /= pagesz;
+ cmdlist = vm_alloc_frame(clen);
+ if (cmdlist == 0) {
+ pr_trace("failed to alloc command list\n");
+ return -ENOMEM;
+ }
+
+ /* Allocate FIS receive area */
+ dp->cmdlist = PHYS_TO_VIRT(cmdlist);
+ fra = vm_alloc_frame(1);
+ if (fra == 0) {
+ pr_trace("failed to allocate FIS receive area\n");
+ vm_free_frame(cmdlist, clen);
+ return -ENOMEM;
+ }
+
+ dp->fra = PHYS_TO_VIRT(fra);
+
+ /* Write the command list */
+ lo = cmdlist & 0xFFFFFFFF;
+ hi = cmdlist >> 32;
+ mmio_write32(&port->clb, lo);
+ mmio_write32(&port->clbu, hi);
+
+ /* Write the FIS receive area */
+ lo = fra & 0xFFFFFFFF;
+ hi = fra >> 32;
+ mmio_write32(&port->fb, lo);
+ mmio_write32(&port->fbu, hi);
+
+ /* Each command header has a H2D FIS area */
+ for (int i = 0; i < hba->nslots; ++i) {
+ tmp = vm_alloc_frame(1);
+ dp->cmdlist[i].prdtl = 1;
+ dp->cmdlist[i].ctba = tmp;
+ }
+
+ mmio_write32(&port->serr, 0xFFFFFFFF);
+
+ if ((error = hba_port_start(port)) < 0) {
+ for (int i = 0; i < hba->nslots; ++i) {
+ vm_free_frame(dp->cmdlist[i].ctba, 1);
+ }
+ vm_free_frame(cmdlist, clen);
+ vm_free_frame(fra, 1);
+ pr_trace("failed to start port %d\n", portno);
+ return error;
+ }
+
+ ahci_identify(hba, dp);
+ return ahci_register(dp, hba);
+}
+
+/*
+ * Scan the HBA for implemented ports
+ */
+static int
+ahci_hba_scan(struct ahci_hba *hba)
+{
+ struct hba_memspace *abar = hba->io;
+ uint32_t pi;
+ size_t len;
+
+ len = hba->nports * sizeof(struct hba_device);
+ devs_max = hba->nports;
+ if ((devs = dynalloc(len)) == NULL) {
+ pr_trace("failed to allocate dev descriptors\n");
+ return -ENOMEM;
+ }
+
+ memset(devs, 0, len);
+ pi = mmio_read32(&abar->pi);
+ for (int i = 0; i < sizeof(pi) * 8; ++i) {
+ if (ISSET(pi, BIT(i))) {
+ ahci_init_port(hba, i);
+ }
+ }
+
+ return 0;
+}
+
static int
ahci_hba_init(struct ahci_hba *hba)
{
struct hba_memspace *abar = hba->io;
int error;
uint32_t tmp;
+ uint32_t cap, pi;
/*
* God knows what state the HBA is in by the time
@@ -132,6 +986,12 @@ ahci_hba_init(struct ahci_hba *hba)
}
pr_trace("successfully performed a hard reset\n");
+ cap = mmio_read32(&abar->cap);
+ hba->maxports = AHCI_CAP_NP(cap);
+ hba->nslots = AHCI_CAP_NCS(cap);
+ hba->ems = AHCI_CAP_EMS(cap);
+ hba->sal = AHCI_CAP_SAL(cap);
+ hba->sss = AHCI_CAP_SSS(cap);
/*
* The HBA provides backwards compatibility with
@@ -142,17 +1002,51 @@ ahci_hba_init(struct ahci_hba *hba)
tmp = mmio_read32(&abar->ghc);
tmp |= AHCI_GHC_AE;
mmio_write32(&abar->ghc, tmp);
+
+ /*
+ * CAP.NCS reports the maximum number of ports the
+ * HBA silicon supports but a lot of hardware will
+ * not implement the full number of ports supported.
+ *
+ * the `PI' register is a bit-significant register
+ * used to determine which ports are implemented,
+ * therefore we can just count how many bits are
+ * set in this register and that would be how many
+ * ports are implemented total.
+ */
+ pi = mmio_read32(&abar->pi);
+ hba->nports = popcnt(pi);
+ pr_trace("hba implements %d port(s)\n", hba->nports);
+
+ if ((error = ahci_hba_scan(hba)) != 0) {
+ return error;
+ }
+
return 0;
}
+/*
+ * Init PCI related controller bits
+ */
+static void
+ahci_init_pci(void)
+{
+ uint32_t tmp;
+
+ /* Enable bus mastering and MMIO */
+ tmp = pci_readl(ahci_dev, PCIREG_CMDSTATUS);
+ tmp |= (PCI_BUS_MASTERING | PCI_MEM_SPACE);
+ pci_writel(ahci_dev, PCIREG_CMDSTATUS, tmp);
+}
+
static int
ahci_init(void)
{
struct pci_lookup lookup;
int status;
- struct ahci_hba hba;
void *abar_vap = NULL;
+ g_hba.major = 0;
lookup.pci_class = 0x01;
lookup.pci_subclass = 0x06;
@@ -193,14 +1087,20 @@ ahci_init(void)
* ahci_dev struct, so that we can perform MMIO and then issue
* a hard reset.
*/
-
if ((status = pci_map_bar(ahci_dev, 5, &abar_vap)) != 0) {
return status;
}
- hba.io = (struct hba_memspace*)abar_vap;
- ahci_hba_init(&hba);
+ ahci_init_pci();
+ g_hba.io = (struct hba_memspace*)abar_vap;
+ ahci_hba_init(&g_hba);
return 0;
}
-DRIVER_EXPORT(ahci_init);
+static struct bdevsw ahci_bdevsw = {
+ .read = ahci_dev_read,
+ .write = ahci_dev_write,
+ .bsize = ahci_dev_bsize
+};
+
+DRIVER_EXPORT(ahci_init, "ahci");
diff --git a/sys/dev/ic/ahci_ctl.c b/sys/dev/ic/ahci_ctl.c
new file mode 100644
index 0000000..282a141
--- /dev/null
+++ b/sys/dev/ic/ahci_ctl.c
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <dev/ic/ahcivar.h>
+#include <fs/ctlfs.h>
+#include <string.h>
+
+static int
+ctl_bsize_read(struct ctlfs_dev *cdp, struct sio_txn *sio)
+{
+ uint32_t bsize = AHCI_SECTOR_SIZE;
+ uint32_t len = sizeof(bsize);
+
+ if (sio == NULL) {
+ return -EINVAL;
+ }
+ if (sio->buf == NULL) {
+ return -EINVAL;
+ }
+
+ if (sio->len < len) {
+ len = sio->len;
+ }
+
+ memcpy(sio->buf, &bsize, len);
+ return len;
+}
+
+/*
+ * Operations for /ctl/sdx/bsize
+ */
+const struct ctlops g_sata_bsize_ops = {
+ .read = ctl_bsize_read,
+ .write = NULL,
+};
diff --git a/sys/dev/ic/nvme.c b/sys/dev/ic/nvme.c
index 6753072..c65d7e0 100644
--- a/sys/dev/ic/nvme.c
+++ b/sys/dev/ic/nvme.c
@@ -309,6 +309,35 @@ nvme_poll_submit_cmd(struct nvme_queue *q, struct nvme_cmd cmd)
return 0;
}
+/*
+ * Get NVMe log page
+ *
+ * @ctrl: NVMe controller to target
+ * @buf: Data buffer
+ * @lid: Log identifier
+ * @len: Length (in bytes)
+ */
+static int
+nvme_get_logpage(struct nvme_ctrl *ctrl, void *buf, uint8_t lid, uint32_t len)
+{
+ struct nvme_cmd cmd = {0};
+ struct nvme_get_logpage_cmd *cmdp;
+
+ if (!is_4k_aligned(buf)) {
+ return -1;
+ }
+
+ cmdp = &cmd.get_logpage;
+ cmdp->opcode = NVME_OP_GET_LOGPAGE;
+ cmdp->nsid = 0xFFFFFFFF;
+ cmdp->lid = lid;
+ cmdp->numdl = len / 4;
+ cmdp->numdu = 0;
+ cmdp->prp1 = VIRT_TO_PHYS(buf);
+ cmdp->prp2 = 0;
+ return nvme_poll_submit_cmd(&ctrl->adminq, cmd);
+}
+
static int
nvme_identify(struct nvme_ctrl *ctrl, void *buf, uint32_t nsid, uint8_t cns)
{
@@ -425,7 +454,7 @@ nvme_dev_rw(dev_t dev, struct sio_txn *sio, bool write)
*/
ns = nvme_get_ns(dev);
if (__unlikely(ns == NULL))
- return -EIO;
+ return -ENODEV;
/* Calculate the block count and offset */
block_count = ALIGN_UP(sio->len, ns->lba_bsize);
@@ -470,6 +499,12 @@ nvme_dev_read(dev_t dev, struct sio_txn *sio, int flags)
return nvme_dev_rw(dev, sio, false);
}
+static int
+nvme_dev_write(dev_t dev, struct sio_txn *sio, int flags)
+{
+ return nvme_dev_rw(dev, sio, true);
+}
+
/*
* Initializes an NVMe namespace.
*
@@ -543,6 +578,7 @@ nvme_init_ctrl(struct nvme_bar *bar)
uint16_t mqes;
uint8_t *nsids;
struct nvme_ctrl ctrl = { .bar = bar };
+ struct nvme_smart_data *smart;
struct nvme_queue *adminq;
struct nvme_id *id;
@@ -566,14 +602,21 @@ nvme_init_ctrl(struct nvme_bar *bar)
return error;
}
+ smart = dynalloc_memalign(sizeof(*smart), 0x1000);
+ if (smart == NULL) {
+ return -ENOMEM;
+ }
+
id = dynalloc_memalign(sizeof(*id), 0x1000);
if (id == NULL) {
+ dynfree(smart);
return -ENOMEM;
}
nsids = dynalloc_memalign(0x1000, 0x1000);
if (nsids == NULL) {
dynfree(id);
+ dynfree(smart);
return -ENOMEM;
}
@@ -581,6 +624,18 @@ nvme_init_ctrl(struct nvme_bar *bar)
nvme_log_ctrl_id(id);
nvme_identify(&ctrl, nsids, 0, ID_CNS_NSID_LIST);
+ /*
+ * Attempt to read some SMART data but don't bother
+ * if it fails in any way.
+ */
+ error = nvme_get_logpage(&ctrl, smart, NVME_LOGPAGE_SMART, sizeof(*smart));
+ if (error == 0) {
+ if (smart->temp != 0 && smart->temp > 283)
+ pr_trace("temp: %d K\n", smart->temp);
+
+ pr_trace("%d%% used\n", smart->percent_used);
+ }
+
ctrl.sqes = id->sqes >> 4;
ctrl.cqes = id->cqes >> 4;
@@ -607,6 +662,7 @@ nvme_init_ctrl(struct nvme_bar *bar)
dynfree(id);
dynfree(nsids);
+ dynfree(smart);
return 0;
}
@@ -659,7 +715,7 @@ nvme_init(void)
static struct bdevsw nvme_bdevsw = {
.read = nvme_dev_read,
- .write = nowrite
+ .write = nvme_dev_write
};
-DRIVER_EXPORT(nvme_init);
+DRIVER_EXPORT(nvme_init, "nvme");
diff --git a/sys/dev/pci/pci.c b/sys/dev/pci/pci.c
index 8328ffc..9dfb90e 100644
--- a/sys/dev/pci/pci.c
+++ b/sys/dev/pci/pci.c
@@ -31,14 +31,33 @@
#include <sys/queue.h>
#include <sys/syslog.h>
#include <sys/errno.h>
+#include <sys/spinlock.h>
+#include <sys/mmio.h>
#include <dev/pci/pci.h>
#include <dev/pci/pciregs.h>
+#include <dev/acpi/acpi.h>
+#include <dev/acpi/tables.h>
+#include <machine/pci/pci.h>
#include <vm/dynalloc.h>
+#include <vm/vm.h>
#include <lib/assert.h>
#define pr_trace(fmt, ...) kprintf("pci: " fmt, ##__VA_ARGS__)
static TAILQ_HEAD(, pci_device) device_list;
+static struct spinlock devlist_lock = {0};
+static struct acpi_mcfg *mcfg;
+
+struct cam_hook {
+ /* PCI CAM */
+ pcireg_t(*cam_readl)(struct pci_device *dev, uint32_t off);
+ void(*cam_writel)(struct pci_device *dev, uint32_t off, pcireg_t val);
+
+ /* PCIe ECAM */
+ pcireg_t(*ecam_readl)(struct pci_device *dev, uint32_t off);
+ void(*ecam_writel)(struct pci_device *dev, uint32_t off, pcireg_t val);
+ void *ecam_base[1];
+} cam_hook = { NULL };
static bool
pci_dev_exists(uint8_t bus, uint8_t slot, uint8_t func)
@@ -121,6 +140,9 @@ pci_set_device_info(struct pci_device *dev)
dev->prog_if = PCIREG_PROGIF(classrev);
dev->hdr_type = (uint8_t)pci_readl(dev, PCIREG_HDRTYPE);
+ /* This is a PCIe device if it has CAP ID of 0x10 */
+ dev->pci_express = pci_get_cap(dev, 0x10) != 0;
+
/* Set type-specific data */
switch (dev->hdr_type & ~BIT(7)) {
case PCI_HDRTYPE_NORMAL:
@@ -149,6 +171,53 @@ pci_set_device_info(struct pci_device *dev)
static void
pci_scan_bus(uint8_t bus);
+static inline vaddr_t
+pcie_ecam_addr(struct pci_device *dev)
+{
+ vaddr_t base = (vaddr_t)cam_hook.ecam_base[0];
+
+ base += dev->bus << 20 |
+ dev->slot << 15 |
+ dev->func << 12;
+ return base;
+}
+
+static pcireg_t
+pcie_ecam_readl(struct pci_device *dev, uint32_t offset)
+{
+ vaddr_t address;
+
+ address = pcie_ecam_addr(dev);
+ address += (offset & ~3);
+ return mmio_read32((void *)address);
+}
+
+static void
+pcie_ecam_writel(struct pci_device *dev, uint32_t offset, pcireg_t val)
+{
+ vaddr_t address;
+
+ address = pcie_ecam_addr(dev);
+ address += (offset & ~3);
+ mmio_write32((void *)address, val);
+}
+
+static int
+pcie_init(struct acpi_mcfg_base *base)
+{
+ void *iobase;
+
+ pr_trace("[group %02d] @ bus [%02d - %02d]\n", base->seg_grpno,
+ base->bus_start, base->bus_end);
+ pr_trace("ecam @ %p\n", base->base_pa);
+
+ iobase = PHYS_TO_VIRT(base->base_pa);
+ cam_hook.ecam_base[0] = iobase;
+ cam_hook.ecam_writel = pcie_ecam_writel;
+ cam_hook.ecam_readl = pcie_ecam_readl;
+ return 0;
+}
+
/*
* Attempt to register a device.
*
@@ -262,12 +331,55 @@ pci_get_device(struct pci_lookup lookup, uint16_t lookup_type)
return NULL;
}
+
+void
+pci_add_device(struct pci_device *dev)
+{
+ spinlock_acquire(&devlist_lock);
+ TAILQ_INSERT_TAIL(&device_list, dev, link);
+ spinlock_release(&devlist_lock);
+}
+
+
+pcireg_t
+pci_readl(struct pci_device *dev, uint32_t offset)
+{
+ bool have_ecam = cam_hook.ecam_readl != NULL;
+
+ if (dev->pci_express && have_ecam) {
+ return cam_hook.ecam_readl(dev, offset);
+ }
+
+ return cam_hook.cam_readl(dev, offset);
+}
+
+void
+pci_writel(struct pci_device *dev, uint32_t offset, pcireg_t val)
+{
+ bool have_ecam = cam_hook.ecam_writel != NULL;
+
+ if (dev->pci_express && have_ecam) {
+ cam_hook.ecam_writel(dev, offset, val);
+ return;
+ }
+
+ cam_hook.cam_writel(dev, offset, val);
+}
+
int
pci_init(void)
{
size_t ndev;
TAILQ_INIT(&device_list);
+ mcfg = acpi_query("MCFG");
+ if (mcfg != NULL) {
+ pcie_init(&mcfg->base[0]);
+ }
+
+ cam_hook.cam_readl = md_pci_readl;
+ cam_hook.cam_writel = md_pci_writel;
+
/* Recursively scan bus 0 */
pci_scan_bus(0);
ndev = TAILQ_NELEM(&device_list);
diff --git a/sys/dev/phy/e1000.c b/sys/dev/phy/e1000.c
new file mode 100644
index 0000000..41a2a27
--- /dev/null
+++ b/sys/dev/phy/e1000.c
@@ -0,0 +1,358 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/driver.h>
+#include <sys/errno.h>
+#include <sys/syslog.h>
+#include <sys/mmio.h>
+#include <dev/phy/e1000regs.h>
+#include <dev/pci/pci.h>
+#include <dev/pci/pciregs.h>
+#include <dev/timer.h>
+#include <net/if_var.h>
+#include <string.h>
+
+#define pr_trace(fmt, ...) kprintf("e1000: " fmt, ##__VA_ARGS__)
+#define pr_error(...) pr_trace(__VA_ARGS__)
+
+#define E1000_VENDOR 0x8086
+#define E1000_DEVICE 0x100E
+#define E1000_TIMEOUT 500 /* In msec */
+
+static struct timer tmr;
+static struct pci_device *e1000;
+static struct netif netif;
+
+struct e1000_nic {
+ void *vap;
+ uint8_t has_eeprom : 1;
+ uint16_t eeprom_size;
+ uint16_t io_port;
+};
+
+static int
+e1000_poll_reg(volatile uint32_t *reg, uint32_t bits, bool pollset)
+{
+ size_t usec_start, usec;
+ size_t elapsed_msec;
+ uint32_t val;
+ bool tmp;
+
+ usec_start = tmr.get_time_usec();
+
+ for (;;) {
+ val = mmio_read32(reg);
+ tmp = (pollset) ? ISSET(val, bits) : !ISSET(val, bits);
+
+ usec = tmr.get_time_usec();
+ elapsed_msec = (usec - usec_start) / 1000;
+
+ /* If tmp is set, the register updated in time */
+ if (tmp) {
+ break;
+ }
+
+ /* Exit with an error if we timeout */
+ if (elapsed_msec > E1000_TIMEOUT) {
+ return -ETIME;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Query information about any EEPROMs for diagnostic
+ * purposes.
+ *
+ * TODO: Some wacky older chips don't show their presence
+ * too easily, we could fallback to microwire / SPI
+ * bit banging to see if it responds to us manually
+ * clocking a dummy read operation in.
+ */
+static void
+eeprom_query(struct e1000_nic *np)
+{
+ uint16_t size_bits = 1024;
+ uint32_t eecd, *eecd_p;
+ const char *typestr = "microwire";
+
+ eecd_p = PTR_OFFSET(np->vap, E1000_EECD);
+
+ /*
+ * First we should check if there is an EEPROM
+ * on-board as if not, there is nothing we can do
+ * here.
+ */
+ eecd = mmio_read32(eecd_p);
+ if (!ISSET(eecd, E1000_EECD_PRES)) {
+ return;
+ }
+
+ np->has_eeprom = 1;
+ if (ISSET(eecd, E1000_EECD_TYPE)) {
+ typestr = "SPI";
+ }
+ if (ISSET(eecd, E1000_EECD_SIZE)) {
+ size_bits = 4096;
+ }
+
+ np->eeprom_size = size_bits;
+ pr_trace("%d-bit %s EEPROM detected\n", size_bits, typestr);
+}
+
+/*
+ * If there is no EEPROM, we can still read
+ * the MAC address through the Receive address
+ * registers
+ *
+ * XXX: This is typically only used as a fallback.
+ *
+ * Returns a less than zero value if an ethernet
+ * address is not found, which would be kind of
+ * not good.
+ *
+ * @np: NIC descriptor
+ * @addr: Pointer to MAC address data
+ */
+static int
+e1000_read_recvaddr(struct e1000_nic *np, struct netif_addr *addr)
+{
+ const uint32_t RECVADDR_OFF = 0x5400;
+ uint32_t tmp;
+ uint32_t *dword_p;
+
+ dword_p = PTR_OFFSET(np->vap, RECVADDR_OFF);
+
+ if (dword_p[0] == 0) {
+ pr_error("bad hwaddr in recvaddr\n");
+ return -ENOTSUP;
+ }
+
+ /* DWORD 0 */
+ tmp = mmio_read32(&dword_p[0]);
+ addr->data[0] = tmp & 0xFF;
+ addr->data[1] = (tmp >> 8) & 0xFF;
+ addr->data[2] = (tmp >> 16) & 0xFF;
+ addr->data[3] = (tmp >> 24) & 0xFF;
+
+ /* DWORD 1 */
+ tmp = mmio_read32(&dword_p[1]);
+ addr->data[4] = tmp & 0xFF;
+ addr->data[5] = (tmp >> 8) & 0xFF;
+ return 0;
+}
+
+/*
+ * Read 16-bytes from the NIC's on-board EEPROM.
+ *
+ * XXX: This should only be used if the caller is
+ * certain that the NIC has an EEPROM
+ *
+ * @addr: EEPROM address to read from
+ *
+ * A returned value of 0xFFFF should be seen as invalid.
+ */
+static uint16_t
+eeprom_readw(struct e1000_nic *np, uint8_t addr)
+{
+ uint32_t eerd, *eerd_p;
+ int error;
+
+ if (!np->has_eeprom) {
+ pr_error("e1000_read_eeprom: EEPROM not present\n");
+ return 0xFFFF;
+ }
+
+ eerd_p = PTR_OFFSET(np->vap, E1000_EERD);
+ eerd = (addr << 8) | E1000_EERD_START;
+ mmio_write32(eerd_p, eerd);
+
+ error = e1000_poll_reg(eerd_p, E1000_EERD_DONE, true);
+ if (error < 0) {
+ pr_error("e1000_read_eeprom: timeout\n");
+ return 0xFFFF;
+ }
+
+ eerd = mmio_read32(eerd_p);
+ return (eerd >> 16) & 0xFFFF;
+}
+
+/*
+ * Read the MAC address from the NICs EEPROM.
+ *
+ * XXX: This should usually work, however if the NIC does
+ * not have an on-board EEPROM, this will fail. In such
+ * cases, e1000_read_recvaddr() can be called instead.
+ *
+ * @np: NIC descriptor
+ * @addr: Pointer to MAC address data
+ */
+static int
+e1000_read_macaddr(struct e1000_nic *np, struct netif_addr *addr)
+{
+ uint16_t eeprom_word;
+
+ if (!np->has_eeprom) {
+ pr_trace("EEPROM not present, trying recvaddr\n");
+ return e1000_read_recvaddr(np, addr);
+ }
+
+ /* Word 0 */
+ eeprom_word = eeprom_readw(np, E1000_HWADDR0);
+ addr->data[0] = (eeprom_word & 0xFF);
+ addr->data[1] = (eeprom_word >> 8) & 0xFF;
+
+ /* Word 1 */
+ eeprom_word = eeprom_readw(np, E1000_HWADDR1);
+ addr->data[2] = (eeprom_word & 0xFF);
+ addr->data[3] = (eeprom_word >> 8) & 0xFF;
+
+ /* Word 2 */
+ eeprom_word = eeprom_readw(np, E1000_HWADDR2);
+ addr->data[4] = (eeprom_word & 0xFF);
+ addr->data[5] = (eeprom_word >> 8) & 0xFF;
+ return 0;
+}
+
+/*
+ * Reset the entire E1000
+ */
+static int
+e1000_reset(struct e1000_nic *np)
+{
+ uint32_t ctl, *ctl_p;
+ int error;
+
+ ctl_p = PTR_OFFSET(np->vap, E1000_CTL);
+ ctl = mmio_read32(&ctl_p);
+ ctl |= E1000_CTL_RST;
+ mmio_write32(&ctl_p, ctl);
+
+ error = e1000_poll_reg(ctl_p, E1000_CTL_RST, false);
+ if (error < 0) {
+ pr_error("reset timeout\n");
+ return error;
+ }
+
+ return 0;
+}
+
+/*
+ * Initialize an E1000(e) chip
+ */
+static int
+e1000_chip_init(struct e1000_nic *np)
+{
+ struct netif_addr *addr = &netif.addr;
+ int error;
+
+ /*
+ * To ensure that BIOS/UEFI or whatever firmware got us
+ * here didn't fuck anything up in the process or at the
+ * very least, put the controller in a seemingly alright
+ * state that gives us a suprise screwing in the future,
+ * we'll reset everything to its default startup state.
+ *
+ * Better safe than sorry...
+ */
+ if ((error = e1000_reset(np)) < 0) {
+ return error;
+ }
+
+ eeprom_query(np);
+ if ((error = e1000_read_macaddr(np, addr)) < 0) {
+ return error;
+ }
+
+ pr_trace("MAC address: %x:%x:%x:%x:%x:%x\n",
+ (uint64_t)addr->data[0], (uint64_t)addr->data[1],
+ (uint64_t)addr->data[2], (uint64_t)addr->data[3],
+ (uint64_t)addr->data[4], (uint64_t)addr->data[5]);
+
+ return 0;
+}
+
+/*
+ * Enables PCI specific bits like bus mastering (for DMA)
+ * as well as MMIO.
+ */
+static void
+e1000_init_pci(void)
+{
+ uint32_t tmp;
+
+ tmp = pci_readl(e1000, PCIREG_CMDSTATUS);
+ tmp |= (PCI_BUS_MASTERING | PCI_MEM_SPACE);
+ pci_writel(e1000, PCIREG_CMDSTATUS, tmp);
+}
+
+static int
+e1000_init(void)
+{
+ struct pci_lookup lookup;
+ struct e1000_nic nic;
+ int status;
+
+ lookup.vendor_id = E1000_VENDOR;
+ lookup.device_id = E1000_DEVICE;
+ e1000 = pci_get_device(lookup, PCI_DEVICE_ID | PCI_VENDOR_ID);
+ if (e1000 == NULL) {
+ return -ENODEV;
+ }
+
+ /* Get a GP timer */
+ if (req_timer(TIMER_GP, &tmr) != TMRR_SUCCESS) {
+ pr_error("failed to fetch general purpose timer\n");
+ return -ENODEV;
+ }
+
+ /* We need msleep() */
+ if (tmr.msleep == NULL) {
+ pr_error("general purpose timer has no msleep()\n");
+ return -ENODEV;
+ }
+
+ memset(&nic, 0, sizeof(nic));
+ pr_trace("e1000 at pci%d:%x.%x.%d\n",
+ e1000->bus, e1000->device_id, e1000->func,
+ e1000->slot);
+
+ if ((status = pci_map_bar(e1000, 0, &nic.vap)) != 0) {
+ pr_error("failed to map BAR0\n");
+ return status;
+ }
+
+ e1000_init_pci();
+ e1000_chip_init(&nic);
+ return 0;
+}
+
+DRIVER_EXPORT(e1000_init, "e1000");
diff --git a/sys/dev/phy/et131x.c b/sys/dev/phy/et131x.c
new file mode 100644
index 0000000..d7764ae
--- /dev/null
+++ b/sys/dev/phy/et131x.c
@@ -0,0 +1,338 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * This driver is the product of reverse engineering
+ * work done by Ian Marco Moffett and the OSMORA team.
+ *
+ * Please refer to share/docs/hw/et131x.txt
+ */
+
+#include <sys/types.h>
+#include <sys/driver.h>
+#include <sys/errno.h>
+#include <sys/syslog.h>
+#include <sys/mmio.h>
+#include <dev/pci/pci.h>
+#include <dev/pci/pciregs.h>
+#include <dev/phy/et131xregs.h>
+#include <dev/timer.h>
+#include <net/if_var.h>
+
+#define VENDOR_ID 0x11C1 /* Agere */
+#define DEVICE_ID 0xED00
+
+#define pr_trace(fmt, ...) kprintf("et131x: " fmt, ##__VA_ARGS__)
+#define pr_error(...) pr_trace(__VA_ARGS__)
+
+/*
+ * The ET131X has 1024 words of internal RAM used to
+ * store/buffer packet data before reception or transmission.
+ * The card allows us to decide how large the TX/RX buffers would
+ * be split up. We split the RX/TX 50/50 as a nice balanced default.
+ * Might need to later adjust based on various system needs
+ * (e.g., heavy TX or RX) to avoid thrashing any of the buffers.
+ *
+ */
+#define INTERNAL_MEMSIZE 1024 /* In words */
+#define INTERNAL_MEM_RXOFF 0x1FF /* 50/50 split */
+
+/* Helpful constants */
+#define ETHERFRAME_LEN 1518 /* Length of ethernet frame */
+#define ETHER_FCS_LEN 4 /* Length of frame check seq */
+#define RX_MEM_END 0x2BC
+
+struct netcard {
+ struct et131x_iospace *io;
+};
+
+static struct pci_device *dev;
+static struct netcard g_card;
+static struct timer tmr;
+
+/*
+ * Software reset the ET131X
+ *
+ * @io: Register space
+ */
+static void
+et131x_soft_reset(struct netcard *card)
+{
+ struct et131x_iospace *io = card->io;
+ uint32_t tmp;
+
+ tmp = (
+ MAC_CFG1_RESET_TXMC |
+ MAC_CFG1_RESET_RXMC |
+ MAC_CFG1_RESET_TXFUNC |
+ MAC_CFG1_RESET_RXFUNC |
+ MAC_CFG1_SOFTRST |
+ MAC_CFG1_SIMRST
+ );
+
+ /*
+ * Reset the MAC core, bring it down. After that,
+ * we perform a global reset to bring the whole
+ * chip down.
+ */
+ mmio_write32(&io->mac.cfg1, tmp);
+ mmio_write32(&io->global.sw_reset, GBL_RESET_ALL);
+
+ /*
+ * Reset the MAC again for good measure, but
+ * this time a little softer. We already slammed
+ * the poor thing.
+ */
+ tmp &= ~(MAC_CFG1_SOFTRST | MAC_CFG1_SIMRST);
+ mmio_write32(&io->mac.cfg1, tmp);
+ mmio_write32(&io->mac.cfg1, 0);
+}
+
+/*
+ * Write to the PHY through MII
+ *
+ * @io: Register space
+ * @addr: PHY address
+ * @reg: PHY register
+ * @v: Value to write
+ */
+static int
+et131x_mii_write(struct netcard *card, uint8_t addr, uint8_t reg, uint16_t v)
+{
+ struct et131x_iospace *io = card->io;
+ uint16_t mii_addr;
+ uint32_t tmp, mgmt_addr_old;
+ uint32_t mgmt_cmd_old;
+ uint8_t ndelay = 0;
+ int retval = 0;
+
+ /* Save MII management regs state */
+ mgmt_cmd_old = mmio_read32(&io->mac.mii_mgmt_cmd);
+ mgmt_addr_old = mmio_read32(&io->mac.mii_mgmt_addr);
+ mii_addr = MAC_MII_ADDR(addr, reg);
+
+ /*
+ * Stop any transactions that are currently
+ * happening on the MDIO bus and prepare the
+ * write.
+ */
+ mmio_write32(&io->mac.mii_mgmt_cmd, 0);
+ mmio_write32(&io->mac.mii_mgmt_addr, mii_addr);
+ mmio_write32(&io->mac.mii_mgmt_ctrl, v);
+
+ for (;;) {
+ tmr.usleep(50);
+ ++ndelay;
+
+ tmp = mmio_read32(&io->mac.mii_mgmt_indicator);
+ if (!ISSET(tmp, MAC_MGMT_BUSY))
+ break;
+ if (ndelay >= 50)
+ break;
+ }
+
+ if (ndelay >= 50) {
+ pr_error("could not write PHY reg %x (status=%x)\n", reg, tmp);
+ retval = -EIO;
+ goto done;
+ }
+
+done:
+ /* Stop operations and restore state */
+ mmio_write32(&io->mac.mii_mgmt_cmd, 0);
+ mmio_write32(&io->mac.mii_mgmt_addr, mgmt_addr_old);
+ mmio_write32(&io->mac.mii_mgmt_cmd, mgmt_cmd_old);
+ return retval;
+}
+
+/*
+ * Initialize PCI related things for the
+ * chip.
+ */
+static void
+et131x_init_pci(void)
+{
+ uint32_t tmp;
+
+ /* Enable bus mastering and MMIO */
+ tmp = pci_readl(dev, PCIREG_CMDSTATUS);
+ tmp |= (PCI_BUS_MASTERING | PCI_MEM_SPACE);
+ pci_writel(dev, PCIREG_CMDSTATUS, tmp);
+}
+
+/*
+ * Blink both LEDs of the card
+ *
+ * @io: Register space
+ * @count: Number of times to blink
+ * @delay: Millisecond delay between blinks
+ */
+static void
+et131x_blink(struct netcard *card, uint32_t count, uint16_t delay)
+{
+ uint16_t on_val;
+
+ on_val = (LED_ON << LED_LINK_SHIFT);
+ on_val |= (LED_ON << LED_TXRX_SHIFT);
+ for (uint32_t i = 0; i < count; ++i) {
+ et131x_mii_write(card, 0, PHY_LED2, on_val);
+ tmr.msleep(delay);
+ et131x_mii_write(card, 0, PHY_LED2, LED_ALL_OFF);
+ tmr.msleep(delay);
+ }
+}
+
+/*
+ * Initialize the MAC into a functional
+ * state.
+ *
+ * @io: Register space.
+ */
+static void
+et131x_mac_init(struct netcard *card)
+{
+ struct et131x_iospace *io = card->io;
+ struct mac_regs *mac = &io->mac;
+ struct global_regs *global = &io->global;
+ struct netif_addr addr;
+ uint32_t ipg_tmp, tmp;
+
+ /*
+ * Okay so we need to reset the card so it doesn't
+ * do undefined bullshit. God forbid we get undefined
+ * behaviour without having a fucking official datasheet.
+ * Most would end themselves right then and there.
+ *
+ * Now, after we've done that, we must ensure that any
+ * packets larger than ETHERFRAME_LEN are truncated by
+ * the MAC. Again, something like an internal buffer
+ * overrun during TX/RX would be quite fucking horrible.
+ *
+ * We also want to clear the MAC interface control and MII
+ * clock to ensure it is in a known state.
+ */
+ et131x_soft_reset(card);
+ mmio_write32(&mac->max_fm_len, ETHERFRAME_LEN);
+ mmio_write32(&mac->if_ctrl, 0);
+ mmio_write32(&mac->mii_mgmt_cfg, MAC_MIIMGMT_CLK_RST);
+
+ /*
+ * Split the RX/TX memory 50/50, put the internal RX
+ * buffer right at the start into the first half, and
+ * the TX buffer right after the RX buffer.
+ */
+ mmio_write32(&global->rxq_start, 0);
+ mmio_write32(&global->rxq_end, RX_MEM_END);
+ mmio_write32(&global->txq_start, RX_MEM_END + 1);
+ mmio_write32(&global->txq_end, INTERNAL_MEMSIZE - 1);
+
+ /* Disable loopbacks, watchdog timer, clear MSI config */
+ mmio_write32(&global->loopback, 0);
+ mmio_write32(&global->msi_config, 0);
+ mmio_write32(&global->watchdog_timer, 0);
+
+ /*
+ * Set up half duplex config
+ *
+ * - BEB trunc (0xA)
+ * - Excess defer
+ * - Re-transmit (0xF)
+ * - Collision window
+ */
+ mmio_write32(&mac->hfdp, 0x00A1F037);
+
+ /*
+ * Setup the MAC interpacket gap register
+ *
+ * - IPG1 (0x38)
+ * - IPG2 (0x58)
+ * - B2B (0x60)
+ */
+ ipg_tmp = ((0x50 << 8) | 0x38005860);
+ mmio_write32(&mac->ipg, ipg_tmp);
+
+ /* MAC address dword 0 */
+ tmp = pci_readl(dev, PCI_MAC_ADDRESS);
+ addr.data[0] = tmp & 0xFF;
+ addr.data[1] = (tmp >> 8) & 0xFF;
+ addr.data[2] = (tmp >> 16) & 0xFF;
+ addr.data[3] = (tmp >> 24) & 0xFF;
+
+ /* MAC address word 1 */
+ tmp = pci_readl(dev, PCI_MAC_ADDRESS + 4);
+ addr.data[4] = tmp & 0xFF;
+ addr.data[5] = (tmp >> 8) & 0xFF;
+
+ /* Print out the MAC address */
+ pr_trace("MAC address: %x:%x:%x:%x:%x:%x\n",
+ (uint64_t)addr.data[0], (uint64_t)addr.data[1],
+ (uint64_t)addr.data[2], (uint64_t)addr.data[3],
+ (uint64_t)addr.data[4], (uint64_t)addr.data[5]);
+}
+
+static int
+et131x_init(void)
+{
+ struct pci_lookup lookup;
+ int error;
+
+ lookup.vendor_id = VENDOR_ID;
+ lookup.device_id = DEVICE_ID;
+ dev = pci_get_device(lookup, PCI_VENDOR_ID | PCI_DEVICE_ID);
+ if (dev == NULL) {
+ return -ENODEV;
+ }
+
+ pr_trace("Agere ET1310 Ethernet ctl <phy? at pci%d:%x.%x.%d>\n",
+ dev->bus, dev->device_id, dev->func,
+ dev->slot);
+
+ /* Try to request a general purpose timer */
+ if (req_timer(TIMER_GP, &tmr) != TMRR_SUCCESS) {
+ pr_error("failed to fetch general purpose timer\n");
+ return -ENODEV;
+ }
+
+ /* Ensure it has get_time_usec() */
+ if (tmr.usleep == NULL) {
+ pr_error("general purpose timer has no usleep()\n");
+ return -ENODEV;
+ }
+
+ if ((error = pci_map_bar(dev, 0, (void *)&g_card.io)) != 0) {
+ return error;
+ }
+
+ et131x_init_pci();
+ et131x_mac_init(&g_card);
+ et131x_blink(&g_card, 4, 150);
+ return 0;
+}
+
+DRIVER_DEFER(et131x_init, "et131x");
diff --git a/sys/dev/phy/rt8139.c b/sys/dev/phy/rtl.c
index e2f87e1..d096d1a 100644
--- a/sys/dev/phy/rt8139.c
+++ b/sys/dev/phy/rtl.c
@@ -30,29 +30,30 @@
#include <sys/types.h>
#include <sys/errno.h>
#include <sys/syslog.h>
+#include <sys/spinlock.h>
#include <sys/driver.h>
+#include <sys/device.h>
#include <dev/pci/pci.h>
-#include <dev/phy/rt8139.h>
+#include <dev/phy/rtl.h>
#include <dev/timer.h>
#include <dev/pci/pciregs.h>
+#include <net/netbuf.h>
+#include <net/if_var.h>
#include <vm/physmem.h>
+#include <vm/dynalloc.h>
#include <vm/vm.h>
#include <machine/pio.h>
+#include <machine/intr.h>
#include <string.h>
-/* TODO: Make this smoother */
-#if defined(__x86_64__)
-#include <machine/intr.h>
-#include <machine/ioapic.h>
-#include <machine/lapic.h>
-#include <machine/idt.h>
-#endif
+#define IFNAME "rt0"
-#define pr_trace(fmt, ...) kprintf("rt8139: " fmt, ##__VA_ARGS__)
+#define pr_trace(fmt, ...) kprintf("rt81xx: " fmt, ##__VA_ARGS__)
#define pr_error(...) pr_trace(__VA_ARGS__)
#define RX_BUF_SIZE 3 /* In pages */
#define RX_REAL_BUF_SIZE 8192 /* In bytes */
+#define TXQ_ENTRIES 4
#define RX_PTR_MASK (~3)
@@ -63,11 +64,26 @@
#define HAVE_PIO 0
#endif /* _MACHINE_HAVE_PIO */
+static struct spinlock netif_lock;
+static struct netbuf netif_buf[TXQ_ENTRIES];
static struct pci_device *dev;
+static struct netif netif;
static struct timer tmr;
+static uint32_t tx_ptr = 0;
+static uint32_t netif_enq_ptr = 0;
static uint16_t ioport;
static paddr_t rxbuf, txbuf;
+/* TXAD regs */
+static uint16_t tsads[TXQ_ENTRIES] = {
+ RT_TXAD_N(0), RT_TXAD_N(4),
+ RT_TXAD_N(8), RT_TXAD_N(12)
+};
+static uint16_t tsds[TXQ_ENTRIES] = {
+ RT_TXSTATUS_N(0), RT_TXSTATUS_N(4),
+ RT_TXSTATUS_N(8), RT_TXSTATUS_N(8)
+};
+
/*
* Write to an RTL8139 register
*
@@ -156,53 +172,112 @@ rt_poll(uint8_t reg, uint8_t size, uint32_t bits, bool pollset)
return val;
}
-#if defined(__x86_64__)
-__isr static void
-rt8139_pin_irq(void *sp)
+static int
+rt_tx(void *packet, size_t len)
+{
+ static uint32_t tx_ptr = 0;
+ void *tx_data;
+ paddr_t tx_pa;
+
+ tx_data = dynalloc(len);
+ if (tx_data == NULL) {
+ return -ENOMEM;
+ }
+
+ memcpy(tx_data, packet, len);
+ tx_pa = VIRT_TO_PHYS(tx_data);
+ rt_write(tsads[tx_ptr], 4, tx_pa);
+ rt_write(tsds[tx_ptr++], 4, len);
+ if (tx_ptr > TXQ_ENTRIES - 1) {
+ tx_ptr = 0;
+ }
+ return 0;
+}
+
+static void
+__rt81xx_tx_start(struct netif *nifp)
+{
+ struct netbuf *dest;
+ int error;
+
+ for (int i = 0; i < netif_enq_ptr; ++i) {
+ dest = &netif_buf[i];
+ error = rt_tx(dest->data, dest->len);
+ if (error < 0) {
+ pr_error("tx_start fail @queue %d (errno=%d)\n", i, error);
+ }
+ }
+}
+
+static void
+rt81xx_tx_start(struct netif *nifp)
+{
+ spinlock_acquire(&netif_lock);
+ __rt81xx_tx_start(nifp);
+ spinlock_release(&netif_lock);
+}
+
+static int
+rt81xx_tx_enq(struct netif *nifp, struct netbuf *nbp, void *data)
+{
+ struct netbuf *dest;
+
+ spinlock_acquire(&netif_lock);
+ dest = &netif_buf[netif_enq_ptr++];
+ memcpy(dest, nbp, sizeof(*dest));
+
+ if (netif_enq_ptr > TXQ_ENTRIES - 1) {
+ __rt81xx_tx_start(nifp);
+ netif_enq_ptr = 0;
+ }
+ spinlock_release(&netif_lock);
+ return 0;
+}
+
+static int
+rt81xx_intr(void *sp)
{
- static uint32_t packet_ptr = 0;
uint16_t len;
uint16_t *p;
uint16_t status;
status = rt_read(RT_INTRSTATUS, 2);
- p = (uint16_t *)(rxbuf + packet_ptr);
+ p = (uint16_t *)(rxbuf + tx_ptr);
len = *(p + 1); /* Length after header */
p += 2; /* Points to data now */
- if (status & RT_TOK) {
- return;
+ if (!ISSET(status, RT_TOK | RT_ROK)) {
+ return 0;
+ }
+
+ if (ISSET(status, RT_TOK)) {
+ pr_trace("sent packet\n");
+ return 1;
}
/* Update rxbuf offset in CAPR */
- packet_ptr = (packet_ptr + len + 4 + 3) & RX_PTR_MASK;
- if (packet_ptr > RX_REAL_BUF_SIZE) {
- packet_ptr -= RX_REAL_BUF_SIZE;
+ tx_ptr = (tx_ptr + len + 4 + 3) & RX_PTR_MASK;
+ if (tx_ptr > RX_REAL_BUF_SIZE) {
+ tx_ptr -= RX_REAL_BUF_SIZE;
}
- rt_write(RT_RXBUFTAIL, 2, packet_ptr - 0x10);
+ rt_write(RT_RXBUFTAIL, 2, tx_ptr - 0x10);
rt_write(RT_INTRSTATUS, 2, RT_ACKW);
- lapic_eoi();
+ return 1; /* handled */
}
static int
-rtl8139_irq_init(void)
+rt81xx_irq_init(void)
{
- int vec;
+ struct intr_hand ih;
- vec = intr_alloc_vector("rt8139", IPL_BIO);
- if (vec < 0) {
- return vec;
+ ih.func = rt81xx_intr;
+ ih.priority = IPL_BIO;
+ ih.irq = dev->irq_line;
+ if (intr_register("rt81xx", &ih) == NULL) {
+ return -EIO;
}
-
- /* Map interrupt vector to IRQ */
- idt_set_desc(vec, IDT_INT_GATE, ISR(rt8139_pin_irq), 0);
- ioapic_set_vec(dev->irq_line, vec);
- ioapic_irq_unmask(dev->irq_line);
return 0;
}
-#else
-#define rtl8139_irq_init(...) -ENOTSUP
-#endif
static void
rt_init_pci(void)
@@ -218,6 +293,11 @@ rt_init_pci(void)
static int
rt_init_mac(void)
{
+ struct netif_addr *addr = &netif.addr;
+ uint8_t conf;
+ uint32_t tmp;
+ int error;
+
/*
* First step is ensuring the MAC is in known
* and consistent state by resetting it. God
@@ -226,7 +306,52 @@ rt_init_mac(void)
ioport = dev->bar[0] & ~1;
pr_trace("resetting MAC...\n");
rt_write(RT_CHIPCMD, 1, RT_RST);
- rt_poll(RT_CHIPCMD, 1, RT_RST, 0);
+ error = rt_poll(RT_CHIPCMD, 1, RT_RST, 0);
+ if (error < 0) {
+ pr_error("RTL8139 reset timeout\n");
+ return error;
+ }
+
+ /*
+ * Tell the RTL8139 to load config data from
+ * the 93C46. This is done by clearing EEM1
+ * and setting EEM0. This whole process should
+ * take roughly 2 milliseconds.
+ *
+ * XXX: EEPROM autoloads *should* happen during a hardware
+ * reset but some cards might not follow spec so force
+ * it.
+ */
+ conf = rt_read(RT_CFG9346, 1);
+ conf &= ~RT_EEM1;
+ conf |= RT_EEM0;
+ rt_write(RT_CFG9346, 1, conf);
+
+ /* MAC address dword 0 */
+ tmp = rt_read(RT_IDR0, 4);
+ addr->data[0] = tmp & 0xFF;
+ addr->data[1] = (tmp >> 8) & 0xFF;
+ addr->data[2] = (tmp >> 16) & 0xFF;
+ addr->data[3] = (tmp >> 24) & 0xFF;
+
+ /* MAC address word 1 */
+ tmp = rt_read(RT_IDR2, 4);
+ addr->data[4] = (tmp >> 16) & 0xFF;
+ addr->data[5] = (tmp >> 24) & 0xFF;
+
+ pr_trace("MAC address: %x:%x:%x:%x:%x:%x\n",
+ (uint64_t)addr->data[0], (uint64_t)addr->data[1],
+ (uint64_t)addr->data[2], (uint64_t)addr->data[3],
+ (uint64_t)addr->data[4], (uint64_t)addr->data[5]);
+
+ /*
+ * Alright, now we don't want those EEM bits
+ * sticking lopsided so lets put the RTL8139
+ * back into normal operation...
+ */
+ conf = rt_read(RT_CFG9346, 1);
+ conf &= ~(RT_EEM1 | RT_EEM0);
+ rt_write(RT_CFG9346, 1, conf);
rxbuf = vm_alloc_frame(RX_BUF_SIZE);
txbuf = vm_alloc_frame(RX_BUF_SIZE);
@@ -241,6 +366,11 @@ rt_init_mac(void)
return -ENOMEM;
}
+ memcpy(netif.name, IFNAME, strlen(IFNAME) + 1);
+ netif.tx_enq = rt81xx_tx_enq;
+ netif.tx_start = rt81xx_tx_start;
+ netif_add(&netif);
+
/*
* Configure the chip:
*
@@ -258,19 +388,17 @@ rt_init_mac(void)
* - Enable interrupts through ROK/TOK
* - Enable RX state machines
*
- * TODO: Support TX
- *
*/
- rtl8139_irq_init();
+ rt81xx_irq_init();
rt_write(RT_RXBUF, 4, rxbuf);
rt_write(RT_RXCONFIG, 4, RT_AB | RT_AM | RT_APM | RT_AAP);
rt_write(RT_INTRMASK, 2, RT_ROK | RT_TOK);
- rt_write(RT_CHIPCMD, 1, RT_RE);
+ rt_write(RT_CHIPCMD, 1, RT_RE | RT_TE);
return 0;
}
static int
-rt813l_init(void)
+rt81xx_init(void)
{
struct pci_lookup lookup;
@@ -312,4 +440,4 @@ rt813l_init(void)
return rt_init_mac();
}
-DRIVER_EXPORT(rt813l_init);
+DRIVER_DEFER(rt81xx_init, "rtl81xx");
diff --git a/sys/dev/random/entropy.c b/sys/dev/random/entropy.c
new file mode 100644
index 0000000..4e723a4
--- /dev/null
+++ b/sys/dev/random/entropy.c
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include <dev/random/entropy.h>
+#include <crypto/siphash.h>
+
+void
+mix_entropy(struct entropy_pool *ep, const uint8_t *input,
+ size_t input_len, uint32_t input_entropy_bits)
+{
+ char key[16] = {0,1,2,3,4,5,6,7,8,9,0xa,0xb,0xc,0xd,0xe,0xf};
+ uint64_t hash_result;
+ uint8_t buffer[ENTROPY_POOL_SIZE + input_len];
+ memcpy(buffer, ep->pool, ENTROPY_POOL_SIZE);
+ memcpy(buffer + ENTROPY_POOL_SIZE, input, input_len);
+
+ hash_result = siphash24(buffer, sizeof(buffer), key);
+
+ for (int i = 0; i < 8; ++i) {
+ ep->pool[i] ^= (hash_result >> (i * 8)) & 0xFF;
+ }
+
+ ep->entropy_bits += input_entropy_bits;
+ if (ep->entropy_bits > ENTROPY_POOL_SIZE * 8) {
+ ep->entropy_bits = ENTROPY_POOL_SIZE * 8;
+ }
+}
diff --git a/sys/dev/random/random.c b/sys/dev/random/random.c
new file mode 100644
index 0000000..9bca719
--- /dev/null
+++ b/sys/dev/random/random.c
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/sio.h>
+#include <sys/device.h>
+#include <sys/driver.h>
+#include <dev/random/entropy.h>
+#include <crypto/chacha20.h>
+#include <crypto/siphash.h>
+#include <fs/devfs.h>
+#include <string.h>
+
+static struct cdevsw random_cdevsw;
+static struct entropy_pool entropy;
+
+uint8_t key[32] = {0};
+uint8_t nonce[12] = {0};
+uint32_t state[16];
+uint32_t tsc;
+
+static inline uint64_t
+read_tsc(void)
+{
+ uint32_t lo, hi;
+ __asm__ volatile ("rdtsc" : "=a"(lo), "=d"(hi));
+ return ((uint64_t)hi << 32) | lo;
+}
+
+static int
+random_read(dev_t dev, struct sio_txn *sio, int flags)
+{
+ tsc = read_tsc();
+ mix_entropy(&entropy, (uint8_t *)&tsc, sizeof(tsc), 1);
+
+ chacha20_init(state, entropy.pool, nonce, 0);
+ chacha20_encrypt(state, NULL, sio->buf, sio->len);
+
+ return sio->len;
+}
+
+static int
+random_init(void)
+{
+ char devname[] = "random";
+ devmajor_t major;
+ dev_t dev;
+
+ /* Register the device here */
+ major = dev_alloc_major();
+ dev = dev_alloc(major);
+ dev_register(major, dev, &random_cdevsw);
+ devfs_create_entry(devname, major, dev, 0444);
+
+ return 0;
+}
+
+static struct cdevsw random_cdevsw = {
+ .read = random_read,
+ .write = nowrite
+};
+
+DRIVER_EXPORT(random_init, "random");
diff --git a/sys/dev/usb/xhci.c b/sys/dev/usb/xhci.c
index 67a1e4e..e14cb44 100644
--- a/sys/dev/usb/xhci.c
+++ b/sys/dev/usb/xhci.c
@@ -37,6 +37,8 @@
#include <dev/usb/xhciregs.h>
#include <dev/usb/xhcivar.h>
#include <dev/pci/pci.h>
+#include <dev/pci/pciregs.h>
+#include <dev/acpi/acpi.h>
#include <vm/physmem.h>
#include <vm/dynalloc.h>
#include <assert.h>
@@ -55,10 +57,11 @@
static struct pci_device *hci_dev;
static struct timer tmr;
-__attribute__((__interrupt__)) static void
-xhci_common_isr(void *sf)
+static int
+xhci_intr(void *sf)
{
pr_trace("received xHCI interrupt (via PCI MSI-X)\n");
+ return 1; /* handled */
}
/*
@@ -68,11 +71,16 @@ xhci_common_isr(void *sf)
static inline uint32_t *
xhci_get_portsc(struct xhci_hc *hc, uint8_t portno)
{
- if (portno > hc->maxports) {
- portno = hc->maxports;
+ if (portno >= hc->maxports) {
+ return NULL;
+ }
+
+ /* Zero based */
+ if (portno > 0) {
+ --portno;
}
- return PTR_OFFSET(hc->opregs, 0x400 + (0x10 * (portno - 1)));
+ return PTR_OFFSET(hc->opregs, 0x400 + (0x10 * portno));
}
static int
@@ -145,15 +153,17 @@ xhci_parse_ecp(struct xhci_hc *hc)
break;
case XHCI_ECAP_USBLEGSUP:
/* Begin xHC BIOS handoff to us */
- pr_trace("establishing xHC ownership...\n");
- val |= XHCI_OS_SEM;
- mmio_write32(p, val);
-
- /* Ensure the xHC responded correctly */
- if (xhci_poll32(p, XHCI_OS_SEM, 1) < 0)
- return -EIO;
- if (xhci_poll32(p, XHCI_BIOS_SEM, 0) < 0)
- return -EIO;
+ if (!ISSET(hc->quirks, XHCI_QUIRK_HANDOFF)) {
+ pr_trace("establishing xHC ownership...\n");
+ val |= XHCI_OS_SEM;
+ mmio_write32(p, val);
+
+ /* Ensure the xHC responded correctly */
+ if (xhci_poll32(p, XHCI_OS_SEM, 1) < 0)
+ return -EIO;
+ if (xhci_poll32(p, XHCI_BIOS_SEM, 0) < 0)
+ return -EIO;
+ }
break;
}
@@ -171,6 +181,7 @@ xhci_init_scratchpads(struct xhci_hc *hc)
struct xhci_caps *caps = XHCI_CAPS(hc->base);
uint16_t max_bufs_lo, max_bufs_hi;
uint16_t max_bufs;
+ size_t len;
uintptr_t *bufarr, tmp;
max_bufs_lo = XHCI_MAX_SP_LO(caps->hcsparams1);
@@ -184,8 +195,9 @@ xhci_init_scratchpads(struct xhci_hc *hc)
return 0;
}
- pr_trace("using %d pages for xHC scratchpads\n");
- bufarr = dynalloc_memalign(sizeof(uintptr_t)*max_bufs, 0x1000);
+ len = sizeof(uint64_t) * max_bufs;
+ pr_trace("using %d bytes for xHC scratchpads\n", len);
+ bufarr = dynalloc_memalign(len, 0x1000);
if (bufarr == NULL) {
pr_error("failed to allocate scratchpad buffer array\n");
return -1;
@@ -193,12 +205,12 @@ xhci_init_scratchpads(struct xhci_hc *hc)
for (size_t i = 0; i < max_bufs; ++i) {
tmp = vm_alloc_frame(1);
- memset(PHYS_TO_VIRT(tmp), 0, 0x1000);
if (tmp == 0) {
/* TODO: Shutdown, free memory */
pr_error("failed to fill scratchpad buffer array\n");
return -1;
}
+ memset(PHYS_TO_VIRT(tmp), 0, 0x1000);
bufarr[i] = tmp;
}
@@ -214,7 +226,7 @@ xhci_alloc_dcbaa(struct xhci_hc *hc)
{
size_t size;
- size = sizeof(uintptr_t) * hc->maxslots;
+ size = sizeof(uintptr_t) * (hc->maxslots + 1);
hc->dcbaap = dynalloc_memalign(size, 0x1000);
__assert(hc->dcbaap != NULL);
return VIRT_TO_PHYS(hc->dcbaap);
@@ -229,7 +241,7 @@ xhci_init_msix(struct xhci_hc *hc)
struct msi_intr intr;
intr.name = "xHCI MSI-X";
- intr.handler = xhci_common_isr;
+ intr.handler = xhci_intr;
return pci_enable_msix(hci_dev, &intr);
}
@@ -251,7 +263,7 @@ xhci_init_evring(struct xhci_hc *hc)
memset(segtab, 0, DEFAULT_PAGESIZE);
/* Set the size of the event ring segment table */
- erst_size = PTR_OFFSET(runtime, 0x28);
+ erst_size = PTR_OFFSET(runtime, XHCI_RT_ERSTSZ);
mmio_write32(erst_size, 1);
/* Allocate the event ring segment */
@@ -261,24 +273,24 @@ xhci_init_evring(struct xhci_hc *hc)
/* setup the event ring segment */
segtab->base = VIRT_TO_PHYS(tmp_p);
- segtab->base = ((uintptr_t)segtab->base) + (2 * 4096) & ~0xF;
+ segtab->base = ((uintptr_t)segtab->base);
segtab->size = XHCI_EVRING_LEN;
/* Setup the event ring dequeue pointer */
- erdp = PTR_OFFSET(runtime, 0x38);
+ erdp = PTR_OFFSET(runtime, XHCI_RT_ERDP);
mmio_write64(erdp, segtab->base);
/* Point ERSTBA to our event ring segment */
- erstba = PTR_OFFSET(runtime, 0x30);
+ erstba = PTR_OFFSET(runtime, XHCI_RT_ERSTBA);
mmio_write64(erstba, VIRT_TO_PHYS(segtab));
hc->evring = PHYS_TO_VIRT(segtab->base);
/* Setup interrupt moderation */
- imod = PTR_OFFSET(runtime, 0x24);
+ imod = PTR_OFFSET(runtime, XHCI_RT_IMOD);
mmio_write32(imod, XHCI_IMOD_DEFAULT);
/* Enable interrupts */
- iman = PTR_OFFSET(runtime, 0x20);
+ iman = PTR_OFFSET(runtime, XHCI_RT_IMAN);
tmp = mmio_read32(iman);
mmio_write32(iman, tmp | XHCI_IMAN_IE);
}
@@ -328,6 +340,13 @@ xhci_reset(struct xhci_hc *hc)
return error;
}
+ /* Wait longer if the xHC is not ready */
+ error = xhci_poll32(&opregs->usbsts, USBSTS_CNR, false);
+ if (error < 0) {
+ pr_error("xhci_reset: xHC ready wait timeout\n");
+ return error;
+ }
+
return 0;
}
@@ -365,13 +384,33 @@ xhci_start_hc(struct xhci_hc *hc)
/* Don't start up if we are already running */
usbcmd = mmio_read32(&opregs->usbcmd);
if (ISSET(usbcmd, USBCMD_RUN))
- return -EBUSY;
+ return 0;
usbcmd |= USBCMD_RUN;
mmio_write32(&opregs->usbcmd, usbcmd);
return 0;
}
+/*
+ * Stop and bring down the host controller.
+ * Returns 0 on success.
+ */
+static int
+xhci_stop_hc(struct xhci_hc *hc)
+{
+ struct xhci_opregs *opregs = hc->opregs;
+ uint32_t usbcmd;
+
+ /* Don't continue if we aren't running */
+ usbcmd = mmio_read32(&opregs->usbcmd);
+ if (!ISSET(usbcmd, USBCMD_RUN))
+ return 0;
+
+ usbcmd &= ~USBCMD_RUN;
+ mmio_write32(&opregs->usbcmd, usbcmd);
+ return 0;
+}
+
static int
xhci_init_ports(struct xhci_hc *hc)
{
@@ -381,6 +420,9 @@ xhci_init_ports(struct xhci_hc *hc)
for (size_t i = 1; i < hc->maxports; ++i) {
portsc_p = xhci_get_portsc(hc, i);
+ if (portsc_p == NULL) {
+ continue;
+ }
portsc = mmio_read32(portsc_p);
/*
@@ -414,6 +456,28 @@ xhci_init_hc(struct xhci_hc *hc)
uintptr_t dcbaap, cmdring;
struct xhci_caps *caps;
struct xhci_opregs *opregs;
+ const char *vendor;
+
+ /*
+ * The firmware on some Dell machines handle the
+ * xHCI BIOS/OS handoff very poorly. Updating the
+ * the OS semaphore in the USBLEGSUP register will
+ * result in the chipset firing off an SMI which is
+ * supposed to perform the actual handoff.
+ *
+ * However, Dell is stupid as always and the machine
+ * can get stuck in SMM which results in the machine
+ * locking up in a *very* bad way. In other words, the
+ * OS execution is literally halted and further SMIs like
+ * thermal, power, and fan events are deferred forever
+ * (no bueno!!). The best thing to do is to not perform
+ * a handoff if the host board is by Dell (bad Dell!!).
+ */
+ vendor = acpi_oemid();
+ if (memcmp(vendor, "DELL", 4) == 0) {
+ pr_trace("detected xhc handoff quirk\n");
+ hc->quirks |= XHCI_QUIRK_HANDOFF;
+ }
caps = (struct xhci_caps *)hc->base;
caplength = mmio_read8(&caps->caplength);
@@ -432,8 +496,15 @@ xhci_init_hc(struct xhci_hc *hc)
return -1;
}
+ pr_trace("stopping xHC chip...\n");
+ if ((error = xhci_stop_hc(hc)) != 0) {
+ pr_error("run/stop timeout\n");
+ return error;
+ }
+
pr_trace("resetting xHC chip...\n");
if ((error = xhci_reset(hc)) != 0) {
+ pr_error("reset timeout\n");
return error;
}
@@ -470,6 +541,16 @@ xhci_init_hc(struct xhci_hc *hc)
return 0;
}
+static void
+xhci_init_pci(void)
+{
+ uint32_t tmp;
+
+ tmp = pci_readl(hci_dev, PCIREG_CMDSTATUS);
+ tmp |= (PCI_BUS_MASTERING | PCI_MEM_SPACE);
+ pci_writel(hci_dev, PCIREG_CMDSTATUS, tmp);
+}
+
static int
xhci_init(void)
{
@@ -502,7 +583,8 @@ xhci_init(void)
return -ENODEV;
}
+ xhci_init_pci();
return xhci_init_hc(&xhc);
}
-DRIVER_EXPORT(xhci_init);
+DRIVER_EXPORT(xhci_init, "xhci");
diff --git a/sys/dev/video/fbdev.c b/sys/dev/video/fbdev.c
index f21c769..6b1c6c8 100644
--- a/sys/dev/video/fbdev.c
+++ b/sys/dev/video/fbdev.c
@@ -28,17 +28,65 @@
*/
#include <sys/types.h>
+#include <sys/errno.h>
#include <sys/limine.h>
+#include <sys/device.h>
+#include <sys/driver.h>
+#include <sys/fbdev.h>
#include <dev/video/fbdev.h>
+#include <fs/devfs.h>
+#include <fs/ctlfs.h>
+#include <vm/vm.h>
+#include <string.h>
#define FRAMEBUFFER \
framebuffer_req.response->framebuffers[0]
+static struct cdevsw fb_cdevsw;
+static const struct ctlops fb_size_ctl;
static volatile struct limine_framebuffer_request framebuffer_req = {
.id = LIMINE_FRAMEBUFFER_REQUEST,
.revision = 0
};
+static paddr_t
+fbdev_mmap(dev_t dev, size_t size, off_t off, int flags)
+{
+ size_t max_bounds;
+
+ max_bounds = FRAMEBUFFER->pitch * FRAMEBUFFER->height;
+ if ((off + size) > max_bounds) {
+ return 0;
+ }
+
+ return VIRT_TO_PHYS(FRAMEBUFFER->address);
+}
+
+static int
+ctl_attr_read(struct ctlfs_dev *cdp, struct sio_txn *sio)
+{
+ struct fbattr attr;
+ size_t len = sizeof(attr);
+
+ if (sio == NULL) {
+ return -EINVAL;
+ }
+ if (sio->buf == NULL) {
+ return -EINVAL;
+ }
+
+ if (len > sio->len) {
+ len = sio->len;
+ }
+
+ attr.width = FRAMEBUFFER->width;
+ attr.height = FRAMEBUFFER->height;
+ attr.pitch = FRAMEBUFFER->pitch;
+ attr.bpp = FRAMEBUFFER->bpp;
+ memcpy(sio->buf, &attr, len);
+ return len;
+}
+
struct fbdev
fbdev_get(void)
{
@@ -51,3 +99,40 @@ fbdev_get(void)
ret.bpp = FRAMEBUFFER->bpp;
return ret;
}
+
+static int
+fbdev_init(void)
+{
+ struct ctlfs_dev ctl;
+ char devname[] = "fb0";
+ devmajor_t major;
+ dev_t dev;
+
+ /* Register the device here */
+ major = dev_alloc_major();
+ dev = dev_alloc(major);
+ dev_register(major, dev, &fb_cdevsw);
+ devfs_create_entry(devname, major, dev, 0444);
+
+
+ /* Register control files */
+ ctl.mode = 0444;
+ ctlfs_create_node(devname, &ctl);
+ ctl.devname = devname;
+ ctl.ops = &fb_size_ctl;
+ ctlfs_create_entry("attr", &ctl);
+ return 0;
+}
+
+static struct cdevsw fb_cdevsw = {
+ .read = noread,
+ .write = nowrite,
+ .mmap = fbdev_mmap
+};
+
+static const struct ctlops fb_size_ctl = {
+ .read = ctl_attr_read,
+ .write = NULL,
+};
+
+DRIVER_EXPORT(fbdev_init, "fbdev");
diff --git a/sys/fs/ctlfs.c b/sys/fs/ctlfs.c
new file mode 100644
index 0000000..b86fa0a
--- /dev/null
+++ b/sys/fs/ctlfs.c
@@ -0,0 +1,440 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/vnode.h>
+#include <sys/device.h>
+#include <sys/syslog.h>
+#include <sys/mount.h>
+#include <sys/queue.h>
+#include <fs/ctlfs.h>
+#include <vm/dynalloc.h>
+#include <string.h>
+
+#define CTLFS_MPNAME "ctl"
+#define CTLFS_ENTRY_MAG 0x43454E54UL /* 'CENT' */
+#define CTLFS_NODE_MAG 0x43544C4EUL /* 'CTLN' */
+
+#define pr_trace(fmt, ...) kprintf("ctlfs: " fmt, ##__VA_ARGS__)
+#define pr_error(...) pr_trace(__VA_ARGS__)
+
+static const struct vops ctlfs_vops;
+
+struct ctlfs_hdr {
+ uint32_t magic;
+ char *name;
+};
+
+/*
+ * Control fs entry, represents a control
+ * file within a ctlfs node.
+ * -- HDR START --
+ * @magic: Magic number [MUST BE FIRST] (CTLFS_ENTRY_MAG)
+ * @name: Entry name [MUST BE SECOND]
+ * -- HDR END --
+ * @parent: Parent (ctlfs_node)
+ * @io: Ctlfs operations.
+ * @mode: Access flags.
+ * @link: TAILQ link.
+ */
+struct ctlfs_entry {
+ uint32_t magic;
+ char *name;
+ struct ctlfs_node *parent;
+ const struct ctlops *io;
+ mode_t mode;
+ TAILQ_ENTRY(ctlfs_entry) link;
+};
+
+/*
+ * Control fs node, represents a directory
+ * within ctlfs. These directories represent
+ * devices, each device directory contains
+ * control files.
+ *
+ * For example:
+ *
+ * /ctl/sd1/bsize # Block size
+ * /ctl/sd1/health # Health
+ * [et cetera]
+ *
+ * @magic: Magic number [MUST BE FIRST] (CTLFS_NODE_MAG)
+ * @name: Name of node [MUST BE SECOND]
+ * @mode: Access flags.
+ * @major: Device major number.
+ * @minor: Device major number.
+ * @eq: Entries for this ctlfs node.
+ */
+struct ctlfs_node {
+ uint32_t magic;
+ char *name;
+ mode_t mode;
+ TAILQ_HEAD(, ctlfs_entry) eq;
+ TAILQ_ENTRY(ctlfs_node) link;
+};
+
+static TAILQ_HEAD(, ctlfs_node) nodeq;
+
+/*
+ * Look up entries within a ctlfs
+ * node by name.
+ */
+static struct ctlfs_entry *
+entry_lookup(struct ctlfs_node *cnp, const char *name)
+{
+ struct ctlfs_entry *ep;
+
+ TAILQ_FOREACH(ep, &cnp->eq, link) {
+ if (strcmp(ep->name, name) == 0) {
+ return ep;
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * Lookup a ctlfs entry by name.
+ */
+static struct ctlfs_node *
+node_lookup(const char *name)
+{
+ struct ctlfs_node *cnp;
+
+ TAILQ_FOREACH(cnp, &nodeq, link) {
+ if (strcmp(cnp->name, name) == 0) {
+ return cnp;
+ }
+ }
+
+ return NULL;
+}
+
+static int
+ctlfs_init(struct fs_info *fip)
+{
+ struct vnode *vp;
+ struct mount *mp;
+ int error;
+
+ if ((error = vfs_alloc_vnode(&vp, VDIR)) != 0) {
+ pr_error("failed to alloc vnode\n");
+ return error;
+ }
+
+ vp->vops = &ctlfs_vops;
+ if ((mp = vfs_alloc_mount(vp, fip)) == NULL) {
+ pr_trace("failed to alloc mountpoint\n");
+ return -ENOMEM;
+ }
+
+ error = vfs_name_mount(mp, CTLFS_MPNAME);
+ if (error != 0) {
+ pr_trace("failed to mount @ /%s\n", CTLFS_MPNAME);
+ return error;
+ }
+
+ TAILQ_INSERT_TAIL(&g_mountlist, mp, mnt_list);
+ TAILQ_INIT(&nodeq);
+ return 0;
+}
+
+static int
+ctlfs_lookup(struct vop_lookup_args *args)
+{
+ int error;
+ const char *name = args->name;
+ struct vnode *vp, *dirvp;
+ struct ctlfs_node *cnp = NULL;
+ struct ctlfs_entry *enp = NULL;
+
+ if (name == NULL) {
+ return -EINVAL;
+ }
+
+ dirvp = args->dirvp;
+ if (dirvp == NULL) {
+ return -EIO;
+ }
+
+ /*
+ * If we already have data within this vnode
+ * it *might* be a control node but we'll have
+ * to verify its magic number...
+ */
+ if (dirvp->data != NULL) {
+ cnp = (struct ctlfs_node *)dirvp->data;
+ if (cnp->magic != CTLFS_NODE_MAG) {
+ pr_error("bad `cnp' magic (name=%s)\n", name);
+ return -EIO;
+ }
+ }
+
+ /*
+ * Handle cases where we are looking up
+ * relative to a control node.
+ */
+ if (cnp != NULL) {
+ enp = entry_lookup(cnp, name);
+ if (enp == NULL) {
+ return -ENOENT;
+ }
+
+ /* Create a vnode for this enp */
+ error = vfs_alloc_vnode(&vp, VCHR);
+ if (error != 0) {
+ return error;
+ }
+
+ vp->data = (void *)enp;
+ vp->vops = &ctlfs_vops;
+ *args->vpp = vp;
+ return 0;
+ }
+
+ /* Does this entry exist? */
+ if ((cnp = node_lookup(name)) == NULL) {
+ return -ENOENT;
+ }
+
+ if ((error = vfs_alloc_vnode(&vp, VDIR)) != 0) {
+ return error;
+ }
+
+ vp->data = cnp;
+ vp->vops = &ctlfs_vops;
+ *args->vpp = vp;
+ return 0;
+}
+
+static int
+ctlfs_get_ops(struct vnode *vp, struct ctlfs_entry **enpres,
+ const struct ctlops **iopres)
+{
+ const struct ctlops *iop;
+ struct ctlfs_entry *enp;
+
+ if (enpres == NULL || iopres == NULL) {
+ return -EINVAL;
+ }
+
+ if ((enp = vp->data) == NULL) {
+ pr_error("no vnode data for ctlfs entry\n");
+ return -EIO;
+ }
+ if (enp->magic != CTLFS_ENTRY_MAG) {
+ pr_error("ctlfs entry has bad magic\n");
+ return -EIO;
+ }
+ if ((iop = enp->io) == NULL) {
+ pr_error("no i/o ops for ctlfs entry\n");
+ return -EIO;
+ }
+
+ *enpres = enp;
+ *iopres = iop;
+ return 0;
+}
+
+/*
+ * Create a ctlfs node (directory) within the
+ * root fs.
+ *
+ * @name: Node name (e.g., "sd1" for "/ctl/sd1/")
+ * @dp: Device related arguments (see ctlfs_dev)
+ * Args used:
+ * - mode (access flags)
+ *
+ */
+int
+ctlfs_create_node(const char *name, const struct ctlfs_dev *dp)
+{
+ struct ctlfs_node *cnp;
+ size_t namelen;
+
+ if (name == NULL || dp == NULL) {
+ return -EINVAL;
+ }
+
+ cnp = dynalloc(sizeof(*cnp));
+ if (cnp == NULL) {
+ return -ENOMEM;
+ }
+
+ namelen = strlen(name);
+ cnp->name = dynalloc(namelen + 1);
+ if (cnp->name == NULL) {
+ dynfree(cnp);
+ return -ENOMEM;
+ }
+
+ memcpy(cnp->name, name, namelen);
+ cnp->name[namelen] = '\0';
+ cnp->mode = dp->mode;
+ cnp->magic = CTLFS_NODE_MAG;
+ TAILQ_INSERT_TAIL(&nodeq, cnp, link);
+ TAILQ_INIT(&cnp->eq);
+ return 0;
+}
+
+/*
+ * Create a ctlfs entry within a specific node.
+ *
+ * @name: Name e.g., "/health" for "/ctl/xxx/health".
+ * @dp: Device related arguments (see ctlfs_dev)
+ * Args used:
+ * - devname (name of device)
+ * - mode (access flags)
+ * - ops (operations vector)
+ */
+int
+ctlfs_create_entry(const char *name, const struct ctlfs_dev *dp)
+{
+ struct ctlfs_entry *enp;
+ struct ctlfs_node *parent;
+ size_t namelen;
+
+ if (name == NULL || dp == NULL) {
+ return -EINVAL;
+ }
+ if (dp->devname == NULL) {
+ return -EINVAL;
+ }
+ if (dp->ops == NULL) {
+ return -EINVAL;
+ }
+
+ parent = node_lookup(dp->devname);
+ if (parent == NULL) {
+ pr_trace("could not find %s\n", dp->devname);
+ return -ENOENT;
+ }
+
+ enp = dynalloc(sizeof(*enp));
+ if (enp == NULL) {
+ return -ENOMEM;
+ }
+
+ namelen = strlen(name);
+ enp->name = dynalloc(namelen + 1);
+ if (enp->name == NULL) {
+ dynfree(enp);
+ return -ENOMEM;
+ }
+
+ memcpy(enp->name, name, namelen);
+ enp->name[namelen] = '\0';
+ enp->io = dp->ops;
+ enp->magic = CTLFS_ENTRY_MAG;
+ enp->mode = dp->mode;
+ enp->parent = parent;
+ TAILQ_INSERT_TAIL(&parent->eq, enp, link);
+ return 0;
+}
+
+/*
+ * Read a control file
+ *
+ * Args passed to driver:
+ * - ctlfs_dev.ctlname
+ * - ctlfs_dev.iop
+ * - ctlfs_dev.mode
+ */
+static int
+ctlfs_read(struct vnode *vp, struct sio_txn *sio)
+{
+ const struct ctlops *iop;
+ struct ctlfs_entry *enp;
+ struct ctlfs_dev dev;
+ int error;
+
+ if ((error = ctlfs_get_ops(vp, &enp, &iop)) < 0) {
+ return error;
+ }
+ if (iop->read == NULL) {
+ pr_trace("no read op for ctlfs entry\n");
+ return -EIO;
+ }
+
+ dev.ctlname = enp->name;
+ dev.ops = iop;
+ dev.mode = enp->mode;
+ return iop->read(&dev, sio);
+}
+
+/*
+ * Write a control file
+ *
+ * Args passed to driver:
+ * - ctlfs_dev.ctlname
+ * - ctlfs_dev.iop
+ * - ctlfs_dev.mode
+ */
+static int
+ctlfs_write(struct vnode *vp, struct sio_txn *sio)
+{
+ const struct ctlops *iop;
+ struct ctlfs_entry *enp;
+ struct ctlfs_dev dev;
+ int error;
+
+ if ((error = ctlfs_get_ops(vp, &enp, &iop)) < 0) {
+ return error;
+ }
+ if (iop->write == NULL) {
+ pr_trace("no write op for ctlfs entry\n");
+ return -EIO;
+ }
+
+ dev.ctlname = enp->name;
+ dev.ops = iop;
+ dev.mode = enp->mode;
+ return iop->write(&dev, sio);
+}
+
+static int
+ctlfs_reclaim(struct vnode *vp)
+{
+ vp->data = NULL;
+ return 0;
+}
+
+static const struct vops ctlfs_vops = {
+ .lookup = ctlfs_lookup,
+ .read = ctlfs_read,
+ .getattr = NULL,
+ .write = ctlfs_write,
+ .reclaim = ctlfs_reclaim,
+ .create = NULL
+};
+
+const struct vfsops g_ctlfs_vfsops = {
+ .init = ctlfs_init
+};
diff --git a/sys/fs/devfs.c b/sys/fs/devfs.c
index 024239d..3bd1b11 100644
--- a/sys/fs/devfs.c
+++ b/sys/fs/devfs.c
@@ -30,6 +30,8 @@
#include <sys/types.h>
#include <sys/vnode.h>
#include <sys/errno.h>
+#include <sys/stat.h>
+#include <sys/syslog.h>
#include <sys/mount.h>
#include <sys/device.h>
#include <fs/devfs.h>
@@ -37,6 +39,7 @@
#include <string.h>
struct devfs_node {
+ struct devstat stat;
char *name;
uint8_t is_block : 1;
mode_t mode;
@@ -126,6 +129,8 @@ devfs_lookup(struct vop_lookup_args *args)
vp->data = dnp;
vp->vops = &g_devfs_vops;
+ vp->major = dnp->major;
+ vp->dev = dnp->dev;
*args->vpp = vp;
return 0;
}
@@ -136,6 +141,8 @@ devfs_getattr(struct vop_getattr_args *args)
struct vnode *vp;
struct vattr *attr;
struct devfs_node *dnp;
+ struct bdevsw *bdev;
+ size_t size = 0;
vp = args->vp;
if ((dnp = vp->data) == NULL) {
@@ -145,6 +152,13 @@ devfs_getattr(struct vop_getattr_args *args)
return -EIO;
}
+ if (dnp->is_block) {
+ bdev = dev_get(dnp->major, dnp->dev);
+ if (bdev->bsize != NULL) {
+ size = bdev->bsize(dnp->dev);
+ }
+ }
+
/*
* Set stat attributes from device node structure
* found within vnode data.
@@ -153,20 +167,13 @@ devfs_getattr(struct vop_getattr_args *args)
* size is hardwired to 0.
*/
attr->mode = dnp->mode;
- attr->size = 0;
+ attr->size = size;
return 0;
}
static int
devfs_reclaim(struct vnode *vp)
{
- struct devfs_node *dnp;
-
- if ((dnp = vp->data) != NULL) {
- dynfree(dnp->name);
- dynfree(vp->data);
- }
-
vp->data = NULL;
return 0;
}
@@ -175,6 +182,7 @@ static int
devfs_read(struct vnode *vp, struct sio_txn *sio)
{
struct devfs_node *dnp;
+ struct devstat *statp;
void *devsw;
if ((dnp = vp->data) == NULL)
@@ -185,6 +193,9 @@ devfs_read(struct vnode *vp, struct sio_txn *sio)
if (!dnp->is_block)
return cdevsw_read(devsw, dnp->dev, sio);
+ statp = &dnp->stat;
+ ++statp->nreads;
+
/* Block device */
return bdevsw_read(devsw, dnp->dev, sio);
}
@@ -193,6 +204,7 @@ static int
devfs_write(struct vnode *vp, struct sio_txn *sio)
{
struct devfs_node *dnp;
+ struct devstat *statp;
void *devsw;
if ((dnp = vp->data) == NULL)
@@ -204,6 +216,9 @@ devfs_write(struct vnode *vp, struct sio_txn *sio)
return cdevsw_write(devsw, dnp->dev, sio);
}
+ statp = &dnp->stat;
+ ++statp->nwrites;
+
/* Block device */
return bdevsw_write(devsw, dnp->dev, sio);
}
@@ -228,6 +243,24 @@ devfs_init(struct fs_info *fip)
return 0;
}
+int
+devfs_devstat(struct vnode *vp, struct devstat *res)
+{
+ struct devfs_node *dnp;
+
+ if ((dnp = vp->data) == NULL) {
+ return -EIO;
+ }
+
+ /* Not supported on char devices */
+ if (!dnp->is_block) {
+ return -ENOTSUP;
+ }
+
+ *res = dnp->stat;
+ return 0;
+}
+
/*
* Create an entry within devfs.
*
@@ -240,6 +273,7 @@ int
devfs_create_entry(const char *name, devmajor_t major, dev_t dev, mode_t mode)
{
struct devfs_node *dnp;
+ struct devstat *statp;
size_t name_len;
dnp = dynalloc(sizeof(*dnp));
@@ -253,9 +287,13 @@ devfs_create_entry(const char *name, devmajor_t major, dev_t dev, mode_t mode)
return -ENOMEM;
}
+ statp = &dnp->stat;
+ statp->nwrites = 0;
+ statp->nreads = 0;
+
memcpy(dnp->name, name, name_len);
dnp->name[name_len] = '\0';
-
+ dnp->is_block = ISSET(mode, S_IFBLK) ? 1 : 0;
dnp->major = major;
dnp->dev = dev;
dnp->mode = mode;
@@ -268,7 +306,8 @@ const struct vops g_devfs_vops = {
.reclaim = devfs_reclaim,
.read = devfs_read,
.write = devfs_write,
- .getattr = devfs_getattr
+ .getattr = devfs_getattr,
+ .create = NULL
};
const struct vfsops g_devfs_vfsops = {
diff --git a/sys/fs/initramfs.c b/sys/fs/initramfs.c
index fd746ef..beb2e84 100644
--- a/sys/fs/initramfs.c
+++ b/sys/fs/initramfs.c
@@ -33,12 +33,16 @@
#include <sys/errno.h>
#include <sys/limine.h>
#include <sys/panic.h>
+#include <sys/param.h>
#include <sys/vnode.h>
#include <fs/initramfs.h>
#include <vm/dynalloc.h>
#include <string.h>
-#define CPIO_TRAILER "TRAILER!!!"
+#define OMAR_EOF "RAMO"
+#define OMAR_REG 0
+#define OMAR_DIR 1
+#define BLOCK_SIZE 512
/*
* File or directory.
@@ -51,20 +55,22 @@ struct initramfs_node {
};
/*
- * ODC CPIO header
+ * The OMAR file header, describes the basics
+ * of a file.
+ *
+ * @magic: Header magic ("OMAR")
+ * @len: Length of the file
+ * @namelen: Length of the filename
+ * @rev: OMAR revision
+ * @mode: File permissions
*/
-struct cpio_hdr {
- char c_magic[6];
- char c_dev[6];
- char c_ino[6];
- char c_mode[6];
- char c_uid[6];
- char c_gid[6];
- char c_nlink[6];
- char c_rdev[6];
- char c_mtime[11];
- char c_namesize[6];
- char c_filesize[11];
+struct __packed omar_hdr {
+ char magic[4];
+ uint8_t type;
+ uint8_t namelen;
+ uint32_t len;
+ uint8_t rev;
+ uint32_t mode;
};
static volatile struct limine_module_request mod_req = {
@@ -92,21 +98,6 @@ get_module(const char *path, uint64_t *size) {
}
/*
- * Convert octal to base 10
- */
-static uint32_t
-oct2dec(const char *in, size_t sz)
-{
- size_t val = 0;
-
- for (size_t i = 0; i < sz; ++i) {
- val = val * 8 + (in[i] - '0');
- }
-
- return val;
-}
-
-/*
* Get a file from initramfs
*
* @path: Path of file to get.
@@ -115,41 +106,54 @@ oct2dec(const char *in, size_t sz)
static int
initramfs_get_file(const char *path, struct initramfs_node *res)
{
- const struct cpio_hdr *hdr;
struct initramfs_node node;
- uintptr_t addr;
- size_t namesize, filesize;
- mode_t mode;
+ const struct omar_hdr *hdr;
+ const char *p, *name;
+ char namebuf[256];
+ off_t off;
- addr = (uintptr_t)initramfs;
+ p = initramfs;
for (;;) {
- hdr = (void *)addr;
- namesize = oct2dec(hdr->c_namesize, sizeof(hdr->c_namesize));
- filesize = oct2dec(hdr->c_filesize, sizeof(hdr->c_filesize));
- mode = oct2dec(hdr->c_mode, sizeof(hdr->c_mode));
+ hdr = (struct omar_hdr *)p;
+ if (strncmp(hdr->magic, OMAR_EOF, sizeof(OMAR_EOF)) == 0) {
+ break;
+ }
- /* Make sure the magic is correct */
- if (strncmp(hdr->c_magic, "070707", 6) != 0) {
+ /* Ensure the file is valid */
+ if (strncmp(hdr->magic, "OMAR", 4) != 0) {
+ /* Bad magic */
+ return -EINVAL;
+ }
+ if (hdr->namelen > sizeof(namebuf) - 1) {
return -EINVAL;
}
- addr += sizeof(struct cpio_hdr);
- node.path = (const char *)addr;
+ name = (char *)p + sizeof(struct omar_hdr);
+ memcpy(namebuf, name, hdr->namelen);
+ namebuf[hdr->namelen] = '\0';
- /* Is this the requested file? */
- if (strcmp(node.path, path) == 0) {
- node.data = (void *)(addr + namesize);
- node.size = filesize;
- node.mode = mode;
+ /* Compute offset to next block */
+ if (hdr->type == OMAR_DIR) {
+ off = 512;
+ } else {
+ off = ALIGN_UP(sizeof(*hdr) + hdr->namelen + hdr->len, BLOCK_SIZE);
+ }
+
+ /* Skip header and name, right to the data */
+ p = (char *)hdr + sizeof(struct omar_hdr);
+ p += hdr->namelen;
+
+ if (strcmp(namebuf, path) == 0) {
+ node.mode = hdr->mode;
+ node.size = hdr->len;
+ node.data = (void *)p;
*res = node;
return 0;
}
- /* Get next header and see if we are at the end */
- addr += (namesize + filesize);
- if (strcmp(node.path, CPIO_TRAILER) == 0) {
- break;
- }
+ hdr = (struct omar_hdr *)((char *)hdr + off);
+ p = (char *)hdr;
+ memset(namebuf, 0, sizeof(namebuf));
}
return -ENOENT;
@@ -223,6 +227,8 @@ initramfs_read(struct vnode *vp, struct sio_txn *sio)
return -EIO;
if (sio->buf == NULL)
return -EIO;
+ if (sio->len > n->size)
+ sio->len = n->size;
src = n->data;
dest = sio->buf;
@@ -256,9 +262,9 @@ initramfs_init(struct fs_info *fip)
struct mount *mp;
int status;
- initramfs = get_module("/boot/ramfs.cpio", &initramfs_size);
+ initramfs = get_module("/boot/ramfs.omar", &initramfs_size);
if (initramfs == NULL) {
- panic("failed to open initramfs cpio image\n");
+ panic("failed to open initramfs OMAR image\n");
}
status = vfs_alloc_vnode(&g_root_vnode, VDIR);
@@ -277,7 +283,8 @@ const struct vops g_initramfs_vops = {
.read = initramfs_read,
.write = NULL,
.reclaim = initramfs_reclaim,
- .getattr = initramfs_getattr
+ .getattr = initramfs_getattr,
+ .create = NULL,
};
const struct vfsops g_initramfs_vfsops = {
diff --git a/sys/fs/tmpfs.c b/sys/fs/tmpfs.c
new file mode 100644
index 0000000..4fd9e85
--- /dev/null
+++ b/sys/fs/tmpfs.c
@@ -0,0 +1,430 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/mount.h>
+#include <sys/errno.h>
+#include <sys/syslog.h>
+#include <sys/types.h>
+#include <sys/cdefs.h>
+#include <sys/param.h>
+#include <sys/panic.h>
+#include <sys/vnode.h>
+#include <vm/dynalloc.h>
+#include <vm/vm_obj.h>
+#include <vm/vm_page.h>
+#include <vm/vm_pager.h>
+#include <fs/tmpfs.h>
+#include <string.h>
+
+#define ROOT_RPATH "/tmp"
+#define TMPFS_BSIZE DEFAULT_PAGESIZE
+
+#define pr_trace(fmt, ...) kprintf("tmpfs: " fmt, ##__VA_ARGS__)
+#define pr_error(...) pr_trace(__VA_ARGS__)
+
+static TAILQ_HEAD(, tmpfs_node) root;
+
+/*
+ * Generate a vnode for a specific tmpfs
+ * node.
+ */
+static int
+tmpfs_ref(struct tmpfs_node *np)
+{
+ struct vnode *vp = NULL;
+ int retval = 0;
+
+ if (np->vp == NULL) {
+ spinlock_acquire(&np->lock);
+ retval = vfs_alloc_vnode(&vp, np->type);
+ np->vp = vp;
+ spinlock_release(&np->lock);
+ }
+
+ if (vp != NULL) {
+ vp->data = np;
+ vp->vops = &g_tmpfs_vops;
+ }
+
+ return retval;
+}
+
+/*
+ * Perform lookup within the tmpfs namespace
+ *
+ * XXX: This operations is serialized
+ * TODO: Support multiple directories (only fs root now)
+ *
+ * @rpath: /tmp/ relative path to lookup
+ * @res: The result is written here (must NOT be NULL)
+ */
+static int
+tmpfs_do_lookup(const char *rpath, struct tmpfs_node **res)
+{
+ struct tmpfs_node *cnp;
+ struct tmpfs_node *dirent;
+ int error = 0;
+
+ /*
+ * If the directory is the node that we are
+ * looking for, return it. But if it is not
+ * and it is empty then there is nothing
+ * we can do.
+ */
+ cnp = TAILQ_FIRST(&root);
+ if (strcmp(cnp->rpath, rpath) == 0) {
+ *res = cnp;
+ return 0;
+ }
+ if (TAILQ_NELEM(&cnp->dirents) == 0) {
+ return -ENOENT;
+ }
+
+ /*
+ * Go through each tmpfs dirent to see if we can
+ * find the file we are looking for.
+ */
+ spinlock_acquire(&cnp->lock);
+ dirent = TAILQ_FIRST(&cnp->dirents);
+ while (dirent != NULL) {
+ if (strcmp(dirent->rpath, rpath) == 0) {
+ break;
+ }
+
+ dirent = TAILQ_NEXT(dirent, link);
+ }
+
+ spinlock_release(&cnp->lock);
+ if (dirent == NULL) {
+ return -ENOENT;
+ }
+
+ if ((error = tmpfs_ref(dirent)) != 0) {
+ return error;
+ }
+
+ *res = dirent;
+ return 0;
+}
+
+/*
+ * TMPFS lookup callback for the VFS
+ *
+ * Takes some arguments and returns a vnode
+ * in args->vpp
+ */
+static int
+tmpfs_lookup(struct vop_lookup_args *args)
+{
+ struct tmpfs_node *np;
+ int error;
+
+ if (args == NULL) {
+ return -EINVAL;
+ }
+ if (args->name == NULL) {
+ return -EINVAL;
+ }
+
+ /*
+ * Attempt to find the node we want, if it already
+ * has a vnode attached to it then that's something we
+ * want. However we should allocate a new vnode if we
+ * need to.
+ */
+ error = tmpfs_do_lookup(args->name, &np);
+ if (error != 0) {
+ return error;
+ }
+
+ *args->vpp = np->vp;
+ return 0;
+}
+
+/*
+ * TMPFS create callback for the VFS
+ *
+ * Creates a new TMPFS node
+ */
+static int
+tmpfs_create(struct vop_create_args *args)
+{
+ const char *pcp = args->path; /* Stay away from boat, kids */
+ struct vnode *dirvp;
+ struct tmpfs_node *np;
+ struct tmpfs_node *root_np;
+ int error;
+
+ /* Validate inputs */
+ if (args == NULL)
+ return -EINVAL;
+ if (pcp == NULL)
+ return -EIO;
+ if ((dirvp = args->dirvp) == NULL)
+ return -EIO;
+
+ /* Remove the leading "/tmp/" */
+ pcp += sizeof(ROOT_RPATH);
+ if (*pcp == '\0') {
+ return -ENOENT;
+ }
+
+ np = dynalloc(sizeof(*np));
+ if (np == NULL) {
+ return -ENOMEM;
+ }
+
+ memset(np, 0, sizeof(*np));
+
+ /*
+ * TODO: Support multiple directories.
+ *
+ * XXX: We currently only create a TMPFS_REG node as
+ * to keep things initially simple.
+ */
+ root_np = TAILQ_FIRST(&root);
+ np->dirvp = dirvp;
+ np->type = TMPFS_REG;
+ np->real_size = 0;
+ np->mode = 0700;
+ memcpy(np->rpath, pcp, strlen(pcp) + 1);
+ TAILQ_INSERT_TAIL(&root_np->dirents, np, link);
+
+ if ((error = tmpfs_ref(np)) != 0) {
+ return error;
+ }
+
+ *args->vpp = np->vp;
+ return 0;
+}
+
+/*
+ * TMPFS write callback for VFS
+ *
+ * Node buffers are orthogonally managed. That is, each
+ * node has their own respective data buffers. When
+ * writing to a node, we need to take into account of the
+ * length of the buffer. This value may need to expanded as
+ * well as more pages allocated if the amount of bytes to
+ * be written exceeds it.
+ */
+static int
+tmpfs_write(struct vnode *vp, struct sio_txn *sio)
+{
+ struct tmpfs_node *np;
+ off_t res_off;
+ uint8_t *buf;
+
+ if (sio->buf == NULL || sio->len == 0) {
+ return -EINVAL;
+ }
+
+ /* This should not happen but you never know */
+ if ((np = vp->data) == NULL) {
+ return -EIO;
+ }
+
+ /* Is this even a regular file? */
+ if (np->type != VREG) {
+ return -EISDIR;
+ }
+
+ spinlock_acquire(&np->lock);
+
+ /*
+ * If the residual byte count is zero, we need to
+ * allocate a new page to be used. However if this
+ * fails we'll throw back an -ENOMEM.
+ */
+ if (np->len == 0) {
+ np->data = dynalloc(TMPFS_BSIZE);
+ if (np->data == NULL) {
+ spinlock_release(&np->lock);
+ return -ENOMEM;
+ }
+ np->len += TMPFS_BSIZE;
+ }
+
+ /*
+ * Bring up the real size if we are writing
+ * more bytes.
+ */
+ res_off = sio->offset + sio->len;
+ if (res_off > np->real_size) {
+ np->real_size = res_off;
+ }
+
+ /*
+ * If the length to be written exceeds the residual byte
+ * count. We will try to expand the buffer by the page
+ * size. However, if this fails, we will split the write
+ * into a suitable size that does not overflow what we
+ * have left.
+ */
+ if (res_off > np->len) {
+ np->data = dynrealloc(np->data, (sio->offset + sio->len));
+ if (np->data == NULL) {
+ sio->len = np->len;
+ } else {
+ np->len = sio->offset + sio->len;
+ }
+ }
+
+ buf = np->data;
+ memcpy(&buf[sio->offset], sio->buf, sio->len);
+ spinlock_release(&np->lock);
+ return sio->len;
+}
+
+/*
+ * TMPFS read callback for VFS
+ */
+static int
+tmpfs_read(struct vnode *vp, struct sio_txn *sio)
+{
+ struct tmpfs_node *np;
+ uint8_t *buf;
+
+ if (sio->buf == NULL || sio->len == 0) {
+ return -EINVAL;
+ }
+
+ /* This should not happen but you never know */
+ if ((np = vp->data) == NULL) {
+ return -EIO;
+ }
+
+ /*
+ * The node data is only allocated during writes, if
+ * we read this file before a write was ever done to it,
+ * np->data will be NULL. We must handle this.
+ */
+ if (np->data == NULL) {
+ return 0;
+ }
+
+ /* Is this even a regular file? */
+ if (np->type != VREG) {
+ return -EISDIR;
+ }
+
+ spinlock_acquire(&np->lock);
+
+ if (sio->offset > np->real_size) {
+ return -EINVAL;
+ }
+
+ buf = np->data;
+ memcpy(sio->buf, &buf[sio->offset], sio->len);
+ spinlock_release(&np->lock);
+ return sio->len;
+}
+
+/*
+ * TMPFS get attribute callback for VFS
+ */
+static int
+tmpfs_getattr(struct vop_getattr_args *args)
+{
+ struct vnode *vp;
+ struct tmpfs_node *np;
+ struct vattr attr;
+
+ if ((vp = args->vp) == NULL) {
+ return -EIO;
+ }
+ if ((np = vp->data) == NULL) {
+ return -EIO;
+ }
+
+ memset(&attr, VNOVAL, sizeof(attr));
+ attr.size = np->real_size;
+ attr.mode = np->mode;
+ *args->res = attr;
+ return 0;
+}
+
+static int
+tmpfs_reclaim(struct vnode *vp)
+{
+ struct tmpfs_node *np;
+
+ if ((np = vp->data) == NULL) {
+ return 0;
+ }
+
+ np->vp = NULL;
+ return 0;
+}
+
+static int
+tmpfs_init(struct fs_info *fip)
+{
+ struct tmpfs_node *np;
+ struct vnode *vp;
+ struct mount *mp;
+ int error;
+
+ /* Grab ourselves a new vnode for /tmp */
+ if ((error = vfs_alloc_vnode(&vp, VDIR)) != 0) {
+ return error;
+ }
+
+ vp->vops = &g_tmpfs_vops;
+ mp = vfs_alloc_mount(vp, fip);
+ vfs_name_mount(mp, "tmp");
+ TAILQ_INSERT_TAIL(&g_mountlist, mp, mnt_list);
+
+ /* Pre-allocate the first entry */
+ if ((np = dynalloc(sizeof(*np))) == NULL) {
+ return -ENOMEM;
+ }
+
+ TAILQ_INIT(&root);
+ memset(np, 0, sizeof(*np));
+
+ memcpy(np->rpath, ROOT_RPATH, sizeof(ROOT_RPATH));
+ np->type = TMPFS_DIR;
+ TAILQ_INIT(&np->dirents);
+ TAILQ_INSERT_TAIL(&root, np, link);
+ return 0;
+}
+
+const struct vops g_tmpfs_vops = {
+ .lookup = tmpfs_lookup,
+ .getattr = tmpfs_getattr,
+ .read = tmpfs_read,
+ .write = tmpfs_write,
+ .reclaim = tmpfs_reclaim,
+ .create = tmpfs_create,
+};
+
+const struct vfsops g_tmpfs_vfsops = {
+ .init = tmpfs_init
+};
diff --git a/sys/include/arch/aarch64/board.h b/sys/include/arch/aarch64/board.h
new file mode 100644
index 0000000..bba421f
--- /dev/null
+++ b/sys/include/arch/aarch64/board.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _MACHINE_BOARD_H_
+#define _MACHINE_BOARD_H_
+
+/* Board implementer */
+#define BOARD_ARM_LIMITED 0x41 /* ARM Limited */
+#define BOARD_BROADCOM 0x42 /* Broadcom corp */
+#define BOARD_CAVIUM 0x43 /* Calvium Inc */
+#define BOARD_DIGITAL_EQUIP 0x44 /* Digital Equipment Corporation */
+#define BOARD_FUJITSU 0x46 /* Fujitsu Ltd */
+
+/*
+ * Board information, contains a part number
+ * and an implementer number.
+ */
+struct board_info {
+ uint8_t implementer;
+ uint16_t partno : 12;
+};
+
+void md_get_board(struct board_info *res);
+
+#endif /* !_MACHINE_BOARD_H_ */
diff --git a/sys/include/arch/aarch64/cdefs.h b/sys/include/arch/aarch64/cdefs.h
index a22c436..aaf8649 100644
--- a/sys/include/arch/aarch64/cdefs.h
+++ b/sys/include/arch/aarch64/cdefs.h
@@ -36,5 +36,6 @@
#define md_pause() __ASMV("yield")
#define md_intoff() __ASMV("msr daifset, #2")
#define md_inton() __ASMV("msr daifclr, #2")
+#define md_hlt() __ASMV("hlt #0")
#endif /* !_AARCH64_CDEFS_H_ */
diff --git a/sys/include/arch/aarch64/cpu.h b/sys/include/arch/aarch64/cpu.h
index a6ccdec..8c2d837 100644
--- a/sys/include/arch/aarch64/cpu.h
+++ b/sys/include/arch/aarch64/cpu.h
@@ -39,7 +39,10 @@ struct cpu_info {
struct cpu_info *self;
};
+__dead void cpu_halt_all(void);
void cpu_startup(struct cpu_info *ci);
+void cpu_halt_others(void);
+
void mp_bootstrap_aps(struct cpu_info *ci);
struct cpu_info *this_cpu(void);
diff --git a/sys/include/arch/aarch64/exception.h b/sys/include/arch/aarch64/exception.h
new file mode 100644
index 0000000..9e89c81
--- /dev/null
+++ b/sys/include/arch/aarch64/exception.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _MACHINE_EXCEPTION_H_
+#define _MACHINE_EXCEPTION_H_
+
+#include <sys/types.h>
+#include <machine/frame.h>
+
+/* Exception class */
+#define EC_UNKNOWN 0x00 /* Unknown type */
+#define EC_WF 0x01 /* Trapped WF instruction */
+#define EC_MCRMRC 0x03 /* Trapped MCR/MRC */
+#define EC_MCRRC 0x04 /* Trapped MCRR/MRRC */
+#define EC_LDCSTC 0x06 /* Trapped LDC/STC */
+#define EC_SVE 0x07 /* Trapped SVE/SIMD/FP op */
+#define EC_BRE 0x0D /* Branch target exception */
+#define EC_ILLX 0x0E /* Illegal execution state */
+#define EC_SVC64 0x15 /* AARCH64 SVC */
+#define EC_PCALIGN 0x22 /* PC alignment fault */
+#define EC_DABORT 0x24 /* Data abort (w/o ELx change) */
+#define EC_EDABORT 0x25 /* Data abort (w/ ELx change) */
+#define EC_SPALIGN 0x26 /* SP alignment fault */
+#define EC_SERR 0x2F /* System error (what the fuck!) */
+
+void handle_exception(struct trapframe *tf);
+
+#endif /* !_MACHINE_EXCEPTION_H_ */
diff --git a/sys/include/arch/aarch64/frame.h b/sys/include/arch/aarch64/frame.h
index ec11533..143f4d0 100644
--- a/sys/include/arch/aarch64/frame.h
+++ b/sys/include/arch/aarch64/frame.h
@@ -31,43 +31,10 @@
#define _MACHINE_FRAME_H_
#include <sys/types.h>
+#include <sys/cdefs.h>
typedef uint64_t lreg_t;
-
-/* General purpose registers */
-struct gpregs {
- lreg_t x0;
- lreg_t x1;
- lreg_t x2;
- lreg_t x3;
- lreg_t x4;
- lreg_t x5;
- lreg_t x6;
- lreg_t x7;
- lreg_t x8;
- lreg_t x9;
- lreg_t x10;
- lreg_t x11;
- lreg_t x12;
- lreg_t x13;
- lreg_t x14;
- lreg_t x15;
- lreg_t x16;
- lreg_t x17;
- lreg_t x18;
- lreg_t x19;
- lreg_t x20;
- lreg_t x21;
- lreg_t x22;
- lreg_t x23;
- lreg_t x24;
- lreg_t x25;
- lreg_t x26;
- lreg_t x27;
- lreg_t x28;
- lreg_t x29;
- lreg_t x30;
-};
+typedef uint64_t frament_t;
/* Stack regs */
struct sregs {
@@ -83,14 +50,43 @@ struct pstat {
lreg_t spsr_el3;
};
-struct trapframe {
- struct gpregs gp;
- struct sregs stack;
- struct pstat status;
- lreg_t elr_el1;
- lreg_t elr_el2;
- lreg_t elr_el3;
- lreg_t pc;
+struct __aligned(16) trapframe {
+ lreg_t x30;
+ lreg_t x29;
+ lreg_t x28;
+ lreg_t x27;
+ lreg_t x26;
+ lreg_t x25;
+ lreg_t x24;
+ lreg_t x23;
+ lreg_t x22;
+ lreg_t x21;
+ lreg_t x20;
+ lreg_t x19;
+ lreg_t x18;
+ lreg_t x17;
+ lreg_t x16;
+ lreg_t x15;
+ lreg_t x14;
+ lreg_t x13;
+ lreg_t x12;
+ lreg_t x11;
+ lreg_t x10;
+ lreg_t x9;
+ lreg_t x8;
+ lreg_t x7;
+ lreg_t x6;
+ lreg_t x5;
+ lreg_t x4;
+ lreg_t x3;
+ lreg_t x2;
+ lreg_t x1;
+ lreg_t x0;
+ lreg_t elr;
+ lreg_t esr;
+ frament_t trapno;
};
+#define TF_IP(TFP) ((TFP)->pc)
+
#endif /* !_MACHINE_FRAME_H_ */
diff --git a/sys/include/arch/aarch64/frameasm.h b/sys/include/arch/aarch64/frameasm.h
new file mode 100644
index 0000000..ca7f81a
--- /dev/null
+++ b/sys/include/arch/aarch64/frameasm.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _MACHINE_FRAMEASM_H_
+#define _MACHINE_FRAMEASM_H_
+
+/* XXX: Must be 16-byte aligned!!! */
+#define XFRAME_STACK_SIZE (38 * 8)
+
+/* Trap numbers */
+#define TRAPNO_UNKNOWN #0
+#define TRAPNO_XSYNC #1 /* Synchronous */
+#define TRAPNO_XIRQ #2 /* IRQ */
+#define TRAPNO_XFIQ #3 /* FIQ */
+#define TRAPNO_XSERR #4 /* System error */
+
+#define PUSH_XFRAME(TRAPNO) \
+ sub sp, sp, #XFRAME_STACK_SIZE ; \
+ stp x30, x29, [sp, #(0 * 8)] ; \
+ stp x28, x27, [sp, #(2 * 8)] ; \
+ stp x26, x25, [sp, #(4 * 8)] ; \
+ stp x24, x23, [sp, #(6 * 8)] ; \
+ stp x22, x21, [sp, #(8 * 8)] ; \
+ stp x20, x19, [sp, #(10 * 8)] ; \
+ stp x18, x17, [sp, #(12 * 8)] ; \
+ stp x16, x15, [sp, #(14 * 8)] ; \
+ stp x14, x13, [sp, #(16 * 8)] ; \
+ stp x12, x11, [sp, #(18 * 8)] ; \
+ stp x10, x9, [sp, #(20 * 8)] ; \
+ stp x8, x7, [sp, #(22 * 8)] ; \
+ stp x6, x5, [sp, #(24 * 8)] ; \
+ stp x4, x3, [sp, #(26 * 8)] ; \
+ stp x2, x1, [sp, #(28 * 8)] ; \
+ str x0, [sp, #(30 * 8)] ; \
+ ; \
+ mrs x0, elr_el1 ; \
+ str x0, [sp, #(31 * 8)] ; \
+ mrs x0, esr_el1 ; \
+ str x0, [sp, #(32 * 8)] ; \
+ mov x0, TRAPNO ; \
+ str x0, [sp, #(33 * 8)] ; \
+ mov x0, sp
+
+#define POP_XFRAME() \
+ ldr x0, [sp, #(30 * 8)] ; \
+ ldp x2, x1, [sp, #(28 * 8)] ; \
+ ldp x4, x3, [sp, #(26 * 8)] ; \
+ ldp x6, x5, [sp, #(24 * 8)] ; \
+ ldp x8, x7, [sp, #(22 * 8)] ; \
+ ldp x10, x9, [sp, #(20 * 8)] ; \
+ ldp x12, x11, [sp, #(18 * 8)] ; \
+ ldp x14, x13, [sp, #(16 * 8)] ; \
+ ldp x16, x15, [sp, #(14 * 8)] ; \
+ ldp x18, x17, [sp, #(12 * 8)] ; \
+ ldp x20, x19, [sp, #(10 * 8)] ; \
+ ldp x22, x21, [sp, #(8 * 8)] ; \
+ ldp x24, x23, [sp, #(6 * 8)] ; \
+ ldp x26, x25, [sp, #(4 * 8)] ; \
+ ldp x28, x27, [sp, #(2 * 8)] ; \
+ ldp x30, x29, [sp, #(0 * 8)] ; \
+ add sp, sp, #XFRAME_STACK_SIZE
+
+#endif /* !_MACHINE_FRAMEASM_H_ */
diff --git a/sys/include/arch/aarch64/intr.h b/sys/include/arch/aarch64/intr.h
new file mode 100644
index 0000000..b85564f
--- /dev/null
+++ b/sys/include/arch/aarch64/intr.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _MACHINE_INTR_H_
+#define _MACHINE_INTR_H_
+
+#include <sys/types.h>
+
+/*
+ * Interrupt priority levels
+ */
+#define IPL_NONE 0 /* Don't defer anything */
+#define IPL_BIO 1 /* Block I/O */
+#define IPL_CLOCK 2 /* Clock */
+#define IPL_HIGH 3 /* Defer everything */
+
+struct intr_entry {
+ int priority;
+};
+
+struct intr_hand {
+ int(*func)(void *);
+ char *name;
+ int priority;
+ int irq;
+ int vector;
+};
+
+void *intr_register(const char *name, const struct intr_hand *ih);
+
+#endif /* !_MACHINE_INTR_H_ */
diff --git a/sys/include/arch/aarch64/param.h b/sys/include/arch/aarch64/param.h
new file mode 100644
index 0000000..c074ffb
--- /dev/null
+++ b/sys/include/arch/aarch64/param.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _AARCH64_PARAM_H_
+#define _AARCH64_PARAM_H_
+
+#define M_WORD_SIZE 4
+
+#endif /* !_AARCH64_PARAM_H_ */
diff --git a/sys/include/arch/aarch64/pci/pci.h b/sys/include/arch/aarch64/pci/pci.h
new file mode 100644
index 0000000..189a423
--- /dev/null
+++ b/sys/include/arch/aarch64/pci/pci.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _MACHINE_PCI_H_
+#define _MACHINE_PCI_H_
+
+#include <sys/types.h>
+#include <sys/cdefs.h>
+#include <dev/pci/pci.h>
+
+__weak pcireg_t md_pci_readl(struct pci_device *dev, uint32_t off);
+__weak void md_pci_writel(struct pci_device *dev, uint32_t off, pcireg_t val);
+
+#endif /* !_MACHINE_PCI_H_ */
diff --git a/sys/include/arch/amd64/asm.h b/sys/include/arch/amd64/asm.h
index 8d2c812..aca49d2 100644
--- a/sys/include/arch/amd64/asm.h
+++ b/sys/include/arch/amd64/asm.h
@@ -34,6 +34,16 @@
#include <sys/param.h>
#include <machine/msr.h>
+/* CR4 bits */
+#define CR4_TSD BIT(2) /* Timestamp disable */
+#define CR4_DE BIT(3) /* Debugging extensions */
+#define CR4_PSE BIT(4) /* Page size extensions */
+#define CR4_PCE BIT(8) /* Performance monitoring counter enable */
+#define CR4_UMIP BIT(11) /* User mode instruction prevention */
+#define CR4_LA57 BIT(12) /* Level 5 paging enable */
+#define CR4_VMXE BIT(13) /* Virtual machine extensions enable */
+#define CR4_SMXE BIT(14) /* Safer mode extensions enable */
+
/*
* Contains information for the current
* core. Stored in %GS.
diff --git a/sys/include/arch/amd64/bus.h b/sys/include/arch/amd64/bus.h
index 00cb3ba..25088b4 100644
--- a/sys/include/arch/amd64/bus.h
+++ b/sys/include/arch/amd64/bus.h
@@ -36,13 +36,7 @@
struct bus_resource;
-/*
- * Hyra assumes that the bootloader uses PDE[256] for some
- * higher half mappings. To avoid conflicts with those mappings,
- * this offset is used to start device memory at PDE[257]. This
- * will give us more than enough space.
- */
-#define MMIO_OFFSET (VM_HIGHER_HALF + 0x8000000000)
+#define MMIO_OFFSET VM_HIGHER_HALF
/* Resource signature size max */
#define RSIG_MAX 16
diff --git a/sys/include/arch/amd64/cdefs.h b/sys/include/arch/amd64/cdefs.h
index 256fd8b..d038a15 100644
--- a/sys/include/arch/amd64/cdefs.h
+++ b/sys/include/arch/amd64/cdefs.h
@@ -31,7 +31,7 @@
#define _AMD64_CDEFS_H_
#include <sys/cdefs.h>
-#include <machine/sync.h>
+#include <machine/cpu.h>
/*
* Please use CLI wisely, it is a good idea to use
@@ -41,5 +41,15 @@
#define md_pause() __ASMV("rep; nop") /* (F3 90) PAUSE */
#define md_intoff() __ASMV("cli") /* Clear interrupts */
#define md_inton() __ASMV("sti") /* Enable interrupts */
+#define md_hlt() cpu_halt() /* Halt the processor */
+
+/*
+ * AMD64 specific defines
+ */
+#define __invlpg(VA) \
+ __ASMV("invlpg %0" \
+ : \
+ : "m" ((VA)) \
+ : "memory")
#endif /* !_AMD64_CDEFS_H_ */
diff --git a/sys/include/arch/amd64/cpu.h b/sys/include/arch/amd64/cpu.h
index ce42416..5adff29 100644
--- a/sys/include/arch/amd64/cpu.h
+++ b/sys/include/arch/amd64/cpu.h
@@ -33,27 +33,81 @@
#include <sys/types.h>
#include <sys/cdefs.h>
#include <sys/proc.h>
+#include <sys/sched.h>
+#include <sys/spinlock.h>
#include <machine/tss.h>
+#include <machine/cdefs.h>
+#include <machine/intr.h>
#define CPU_IRQ(IRQ_N) (BIT((IRQ_N)) & 0xFF)
+/* Feature bits */
+#define CPU_FEAT_SMAP BIT(0)
+#define CPU_FEAT_SMEP BIT(1)
+#define CPU_FEAT_UMIP BIT(2)
+#define CPU_FEAT_TSCINV BIT(3) /* TSC invariant */
+
+/* CPU vendors */
+#define CPU_VENDOR_OTHER 0x00000000
+#define CPU_VENDOR_INTEL 0x00000001
+#define CPU_VENDOR_AMD 0x00000002
+
+typedef uint32_t ipi_pend_t;
+
struct cpu_info {
uint32_t apicid;
+ uint32_t feat;
+ uint32_t vendor; /* Vendor (see CPU_VENDOR_*) */
+ uint8_t preempt : 1; /* CPU is preemptable */
+ uint8_t ipi_dispatch : 1; /* 1: IPIs being dispatched */
+ ipi_pend_t ipi_pending;
+ uint8_t id; /* MI Logical ID */
+ uint8_t model : 4; /* CPU model number */
+ uint8_t family : 4; /* CPU family ID */
uint8_t has_x2apic : 1;
+ uint8_t tlb_shootdown : 1;
+ uint8_t online : 1; /* CPU online */
uint8_t ipl;
size_t lapic_tmr_freq;
uint8_t irq_mask;
+ vaddr_t shootdown_va;
+ struct sched_cpu stat;
struct tss_entry *tss;
struct proc *curtd;
+ struct spinlock lock;
struct cpu_info *self;
};
__dead void cpu_halt_all(void);
+void cpu_halt_others(void);
void cpu_startup(struct cpu_info *ci);
+void cpu_enable_smep(void);
+void cpu_disable_smep(void);
+
+struct cpu_info *cpu_get(uint32_t index);
+struct sched_cpu *cpu_get_stat(uint32_t cpu_index);
+
+uint32_t cpu_count(void);
+void cpu_shootdown_tlb(vaddr_t va);
+
struct cpu_info *this_cpu(void);
void mp_bootstrap_aps(struct cpu_info *ci);
extern struct cpu_info g_bsp_ci;
+__always_inline static inline void
+cpu_halt(void)
+{
+ struct cpu_info *ci = this_cpu();
+
+ if (ci != NULL)
+ ci->online = 0;
+
+ __ASMV("hlt");
+
+ if (ci != NULL)
+ ci->online = 1;
+}
+
#endif /* !_MACHINE_CPU_H_ */
diff --git a/sys/include/arch/amd64/frame.h b/sys/include/arch/amd64/frame.h
index 31dcdef..2bd9a7c 100644
--- a/sys/include/arch/amd64/frame.h
+++ b/sys/include/arch/amd64/frame.h
@@ -58,4 +58,6 @@ struct trapframe {
uint64_t ss;
};
+#define TF_IP(TFP) ((TFP)->rip)
+
#endif /* !_MACHINE_FRAME_H_ */
diff --git a/sys/include/arch/amd64/frameasm.h b/sys/include/arch/amd64/frameasm.h
index 22217eb..4dc075e 100644
--- a/sys/include/arch/amd64/frameasm.h
+++ b/sys/include/arch/amd64/frameasm.h
@@ -30,6 +30,8 @@
#ifndef _MACHINE_FRAMEASM_H_
#define _MACHINE_FRAMEASM_H_
+#define ALIGN_TEXT .align 8, 0x90
+
/*
* If the interrupt has an error code, this macro shall
* be used to create the trapframe.
@@ -121,6 +123,7 @@
*/
#define TRAPENTRY_EC(ENTLABEL, TRAPNO) \
ENTLABEL: ; \
+ cli ; \
testq $0x3, 16(%rsp) ; \
jz 1f ; \
lfence ; \
@@ -133,7 +136,8 @@
jz 2f ; \
lfence ; \
swapgs ; \
- 2: iretq
+ 2: sti ; \
+ iretq
/*
* Trap entry where no error code is on
@@ -141,6 +145,7 @@
*/
#define TRAPENTRY(ENTLABEL, TRAPNO) \
ENTLABEL: ; \
+ cli ; \
testq $0x3, 8(%rsp) ; \
jz 1f ; \
lfence ; \
@@ -153,6 +158,7 @@
jz 2f ; \
lfence ; \
swapgs ; \
- 2: iretq
+ 2: sti ; \
+ iretq
#endif /* !_MACHINE_FRAMEASM_H_ */
diff --git a/sys/include/arch/amd64/gdt.h b/sys/include/arch/amd64/gdt.h
index f87416f..0c5faf1 100644
--- a/sys/include/arch/amd64/gdt.h
+++ b/sys/include/arch/amd64/gdt.h
@@ -4,18 +4,48 @@
#include <sys/types.h>
#include <sys/cdefs.h>
+#define GDT_TSS_INDEX 5
+#define GDT_ENTRY_COUNT 7
+
+/* Segment selectors */
#define KERNEL_CS 0x08
#define KERNEL_DS 0x10
-#define USER_CS 0x18
-#define USER_DS 0x20
-#define GDT_TSS 5
+#define USER_CS 0x18
+#define USER_DS 0x20
+
+/*
+ * Bit definitions for regular segment descriptors
+ *
+ * See Intel SPG 3/25 Section 3.4.5 - Segment Descriptors
+ */
+
+#define GDT_ATTRIBUTE_ACCESSED BIT(0) /* Accessed */
+#define GDT_ATTRIBUTE_EXECUTABLE BIT(3) /* Executable */
+#define GDT_ATTRIBUTE_NONSYSTEM BIT(4) /* Code/data */
+#define GDT_ATTRIBUTE_PRESENT BIT(7) /* Present */
+#define GDT_ATTRIBUTE_64BIT_CODE BIT(13) /* 64-bit code */
+#define GDT_ATTRIBUTE_32BIT BIT(14) /* 32-bit code/data */
+#define GDT_ATTRIBUTE_GRANULARITY BIT(15) /* 4KiB limit granularity */
+
+/* Attributes for executable segments */
+#define GDT_ATTRIBUTE_READABLE BIT(1) /* Readable */
+#define GDT_ATTRIBUTE_CONFORMING BIT(2) /* Conforming */
+
+/* Attributes for non-executable segments */
+#define GDT_ATTRIBUTE_WRITABLE BIT(1) /* Writable */
+#define GDT_ATTRIBUTE_EXPANDS_DOWN BIT(2) /* See SPG 3/25 Section 6.8.1 */
+
+/* DPL (Descriptor Privilege Level) specifier */
+#define GDT_ATTRIBUTE_DPL0 0
+#define GDT_ATTRIBUTE_DPL1 (1 << 5)
+#define GDT_ATTRIBUTE_DPL2 (2 << 5)
+#define GDT_ATTRIBUTE_DPL3 (3 << 5)
struct __packed gdt_entry {
uint16_t limit;
uint16_t base_low;
uint8_t base_mid;
- uint8_t access;
- uint8_t granularity;
+ uint16_t attributes;
uint8_t base_hi;
};
@@ -24,27 +54,28 @@ struct __packed gdtr {
uintptr_t offset;
};
+extern struct gdt_entry g_gdt_data[GDT_ENTRY_COUNT];
+extern const struct gdtr g_gdtr;
+
__always_inline static inline void
-gdt_load(struct gdtr *gdtr)
+gdt_load(void)
{
- __ASMV("lgdt %0\n"
- "push $8\n" /* Push CS */
- "lea 1f(%%rip), %%rax\n" /* Load 1 label address into RAX */
- "push %%rax\n" /* Push the return address (label 1) */
- "lretq\n" /* Far return to update CS */
- "1:\n"
- " mov $0x10, %%eax\n"
- " mov %%eax, %%ds\n"
- " mov %%eax, %%es\n"
- " mov %%eax, %%fs\n"
- " mov %%eax, %%gs\n"
- " mov %%eax, %%ss\n"
- :
- : "m" (*gdtr)
- : "rax", "memory"
- );
+ __ASMV("lgdt %0\n"
+ "push %1\n" /* Push code segment selector */
+ "lea 1f(%%rip), %%rax\n" /* Load label 1 address into RAX */
+ "push %%rax\n" /* Push return address (label 1) */
+ "lretq\n" /* Far return to update CS */
+ "1:\n"
+ " mov %2, %%ax\n" /* Load data segment selectors */
+ " mov %%ax, %%ds\n"
+ " mov %%ax, %%es\n"
+ " mov %%ax, %%fs\n"
+ " mov %%ax, %%gs\n"
+ " mov %%ax, %%ss\n"
+ :
+ : "m" (g_gdtr), "i"(KERNEL_CS), "i"(KERNEL_DS)
+ : "rax", "memory"
+ );
}
-extern struct gdt_entry g_gdt_data[256];
-
#endif /* !AMD64_GDT_H_ */
diff --git a/sys/include/arch/amd64/intr.h b/sys/include/arch/amd64/intr.h
index c643945..6e011fa 100644
--- a/sys/include/arch/amd64/intr.h
+++ b/sys/include/arch/amd64/intr.h
@@ -35,6 +35,8 @@
#define IST_SCHED 1U
#define IST_HW_IRQ 2U
#define IST_SW_INT 3U
+#define IST_SYSCALL 4U
+#define IST_DBFLT 5U
/* Upper 4 bits of interrupt vector */
#define IPL_SHIFT 4
@@ -47,11 +49,65 @@
#define IPL_CLOCK 2 /* Clock */
#define IPL_HIGH 3 /* Defer everything */
-struct intr_entry {
+#define N_IPIVEC 4 /* Number of vectors reserved for IPIs */
+#define IPI_PER_VEC 16 /* Max IPIs per vector */
+
+struct intr_hand;
+
+/*
+ * Contains information passed to driver
+ *
+ * @ihp: Interrupt handler
+ * @data: Driver specific data
+ */
+struct intr_data {
+ struct intr_hand *ihp;
+ union {
+ void *data;
+ uint64_t data_u64;
+ };
+};
+
+/*
+ * Interrupt handler
+ *
+ * [r]: Required for intr_register()
+ * [o]: Not required for intr_register()
+ * [v]: Returned by intr_register()
+ * [i]: Internal
+ *
+ * @func: The actual handler [r]
+ * @data: Interrupt data [o/v]
+ * @nintr: Number of times it fired [o]
+ * @name: Interrupt name [v]
+ * @priority: Interrupt priority [r]
+ * @irq: Interrupt request number [o]
+ * @vector: Interrupt vector [v]
+ *
+ * XXX: `name' must be null terminated ('\0')
+ *
+ * XXX: `irq` can be set to -1 for MSI/MSI-X
+ * interrupts.
+ *
+ * XXX: `func` must be the first field in this
+ * structure so that it may be called through
+ * assembly.
+ *
+ * XXX: `ist' should usually be set to -1 but can be
+ * used if an interrupt requires its own stack.
+ */
+struct intr_hand {
+ int(*func)(void *);
+ size_t nintr;
+ struct intr_data data;
+ char *name;
int priority;
+ int irq;
+ int vector;
};
-int intr_alloc_vector(const char *name, uint8_t priority);
+void *intr_register(const char *name, const struct intr_hand *ih);
+
int splraise(uint8_t s);
void splx(uint8_t s);
diff --git a/sys/include/arch/amd64/ioapic.h b/sys/include/arch/amd64/ioapic.h
index c11a85c..4cae800 100644
--- a/sys/include/arch/amd64/ioapic.h
+++ b/sys/include/arch/amd64/ioapic.h
@@ -31,7 +31,8 @@
#define _MACHINE_IOAPIC_H_
#include <sys/types.h>
-#include <dev/acpi/tables.h>
+
+struct ioapic;
void ioapic_init(struct ioapic *p);
void ioapic_gsi_mask(uint8_t gsi);
diff --git a/sys/include/arch/amd64/ipi.h b/sys/include/arch/amd64/ipi.h
new file mode 100644
index 0000000..48243e7
--- /dev/null
+++ b/sys/include/arch/amd64/ipi.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _MACHINE_IPI_H_
+#define _MACHINE_IPI_H_
+
+#include <sys/types.h>
+#include <machine/cpu.h>
+#include <machine/lapic.h>
+
+/*
+ * IPI_VECTOR is the main vector used for all misc
+ * IPIs, the HALT_VECTOR is used to slam the system
+ * to a screetching halt.
+ */
+#define IPI_VECTOR 0x21
+#define HALT_VECTOR 0x22
+
+/* Fixed IPI IDs */
+#define IPI_TLB 0
+
+/*
+ * Represents an interprocessor interrupt
+ * handler.
+ *
+ * @cookie: Used to verifying an instance
+ * @id: IPI ID (identifies the IPI)
+ * @mask: If set, IPIs are ignored
+ * @handler: Handler routine
+ */
+struct cpu_ipi {
+ uint16_t cookie;
+ uint8_t id;
+ int(*handler)(struct cpu_ipi *ipi);
+};
+
+int md_ipi_alloc(struct cpu_ipi **res);
+int md_ipi_send(struct cpu_info *ci, ipi_pend_t ipi);
+void md_ipi_init(void);
+
+#endif /* !_MACHINE_IPI_H_ */
diff --git a/sys/include/arch/amd64/isa/i8042var.h b/sys/include/arch/amd64/isa/i8042var.h
index ebd96ad..9619920 100644
--- a/sys/include/arch/amd64/isa/i8042var.h
+++ b/sys/include/arch/amd64/isa/i8042var.h
@@ -74,6 +74,10 @@
#define I8042_LED_NUM BIT(1)
#define I8042_LED_CAPS BIT(2)
+/* Extended scancode types */
+#define I8042_XSC_ENDPR 0 /* End pressed */
+#define I8042_XSC_ENDRL 1 /* End released */
+
/* Quirks */
#define I8042_HOSTILE BIT(0) /* If EC likes throwing NMIs */
@@ -82,7 +86,5 @@ void i8042_quirk(int mask);
/* Internal - do not use */
void i8042_sync(void);
-void i8042_kb_isr(void);
-void i8042_kb_event(void);
#endif /* _I8042VAR_H_ */
diff --git a/sys/include/arch/amd64/lapic.h b/sys/include/arch/amd64/lapic.h
index 19d9c2c..a566515 100644
--- a/sys/include/arch/amd64/lapic.h
+++ b/sys/include/arch/amd64/lapic.h
@@ -45,6 +45,6 @@ void lapic_init(void);
void lapic_eoi(void);
void lapic_send_ipi(uint8_t id, uint8_t shorthand, uint8_t vector);
-extern uintptr_t g_lapic_base;
+extern void *g_lapic_base;
#endif /* !_MACHINE_LAPIC_H_ */
diff --git a/sys/include/arch/amd64/param.h b/sys/include/arch/amd64/param.h
new file mode 100644
index 0000000..6ea3fca
--- /dev/null
+++ b/sys/include/arch/amd64/param.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _AMD64_PARAM_H_
+#define _AMD64_PARAM_H_
+
+#define M_WORD_SIZE 8
+
+#endif /* !_AMD64_PARAM_H_ */
diff --git a/sys/include/arch/amd64/pci/pci.h b/sys/include/arch/amd64/pci/pci.h
new file mode 100644
index 0000000..189a423
--- /dev/null
+++ b/sys/include/arch/amd64/pci/pci.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _MACHINE_PCI_H_
+#define _MACHINE_PCI_H_
+
+#include <sys/types.h>
+#include <sys/cdefs.h>
+#include <dev/pci/pci.h>
+
+__weak pcireg_t md_pci_readl(struct pci_device *dev, uint32_t off);
+__weak void md_pci_writel(struct pci_device *dev, uint32_t off, pcireg_t val);
+
+#endif /* !_MACHINE_PCI_H_ */
diff --git a/sys/include/arch/amd64/tsc.h b/sys/include/arch/amd64/tsc.h
new file mode 100644
index 0000000..d9eed4f
--- /dev/null
+++ b/sys/include/arch/amd64/tsc.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _MACHINE_TSC_H_
+#define _MACHINE_TSC_H_
+
+#include <sys/types.h>
+#include <sys/cdefs.h>
+#include <sys/param.h>
+
+uint64_t rdtsc_rel(void);
+
+__always_inline static inline uint64_t
+rdtsc(void)
+{
+ uint32_t lo, hi;
+
+ __ASMV(
+ "rdtsc"
+ : "=d" (hi),
+ "=a" (lo)
+ :
+ : "memory"
+ );
+
+ return COMBINE32(hi, lo);
+}
+
+#endif /* !_MACHINE_TSC_H_ */
diff --git a/sys/include/crypto/chacha20.h b/sys/include/crypto/chacha20.h
new file mode 100644
index 0000000..d35702a
--- /dev/null
+++ b/sys/include/crypto/chacha20.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdint.h>
+#include <stddef.h>
+#include <string.h>
+
+#define ROTL(a,b) (((a) << (b)) | ((a) >> (32 - (b))))
+
+#define QR(a,b,c,d) \
+ a += b; d ^= a; d = ROTL(d, 16); \
+ c += d; b ^= c; b = ROTL(b, 12); \
+ a += b; d ^= a; d = ROTL(d, 8); \
+ c += d; b ^= c; b = ROTL(b, 7);
+
+void chacha20_init(uint32_t state[16], const uint8_t key[32],
+ const uint8_t nonce[12], uint32_t counter);
+
+void chacha20_block(uint32_t state[16], uint8_t out[64]);
+void chacha20_encrypt(uint32_t state[16], uint8_t *in, uint8_t *out, size_t len);
+
diff --git a/sys/include/crypto/siphash.h b/sys/include/crypto/siphash.h
new file mode 100644
index 0000000..ecabb4a
--- /dev/null
+++ b/sys/include/crypto/siphash.h
@@ -0,0 +1,34 @@
+/* <MIT License>
+ Copyright (c) 2013 Marek Majkowski <marek@popcount.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+ </MIT License>
+
+ Original location:
+ https://github.com/majek/csiphash/
+
+ Solution inspired by code from:
+ Samuel Neves (supercop/crypto_auth/siphash24/little)
+ djb (supercop/crypto_auth/siphash24/little2)
+ Jean-Philippe Aumasson (https://131002.net/siphash/siphash24.c)
+*/
+
+#include <stdint.h>
+
+uint64_t siphash24(const void *src, unsigned long src_sz, const char k[16]);
diff --git a/sys/include/dev/acpi/acpi.h b/sys/include/dev/acpi/acpi.h
index 9cd6b87..5108748 100644
--- a/sys/include/dev/acpi/acpi.h
+++ b/sys/include/dev/acpi/acpi.h
@@ -30,8 +30,15 @@
#ifndef _ACPI_H_
#define _ACPI_H_
+#include <sys/types.h>
+
+#define ACPI_SLEEP_S5 0x00000000
+
const char *acpi_oemid(void);
void *acpi_query(const char *query);
+
+paddr_t acpi_rsdp(void);
+int acpi_sleep(int type);
void acpi_init(void);
#endif /* !_ACPI_H_ */
diff --git a/sys/include/dev/acpi/tables.h b/sys/include/dev/acpi/tables.h
index 5215c86..d31cbe0 100644
--- a/sys/include/dev/acpi/tables.h
+++ b/sys/include/dev/acpi/tables.h
@@ -118,6 +118,43 @@ struct __packed acpi_gas {
uint64_t address;
};
+/*
+ * ACPI Address Space ID definitions for GAS
+ *
+ * See section 5.2.3.2 of the ACPI software programming
+ * manual.
+ *
+ * XXX: 0x0B->0x7E is reserved as well as 0x80->0xBF
+ * and 0xC0->0xFF is OEM defined. Values other than
+ * the ones specified below are either garbage or
+ * OEM specific values.
+ */
+#define ACPI_GAS_SYSMEM 0x00 /* System memory space */
+#define ACPI_GAS_SYSIO 0x01 /* System I/O space */
+#define ACPI_GAS_PCICONF 0x02 /* PCI configuration space */
+#define ACPI_GAS_EC 0x03 /* Embedded controller */
+#define ACPI_GAS_SMBUS 0x04 /* System management bus */
+#define ACPI_GAS_CMOS 0x05 /* System CMOS */
+#define ACPI_GAS_PCIBAR 0x06 /* PCI BAR target */
+#define ACPI_GAS_IPMI 0x07 /* IPMI (sensor monitoring) */
+#define ACPI_GAS_GPIO 0x08 /* General Purpose I/O */
+#define ACPI_GAS_GSBUS 0x09 /* GenericSerialBus */
+#define ACPI_GAS_PLATCOM 0x0A /* Platform Communications Channel */
+
+/*
+ * ACPI address size definitions for GAS
+ *
+ * See section 5.2.3.2 of the ACPI software programming
+ * manual.
+ *
+ * This is really retarded Intel and Microsoft, thank you.
+ */
+#define ACPI_GAS_UNDEF 0 /* Undefined (legacy reasons) */
+#define ACPI_GAS_BYTE 1 /* Byte access */
+#define ACPI_GAS_WORD 2 /* Word access */
+#define ACPI_GAS_DWORD 3 /* Dword access */
+#define ACPI_GAS_QWORD 4 /* Qword access */
+
struct __packed acpi_hpet {
struct acpi_header hdr;
uint8_t hardware_rev_id;
@@ -132,4 +169,65 @@ struct __packed acpi_hpet {
uint8_t page_protection;
};
+/*
+ * PCIe / ACPI MCFG base address description
+ * table.
+ *
+ * @base_pa: Enhanced configuration base [physical]
+ * @seg_grpno: PCI segment group number
+ * @bus_start: Host bridge bus start
+ * @bus_end: Host bridge bus end
+ */
+struct __packed acpi_mcfg_base {
+ uint64_t base_pa;
+ uint16_t seg_grpno;
+ uint8_t bus_start;
+ uint8_t bus_end;
+ uint32_t reserved;
+};
+
+/*
+ * PCIe / ACPI MCFG structure
+ *
+ * @hdr: ACPI header
+ * @reserved: Do not use
+ * @base: ECAM MMIO address list
+ */
+struct __packed acpi_mcfg {
+ struct acpi_header hdr;
+ uint32_t reserved[2];
+ struct acpi_mcfg_base base[1];
+};
+
+struct __packed dmi_entry32 {
+ char signature[4]; /* _SM_ */
+ uint8_t checksum; /* Sum of table bytes */
+ uint8_t length; /* Length of entry table */
+ uint8_t major; /* DMI major */
+ uint8_t minor; /* DMI minor */
+ uint16_t max_size; /* Max structure size */
+ uint8_t rev; /* Entry revision */
+ char fmt_area[5]; /* Formatted area */
+ char isignature[5]; /* Intermediate signature */
+ uint8_t ichecksum; /* Intermediate checksum */
+ uint16_t table_len; /* Length of SMBIOS structure table */
+ uint32_t addr; /* 32-bit physical start of SMBIOS structure table */
+ uint16_t nstruct; /* Total number of structures */
+ uint8_t bcd_rev;
+};
+
+struct __packed dmi_entry64 {
+ char signature[5]; /* _SM_ */
+ uint8_t checksum; /* Sum of table bytes */
+ uint8_t length; /* Length of entry table */
+ uint8_t major; /* DMI major */
+ uint8_t minor; /* DMI minor */
+ uint8_t docrev;
+ uint8_t entry_rev;
+ uint8_t reserved;
+ uint16_t max_size; /* Max structure size */
+ uint16_t padding;
+ uint64_t addr; /* 64-bit physical address */
+};
+
#endif /* _ACPI_TABLES_H_ */
diff --git a/sys/include/dev/acpi/uacpi.h b/sys/include/dev/acpi/uacpi.h
new file mode 100644
index 0000000..d38e087
--- /dev/null
+++ b/sys/include/dev/acpi/uacpi.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _UACPI_BRIDGE_H_
+#define _UACPI_BRIDGE_H_
+
+int uacpi_init(void);
+
+#endif /* !_UACPI_BRIDGE_H_ */
diff --git a/sys/include/dev/acpi/uacpi/uacpi/acpi.h b/sys/include/dev/acpi/uacpi/uacpi/acpi.h
new file mode 100644
index 0000000..79eb31b
--- /dev/null
+++ b/sys/include/dev/acpi/uacpi/uacpi/acpi.h
@@ -0,0 +1,1430 @@
+#pragma once
+
+#include <uacpi/platform/compiler.h>
+#include <uacpi/helpers.h>
+#include <uacpi/types.h>
+
+/*
+ * -----------------------------------------------------
+ * Common structures provided by the ACPI specification
+ * -----------------------------------------------------
+ */
+
+#define ACPI_RSDP_SIGNATURE "RSD PTR "
+#define ACPI_RSDT_SIGNATURE "RSDT"
+#define ACPI_XSDT_SIGNATURE "XSDT"
+#define ACPI_MADT_SIGNATURE "APIC"
+#define ACPI_FADT_SIGNATURE "FACP"
+#define ACPI_FACS_SIGNATURE "FACS"
+#define ACPI_MCFG_SIGNATURE "MCFG"
+#define ACPI_HPET_SIGNATURE "HPET"
+#define ACPI_SRAT_SIGNATURE "SRAT"
+#define ACPI_SLIT_SIGNATURE "SLIT"
+#define ACPI_DSDT_SIGNATURE "DSDT"
+#define ACPI_SSDT_SIGNATURE "SSDT"
+#define ACPI_PSDT_SIGNATURE "PSDT"
+#define ACPI_ECDT_SIGNATURE "ECDT"
+#define ACPI_RHCT_SIGNATURE "RHCT"
+
+#define ACPI_AS_ID_SYS_MEM 0x00
+#define ACPI_AS_ID_SYS_IO 0x01
+#define ACPI_AS_ID_PCI_CFG_SPACE 0x02
+#define ACPI_AS_ID_EC 0x03
+#define ACPI_AS_ID_SMBUS 0x04
+#define ACPI_AS_ID_SYS_CMOS 0x05
+#define ACPI_AS_ID_PCI_BAR_TGT 0x06
+#define ACPI_AS_ID_IPMI 0x07
+#define ACPI_AS_ID_GP_IO 0x08
+#define ACPI_AS_ID_GENERIC_SBUS 0x09
+#define ACPI_AS_ID_PCC 0x0A
+#define ACPI_AS_ID_FFH 0x7F
+#define ACPI_AS_ID_OEM_BASE 0xC0
+#define ACPI_AS_ID_OEM_END 0xFF
+
+#define ACPI_ACCESS_UD 0
+#define ACPI_ACCESS_BYTE 1
+#define ACPI_ACCESS_WORD 2
+#define ACPI_ACCESS_DWORD 3
+#define ACPI_ACCESS_QWORD 4
+
+UACPI_PACKED(struct acpi_gas {
+ uacpi_u8 address_space_id;
+ uacpi_u8 register_bit_width;
+ uacpi_u8 register_bit_offset;
+ uacpi_u8 access_size;
+ uacpi_u64 address;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_gas, 12);
+
+UACPI_PACKED(struct acpi_rsdp {
+ uacpi_char signature[8];
+ uacpi_u8 checksum;
+ uacpi_char oemid[6];
+ uacpi_u8 revision;
+ uacpi_u32 rsdt_addr;
+
+ // vvvv available if .revision >= 2.0 only
+ uacpi_u32 length;
+ uacpi_u64 xsdt_addr;
+ uacpi_u8 extended_checksum;
+ uacpi_u8 rsvd[3];
+})
+UACPI_EXPECT_SIZEOF(struct acpi_rsdp, 36);
+
+UACPI_PACKED(struct acpi_sdt_hdr {
+ uacpi_char signature[4];
+ uacpi_u32 length;
+ uacpi_u8 revision;
+ uacpi_u8 checksum;
+ uacpi_char oemid[6];
+ uacpi_char oem_table_id[8];
+ uacpi_u32 oem_revision;
+ uacpi_u32 creator_id;
+ uacpi_u32 creator_revision;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_sdt_hdr, 36);
+
+UACPI_PACKED(struct acpi_rsdt {
+ struct acpi_sdt_hdr hdr;
+ uacpi_u32 entries[];
+})
+
+UACPI_PACKED(struct acpi_xsdt {
+ struct acpi_sdt_hdr hdr;
+ uacpi_u64 entries[];
+})
+
+UACPI_PACKED(struct acpi_entry_hdr {
+ /*
+ * - acpi_madt_entry_type for the APIC table
+ * - acpi_srat_entry_type for the SRAT table
+ */
+ uacpi_u8 type;
+ uacpi_u8 length;
+})
+
+// acpi_madt->flags
+#define ACPI_PCAT_COMPAT (1 << 0)
+
+enum acpi_madt_entry_type {
+ ACPI_MADT_ENTRY_TYPE_LAPIC = 0,
+ ACPI_MADT_ENTRY_TYPE_IOAPIC = 1,
+ ACPI_MADT_ENTRY_TYPE_INTERRUPT_SOURCE_OVERRIDE = 2,
+ ACPI_MADT_ENTRY_TYPE_NMI_SOURCE = 3,
+ ACPI_MADT_ENTRY_TYPE_LAPIC_NMI = 4,
+ ACPI_MADT_ENTRY_TYPE_LAPIC_ADDRESS_OVERRIDE = 5,
+ ACPI_MADT_ENTRY_TYPE_IOSAPIC = 6,
+ ACPI_MADT_ENTRY_TYPE_LSAPIC = 7,
+ ACPI_MADT_ENTRY_TYPE_PLATFORM_INTERRUPT_SOURCES = 8,
+ ACPI_MADT_ENTRY_TYPE_LOCAL_X2APIC = 9,
+ ACPI_MADT_ENTRY_TYPE_LOCAL_X2APIC_NMI = 0xA,
+ ACPI_MADT_ENTRY_TYPE_GICC = 0xB,
+ ACPI_MADT_ENTRY_TYPE_GICD = 0xC,
+ ACPI_MADT_ENTRY_TYPE_GIC_MSI_FRAME = 0xD,
+ ACPI_MADT_ENTRY_TYPE_GICR = 0xE,
+ ACPI_MADT_ENTRY_TYPE_GIC_ITS = 0xF,
+ ACPI_MADT_ENTRY_TYPE_MULTIPROCESSOR_WAKEUP = 0x10,
+ ACPI_MADT_ENTRY_TYPE_CORE_PIC = 0x11,
+ ACPI_MADT_ENTRY_TYPE_LIO_PIC = 0x12,
+ ACPI_MADT_ENTRY_TYPE_HT_PIC = 0x13,
+ ACPI_MADT_ENTRY_TYPE_EIO_PIC = 0x14,
+ ACPI_MADT_ENTRY_TYPE_MSI_PIC = 0x15,
+ ACPI_MADT_ENTRY_TYPE_BIO_PIC = 0x16,
+ ACPI_MADT_ENTRY_TYPE_LPC_PIC = 0x17,
+ ACPI_MADT_ENTRY_TYPE_RINTC = 0x18,
+ ACPI_MADT_ENTRY_TYPE_IMSIC = 0x19,
+ ACPI_MADT_ENTRY_TYPE_APLIC = 0x1A,
+ ACPI_MADT_ENTRY_TYPE_PLIC = 0x1B,
+ ACPI_MADT_ENTRY_TYPE_RESERVED = 0x1C, // 0x1C..0x7F
+ ACPI_MADT_ENTRY_TYPE_OEM = 0x80, // 0x80..0xFF
+};
+
+UACPI_PACKED(struct acpi_madt {
+ struct acpi_sdt_hdr hdr;
+ uacpi_u32 local_interrupt_controller_address;
+ uacpi_u32 flags;
+ struct acpi_entry_hdr entries[];
+})
+UACPI_EXPECT_SIZEOF(struct acpi_madt, 44);
+
+/*
+ * - acpi_madt_lapic->flags
+ * - acpi_madt_lsapic->flags
+ * - acpi_madt_x2apic->flags
+ */
+#define ACPI_PIC_ENABLED (1 << 0)
+#define ACPI_PIC_ONLINE_CAPABLE (1 << 1)
+
+UACPI_PACKED(struct acpi_madt_lapic {
+ struct acpi_entry_hdr hdr;
+ uacpi_u8 uid;
+ uacpi_u8 id;
+ uacpi_u32 flags;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_madt_lapic, 8);
+
+UACPI_PACKED(struct acpi_madt_ioapic {
+ struct acpi_entry_hdr hdr;
+ uacpi_u8 id;
+ uacpi_u8 rsvd;
+ uacpi_u32 address;
+ uacpi_u32 gsi_base;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_madt_ioapic, 12);
+
+/*
+ * - acpi_madt_interrupt_source_override->flags
+ * - acpi_madt_nmi_source->flags
+ * - acpi_madt_lapic_nmi->flags
+ * - acpi_madt_platform_interrupt_source->flags
+ * - acpi_madt_x2apic_nmi->flags
+ */
+#define ACPI_MADT_POLARITY_MASK 0b11
+#define ACPI_MADT_POLARITY_CONFORMING 0b00
+#define ACPI_MADT_POLARITY_ACTIVE_HIGH 0b01
+#define ACPI_MADT_POLARITY_ACTIVE_LOW 0b11
+
+#define ACPI_MADT_TRIGGERING_MASK 0b1100
+#define ACPI_MADT_TRIGGERING_CONFORMING 0b0000
+#define ACPI_MADT_TRIGGERING_EDGE 0b0100
+#define ACPI_MADT_TRIGGERING_LEVEL 0b1100
+
+UACPI_PACKED(struct acpi_madt_interrupt_source_override {
+ struct acpi_entry_hdr hdr;
+ uacpi_u8 bus;
+ uacpi_u8 source;
+ uacpi_u32 gsi;
+ uacpi_u16 flags;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_madt_interrupt_source_override, 10);
+
+UACPI_PACKED(struct acpi_madt_nmi_source {
+ struct acpi_entry_hdr hdr;
+ uacpi_u16 flags;
+ uacpi_u32 gsi;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_madt_nmi_source, 8);
+
+UACPI_PACKED(struct acpi_madt_lapic_nmi {
+ struct acpi_entry_hdr hdr;
+ uacpi_u8 uid;
+ uacpi_u16 flags;
+ uacpi_u8 lint;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_madt_lapic_nmi, 6);
+
+UACPI_PACKED(struct acpi_madt_lapic_address_override {
+ struct acpi_entry_hdr hdr;
+ uacpi_u16 rsvd;
+ uacpi_u64 address;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_madt_lapic_address_override, 12);
+
+UACPI_PACKED(struct acpi_madt_iosapic {
+ struct acpi_entry_hdr hdr;
+ uacpi_u8 id;
+ uacpi_u8 rsvd;
+ uacpi_u32 gsi_base;
+ uacpi_u64 address;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_madt_iosapic, 16);
+
+UACPI_PACKED(struct acpi_madt_lsapic {
+ struct acpi_entry_hdr hdr;
+ uacpi_u8 acpi_id;
+ uacpi_u8 id;
+ uacpi_u8 eid;
+ uacpi_u8 reserved[3];
+ uacpi_u32 flags;
+ uacpi_u32 uid;
+ uacpi_char uid_string[];
+})
+UACPI_EXPECT_SIZEOF(struct acpi_madt_lsapic, 16);
+
+// acpi_madt_platform_interrupt_source->platform_flags
+#define ACPI_CPEI_PROCESSOR_OVERRIDE (1 << 0)
+
+UACPI_PACKED(struct acpi_madt_platform_interrupt_source {
+ struct acpi_entry_hdr hdr;
+ uacpi_u16 flags;
+ uacpi_u8 type;
+ uacpi_u8 processor_id;
+ uacpi_u8 processor_eid;
+ uacpi_u8 iosapic_vector;
+ uacpi_u32 gsi;
+ uacpi_u32 platform_flags;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_madt_platform_interrupt_source, 16);
+
+UACPI_PACKED(struct acpi_madt_x2apic {
+ struct acpi_entry_hdr hdr;
+ uacpi_u16 rsvd;
+ uacpi_u32 id;
+ uacpi_u32 flags;
+ uacpi_u32 uid;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_madt_x2apic, 16);
+
+UACPI_PACKED(struct acpi_madt_x2apic_nmi {
+ struct acpi_entry_hdr hdr;
+ uacpi_u16 flags;
+ uacpi_u32 uid;
+ uacpi_u8 lint;
+ uacpi_u8 reserved[3];
+})
+UACPI_EXPECT_SIZEOF(struct acpi_madt_x2apic_nmi, 12);
+
+// acpi_madt_gicc->flags
+#define ACPI_GICC_ENABLED (1 << 0)
+#define ACPI_GICC_PERF_INTERRUPT_MODE (1 << 1)
+#define ACPI_GICC_VGIC_MAINTENANCE_INTERRUPT_MODE (1 << 2)
+#define ACPI_GICC_ONLINE_CAPABLE (1 << 3)
+
+// ACPI_GICC_*_INTERRUPT_MODE
+#define ACPI_GICC_TRIGGERING_EDGE 1
+#define ACPI_GICC_TRIGGERING_LEVEL 0
+
+UACPI_PACKED(struct acpi_madt_gicc {
+ struct acpi_entry_hdr hdr;
+ uacpi_u16 rsvd0;
+ uacpi_u32 interface_number;
+ uacpi_u32 acpi_id;
+ uacpi_u32 flags;
+ uacpi_u32 parking_protocol_version;
+ uacpi_u32 perf_interrupt_gsiv;
+ uacpi_u64 parked_address;
+ uacpi_u64 address;
+ uacpi_u64 gicv;
+ uacpi_u64 gich;
+ uacpi_u32 vgic_maitenante_interrupt;
+ uacpi_u64 gicr_base_address;
+ uacpi_u64 mpidr;
+ uacpi_u8 power_efficiency_class;
+ uacpi_u8 rsvd1;
+ uacpi_u16 spe_overflow_interrupt;
+ uacpi_u16 trbe_interrupt;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_madt_gicc, 82);
+
+UACPI_PACKED(struct acpi_madt_gicd {
+ struct acpi_entry_hdr hdr;
+ uacpi_u16 rsvd0;
+ uacpi_u32 id;
+ uacpi_u64 address;
+ uacpi_u32 system_vector_base;
+ uacpi_u8 gic_version;
+ uacpi_u8 reserved1[3];
+})
+UACPI_EXPECT_SIZEOF(struct acpi_madt_gicd, 24);
+
+// acpi_madt_gic_msi_frame->flags
+#define ACPI_SPI_SELECT (1 << 0)
+
+UACPI_PACKED(struct acpi_madt_gic_msi_frame {
+ struct acpi_entry_hdr hdr;
+ uacpi_u16 rsvd;
+ uacpi_u32 id;
+ uacpi_u64 address;
+ uacpi_u32 flags;
+ uacpi_u16 spi_count;
+ uacpi_u16 spi_base;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_madt_gic_msi_frame, 24);
+
+UACPI_PACKED(struct acpi_madt_gicr {
+ struct acpi_entry_hdr hdr;
+ uacpi_u16 rsvd;
+ uacpi_u64 address;
+ uacpi_u32 length;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_madt_gicr, 16);
+
+UACPI_PACKED(struct acpi_madt_gic_its {
+ struct acpi_entry_hdr hdr;
+ uacpi_u16 rsvd0;
+ uacpi_u32 id;
+ uacpi_u64 address;
+ uacpi_u32 rsvd1;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_madt_gic_its, 20);
+
+UACPI_PACKED(struct acpi_madt_multiprocessor_wakeup {
+ struct acpi_entry_hdr hdr;
+ uacpi_u16 mailbox_version;
+ uacpi_u32 rsvd;
+ uacpi_u64 mailbox_address;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_madt_multiprocessor_wakeup, 16);
+
+#define ACPI_CORE_PIC_ENABLED (1 << 0)
+
+UACPI_PACKED(struct acpi_madt_core_pic {
+ struct acpi_entry_hdr hdr;
+ uacpi_u8 version;
+ uacpi_u32 acpi_id;
+ uacpi_u32 id;
+ uacpi_u32 flags;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_madt_core_pic, 15);
+
+UACPI_PACKED(struct acpi_madt_lio_pic {
+ struct acpi_entry_hdr hdr;
+ uacpi_u8 version;
+ uacpi_u64 address;
+ uacpi_u16 size;
+ uacpi_u16 cascade_vector;
+ uacpi_u64 cascade_vector_mapping;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_madt_lio_pic, 23);
+
+UACPI_PACKED(struct acpi_madt_ht_pic {
+ struct acpi_entry_hdr hdr;
+ uacpi_u8 version;
+ uacpi_u64 address;
+ uacpi_u16 size;
+ uacpi_u64 cascade_vector;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_madt_ht_pic, 21);
+
+UACPI_PACKED(struct acpi_madt_eio_pic {
+ struct acpi_entry_hdr hdr;
+ uacpi_u8 version;
+ uacpi_u8 cascade_vector;
+ uacpi_u8 node;
+ uacpi_u64 node_map;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_madt_eio_pic, 13);
+
+UACPI_PACKED(struct acpi_madt_msi_pic {
+ struct acpi_entry_hdr hdr;
+ uacpi_u8 version;
+ uacpi_u64 address;
+ uacpi_u32 start;
+ uacpi_u32 count;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_madt_msi_pic, 19);
+
+UACPI_PACKED(struct acpi_madt_bio_pic {
+ struct acpi_entry_hdr hdr;
+ uacpi_u8 version;
+ uacpi_u64 address;
+ uacpi_u16 size;
+ uacpi_u16 hardware_id;
+ uacpi_u16 gsi_base;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_madt_bio_pic, 17);
+
+UACPI_PACKED(struct acpi_madt_lpc_pic {
+ struct acpi_entry_hdr hdr;
+ uacpi_u8 version;
+ uacpi_u64 address;
+ uacpi_u16 size;
+ uacpi_u16 cascade_vector;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_madt_lpc_pic, 15);
+
+UACPI_PACKED(struct acpi_madt_rintc {
+ struct acpi_entry_hdr hdr;
+ uacpi_u8 version;
+ uacpi_u8 rsvd;
+ uacpi_u32 flags;
+ uacpi_u64 hart_id;
+ uacpi_u32 uid;
+ uacpi_u32 ext_intc_id;
+ uacpi_u64 address;
+ uacpi_u32 size;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_madt_rintc, 36);
+
+UACPI_PACKED(struct acpi_madt_imsic {
+ struct acpi_entry_hdr hdr;
+ uacpi_u8 version;
+ uacpi_u8 rsvd;
+ uacpi_u32 flags;
+ uacpi_u16 num_ids;
+ uacpi_u16 num_guest_ids;
+ uacpi_u8 guest_index_bits;
+ uacpi_u8 hart_index_bits;
+ uacpi_u8 group_index_bits;
+ uacpi_u8 group_index_shift;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_madt_imsic, 16);
+
+UACPI_PACKED(struct acpi_madt_aplic {
+ struct acpi_entry_hdr hdr;
+ uacpi_u8 version;
+ uacpi_u8 id;
+ uacpi_u32 flags;
+ uacpi_u64 hardware_id;
+ uacpi_u16 idc_count;
+ uacpi_u16 sources_count;
+ uacpi_u32 gsi_base;
+ uacpi_u64 address;
+ uacpi_u32 size;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_madt_aplic, 36);
+
+UACPI_PACKED(struct acpi_madt_plic {
+ struct acpi_entry_hdr hdr;
+ uacpi_u8 version;
+ uacpi_u8 id;
+ uacpi_u64 hardware_id;
+ uacpi_u16 sources_count;
+ uacpi_u16 max_priority;
+ uacpi_u32 flags;
+ uacpi_u32 size;
+ uacpi_u64 address;
+ uacpi_u32 gsi_base;
+
+})
+UACPI_EXPECT_SIZEOF(struct acpi_madt_plic, 36);
+
+enum acpi_srat_entry_type {
+ ACPI_SRAT_ENTRY_TYPE_PROCESSOR_AFFINITY = 0,
+ ACPI_SRAT_ENTRY_TYPE_MEMORY_AFFINITY = 1,
+ ACPI_SRAT_ENTRY_TYPE_X2APIC_AFFINITY = 2,
+ ACPI_SRAT_ENTRY_TYPE_GICC_AFFINITY = 3,
+ ACPI_SRAT_ENTRY_TYPE_GIC_ITS_AFFINITY = 4,
+ ACPI_SRAT_ENTRY_TYPE_GENERIC_INITIATOR_AFFINITY = 5,
+ ACPI_SRAT_ENTRY_TYPE_GENERIC_PORT_AFFINITY = 6,
+ ACPI_SRAT_ENTRY_TYPE_RINTC_AFFINITY = 7,
+};
+
+UACPI_PACKED(struct acpi_srat {
+ struct acpi_sdt_hdr hdr;
+ uacpi_u32 rsvd0;
+ uacpi_u64 rsvd1;
+ struct acpi_entry_hdr entries[];
+})
+UACPI_EXPECT_SIZEOF(struct acpi_srat, 48);
+
+/*
+ * acpi_srat_processor_affinity->flags
+ * acpi_srat_x2apic_affinity->flags
+ */
+#define ACPI_SRAT_PROCESSOR_ENABLED (1 << 0)
+
+UACPI_PACKED(struct acpi_srat_processor_affinity {
+ struct acpi_entry_hdr hdr;
+ uacpi_u8 proximity_domain_low;
+ uacpi_u8 id;
+ uacpi_u32 flags;
+ uacpi_u8 eid;
+ uacpi_u8 proximity_domain_high[3];
+ uacpi_u32 clock_domain;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_srat_processor_affinity, 16);
+
+// acpi_srat_memory_affinity->flags
+#define ACPI_SRAT_MEMORY_ENABLED (1 << 0)
+#define ACPI_SRAT_MEMORY_HOTPLUGGABLE (1 << 1)
+#define ACPI_SRAT_MEMORY_NON_VOLATILE (1 << 2)
+
+UACPI_PACKED(struct acpi_srat_memory_affinity {
+ struct acpi_entry_hdr hdr;
+ uacpi_u32 proximity_domain;
+ uacpi_u16 rsvd0;
+ uacpi_u64 address;
+ uacpi_u64 length;
+ uacpi_u32 rsvd1;
+ uacpi_u32 flags;
+ uacpi_u64 rsdv2;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_srat_memory_affinity, 40);
+
+UACPI_PACKED(struct acpi_srat_x2apic_affinity {
+ struct acpi_entry_hdr hdr;
+ uacpi_u16 rsvd0;
+ uacpi_u32 proximity_domain;
+ uacpi_u32 id;
+ uacpi_u32 flags;
+ uacpi_u32 clock_domain;
+ uacpi_u32 rsvd1;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_srat_x2apic_affinity, 24);
+
+// acpi_srat_gicc_affinity->flags
+#define ACPI_SRAT_GICC_ENABLED (1 << 0)
+
+UACPI_PACKED(struct acpi_srat_gicc_affinity {
+ struct acpi_entry_hdr hdr;
+ uacpi_u32 proximity_domain;
+ uacpi_u32 uid;
+ uacpi_u32 flags;
+ uacpi_u32 clock_domain;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_srat_gicc_affinity, 18);
+
+UACPI_PACKED(struct acpi_srat_gic_its_affinity {
+ struct acpi_entry_hdr hdr;
+ uacpi_u32 proximity_domain;
+ uacpi_u16 rsvd;
+ uacpi_u32 id;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_srat_gic_its_affinity, 12);
+
+// acpi_srat_generic_affinity->flags
+#define ACPI_GENERIC_AFFINITY_ENABLED (1 << 0)
+#define ACPI_GENERIC_AFFINITY_ARCH_TRANSACTIONS (1 << 1)
+
+UACPI_PACKED(struct acpi_srat_generic_affinity {
+ struct acpi_entry_hdr hdr;
+ uacpi_u8 rsvd0;
+ uacpi_u8 handle_type;
+ uacpi_u32 proximity_domain;
+ uacpi_u8 handle[16];
+ uacpi_u32 flags;
+ uacpi_u32 rsvd1;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_srat_generic_affinity, 32);
+
+// acpi_srat_rintc_affinity->flags
+#define ACPI_SRAT_RINTC_AFFINITY_ENABLED (1 << 0)
+
+UACPI_PACKED(struct acpi_srat_rintc_affinity {
+ struct acpi_entry_hdr hdr;
+ uacpi_u16 rsvd;
+ uacpi_u32 proximity_domain;
+ uacpi_u32 uid;
+ uacpi_u32 flags;
+ uacpi_u32 clock_domain;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_srat_rintc_affinity, 20);
+
+UACPI_PACKED(struct acpi_slit {
+ struct acpi_sdt_hdr hdr;
+ uacpi_u64 num_localities;
+ uacpi_u8 matrix[];
+})
+UACPI_EXPECT_SIZEOF(struct acpi_slit, 44);
+
+/*
+ * acpi_gtdt->el*_flags
+ * acpi_gtdt_timer_entry->physical_flags
+ * acpi_gtdt_timer_entry->virtual_flags
+ * acpi_gtdt_watchdog->flags
+ */
+#define ACPI_GTDT_TRIGGERING (1 << 0)
+#define ACPI_GTDT_TRIGGERING_EDGE 1
+#define ACPI_GTDT_TRIGGERING_LEVEL 0
+
+/*
+ * acpi_gtdt->el*_flags
+ * acpi_gtdt_timer_entry->physical_flags
+ * acpi_gtdt_timer_entry->virtual_flags
+ * acpi_gtdt_watchdog->flags
+ */
+#define ACPI_GTDT_POLARITY (1 << 1)
+#define ACPI_GTDT_POLARITY_ACTIVE_LOW 1
+#define ACPI_GTDT_POLARITY_ACTIVE_HIGH 0
+
+// acpi_gtdt->el*_flags
+#define ACPI_GTDT_ALWAYS_ON_CAPABLE (1 << 2)
+
+UACPI_PACKED(struct acpi_gtdt {
+ struct acpi_sdt_hdr hdr;
+ uacpi_u64 cnt_control_base;
+ uacpi_u32 rsvd;
+ uacpi_u32 el1_secure_gsiv;
+ uacpi_u32 el1_secure_flags;
+ uacpi_u32 el1_non_secure_gsiv;
+ uacpi_u32 el1_non_secure_flags;
+ uacpi_u32 el1_virtual_gsiv;
+ uacpi_u32 el1_virtual_flags;
+ uacpi_u32 el2_gsiv;
+ uacpi_u32 el2_flags;
+ uacpi_u64 cnt_read_base;
+ uacpi_u32 platform_timer_count;
+ uacpi_u32 platform_timer_offset;
+
+ // revision >= 3
+ uacpi_u32 el2_virtual_gsiv;
+ uacpi_u32 el2_virtual_flags;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_gtdt, 104);
+
+enum acpi_gtdt_entry_type {
+ ACPI_GTDT_ENTRY_TYPE_TIMER = 0,
+ ACPI_GTDT_ENTRY_TYPE_WATCHDOG = 1,
+};
+
+UACPI_PACKED(struct acpi_gtdt_entry_hdr {
+ uacpi_u8 type;
+ uacpi_u16 length;
+})
+
+UACPI_PACKED(struct acpi_gtdt_timer {
+ struct acpi_gtdt_entry_hdr hdr;
+ uacpi_u8 rsvd;
+ uacpi_u64 cnt_ctl_base;
+ uacpi_u32 timer_count;
+ uacpi_u32 timer_offset;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_gtdt_timer, 20);
+
+// acpi_gtdt_timer_entry->common_flags
+#define ACPI_GTDT_TIMER_ENTRY_SECURE (1 << 0)
+#define ACPI_GTDT_TIMER_ENTRY_ALWAYS_ON_CAPABLE (1 << 1)
+
+UACPI_PACKED(struct acpi_gtdt_timer_entry {
+ uacpi_u8 frame_number;
+ uacpi_u8 rsvd[3];
+ uacpi_u64 cnt_base;
+ uacpi_u64 el0_cnt_base;
+ uacpi_u32 physical_gsiv;
+ uacpi_u32 physical_flags;
+ uacpi_u32 virtual_gsiv;
+ uacpi_u32 virtual_flags;
+ uacpi_u32 common_flags;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_gtdt_timer_entry, 40);
+
+// acpi_gtdt_watchdog->flags
+#define ACPI_GTDT_WATCHDOG_SECURE (1 << 2)
+
+UACPI_PACKED(struct acpi_gtdt_watchdog {
+ struct acpi_gtdt_entry_hdr hdr;
+ uacpi_u8 rsvd;
+ uacpi_u64 refresh_frame;
+ uacpi_u64 control_frame;
+ uacpi_u32 gsiv;
+ uacpi_u32 flags;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_gtdt_watchdog, 28);
+
+// acpi_fdt->iapc_flags
+#define ACPI_IA_PC_LEGACY_DEVS (1 << 0)
+#define ACPI_IA_PC_8042 (1 << 1)
+#define ACPI_IA_PC_NO_VGA (1 << 2)
+#define ACPI_IA_PC_NO_MSI (1 << 3)
+#define ACPI_IA_PC_NO_PCIE_ASPM (1 << 4)
+#define ACPI_IA_PC_NO_CMOS_RTC (1 << 5)
+
+// acpi_fdt->flags
+#define ACPI_WBINVD (1 << 0)
+#define ACPI_WBINVD_FLUSH (1 << 1)
+#define ACPI_PROC_C1 (1 << 2)
+#define ACPI_P_LVL2_UP (1 << 3)
+#define ACPI_PWR_BUTTON (1 << 4)
+#define ACPI_SLP_BUTTON (1 << 5)
+#define ACPI_FIX_RTC (1 << 6)
+#define ACPI_RTC_S4 (1 << 7)
+#define ACPI_TMR_VAL_EXT (1 << 8)
+#define ACPI_DCK_CAP (1 << 9)
+#define ACPI_RESET_REG_SUP (1 << 10)
+#define ACPI_SEALED_CASE (1 << 11)
+#define ACPI_HEADLESS (1 << 12)
+#define ACPI_CPU_SW_SLP (1 << 13)
+#define ACPI_PCI_EXP_WAK (1 << 14)
+#define ACPI_USE_PLATFORM_CLOCK (1 << 15)
+#define ACPI_S4_RTC_STS_VALID (1 << 16)
+#define ACPI_REMOTE_POWER_ON_CAPABLE (1 << 17)
+#define ACPI_FORCE_APIC_CLUSTER_MODEL (1 << 18)
+#define ACPI_FORCE_APIC_PHYS_DEST_MODE (1 << 19)
+#define ACPI_HW_REDUCED_ACPI (1 << 20)
+#define ACPI_LOW_POWER_S0_IDLE_CAPABLE (1 << 21)
+
+// acpi_fdt->arm_flags
+#define ACPI_ARM_PSCI_COMPLIANT (1 << 0)
+#define ACPI_ARM_PSCI_USE_HVC (1 << 1)
+
+UACPI_PACKED(struct acpi_fadt {
+ struct acpi_sdt_hdr hdr;
+ uacpi_u32 firmware_ctrl;
+ uacpi_u32 dsdt;
+ uacpi_u8 int_model;
+ uacpi_u8 preferred_pm_profile;
+ uacpi_u16 sci_int;
+ uacpi_u32 smi_cmd;
+ uacpi_u8 acpi_enable;
+ uacpi_u8 acpi_disable;
+ uacpi_u8 s4bios_req;
+ uacpi_u8 pstate_cnt;
+ uacpi_u32 pm1a_evt_blk;
+ uacpi_u32 pm1b_evt_blk;
+ uacpi_u32 pm1a_cnt_blk;
+ uacpi_u32 pm1b_cnt_blk;
+ uacpi_u32 pm2_cnt_blk;
+ uacpi_u32 pm_tmr_blk;
+ uacpi_u32 gpe0_blk;
+ uacpi_u32 gpe1_blk;
+ uacpi_u8 pm1_evt_len;
+ uacpi_u8 pm1_cnt_len;
+ uacpi_u8 pm2_cnt_len;
+ uacpi_u8 pm_tmr_len;
+ uacpi_u8 gpe0_blk_len;
+ uacpi_u8 gpe1_blk_len;
+ uacpi_u8 gpe1_base;
+ uacpi_u8 cst_cnt;
+ uacpi_u16 p_lvl2_lat;
+ uacpi_u16 p_lvl3_lat;
+ uacpi_u16 flush_size;
+ uacpi_u16 flush_stride;
+ uacpi_u8 duty_offset;
+ uacpi_u8 duty_width;
+ uacpi_u8 day_alrm;
+ uacpi_u8 mon_alrm;
+ uacpi_u8 century;
+ uacpi_u16 iapc_boot_arch;
+ uacpi_u8 rsvd;
+ uacpi_u32 flags;
+ struct acpi_gas reset_reg;
+ uacpi_u8 reset_value;
+ uacpi_u16 arm_boot_arch;
+ uacpi_u8 fadt_minor_verison;
+ uacpi_u64 x_firmware_ctrl;
+ uacpi_u64 x_dsdt;
+ struct acpi_gas x_pm1a_evt_blk;
+ struct acpi_gas x_pm1b_evt_blk;
+ struct acpi_gas x_pm1a_cnt_blk;
+ struct acpi_gas x_pm1b_cnt_blk;
+ struct acpi_gas x_pm2_cnt_blk;
+ struct acpi_gas x_pm_tmr_blk;
+ struct acpi_gas x_gpe0_blk;
+ struct acpi_gas x_gpe1_blk;
+ struct acpi_gas sleep_control_reg;
+ struct acpi_gas sleep_status_reg;
+ uacpi_u64 hypervisor_vendor_identity;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_fadt, 276);
+
+// acpi_facs->flags
+#define ACPI_S4BIOS_F (1 << 0)
+#define ACPI_64BIT_WAKE_SUPPORTED_F (1 << 1)
+
+// acpi_facs->ospm_flags
+#define ACPI_64BIT_WAKE_F (1 << 0)
+
+struct acpi_facs {
+ uacpi_char signature[4];
+ uacpi_u32 length;
+ uacpi_u32 hardware_signature;
+ uacpi_u32 firmware_waking_vector;
+ uacpi_u32 global_lock;
+ uacpi_u32 flags;
+ uacpi_u64 x_firmware_waking_vector;
+ uacpi_u8 version;
+ uacpi_char rsvd0[3];
+ uacpi_u32 ospm_flags;
+ uacpi_char rsvd1[24];
+};
+UACPI_EXPECT_SIZEOF(struct acpi_facs, 64);
+
+UACPI_PACKED(struct acpi_mcfg_allocation {
+ uacpi_u64 address;
+ uacpi_u16 segment;
+ uacpi_u8 start_bus;
+ uacpi_u8 end_bus;
+ uacpi_u32 rsvd;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_mcfg_allocation, 16);
+
+UACPI_PACKED(struct acpi_mcfg {
+ struct acpi_sdt_hdr hdr;
+ uacpi_u64 rsvd;
+ struct acpi_mcfg_allocation entries[];
+})
+UACPI_EXPECT_SIZEOF(struct acpi_mcfg, 44);
+
+// acpi_hpet->block_id
+#define ACPI_HPET_PCI_VENDOR_ID_SHIFT 16
+#define ACPI_HPET_LEGACY_REPLACEMENT_IRQ_ROUTING_CAPABLE (1 << 15)
+#define ACPI_HPET_COUNT_SIZE_CAP (1 << 13)
+#define ACPI_HPET_NUMBER_OF_COMPARATORS_SHIFT 8
+#define ACPI_HPET_NUMBER_OF_COMPARATORS_MASK 0b11111
+#define ACPI_HPET_HARDWARE_REV_ID_MASK 0b11111111
+
+// acpi_hpet->flags
+#define ACPI_HPET_PAGE_PROTECTION_MASK 0b11
+#define ACPI_HPET_PAGE_NO_PROTECTION 0
+#define ACPI_HPET_PAGE_4K_PROTECTED 1
+#define ACPI_HPET_PAGE_64K_PROTECTED 2
+
+UACPI_PACKED(struct acpi_hpet {
+ struct acpi_sdt_hdr hdr;
+ uacpi_u32 block_id;
+ struct acpi_gas address;
+ uacpi_u8 number;
+ uacpi_u16 min_clock_tick;
+ uacpi_u8 flags;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_hpet, 56);
+
+// PM1{a,b}_STS
+#define ACPI_PM1_STS_TMR_STS_IDX 0
+#define ACPI_PM1_STS_BM_STS_IDX 4
+#define ACPI_PM1_STS_GBL_STS_IDX 5
+#define ACPI_PM1_STS_PWRBTN_STS_IDX 8
+#define ACPI_PM1_STS_SLPBTN_STS_IDX 9
+#define ACPI_PM1_STS_RTC_STS_IDX 10
+#define ACPI_PM1_STS_IGN0_IDX 11
+#define ACPI_PM1_STS_PCIEXP_WAKE_STS_IDX 14
+#define ACPI_PM1_STS_WAKE_STS_IDX 15
+
+#define ACPI_PM1_STS_TMR_STS_MASK (1 << ACPI_PM1_STS_TMR_STS_IDX)
+#define ACPI_PM1_STS_BM_STS_MASK (1 << ACPI_PM1_STS_BM_STS_IDX)
+#define ACPI_PM1_STS_GBL_STS_MASK (1 << ACPI_PM1_STS_GBL_STS_IDX)
+#define ACPI_PM1_STS_PWRBTN_STS_MASK (1 << ACPI_PM1_STS_PWRBTN_STS_IDX)
+#define ACPI_PM1_STS_SLPBTN_STS_MASK (1 << ACPI_PM1_STS_SLPBTN_STS_IDX)
+#define ACPI_PM1_STS_RTC_STS_MASK (1 << ACPI_PM1_STS_RTC_STS_IDX)
+#define ACPI_PM1_STS_IGN0_MASK (1 << ACPI_PM1_STS_IGN0_IDX)
+#define ACPI_PM1_STS_PCIEXP_WAKE_STS_MASK (1 << ACPI_PM1_STS_PCIEXP_WAKE_STS_IDX)
+#define ACPI_PM1_STS_WAKE_STS_MASK (1 << ACPI_PM1_STS_WAKE_STS_IDX)
+
+#define ACPI_PM1_STS_CLEAR 1
+
+// PM1{a,b}_EN
+#define ACPI_PM1_EN_TMR_EN_IDX 0
+#define ACPI_PM1_EN_GBL_EN_IDX 5
+#define ACPI_PM1_EN_PWRBTN_EN_IDX 8
+#define ACPI_PM1_EN_SLPBTN_EN_IDX 9
+#define ACPI_PM1_EN_RTC_EN_IDX 10
+#define ACPI_PM1_EN_PCIEXP_WAKE_DIS_IDX 14
+
+#define ACPI_PM1_EN_TMR_EN_MASK (1 << ACPI_PM1_EN_TMR_EN_IDX)
+#define ACPI_PM1_EN_GBL_EN_MASK (1 << ACPI_PM1_EN_GBL_EN_IDX)
+#define ACPI_PM1_EN_PWRBTN_EN_MASK (1 << ACPI_PM1_EN_PWRBTN_EN_IDX)
+#define ACPI_PM1_EN_SLPBTN_EN_MASK (1 << ACPI_PM1_EN_SLPBTN_EN_IDX)
+#define ACPI_PM1_EN_RTC_EN_MASK (1 << ACPI_PM1_EN_RTC_EN_IDX)
+#define ACPI_PM1_EN_PCIEXP_WAKE_DIS_MASK (1 << ACPI_PM1_EN_PCIEXP_WAKE_DIS_IDX)
+
+// PM1{a,b}_CNT_BLK
+#define ACPI_PM1_CNT_SCI_EN_IDX 0
+#define ACPI_PM1_CNT_BM_RLD_IDX 1
+#define ACPI_PM1_CNT_GBL_RLS_IDX 2
+#define ACPI_PM1_CNT_RSVD0_IDX 3
+#define ACPI_PM1_CNT_RSVD1_IDX 4
+#define ACPI_PM1_CNT_RSVD2_IDX 5
+#define ACPI_PM1_CNT_RSVD3_IDX 6
+#define ACPI_PM1_CNT_RSVD4_IDX 7
+#define ACPI_PM1_CNT_RSVD5_IDX 8
+#define ACPI_PM1_CNT_IGN0_IDX 9
+#define ACPI_PM1_CNT_SLP_TYP_IDX 10
+#define ACPI_PM1_CNT_SLP_EN_IDX 13
+#define ACPI_PM1_CNT_RSVD6_IDX 14
+#define ACPI_PM1_CNT_RSVD7_IDX 15
+
+#define ACPI_SLP_TYP_MAX 0x7
+
+#define ACPI_PM1_CNT_SCI_EN_MASK (1 << ACPI_PM1_CNT_SCI_EN_IDX)
+#define ACPI_PM1_CNT_BM_RLD_MASK (1 << ACPI_PM1_CNT_BM_RLD_IDX)
+#define ACPI_PM1_CNT_GBL_RLS_MASK (1 << ACPI_PM1_CNT_GBL_RLS_IDX)
+#define ACPI_PM1_CNT_SLP_TYP_MASK (ACPI_SLP_TYP_MAX << ACPI_PM1_CNT_SLP_TYP_IDX)
+#define ACPI_PM1_CNT_SLP_EN_MASK (1 << ACPI_PM1_CNT_SLP_EN_IDX)
+
+/*
+ * SCI_EN is not in this mask even though the spec says it must be preserved.
+ * This is because it's known to be bugged on some hardware that relies on
+ * software writing 1 to it after resume (as indicated by a similar comment in
+ * ACPICA)
+ */
+#define ACPI_PM1_CNT_PRESERVE_MASK ( \
+ (1 << ACPI_PM1_CNT_RSVD0_IDX) | \
+ (1 << ACPI_PM1_CNT_RSVD1_IDX) | \
+ (1 << ACPI_PM1_CNT_RSVD2_IDX) | \
+ (1 << ACPI_PM1_CNT_RSVD3_IDX) | \
+ (1 << ACPI_PM1_CNT_RSVD4_IDX) | \
+ (1 << ACPI_PM1_CNT_RSVD5_IDX) | \
+ (1 << ACPI_PM1_CNT_IGN0_IDX ) | \
+ (1 << ACPI_PM1_CNT_RSVD6_IDX) | \
+ (1 << ACPI_PM1_CNT_RSVD7_IDX) \
+)
+
+// PM2_CNT
+#define ACPI_PM2_CNT_ARB_DIS_IDX 0
+#define ACPI_PM2_CNT_ARB_DIS_MASK (1 << ACPI_PM2_CNT_ARB_DIS_IDX)
+
+// All bits are reserved but this first one
+#define ACPI_PM2_CNT_PRESERVE_MASK (~((uacpi_u64)ACPI_PM2_CNT_ARB_DIS_MASK))
+
+// SLEEP_CONTROL_REG
+#define ACPI_SLP_CNT_RSVD0_IDX 0
+#define ACPI_SLP_CNT_IGN0_IDX 1
+#define ACPI_SLP_CNT_SLP_TYP_IDX 2
+#define ACPI_SLP_CNT_SLP_EN_IDX 5
+#define ACPI_SLP_CNT_RSVD1_IDX 6
+#define ACPI_SLP_CNT_RSVD2_IDX 7
+
+#define ACPI_SLP_CNT_SLP_TYP_MASK (ACPI_SLP_TYP_MAX << ACPI_SLP_CNT_SLP_TYP_IDX)
+#define ACPI_SLP_CNT_SLP_EN_MASK (1 << ACPI_SLP_CNT_SLP_EN_IDX)
+
+#define ACPI_SLP_CNT_PRESERVE_MASK ( \
+ (1 << ACPI_SLP_CNT_RSVD0_IDX) | \
+ (1 << ACPI_SLP_CNT_IGN0_IDX) | \
+ (1 << ACPI_SLP_CNT_RSVD1_IDX) | \
+ (1 << ACPI_SLP_CNT_RSVD2_IDX) \
+)
+
+// SLEEP_STATUS_REG
+#define ACPI_SLP_STS_WAK_STS_IDX 7
+
+#define ACPI_SLP_STS_WAK_STS_MASK (1 << ACPI_SLP_STS_WAK_STS_IDX)
+
+// All bits are reserved but this last one
+#define ACPI_SLP_STS_PRESERVE_MASK (~((uacpi_u64)ACPI_SLP_STS_WAK_STS_MASK))
+
+#define ACPI_SLP_STS_CLEAR 1
+
+UACPI_PACKED(struct acpi_dsdt {
+ struct acpi_sdt_hdr hdr;
+ uacpi_u8 definition_block[];
+})
+
+UACPI_PACKED(struct acpi_ssdt {
+ struct acpi_sdt_hdr hdr;
+ uacpi_u8 definition_block[];
+})
+
+/*
+ * ACPI 6.5 specification:
+ * Bit [0] - Set if the device is present.
+ * Bit [1] - Set if the device is enabled and decoding its resources.
+ * Bit [2] - Set if the device should be shown in the UI.
+ * Bit [3] - Set if the device is functioning properly (cleared if device
+ * failed its diagnostics).
+ * Bit [4] - Set if the battery is present.
+ */
+#define ACPI_STA_RESULT_DEVICE_PRESENT (1 << 0)
+#define ACPI_STA_RESULT_DEVICE_ENABLED (1 << 1)
+#define ACPI_STA_RESULT_DEVICE_SHOWN_IN_UI (1 << 2)
+#define ACPI_STA_RESULT_DEVICE_FUNCTIONING (1 << 3)
+#define ACPI_STA_RESULT_DEVICE_BATTERY_PRESENT (1 << 4)
+
+#define ACPI_REG_DISCONNECT 0
+#define ACPI_REG_CONNECT 1
+
+UACPI_PACKED(struct acpi_ecdt {
+ struct acpi_sdt_hdr hdr;
+ struct acpi_gas ec_control;
+ struct acpi_gas ec_data;
+ uacpi_u32 uid;
+ uacpi_u8 gpe_bit;
+ uacpi_char ec_id[];
+})
+UACPI_EXPECT_SIZEOF(struct acpi_ecdt, 65);
+
+UACPI_PACKED(struct acpi_rhct_hdr {
+ uacpi_u16 type;
+ uacpi_u16 length;
+ uacpi_u16 revision;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_rhct_hdr, 6);
+
+// acpi_rhct->flags
+#define ACPI_TIMER_CANNOT_WAKE_CPU (1 << 0)
+
+UACPI_PACKED(struct acpi_rhct {
+ struct acpi_sdt_hdr hdr;
+ uacpi_u32 flags;
+ uacpi_u64 timebase_frequency;
+ uacpi_u32 node_count;
+ uacpi_u32 nodes_offset;
+ struct acpi_rhct_hdr entries[];
+})
+UACPI_EXPECT_SIZEOF(struct acpi_rhct, 56);
+
+enum acpi_rhct_entry_type {
+ ACPI_RHCT_ENTRY_TYPE_ISA_STRING = 0,
+ ACPI_RHCT_ENTRY_TYPE_CMO = 1,
+ ACPI_RHCT_ENTRY_TYPE_MMU = 2,
+ ACPI_RHCT_ENTRY_TYPE_HART_INFO = 65535,
+};
+
+UACPI_PACKED(struct acpi_rhct_isa_string {
+ struct acpi_rhct_hdr hdr;
+ uacpi_u16 length;
+ uacpi_u8 isa[];
+})
+UACPI_EXPECT_SIZEOF(struct acpi_rhct_isa_string, 8);
+
+UACPI_PACKED(struct acpi_rhct_cmo {
+ struct acpi_rhct_hdr hdr;
+ uacpi_u8 rsvd;
+ uacpi_u8 cbom_size;
+ uacpi_u8 cbop_size;
+ uacpi_u8 cboz_size;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_rhct_cmo, 10);
+
+enum acpi_rhct_mmu_type {
+ ACPI_RHCT_MMU_TYPE_SV39 = 0,
+ ACPI_RHCT_MMU_TYPE_SV48 = 1,
+ ACPI_RHCT_MMU_TYPE_SV57 = 2,
+};
+
+UACPI_PACKED(struct acpi_rhct_mmu {
+ struct acpi_rhct_hdr hdr;
+ uacpi_u8 rsvd;
+ uacpi_u8 type;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_rhct_mmu, 8);
+
+UACPI_PACKED(struct acpi_rhct_hart_info {
+ struct acpi_rhct_hdr hdr;
+ uacpi_u16 offset_count;
+ uacpi_u32 uid;
+ uacpi_u32 offsets[];
+})
+UACPI_EXPECT_SIZEOF(struct acpi_rhct_hart_info, 12);
+
+#define ACPI_LARGE_ITEM (1 << 7)
+
+#define ACPI_SMALL_ITEM_NAME_IDX 3
+#define ACPI_SMALL_ITEM_NAME_MASK 0xF
+#define ACPI_SMALL_ITEM_LENGTH_MASK 0x7
+
+#define ACPI_LARGE_ITEM_NAME_MASK 0x7F
+
+// Small items
+#define ACPI_RESOURCE_IRQ 0x04
+#define ACPI_RESOURCE_DMA 0x05
+#define ACPI_RESOURCE_START_DEPENDENT 0x06
+#define ACPI_RESOURCE_END_DEPENDENT 0x07
+#define ACPI_RESOURCE_IO 0x08
+#define ACPI_RESOURCE_FIXED_IO 0x09
+#define ACPI_RESOURCE_FIXED_DMA 0x0A
+#define ACPI_RESOURCE_VENDOR_TYPE0 0x0E
+#define ACPI_RESOURCE_END_TAG 0x0F
+
+// Large items
+#define ACPI_RESOURCE_MEMORY24 0x01
+#define ACPI_RESOURCE_GENERIC_REGISTER 0x02
+#define ACPI_RESOURCE_VENDOR_TYPE1 0x04
+#define ACPI_RESOURCE_MEMORY32 0x05
+#define ACPI_RESOURCE_FIXED_MEMORY32 0x06
+#define ACPI_RESOURCE_ADDRESS32 0x07
+#define ACPI_RESOURCE_ADDRESS16 0x08
+#define ACPI_RESOURCE_EXTENDED_IRQ 0x09
+#define ACPI_RESOURCE_ADDRESS64 0x0A
+#define ACPI_RESOURCE_ADDRESS64_EXTENDED 0x0B
+#define ACPI_RESOURCE_GPIO_CONNECTION 0x0C
+#define ACPI_RESOURCE_PIN_FUNCTION 0x0D
+#define ACPI_RESOURCE_SERIAL_CONNECTION 0x0E
+#define ACPI_RESOURCE_PIN_CONFIGURATION 0x0F
+#define ACPI_RESOURCE_PIN_GROUP 0x10
+#define ACPI_RESOURCE_PIN_GROUP_FUNCTION 0x11
+#define ACPI_RESOURCE_PIN_GROUP_CONFIGURATION 0x12
+#define ACPI_RESOURCE_CLOCK_INPUT 0x13
+
+/*
+ * Resources as encoded by the raw AML byte stream.
+ * For decode API & human usable structures refer to uacpi/resources.h
+ */
+UACPI_PACKED(struct acpi_small_item {
+ uacpi_u8 type_and_length;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_small_item, 1);
+
+UACPI_PACKED(struct acpi_resource_irq {
+ struct acpi_small_item common;
+ uacpi_u16 irq_mask;
+ uacpi_u8 flags;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_resource_irq, 4);
+
+UACPI_PACKED(struct acpi_resource_dma {
+ struct acpi_small_item common;
+ uacpi_u8 channel_mask;
+ uacpi_u8 flags;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_resource_dma, 3);
+
+UACPI_PACKED(struct acpi_resource_start_dependent {
+ struct acpi_small_item common;
+ uacpi_u8 flags;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_resource_start_dependent, 2);
+
+UACPI_PACKED(struct acpi_resource_end_dependent {
+ struct acpi_small_item common;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_resource_end_dependent, 1);
+
+UACPI_PACKED(struct acpi_resource_io {
+ struct acpi_small_item common;
+ uacpi_u8 information;
+ uacpi_u16 minimum;
+ uacpi_u16 maximum;
+ uacpi_u8 alignment;
+ uacpi_u8 length;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_resource_io, 8);
+
+UACPI_PACKED(struct acpi_resource_fixed_io {
+ struct acpi_small_item common;
+ uacpi_u16 address;
+ uacpi_u8 length;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_resource_fixed_io, 4);
+
+UACPI_PACKED(struct acpi_resource_fixed_dma {
+ struct acpi_small_item common;
+ uacpi_u16 request_line;
+ uacpi_u16 channel;
+ uacpi_u8 transfer_width;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_resource_fixed_dma, 6);
+
+UACPI_PACKED(struct acpi_resource_vendor_defined_type0 {
+ struct acpi_small_item common;
+ uacpi_u8 byte_data[];
+})
+UACPI_EXPECT_SIZEOF(struct acpi_resource_vendor_defined_type0, 1);
+
+UACPI_PACKED(struct acpi_resource_end_tag {
+ struct acpi_small_item common;
+ uacpi_u8 checksum;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_resource_end_tag, 2);
+
+UACPI_PACKED(struct acpi_large_item {
+ uacpi_u8 type;
+ uacpi_u16 length;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_large_item, 3);
+
+UACPI_PACKED(struct acpi_resource_memory24 {
+ struct acpi_large_item common;
+ uacpi_u8 information;
+ uacpi_u16 minimum;
+ uacpi_u16 maximum;
+ uacpi_u16 alignment;
+ uacpi_u16 length;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_resource_memory24, 12);
+
+UACPI_PACKED(struct acpi_resource_vendor_defined_type1 {
+ struct acpi_large_item common;
+ uacpi_u8 byte_data[];
+})
+UACPI_EXPECT_SIZEOF(struct acpi_resource_vendor_defined_type1, 3);
+
+UACPI_PACKED(struct acpi_resource_memory32 {
+ struct acpi_large_item common;
+ uacpi_u8 information;
+ uacpi_u32 minimum;
+ uacpi_u32 maximum;
+ uacpi_u32 alignment;
+ uacpi_u32 length;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_resource_memory32, 20);
+
+UACPI_PACKED(struct acpi_resource_fixed_memory32 {
+ struct acpi_large_item common;
+ uacpi_u8 information;
+ uacpi_u32 address;
+ uacpi_u32 length;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_resource_fixed_memory32, 12);
+
+UACPI_PACKED(struct acpi_resource_address {
+ struct acpi_large_item common;
+ uacpi_u8 type;
+ uacpi_u8 flags;
+ uacpi_u8 type_flags;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_resource_address, 6);
+
+UACPI_PACKED(struct acpi_resource_address64 {
+ struct acpi_resource_address common;
+ uacpi_u64 granularity;
+ uacpi_u64 minimum;
+ uacpi_u64 maximum;
+ uacpi_u64 translation_offset;
+ uacpi_u64 length;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_resource_address64, 46);
+
+UACPI_PACKED(struct acpi_resource_address32 {
+ struct acpi_resource_address common;
+ uacpi_u32 granularity;
+ uacpi_u32 minimum;
+ uacpi_u32 maximum;
+ uacpi_u32 translation_offset;
+ uacpi_u32 length;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_resource_address32, 26);
+
+UACPI_PACKED(struct acpi_resource_address16 {
+ struct acpi_resource_address common;
+ uacpi_u16 granularity;
+ uacpi_u16 minimum;
+ uacpi_u16 maximum;
+ uacpi_u16 translation_offset;
+ uacpi_u16 length;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_resource_address16, 16);
+
+UACPI_PACKED(struct acpi_resource_address64_extended {
+ struct acpi_resource_address common;
+ uacpi_u8 revision_id;
+ uacpi_u8 rsvd;
+ uacpi_u64 granularity;
+ uacpi_u64 minimum;
+ uacpi_u64 maximum;
+ uacpi_u64 translation_offset;
+ uacpi_u64 length;
+ uacpi_u64 attributes;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_resource_address64_extended, 56);
+
+UACPI_PACKED(struct acpi_resource_extended_irq {
+ struct acpi_large_item common;
+ uacpi_u8 flags;
+ uacpi_u8 num_irqs;
+ uacpi_u32 irqs[];
+})
+UACPI_EXPECT_SIZEOF(struct acpi_resource_extended_irq, 5);
+
+UACPI_PACKED(struct acpi_resource_generic_register {
+ struct acpi_large_item common;
+ uacpi_u8 address_space_id;
+ uacpi_u8 bit_width;
+ uacpi_u8 bit_offset;
+ uacpi_u8 access_size;
+ uacpi_u64 address;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_resource_generic_register, 15);
+
+UACPI_PACKED(struct acpi_resource_gpio_connection {
+ struct acpi_large_item common;
+ uacpi_u8 revision_id;
+ uacpi_u8 type;
+ uacpi_u16 general_flags;
+ uacpi_u16 connection_flags;
+ uacpi_u8 pull_configuration;
+ uacpi_u16 drive_strength;
+ uacpi_u16 debounce_timeout;
+ uacpi_u16 pin_table_offset;
+ uacpi_u8 source_index;
+ uacpi_u16 source_offset;
+ uacpi_u16 vendor_data_offset;
+ uacpi_u16 vendor_data_length;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_resource_gpio_connection, 23);
+
+#define ACPI_SERIAL_TYPE_I2C 1
+#define ACPI_SERIAL_TYPE_SPI 2
+#define ACPI_SERIAL_TYPE_UART 3
+#define ACPI_SERIAL_TYPE_CSI2 4
+#define ACPI_SERIAL_TYPE_MAX ACPI_SERIAL_TYPE_CSI2
+
+UACPI_PACKED(struct acpi_resource_serial {
+ struct acpi_large_item common;
+ uacpi_u8 revision_id;
+ uacpi_u8 source_index;
+ uacpi_u8 type;
+ uacpi_u8 flags;
+ uacpi_u16 type_specific_flags;
+ uacpi_u8 type_specific_revision_id;
+ uacpi_u16 type_data_length;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_resource_serial, 12);
+
+UACPI_PACKED(struct acpi_resource_serial_i2c {
+ struct acpi_resource_serial common;
+ uacpi_u32 connection_speed;
+ uacpi_u16 slave_address;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_resource_serial_i2c, 18);
+
+UACPI_PACKED(struct acpi_resource_serial_spi {
+ struct acpi_resource_serial common;
+ uacpi_u32 connection_speed;
+ uacpi_u8 data_bit_length;
+ uacpi_u8 phase;
+ uacpi_u8 polarity;
+ uacpi_u16 device_selection;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_resource_serial_spi, 21);
+
+UACPI_PACKED(struct acpi_resource_serial_uart {
+ struct acpi_resource_serial common;
+ uacpi_u32 baud_rate;
+ uacpi_u16 rx_fifo;
+ uacpi_u16 tx_fifo;
+ uacpi_u8 parity;
+ uacpi_u8 lines_enabled;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_resource_serial_uart, 22);
+
+UACPI_PACKED(struct acpi_resource_serial_csi2 {
+ struct acpi_resource_serial common;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_resource_serial_csi2, 12);
+
+UACPI_PACKED(struct acpi_resource_pin_function {
+ struct acpi_large_item common;
+ uacpi_u8 revision_id;
+ uacpi_u16 flags;
+ uacpi_u8 pull_configuration;
+ uacpi_u16 function_number;
+ uacpi_u16 pin_table_offset;
+ uacpi_u8 source_index;
+ uacpi_u16 source_offset;
+ uacpi_u16 vendor_data_offset;
+ uacpi_u16 vendor_data_length;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_resource_pin_function, 18);
+
+UACPI_PACKED(struct acpi_resource_pin_configuration {
+ struct acpi_large_item common;
+ uacpi_u8 revision_id;
+ uacpi_u16 flags;
+ uacpi_u8 type;
+ uacpi_u32 value;
+ uacpi_u16 pin_table_offset;
+ uacpi_u8 source_index;
+ uacpi_u16 source_offset;
+ uacpi_u16 vendor_data_offset;
+ uacpi_u16 vendor_data_length;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_resource_pin_configuration, 20);
+
+UACPI_PACKED(struct acpi_resource_pin_group {
+ struct acpi_large_item common;
+ uacpi_u8 revision_id;
+ uacpi_u16 flags;
+ uacpi_u16 pin_table_offset;
+ uacpi_u16 source_lable_offset;
+ uacpi_u16 vendor_data_offset;
+ uacpi_u16 vendor_data_length;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_resource_pin_group, 14);
+
+UACPI_PACKED(struct acpi_resource_pin_group_function {
+ struct acpi_large_item common;
+ uacpi_u8 revision_id;
+ uacpi_u16 flags;
+ uacpi_u16 function;
+ uacpi_u8 source_index;
+ uacpi_u16 source_offset;
+ uacpi_u16 source_lable_offset;
+ uacpi_u16 vendor_data_offset;
+ uacpi_u16 vendor_data_length;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_resource_pin_group_function, 17);
+
+UACPI_PACKED(struct acpi_resource_pin_group_configuration {
+ struct acpi_large_item common;
+ uacpi_u8 revision_id;
+ uacpi_u16 flags;
+ uacpi_u8 type;
+ uacpi_u32 value;
+ uacpi_u8 source_index;
+ uacpi_u16 source_offset;
+ uacpi_u16 source_lable_offset;
+ uacpi_u16 vendor_data_offset;
+ uacpi_u16 vendor_data_length;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_resource_pin_group_configuration, 20);
+
+UACPI_PACKED(struct acpi_resource_clock_input {
+ struct acpi_large_item common;
+ uacpi_u8 revision_id;
+ uacpi_u16 flags;
+ uacpi_u16 divisor;
+ uacpi_u32 numerator;
+ uacpi_u8 source_index;
+})
+UACPI_EXPECT_SIZEOF(struct acpi_resource_clock_input, 13);
diff --git a/sys/include/dev/acpi/uacpi/uacpi/context.h b/sys/include/dev/acpi/uacpi/uacpi/context.h
new file mode 100644
index 0000000..d5a46e5
--- /dev/null
+++ b/sys/include/dev/acpi/uacpi/uacpi/context.h
@@ -0,0 +1,53 @@
+#pragma once
+
+#include <uacpi/types.h>
+#include <uacpi/log.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Set the minimum log level to be accepted by the logging facilities. Any logs
+ * below this level are discarded and not passed to uacpi_kernel_log, etc.
+ *
+ * 0 is treated as a special value that resets the setting to the default value.
+ *
+ * E.g. for a log level of UACPI_LOG_INFO:
+ * UACPI_LOG_DEBUG -> discarded
+ * UACPI_LOG_TRACE -> discarded
+ * UACPI_LOG_INFO -> allowed
+ * UACPI_LOG_WARN -> allowed
+ * UACPI_LOG_ERROR -> allowed
+ */
+void uacpi_context_set_log_level(uacpi_log_level);
+
+/*
+ * Enables table checksum validation at installation time instead of first use.
+ * Note that this makes uACPI map the entire table at once, which not all
+ * hosts are able to handle at early init.
+ */
+void uacpi_context_set_proactive_table_checksum(uacpi_bool);
+
+#ifndef UACPI_BAREBONES_MODE
+/*
+ * Set the maximum number of seconds a While loop is allowed to run for before
+ * getting timed out.
+ *
+ * 0 is treated a special value that resets the setting to the default value.
+ */
+void uacpi_context_set_loop_timeout(uacpi_u32 seconds);
+
+/*
+ * Set the maximum call stack depth AML can reach before getting aborted.
+ *
+ * 0 is treated as a special value that resets the setting to the default value.
+ */
+void uacpi_context_set_max_call_stack_depth(uacpi_u32 depth);
+
+uacpi_u32 uacpi_context_get_loop_timeout(void);
+#endif // !UACPI_BAREBONES_MODE
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/sys/include/dev/acpi/uacpi/uacpi/event.h b/sys/include/dev/acpi/uacpi/uacpi/event.h
new file mode 100644
index 0000000..a21fe6e
--- /dev/null
+++ b/sys/include/dev/acpi/uacpi/uacpi/event.h
@@ -0,0 +1,286 @@
+#pragma once
+
+#include <uacpi/types.h>
+#include <uacpi/uacpi.h>
+#include <uacpi/acpi.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef UACPI_BAREBONES_MODE
+
+typedef enum uacpi_fixed_event {
+ UACPI_FIXED_EVENT_TIMER_STATUS = 1,
+ UACPI_FIXED_EVENT_POWER_BUTTON,
+ UACPI_FIXED_EVENT_SLEEP_BUTTON,
+ UACPI_FIXED_EVENT_RTC,
+ UACPI_FIXED_EVENT_MAX = UACPI_FIXED_EVENT_RTC,
+} uacpi_fixed_event;
+
+UACPI_ALWAYS_ERROR_FOR_REDUCED_HARDWARE(
+uacpi_status uacpi_install_fixed_event_handler(
+ uacpi_fixed_event event, uacpi_interrupt_handler handler, uacpi_handle user
+))
+
+UACPI_ALWAYS_ERROR_FOR_REDUCED_HARDWARE(
+uacpi_status uacpi_uninstall_fixed_event_handler(
+ uacpi_fixed_event event
+))
+
+/*
+ * Enable/disable a fixed event. Note that the event is automatically enabled
+ * upon installing a handler to it.
+ */
+UACPI_ALWAYS_ERROR_FOR_REDUCED_HARDWARE(
+ uacpi_status uacpi_enable_fixed_event(uacpi_fixed_event event)
+)
+UACPI_ALWAYS_ERROR_FOR_REDUCED_HARDWARE(
+ uacpi_status uacpi_disable_fixed_event(uacpi_fixed_event event)
+)
+
+UACPI_ALWAYS_ERROR_FOR_REDUCED_HARDWARE(
+ uacpi_status uacpi_clear_fixed_event(uacpi_fixed_event event)
+)
+
+typedef enum uacpi_event_info {
+ // Event is enabled in software
+ UACPI_EVENT_INFO_ENABLED = (1 << 0),
+
+ // Event is enabled in software (only for wake)
+ UACPI_EVENT_INFO_ENABLED_FOR_WAKE = (1 << 1),
+
+ // Event is masked
+ UACPI_EVENT_INFO_MASKED = (1 << 2),
+
+ // Event has a handler attached
+ UACPI_EVENT_INFO_HAS_HANDLER = (1 << 3),
+
+ // Hardware enable bit is set
+ UACPI_EVENT_INFO_HW_ENABLED = (1 << 4),
+
+ // Hardware status bit is set
+ UACPI_EVENT_INFO_HW_STATUS = (1 << 5),
+} uacpi_event_info;
+
+UACPI_ALWAYS_ERROR_FOR_REDUCED_HARDWARE(
+uacpi_status uacpi_fixed_event_info(
+ uacpi_fixed_event event, uacpi_event_info *out_info
+))
+
+UACPI_ALWAYS_ERROR_FOR_REDUCED_HARDWARE(
+uacpi_status uacpi_gpe_info(
+ uacpi_namespace_node *gpe_device, uacpi_u16 idx,
+ uacpi_event_info *out_info
+))
+
+// Set if the handler wishes to reenable the GPE it just handled
+#define UACPI_GPE_REENABLE (1 << 7)
+
+typedef uacpi_interrupt_ret (*uacpi_gpe_handler)(
+ uacpi_handle ctx, uacpi_namespace_node *gpe_device, uacpi_u16 idx
+);
+
+typedef enum uacpi_gpe_triggering {
+ UACPI_GPE_TRIGGERING_LEVEL = 0,
+ UACPI_GPE_TRIGGERING_EDGE = 1,
+ UACPI_GPE_TRIGGERING_MAX = UACPI_GPE_TRIGGERING_EDGE,
+} uacpi_gpe_triggering;
+
+const uacpi_char *uacpi_gpe_triggering_to_string(
+ uacpi_gpe_triggering triggering
+);
+
+/*
+ * Installs a handler to the provided GPE at 'idx' controlled by device
+ * 'gpe_device'. The GPE is automatically disabled & cleared according to the
+ * configured triggering upon invoking the handler. The event is optionally
+ * re-enabled (by returning UACPI_GPE_REENABLE from the handler)
+ *
+ * NOTE: 'gpe_device' may be null for GPEs managed by \_GPE
+ */
+UACPI_ALWAYS_ERROR_FOR_REDUCED_HARDWARE(
+uacpi_status uacpi_install_gpe_handler(
+ uacpi_namespace_node *gpe_device, uacpi_u16 idx,
+ uacpi_gpe_triggering triggering, uacpi_gpe_handler handler, uacpi_handle ctx
+))
+
+/*
+ * Installs a raw handler to the provided GPE at 'idx' controlled by device
+ * 'gpe_device'. The handler is dispatched immediately after the event is
+ * received, status & enable bits are untouched.
+ *
+ * NOTE: 'gpe_device' may be null for GPEs managed by \_GPE
+ */
+UACPI_ALWAYS_ERROR_FOR_REDUCED_HARDWARE(
+uacpi_status uacpi_install_gpe_handler_raw(
+ uacpi_namespace_node *gpe_device, uacpi_u16 idx,
+ uacpi_gpe_triggering triggering, uacpi_gpe_handler handler, uacpi_handle ctx
+))
+
+UACPI_ALWAYS_ERROR_FOR_REDUCED_HARDWARE(
+uacpi_status uacpi_uninstall_gpe_handler(
+ uacpi_namespace_node *gpe_device, uacpi_u16 idx, uacpi_gpe_handler handler
+))
+
+/*
+ * Marks the GPE 'idx' managed by 'gpe_device' as wake-capable. 'wake_device' is
+ * optional and configures the GPE to generate an implicit notification whenever
+ * an event occurs.
+ *
+ * NOTE: 'gpe_device' may be null for GPEs managed by \_GPE
+ */
+UACPI_ALWAYS_ERROR_FOR_REDUCED_HARDWARE(
+uacpi_status uacpi_setup_gpe_for_wake(
+ uacpi_namespace_node *gpe_device, uacpi_u16 idx,
+ uacpi_namespace_node *wake_device
+))
+
+/*
+ * Mark a GPE managed by 'gpe_device' as enabled/disabled for wake. The GPE must
+ * have previously been marked by calling uacpi_gpe_setup_for_wake. This
+ * function only affects the GPE enable register state following the call to
+ * uacpi_gpe_enable_all_for_wake.
+ *
+ * NOTE: 'gpe_device' may be null for GPEs managed by \_GPE
+ */
+UACPI_ALWAYS_ERROR_FOR_REDUCED_HARDWARE(
+uacpi_status uacpi_enable_gpe_for_wake(
+ uacpi_namespace_node *gpe_device, uacpi_u16 idx
+))
+UACPI_ALWAYS_ERROR_FOR_REDUCED_HARDWARE(
+uacpi_status uacpi_disable_gpe_for_wake(
+ uacpi_namespace_node *gpe_device, uacpi_u16 idx
+))
+
+/*
+ * Finalize GPE initialization by enabling all GPEs not configured for wake and
+ * having a matching AML handler detected.
+ *
+ * This should be called after the kernel power managment subsystem has
+ * enumerated all of the devices, executing their _PRW methods etc., and
+ * marking those it wishes to use for wake by calling uacpi_setup_gpe_for_wake
+ * or uacpi_mark_gpe_for_wake.
+ */
+UACPI_ALWAYS_ERROR_FOR_REDUCED_HARDWARE(
+ uacpi_status uacpi_finalize_gpe_initialization(void)
+)
+
+/*
+ * Enable/disable a general purpose event managed by 'gpe_device'. Internally
+ * this uses reference counting to make sure a GPE is not disabled until all
+ * possible users of it do so. GPEs not marked for wake are enabled
+ * automatically so this API is only needed for wake events or those that don't
+ * have a corresponding AML handler.
+ *
+ * NOTE: 'gpe_device' may be null for GPEs managed by \_GPE
+ */
+UACPI_ALWAYS_ERROR_FOR_REDUCED_HARDWARE(
+uacpi_status uacpi_enable_gpe(
+ uacpi_namespace_node *gpe_device, uacpi_u16 idx
+))
+UACPI_ALWAYS_ERROR_FOR_REDUCED_HARDWARE(
+uacpi_status uacpi_disable_gpe(
+ uacpi_namespace_node *gpe_device, uacpi_u16 idx
+))
+
+/*
+ * Clear the status bit of the event 'idx' managed by 'gpe_device'.
+ *
+ * NOTE: 'gpe_device' may be null for GPEs managed by \_GPE
+ */
+UACPI_ALWAYS_ERROR_FOR_REDUCED_HARDWARE(
+uacpi_status uacpi_clear_gpe(
+ uacpi_namespace_node *gpe_device, uacpi_u16 idx
+))
+
+/*
+ * Suspend/resume a general purpose event managed by 'gpe_device'. This bypasses
+ * the reference counting mechanism and unconditionally clears/sets the
+ * corresponding bit in the enable registers. This is used for switching the GPE
+ * to poll mode.
+ *
+ * NOTE: 'gpe_device' may be null for GPEs managed by \_GPE
+ */
+UACPI_ALWAYS_ERROR_FOR_REDUCED_HARDWARE(
+uacpi_status uacpi_suspend_gpe(
+ uacpi_namespace_node *gpe_device, uacpi_u16 idx
+))
+UACPI_ALWAYS_ERROR_FOR_REDUCED_HARDWARE(
+uacpi_status uacpi_resume_gpe(
+ uacpi_namespace_node *gpe_device, uacpi_u16 idx
+))
+
+/*
+ * Finish handling the GPE managed by 'gpe_device' at 'idx'. This clears the
+ * status registers if it hasn't been cleared yet and re-enables the event if
+ * it was enabled before.
+ *
+ * NOTE: 'gpe_device' may be null for GPEs managed by \_GPE
+ */
+UACPI_ALWAYS_ERROR_FOR_REDUCED_HARDWARE(
+uacpi_status uacpi_finish_handling_gpe(
+ uacpi_namespace_node *gpe_device, uacpi_u16 idx
+))
+
+/*
+ * Hard mask/umask a general purpose event at 'idx' managed by 'gpe_device'.
+ * This is used to permanently silence an event so that further calls to
+ * enable/disable as well as suspend/resume get ignored. This might be necessary
+ * for GPEs that cause an event storm due to the kernel's inability to properly
+ * handle them. The only way to enable a masked event is by a call to unmask.
+ *
+ * NOTE: 'gpe_device' may be null for GPEs managed by \_GPE
+ */
+UACPI_ALWAYS_ERROR_FOR_REDUCED_HARDWARE(
+uacpi_status uacpi_mask_gpe(
+ uacpi_namespace_node *gpe_device, uacpi_u16 idx
+))
+UACPI_ALWAYS_ERROR_FOR_REDUCED_HARDWARE(
+uacpi_status uacpi_unmask_gpe(
+ uacpi_namespace_node *gpe_device, uacpi_u16 idx
+))
+
+/*
+ * Disable all GPEs currently set up on the system.
+ */
+UACPI_ALWAYS_ERROR_FOR_REDUCED_HARDWARE(
+uacpi_status uacpi_disable_all_gpes(void)
+)
+
+/*
+ * Enable all GPEs not marked as wake. This is only needed after the system
+ * wakes from a shallow sleep state and is called automatically by wake code.
+ */
+UACPI_ALWAYS_ERROR_FOR_REDUCED_HARDWARE(
+uacpi_status uacpi_enable_all_runtime_gpes(void)
+)
+
+/*
+ * Enable all GPEs marked as wake. This is only needed before the system goes
+ * to sleep is called automatically by sleep code.
+ */
+UACPI_ALWAYS_ERROR_FOR_REDUCED_HARDWARE(
+uacpi_status uacpi_enable_all_wake_gpes(void)
+)
+
+/*
+ * Install/uninstall a new GPE block, usually defined by a device in the
+ * namespace with a _HID of ACPI0006.
+ */
+UACPI_ALWAYS_ERROR_FOR_REDUCED_HARDWARE(
+uacpi_status uacpi_install_gpe_block(
+ uacpi_namespace_node *gpe_device, uacpi_u64 address,
+ uacpi_address_space address_space, uacpi_u16 num_registers,
+ uacpi_u32 irq
+))
+UACPI_ALWAYS_ERROR_FOR_REDUCED_HARDWARE(
+uacpi_status uacpi_uninstall_gpe_block(
+ uacpi_namespace_node *gpe_device
+))
+
+#endif // !UACPI_BAREBONES_MODE
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/sys/include/dev/acpi/uacpi/uacpi/helpers.h b/sys/include/dev/acpi/uacpi/uacpi/helpers.h
new file mode 100644
index 0000000..520359e
--- /dev/null
+++ b/sys/include/dev/acpi/uacpi/uacpi/helpers.h
@@ -0,0 +1,12 @@
+#pragma once
+
+#include <uacpi/platform/compiler.h>
+
+#define UACPI_BUILD_BUG_ON_WITH_MSG(expr, msg) UACPI_STATIC_ASSERT(!(expr), msg)
+
+#define UACPI_BUILD_BUG_ON(expr) \
+ UACPI_BUILD_BUG_ON_WITH_MSG(expr, "BUILD BUG: " #expr " evaluated to true")
+
+#define UACPI_EXPECT_SIZEOF(type, size) \
+ UACPI_BUILD_BUG_ON_WITH_MSG(sizeof(type) != size, \
+ "BUILD BUG: invalid type size")
diff --git a/sys/include/dev/acpi/uacpi/uacpi/internal/compiler.h b/sys/include/dev/acpi/uacpi/uacpi/internal/compiler.h
new file mode 100644
index 0000000..68033fd
--- /dev/null
+++ b/sys/include/dev/acpi/uacpi/uacpi/internal/compiler.h
@@ -0,0 +1,3 @@
+#pragma once
+
+#include <uacpi/platform/compiler.h>
diff --git a/sys/include/dev/acpi/uacpi/uacpi/internal/context.h b/sys/include/dev/acpi/uacpi/uacpi/internal/context.h
new file mode 100644
index 0000000..ca587f6
--- /dev/null
+++ b/sys/include/dev/acpi/uacpi/uacpi/internal/context.h
@@ -0,0 +1,155 @@
+#pragma once
+
+#include <uacpi/acpi.h>
+#include <uacpi/types.h>
+#include <uacpi/uacpi.h>
+#include <uacpi/internal/dynamic_array.h>
+#include <uacpi/internal/shareable.h>
+#include <uacpi/context.h>
+
+struct uacpi_runtime_context {
+ /*
+ * A local copy of FADT that has been verified & converted to most optimal
+ * format for faster access to the registers.
+ */
+ struct acpi_fadt fadt;
+
+ uacpi_u64 flags;
+
+#ifndef UACPI_BAREBONES_MODE
+ /*
+ * A cached pointer to FACS so that we don't have to look it up in interrupt
+ * contexts as we can't take mutexes.
+ */
+ struct acpi_facs *facs;
+
+ /*
+ * pm1{a,b}_evt_blk split into two registers for convenience
+ */
+ struct acpi_gas pm1a_status_blk;
+ struct acpi_gas pm1b_status_blk;
+ struct acpi_gas pm1a_enable_blk;
+ struct acpi_gas pm1b_enable_blk;
+
+#define UACPI_SLEEP_TYP_INVALID 0xFF
+ uacpi_u8 last_sleep_typ_a;
+ uacpi_u8 last_sleep_typ_b;
+
+ uacpi_u8 s0_sleep_typ_a;
+ uacpi_u8 s0_sleep_typ_b;
+
+ uacpi_bool global_lock_acquired;
+
+#ifndef UACPI_REDUCED_HARDWARE
+ uacpi_bool was_in_legacy_mode;
+ uacpi_bool has_global_lock;
+ uacpi_bool sci_handle_valid;
+ uacpi_handle sci_handle;
+#endif
+ uacpi_u64 opcodes_executed;
+
+ uacpi_u32 loop_timeout_seconds;
+ uacpi_u32 max_call_stack_depth;
+
+ uacpi_u32 global_lock_seq_num;
+
+ /*
+ * These are stored here to protect against stuff like:
+ * - CopyObject(JUNK, \)
+ * - CopyObject(JUNK, \_GL)
+ */
+ uacpi_mutex *global_lock_mutex;
+ uacpi_object *root_object;
+
+#ifndef UACPI_REDUCED_HARDWARE
+ uacpi_handle *global_lock_event;
+ uacpi_handle *global_lock_spinlock;
+ uacpi_bool global_lock_pending;
+#endif
+
+ uacpi_bool bad_timesource;
+ uacpi_u8 init_level;
+#endif // !UACPI_BAREBONES_MODE
+
+#ifndef UACPI_REDUCED_HARDWARE
+ uacpi_bool is_hardware_reduced;
+#endif
+
+ /*
+ * This is a per-table value but we mimic the NT implementation:
+ * treat all other definition blocks as if they were the same revision
+ * as DSDT.
+ */
+ uacpi_bool is_rev1;
+
+ uacpi_u8 log_level;
+};
+
+extern struct uacpi_runtime_context g_uacpi_rt_ctx;
+
+static inline uacpi_bool uacpi_check_flag(uacpi_u64 flag)
+{
+ return (g_uacpi_rt_ctx.flags & flag) == flag;
+}
+
+static inline uacpi_bool uacpi_should_log(enum uacpi_log_level lvl)
+{
+ return lvl <= g_uacpi_rt_ctx.log_level;
+}
+
+static inline uacpi_bool uacpi_is_hardware_reduced(void)
+{
+#ifndef UACPI_REDUCED_HARDWARE
+ return g_uacpi_rt_ctx.is_hardware_reduced;
+#else
+ return UACPI_TRUE;
+#endif
+}
+
+#ifndef UACPI_BAREBONES_MODE
+
+static inline const uacpi_char *uacpi_init_level_to_string(uacpi_u8 lvl)
+{
+ switch (lvl) {
+ case UACPI_INIT_LEVEL_EARLY:
+ return "early";
+ case UACPI_INIT_LEVEL_SUBSYSTEM_INITIALIZED:
+ return "subsystem initialized";
+ case UACPI_INIT_LEVEL_NAMESPACE_LOADED:
+ return "namespace loaded";
+ case UACPI_INIT_LEVEL_NAMESPACE_INITIALIZED:
+ return "namespace initialized";
+ default:
+ return "<invalid>";
+ }
+}
+
+#define UACPI_ENSURE_INIT_LEVEL_AT_LEAST(lvl) \
+ do { \
+ if (uacpi_unlikely(g_uacpi_rt_ctx.init_level < lvl)) { \
+ uacpi_error( \
+ "while evaluating %s: init level %d (%s) is too low, " \
+ "expected at least %d (%s)\n", __FUNCTION__, \
+ g_uacpi_rt_ctx.init_level, \
+ uacpi_init_level_to_string(g_uacpi_rt_ctx.init_level), lvl, \
+ uacpi_init_level_to_string(lvl) \
+ ); \
+ return UACPI_STATUS_INIT_LEVEL_MISMATCH; \
+ } \
+ } while (0)
+
+#define UACPI_ENSURE_INIT_LEVEL_IS(lvl) \
+ do { \
+ if (uacpi_unlikely(g_uacpi_rt_ctx.init_level != lvl)) { \
+ uacpi_error( \
+ "while evaluating %s: invalid init level %d (%s), " \
+ "expected %d (%s)\n", __FUNCTION__, \
+ g_uacpi_rt_ctx.init_level, \
+ uacpi_init_level_to_string(g_uacpi_rt_ctx.init_level), lvl, \
+ uacpi_init_level_to_string(lvl) \
+ ); \
+ return UACPI_STATUS_INIT_LEVEL_MISMATCH; \
+ } \
+ } while (0)
+
+#endif // !UACPI_BAREBONES_MODE
diff --git a/sys/include/dev/acpi/uacpi/uacpi/internal/dynamic_array.h b/sys/include/dev/acpi/uacpi/uacpi/internal/dynamic_array.h
new file mode 100644
index 0000000..4adc00f
--- /dev/null
+++ b/sys/include/dev/acpi/uacpi/uacpi/internal/dynamic_array.h
@@ -0,0 +1,185 @@
+#pragma once
+
+#include <uacpi/types.h>
+#include <uacpi/internal/stdlib.h>
+#include <uacpi/kernel_api.h>
+
+#define DYNAMIC_ARRAY_WITH_INLINE_STORAGE(name, type, inline_capacity) \
+ struct name { \
+ type inline_storage[inline_capacity]; \
+ type *dynamic_storage; \
+ uacpi_size dynamic_capacity; \
+ uacpi_size size_including_inline; \
+ }; \
+
+#define DYNAMIC_ARRAY_SIZE(arr) ((arr)->size_including_inline)
+
+#define DYNAMIC_ARRAY_WITH_INLINE_STORAGE_EXPORTS(name, type, prefix) \
+ prefix uacpi_size name##_inline_capacity(struct name *arr); \
+ prefix type *name##_at(struct name *arr, uacpi_size idx); \
+ prefix type *name##_alloc(struct name *arr); \
+ prefix type *name##_calloc(struct name *arr); \
+ prefix void name##_pop(struct name *arr); \
+ prefix uacpi_size name##_size(struct name *arr); \
+ prefix type *name##_last(struct name *arr) \
+ prefix void name##_clear(struct name *arr);
+
+#ifndef UACPI_BAREBONES_MODE
+#define DYNAMIC_ARRAY_ALLOC_FN(name, type, prefix) \
+ UACPI_MAYBE_UNUSED \
+ prefix type *name##_alloc(struct name *arr) \
+ { \
+ uacpi_size inline_cap; \
+ type *out_ptr; \
+ \
+ inline_cap = name##_inline_capacity(arr); \
+ \
+ if (arr->size_including_inline >= inline_cap) { \
+ uacpi_size dynamic_size; \
+ \
+ dynamic_size = arr->size_including_inline - inline_cap; \
+ if (dynamic_size == arr->dynamic_capacity) { \
+ uacpi_size bytes, type_size; \
+ void *new_buf; \
+ \
+ type_size = sizeof(*arr->dynamic_storage); \
+ \
+ if (arr->dynamic_capacity == 0) { \
+ bytes = type_size * inline_cap; \
+ } else { \
+ bytes = (arr->dynamic_capacity / 2) * type_size; \
+ if (bytes == 0) \
+ bytes += type_size; \
+ \
+ bytes += arr->dynamic_capacity * type_size; \
+ } \
+ \
+ new_buf = uacpi_kernel_alloc(bytes); \
+ if (uacpi_unlikely(new_buf == UACPI_NULL)) \
+ return UACPI_NULL; \
+ \
+ arr->dynamic_capacity = bytes / type_size; \
+ \
+ if (arr->dynamic_storage) { \
+ uacpi_memcpy(new_buf, arr->dynamic_storage, \
+ dynamic_size * type_size); \
+ } \
+ uacpi_free(arr->dynamic_storage, dynamic_size * type_size); \
+ arr->dynamic_storage = new_buf; \
+ } \
+ \
+ out_ptr = &arr->dynamic_storage[dynamic_size]; \
+ goto ret; \
+ } \
+ out_ptr = &arr->inline_storage[arr->size_including_inline]; \
+ ret: \
+ arr->size_including_inline++; \
+ return out_ptr; \
+ }
+
+#define DYNAMIC_ARRAY_CLEAR_FN(name, type, prefix) \
+ prefix void name##_clear(struct name *arr) \
+ { \
+ uacpi_free( \
+ arr->dynamic_storage, \
+ arr->dynamic_capacity * sizeof(*arr->dynamic_storage) \
+ ); \
+ arr->size_including_inline = 0; \
+ arr->dynamic_capacity = 0; \
+ arr->dynamic_storage = UACPI_NULL; \
+ }
+#else
+#define DYNAMIC_ARRAY_ALLOC_FN(name, type, prefix) \
+ UACPI_MAYBE_UNUSED \
+ prefix type *name##_alloc(struct name *arr) \
+ { \
+ uacpi_size inline_cap; \
+ type *out_ptr; \
+ \
+ inline_cap = name##_inline_capacity(arr); \
+ \
+ if (arr->size_including_inline >= inline_cap) { \
+ uacpi_size dynamic_size; \
+ \
+ dynamic_size = arr->size_including_inline - inline_cap; \
+ if (uacpi_unlikely(dynamic_size == arr->dynamic_capacity)) \
+ return UACPI_NULL; \
+ \
+ out_ptr = &arr->dynamic_storage[dynamic_size]; \
+ goto ret; \
+ } \
+ out_ptr = &arr->inline_storage[arr->size_including_inline]; \
+ ret: \
+ arr->size_including_inline++; \
+ return out_ptr; \
+ }
+
+#define DYNAMIC_ARRAY_CLEAR_FN(name, type, prefix) \
+ prefix void name##_clear(struct name *arr) \
+ { \
+ arr->size_including_inline = 0; \
+ arr->dynamic_capacity = 0; \
+ arr->dynamic_storage = UACPI_NULL; \
+ }
+#endif
+
+#define DYNAMIC_ARRAY_WITH_INLINE_STORAGE_IMPL(name, type, prefix) \
+ UACPI_MAYBE_UNUSED \
+ prefix uacpi_size name##_inline_capacity(struct name *arr) \
+ { \
+ return sizeof(arr->inline_storage) / sizeof(arr->inline_storage[0]); \
+ } \
+ \
+ UACPI_MAYBE_UNUSED \
+ prefix uacpi_size name##_capacity(struct name *arr) \
+ { \
+ return name##_inline_capacity(arr) + arr->dynamic_capacity; \
+ } \
+ \
+ prefix type *name##_at(struct name *arr, uacpi_size idx) \
+ { \
+ if (idx >= arr->size_including_inline) \
+ return UACPI_NULL; \
+ \
+ if (idx < name##_inline_capacity(arr)) \
+ return &arr->inline_storage[idx]; \
+ \
+ return &arr->dynamic_storage[idx - name##_inline_capacity(arr)]; \
+ } \
+ \
+ DYNAMIC_ARRAY_ALLOC_FN(name, type, prefix) \
+ \
+ UACPI_MAYBE_UNUSED \
+ prefix type *name##_calloc(struct name *arr) \
+ { \
+ type *ret; \
+ \
+ ret = name##_alloc(arr); \
+ if (ret) \
+ uacpi_memzero(ret, sizeof(*ret)); \
+ \
+ return ret; \
+ } \
+ \
+ UACPI_MAYBE_UNUSED \
+ prefix void name##_pop(struct name *arr) \
+ { \
+ if (arr->size_including_inline == 0) \
+ return; \
+ \
+ arr->size_including_inline--; \
+ } \
+ \
+ UACPI_MAYBE_UNUSED \
+ prefix uacpi_size name##_size(struct name *arr) \
+ { \
+ return arr->size_including_inline; \
+ } \
+ \
+ UACPI_MAYBE_UNUSED \
+ prefix type *name##_last(struct name *arr) \
+ { \
+ return name##_at(arr, arr->size_including_inline - 1); \
+ } \
+ \
+ DYNAMIC_ARRAY_CLEAR_FN(name, type, prefix)
diff --git a/sys/include/dev/acpi/uacpi/uacpi/internal/event.h b/sys/include/dev/acpi/uacpi/uacpi/internal/event.h
new file mode 100644
index 0000000..40ced0d
--- /dev/null
+++ b/sys/include/dev/acpi/uacpi/uacpi/internal/event.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include <uacpi/event.h>
+
+// This fixed event is internal-only, and we don't expose it in the enum
+#define UACPI_FIXED_EVENT_GLOBAL_LOCK 0
+
+UACPI_ALWAYS_OK_FOR_REDUCED_HARDWARE(
+ uacpi_status uacpi_initialize_events_early(void)
+)
+
+UACPI_ALWAYS_OK_FOR_REDUCED_HARDWARE(
+ uacpi_status uacpi_initialize_events(void)
+)
+UACPI_STUB_IF_REDUCED_HARDWARE(
+ void uacpi_deinitialize_events(void)
+)
+
+UACPI_STUB_IF_REDUCED_HARDWARE(
+ void uacpi_events_match_post_dynamic_table_load(void)
+)
+
+UACPI_ALWAYS_ERROR_FOR_REDUCED_HARDWARE(
+ uacpi_status uacpi_clear_all_events(void)
+)
diff --git a/sys/include/dev/acpi/uacpi/uacpi/internal/helpers.h b/sys/include/dev/acpi/uacpi/uacpi/internal/helpers.h
new file mode 100644
index 0000000..f02b589
--- /dev/null
+++ b/sys/include/dev/acpi/uacpi/uacpi/internal/helpers.h
@@ -0,0 +1,7 @@
+#pragma once
+
+#include <uacpi/helpers.h>
+
+#define UACPI_ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+#define UACPI_UNUSED(x) (void)(x)
diff --git a/sys/include/dev/acpi/uacpi/uacpi/internal/interpreter.h b/sys/include/dev/acpi/uacpi/uacpi/internal/interpreter.h
new file mode 100644
index 0000000..410c379
--- /dev/null
+++ b/sys/include/dev/acpi/uacpi/uacpi/internal/interpreter.h
@@ -0,0 +1,24 @@
+#pragma once
+
+#include <uacpi/types.h>
+#include <uacpi/status.h>
+#include <uacpi/internal/namespace.h>
+
+#ifndef UACPI_BAREBONES_MODE
+
+enum uacpi_table_load_cause {
+ UACPI_TABLE_LOAD_CAUSE_LOAD_OP,
+ UACPI_TABLE_LOAD_CAUSE_LOAD_TABLE_OP,
+ UACPI_TABLE_LOAD_CAUSE_INIT,
+ UACPI_TABLE_LOAD_CAUSE_HOST,
+};
+
+uacpi_status uacpi_execute_table(void*, enum uacpi_table_load_cause cause);
+uacpi_status uacpi_osi(uacpi_handle handle, uacpi_object *retval);
+
+uacpi_status uacpi_execute_control_method(
+ uacpi_namespace_node *scope, uacpi_control_method *method,
+ const uacpi_object_array *args, uacpi_object **ret
+);
+
+#endif // !UACPI_BAREBONES_MODE
diff --git a/sys/include/dev/acpi/uacpi/uacpi/internal/io.h b/sys/include/dev/acpi/uacpi/uacpi/internal/io.h
new file mode 100644
index 0000000..839489a
--- /dev/null
+++ b/sys/include/dev/acpi/uacpi/uacpi/internal/io.h
@@ -0,0 +1,77 @@
+#pragma once
+
+#include <uacpi/internal/types.h>
+#include <uacpi/acpi.h>
+#include <uacpi/io.h>
+
+#ifndef UACPI_BAREBONES_MODE
+
+typedef struct uacpi_mapped_gas {
+ uacpi_handle mapping;
+ uacpi_u8 access_bit_width;
+ uacpi_u8 total_bit_width;
+ uacpi_u8 bit_offset;
+
+ uacpi_status (*read)(
+ uacpi_handle, uacpi_size offset, uacpi_u8 width, uacpi_u64 *out
+ );
+ uacpi_status (*write)(
+ uacpi_handle, uacpi_size offset, uacpi_u8 width, uacpi_u64 in
+ );
+
+ void (*unmap)(uacpi_handle, uacpi_size);
+} uacpi_mapped_gas;
+
+uacpi_status uacpi_map_gas_noalloc(
+ const struct acpi_gas *gas, uacpi_mapped_gas *out_mapped
+);
+void uacpi_unmap_gas_nofree(uacpi_mapped_gas *gas);
+
+uacpi_size uacpi_round_up_bits_to_bytes(uacpi_size bit_length);
+
+void uacpi_read_buffer_field(
+ const uacpi_buffer_field *field, void *dst
+);
+void uacpi_write_buffer_field(
+ uacpi_buffer_field *field, const void *src, uacpi_size size
+);
+
+uacpi_status uacpi_field_unit_get_read_type(
+ struct uacpi_field_unit *field, uacpi_object_type *out_type
+);
+
+uacpi_status uacpi_field_unit_get_bit_length(
+ struct uacpi_field_unit *field, uacpi_size *out_length
+);
+
+uacpi_status uacpi_read_field_unit(
+ uacpi_field_unit *field, void *dst, uacpi_size size,
+ uacpi_data_view *wtr_response
+);
+uacpi_status uacpi_write_field_unit(
+ uacpi_field_unit *field, const void *src, uacpi_size size,
+ uacpi_data_view *wtr_response
+);
+
+uacpi_status uacpi_system_memory_read(
+ void *ptr, uacpi_size offset, uacpi_u8 width, uacpi_u64 *out
+);
+uacpi_status uacpi_system_memory_write(
+ void *ptr, uacpi_size offset, uacpi_u8 width, uacpi_u64 in
+);
+
+uacpi_status uacpi_system_io_read(
+ uacpi_handle handle, uacpi_size offset, uacpi_u8 width, uacpi_u64 *out
+);
+uacpi_status uacpi_system_io_write(
+ uacpi_handle handle, uacpi_size offset, uacpi_u8 width, uacpi_u64 in
+);
+
+uacpi_status uacpi_pci_read(
+ uacpi_handle handle, uacpi_size offset, uacpi_u8 width, uacpi_u64 *out
+);
+uacpi_status uacpi_pci_write(
+ uacpi_handle handle, uacpi_size offset, uacpi_u8 width, uacpi_u64 in
+);
+
+#endif // !UACPI_BAREBONES_MODE
diff --git a/sys/include/dev/acpi/uacpi/uacpi/internal/log.h b/sys/include/dev/acpi/uacpi/uacpi/internal/log.h
new file mode 100644
index 0000000..e8b0451
--- /dev/null
+++ b/sys/include/dev/acpi/uacpi/uacpi/internal/log.h
@@ -0,0 +1,23 @@
+#pragma once
+
+#include <uacpi/kernel_api.h>
+#include <uacpi/internal/context.h>
+#include <uacpi/log.h>
+
+#ifdef UACPI_FORMATTED_LOGGING
+#define uacpi_log uacpi_kernel_log
+#else
+UACPI_PRINTF_DECL(2, 3)
+void uacpi_log(uacpi_log_level, const uacpi_char*, ...);
+#endif
+
+#define uacpi_log_lvl(lvl, ...) \
+ do { if (uacpi_should_log(lvl)) uacpi_log(lvl, __VA_ARGS__); } while (0)
+
+#define uacpi_debug(...) uacpi_log_lvl(UACPI_LOG_DEBUG, __VA_ARGS__)
+#define uacpi_trace(...) uacpi_log_lvl(UACPI_LOG_TRACE, __VA_ARGS__)
+#define uacpi_info(...) uacpi_log_lvl(UACPI_LOG_INFO, __VA_ARGS__)
+#define uacpi_warn(...) uacpi_log_lvl(UACPI_LOG_WARN, __VA_ARGS__)
+#define uacpi_error(...) uacpi_log_lvl(UACPI_LOG_ERROR, __VA_ARGS__)
+
+void uacpi_logger_initialize(void);
diff --git a/sys/include/dev/acpi/uacpi/uacpi/internal/mutex.h b/sys/include/dev/acpi/uacpi/uacpi/internal/mutex.h
new file mode 100644
index 0000000..4fa2c9b
--- /dev/null
+++ b/sys/include/dev/acpi/uacpi/uacpi/internal/mutex.h
@@ -0,0 +1,82 @@
+#pragma once
+
+#include <uacpi/internal/types.h>
+#include <uacpi/kernel_api.h>
+
+#ifndef UACPI_BAREBONES_MODE
+
+uacpi_bool uacpi_this_thread_owns_aml_mutex(uacpi_mutex*);
+
+uacpi_status uacpi_acquire_aml_mutex(uacpi_mutex*, uacpi_u16 timeout);
+uacpi_status uacpi_release_aml_mutex(uacpi_mutex*);
+
+static inline uacpi_status uacpi_acquire_native_mutex(uacpi_handle mtx)
+{
+ if (uacpi_unlikely(mtx == UACPI_NULL))
+ return UACPI_STATUS_INVALID_ARGUMENT;
+
+ return uacpi_kernel_acquire_mutex(mtx, 0xFFFF);
+}
+
+uacpi_status uacpi_acquire_native_mutex_with_timeout(
+ uacpi_handle mtx, uacpi_u16 timeout
+);
+
+static inline uacpi_status uacpi_release_native_mutex(uacpi_handle mtx)
+{
+ if (uacpi_unlikely(mtx == UACPI_NULL))
+ return UACPI_STATUS_INVALID_ARGUMENT;
+
+ uacpi_kernel_release_mutex(mtx);
+ return UACPI_STATUS_OK;
+}
+
+static inline uacpi_status uacpi_acquire_native_mutex_may_be_null(
+ uacpi_handle mtx
+)
+{
+ if (mtx == UACPI_NULL)
+ return UACPI_STATUS_OK;
+
+ return uacpi_kernel_acquire_mutex(mtx, 0xFFFF);
+}
+
+static inline uacpi_status uacpi_release_native_mutex_may_be_null(
+ uacpi_handle mtx
+)
+{
+ if (mtx == UACPI_NULL)
+ return UACPI_STATUS_OK;
+
+ uacpi_kernel_release_mutex(mtx);
+ return UACPI_STATUS_OK;
+}
+
+struct uacpi_recursive_lock {
+ uacpi_handle mutex;
+ uacpi_size depth;
+ uacpi_thread_id owner;
+};
+
+uacpi_status uacpi_recursive_lock_init(struct uacpi_recursive_lock *lock);
+uacpi_status uacpi_recursive_lock_deinit(struct uacpi_recursive_lock *lock);
+
+uacpi_status uacpi_recursive_lock_acquire(struct uacpi_recursive_lock *lock);
+uacpi_status uacpi_recursive_lock_release(struct uacpi_recursive_lock *lock);
+
+struct uacpi_rw_lock {
+ uacpi_handle read_mutex;
+ uacpi_handle write_mutex;
+ uacpi_size num_readers;
+};
+
+uacpi_status uacpi_rw_lock_init(struct uacpi_rw_lock *lock);
+uacpi_status uacpi_rw_lock_deinit(struct uacpi_rw_lock *lock);
+
+uacpi_status uacpi_rw_lock_read(struct uacpi_rw_lock *lock);
+uacpi_status uacpi_rw_unlock_read(struct uacpi_rw_lock *lock);
+
+uacpi_status uacpi_rw_lock_write(struct uacpi_rw_lock *lock);
+uacpi_status uacpi_rw_unlock_write(struct uacpi_rw_lock *lock);
+
+#endif // !UACPI_BAREBONES_MODE
diff --git a/sys/include/dev/acpi/uacpi/uacpi/internal/namespace.h b/sys/include/dev/acpi/uacpi/uacpi/internal/namespace.h
new file mode 100644
index 0000000..369c5a4
--- /dev/null
+++ b/sys/include/dev/acpi/uacpi/uacpi/internal/namespace.h
@@ -0,0 +1,123 @@
+#pragma once
+
+#include <uacpi/types.h>
+#include <uacpi/internal/shareable.h>
+#include <uacpi/status.h>
+#include <uacpi/namespace.h>
+
+#ifndef UACPI_BAREBONES_MODE
+
+#define UACPI_NAMESPACE_NODE_FLAG_ALIAS (1 << 0)
+
+/*
+ * This node has been uninstalled and has no object associated with it.
+ *
+ * This is used to handle edge cases where an object needs to reference
+ * a namespace node, where the node might end up going out of scope before
+ * the object lifetime ends.
+ */
+#define UACPI_NAMESPACE_NODE_FLAG_DANGLING (1u << 1)
+
+/*
+ * This node is method-local and must not be exposed via public API as its
+ * lifetime is limited.
+ */
+#define UACPI_NAMESPACE_NODE_FLAG_TEMPORARY (1u << 2)
+
+#define UACPI_NAMESPACE_NODE_PREDEFINED (1u << 31)
+
+typedef struct uacpi_namespace_node {
+ struct uacpi_shareable shareable;
+ uacpi_object_name name;
+ uacpi_u32 flags;
+ uacpi_object *object;
+ struct uacpi_namespace_node *parent;
+ struct uacpi_namespace_node *child;
+ struct uacpi_namespace_node *next;
+} uacpi_namespace_node;
+
+uacpi_status uacpi_initialize_namespace(void);
+void uacpi_deinitialize_namespace(void);
+
+uacpi_namespace_node *uacpi_namespace_node_alloc(uacpi_object_name name);
+void uacpi_namespace_node_unref(uacpi_namespace_node *node);
+
+
+uacpi_status uacpi_namespace_node_type_unlocked(
+ const uacpi_namespace_node *node, uacpi_object_type *out_type
+);
+uacpi_status uacpi_namespace_node_is_one_of_unlocked(
+ const uacpi_namespace_node *node, uacpi_object_type_bits type_mask,
+ uacpi_bool *out
+);
+
+uacpi_object *uacpi_namespace_node_get_object(const uacpi_namespace_node *node);
+
+uacpi_object *uacpi_namespace_node_get_object_typed(
+ const uacpi_namespace_node *node, uacpi_object_type_bits type_mask
+);
+
+uacpi_status uacpi_namespace_node_acquire_object(
+ const uacpi_namespace_node *node, uacpi_object **out_obj
+);
+uacpi_status uacpi_namespace_node_acquire_object_typed(
+ const uacpi_namespace_node *node, uacpi_object_type_bits,
+ uacpi_object **out_obj
+);
+
+uacpi_status uacpi_namespace_node_reacquire_object(
+ uacpi_object *obj
+);
+uacpi_status uacpi_namespace_node_release_object(
+ uacpi_object *obj
+);
+
+uacpi_status uacpi_namespace_node_install(
+ uacpi_namespace_node *parent, uacpi_namespace_node *node
+);
+uacpi_status uacpi_namespace_node_uninstall(uacpi_namespace_node *node);
+
+uacpi_namespace_node *uacpi_namespace_node_find_sub_node(
+ uacpi_namespace_node *parent,
+ uacpi_object_name name
+);
+
+enum uacpi_may_search_above_parent {
+ UACPI_MAY_SEARCH_ABOVE_PARENT_NO,
+ UACPI_MAY_SEARCH_ABOVE_PARENT_YES,
+};
+
+enum uacpi_permanent_only {
+ UACPI_PERMANENT_ONLY_NO,
+ UACPI_PERMANENT_ONLY_YES,
+};
+
+enum uacpi_should_lock {
+ UACPI_SHOULD_LOCK_NO,
+ UACPI_SHOULD_LOCK_YES,
+};
+
+uacpi_status uacpi_namespace_node_resolve(
+ uacpi_namespace_node *scope, const uacpi_char *path, enum uacpi_should_lock,
+ enum uacpi_may_search_above_parent, enum uacpi_permanent_only,
+ uacpi_namespace_node **out_node
+);
+
+uacpi_status uacpi_namespace_do_for_each_child(
+ uacpi_namespace_node *parent, uacpi_iteration_callback descending_callback,
+ uacpi_iteration_callback ascending_callback,
+ uacpi_object_type_bits, uacpi_u32 max_depth, enum uacpi_should_lock,
+ enum uacpi_permanent_only, void *user
+);
+
+uacpi_bool uacpi_namespace_node_is_dangling(uacpi_namespace_node *node);
+uacpi_bool uacpi_namespace_node_is_temporary(uacpi_namespace_node *node);
+uacpi_bool uacpi_namespace_node_is_predefined(uacpi_namespace_node *node);
+
+uacpi_status uacpi_namespace_read_lock(void);
+uacpi_status uacpi_namespace_read_unlock(void);
+
+uacpi_status uacpi_namespace_write_lock(void);
+uacpi_status uacpi_namespace_write_unlock(void);
+
+#endif // !UACPI_BAREBONES_MODE
diff --git a/sys/include/dev/acpi/uacpi/uacpi/internal/notify.h b/sys/include/dev/acpi/uacpi/uacpi/internal/notify.h
new file mode 100644
index 0000000..c1fa8bb
--- /dev/null
+++ b/sys/include/dev/acpi/uacpi/uacpi/internal/notify.h
@@ -0,0 +1,13 @@
+#pragma once
+
+#include <uacpi/internal/types.h>
+#include <uacpi/notify.h>
+
+#ifndef UACPI_BAREBONES_MODE
+
+uacpi_status uacpi_initialize_notify(void);
+void uacpi_deinitialize_notify(void);
+
+uacpi_status uacpi_notify_all(uacpi_namespace_node *node, uacpi_u64 value);
+
+#endif // !UACPI_BAREBONES_MODE
diff --git a/sys/include/dev/acpi/uacpi/uacpi/internal/opcodes.h b/sys/include/dev/acpi/uacpi/uacpi/internal/opcodes.h
new file mode 100644
index 0000000..53ef334
--- /dev/null
+++ b/sys/include/dev/acpi/uacpi/uacpi/internal/opcodes.h
@@ -0,0 +1,1390 @@
+#pragma once
+
+#include <uacpi/types.h>
+
+typedef uacpi_u16 uacpi_aml_op;
+
+#define UACPI_EXT_PREFIX 0x5B
+#define UACPI_EXT_OP(op) ((UACPI_EXT_PREFIX << 8) | (op))
+
+#define UACPI_DUAL_NAME_PREFIX 0x2E
+#define UACPI_MULTI_NAME_PREFIX 0x2F
+#define UACPI_NULL_NAME 0x00
+
+/*
+ * Opcodes that tell the parser VM how to take apart every AML instruction.
+ * Every AML opcode has a list of these that is executed by the parser.
+ */
+enum uacpi_parse_op {
+ UACPI_PARSE_OP_END = 0,
+
+ /*
+ * End the execution of the current instruction with a warning if the item
+ * at decode_ops[pc + 1] is NULL.
+ */
+ UACPI_PARSE_OP_SKIP_WITH_WARN_IF_NULL,
+
+ // Emit a warning as if the current opcode is being skipped
+ UACPI_PARSE_OP_EMIT_SKIP_WARN,
+
+ // SimpleName := NameString | ArgObj | LocalObj
+ UACPI_PARSE_OP_SIMPLE_NAME,
+
+ // SuperName := SimpleName | DebugObj | ReferenceTypeOpcode
+ UACPI_PARSE_OP_SUPERNAME,
+ // The resulting item will be set to null if name couldn't be resolved
+ UACPI_PARSE_OP_SUPERNAME_OR_UNRESOLVED,
+
+ // TermArg := ExpressionOpcode | DataObject | ArgObj | LocalObj
+ UACPI_PARSE_OP_TERM_ARG,
+ UACPI_PARSE_OP_TERM_ARG_UNWRAP_INTERNAL,
+
+ /*
+ * Same as TERM_ARG, but named references are passed as-is.
+ * This means methods are not invoked, fields are not read, etc.
+ */
+ UACPI_PARSE_OP_TERM_ARG_OR_NAMED_OBJECT,
+
+ /*
+ * Same as UACPI_PARSE_OP_TERM_ARG_OR_NAMED_OBJECT but allows unresolved
+ * name strings.
+ */
+ UACPI_PARSE_OP_TERM_ARG_OR_NAMED_OBJECT_OR_UNRESOLVED,
+
+ // Operand := TermArg => Integer
+ UACPI_PARSE_OP_OPERAND,
+
+ // TermArg => String
+ UACPI_PARSE_OP_STRING,
+
+ /*
+ * ComputationalData := ByteConst | WordConst | DWordConst | QWordConst |
+ * String | ConstObj | RevisionOp | DefBuffer
+ */
+ UACPI_PARSE_OP_COMPUTATIONAL_DATA,
+
+ // Target := SuperName | NullName
+ UACPI_PARSE_OP_TARGET,
+
+ // Parses a pkglen
+ UACPI_PARSE_OP_PKGLEN,
+
+ /*
+ * Parses a pkglen and records it, the end of this pkglen is considered
+ * the end of the instruction. The PC is always set to the end of this
+ * package once parser reaches UACPI_PARSE_OP_END.
+ */
+ UACPI_PARSE_OP_TRACKED_PKGLEN,
+
+ /*
+ * Parse a NameString and create the last nameseg.
+ * Note that this errors out if last nameseg already exists.
+ */
+ UACPI_PARSE_OP_CREATE_NAMESTRING,
+
+ /*
+ * same as UACPI_PARSE_OP_CREATE_NAMESTRING, but attempting to create an
+ * already existing object is not fatal if currently loading a table.
+ */
+ UACPI_PARSE_OP_CREATE_NAMESTRING_OR_NULL_IF_LOAD,
+
+ /*
+ * Parse a NameString and put the node into the ready parts array.
+ * Note that this errors out if the referenced node doesn't exist.
+ */
+ UACPI_PARSE_OP_EXISTING_NAMESTRING,
+
+ /*
+ * Same as UACPI_PARSE_OP_EXISTING_NAMESTRING except the op doesn't error
+ * out if namestring couldn't be resolved.
+ */
+ UACPI_PARSE_OP_EXISTING_NAMESTRING_OR_NULL,
+
+ /*
+ * Same as UACPI_PARSE_OP_EXISTING_NAMESTRING, but undefined references
+ * are not fatal if currently loading a table.
+ */
+ UACPI_PARSE_OP_EXISTING_NAMESTRING_OR_NULL_IF_LOAD,
+
+ // Invoke a handler at op_handlers[spec->code]
+ UACPI_PARSE_OP_INVOKE_HANDLER,
+
+ // Allocate an object an put it at the front of the item list
+ UACPI_PARSE_OP_OBJECT_ALLOC,
+
+ UACPI_PARSE_OP_EMPTY_OBJECT_ALLOC,
+
+ // Convert last item into a shallow/deep copy of itself
+ UACPI_PARSE_OP_OBJECT_CONVERT_TO_SHALLOW_COPY,
+ UACPI_PARSE_OP_OBJECT_CONVERT_TO_DEEP_COPY,
+
+ /*
+ * Same as UACPI_PARSE_OP_OBJECT_ALLOC except the type of the allocated
+ * object is specified at decode_ops[pc + 1]
+ */
+ UACPI_PARSE_OP_OBJECT_ALLOC_TYPED,
+
+ // Record current AML program counter as a QWORD immediate
+ UACPI_PARSE_OP_RECORD_AML_PC,
+
+ // Load a QWORD immediate located at decode_ops[pc + 1]
+ UACPI_PARSE_OP_LOAD_INLINE_IMM_AS_OBJECT,
+
+ // Load a decode_ops[pc + 1] byte imm at decode_ops[pc + 2]
+ UACPI_PARSE_OP_LOAD_INLINE_IMM,
+
+ // Load a QWORD zero immediate
+ UACPI_PARSE_OP_LOAD_ZERO_IMM,
+
+ // Load a decode_ops[pc + 1] byte imm from the instructions stream
+ UACPI_PARSE_OP_LOAD_IMM,
+
+ // Same as UACPI_PARSE_OP_LOAD_IMM, expect the resulting value is an object
+ UACPI_PARSE_OP_LOAD_IMM_AS_OBJECT,
+
+ // Create & Load an integer constant representing either true or false
+ UACPI_PARSE_OP_LOAD_FALSE_OBJECT,
+ UACPI_PARSE_OP_LOAD_TRUE_OBJECT,
+
+ // Truncate the last item in the list if needed
+ UACPI_PARSE_OP_TRUNCATE_NUMBER,
+
+ // Ensure the type of item is decode_ops[pc + 1]
+ UACPI_PARSE_OP_TYPECHECK,
+
+ // Install the namespace node specified in items[decode_ops[pc + 1]]
+ UACPI_PARSE_OP_INSTALL_NAMESPACE_NODE,
+
+ // Move item to the previous (preempted) op
+ UACPI_PARSE_OP_OBJECT_TRANSFER_TO_PREV,
+
+ /*
+ * Same as UACPI_PARSE_OP_OBJECT_TRANSFER_TO_PREV, but the object
+ * is copied instead. (Useful when dealing with multiple targets)
+ * TODO: optimize this so that we can optionally move the object
+ * if target was a null target.
+ */
+ UACPI_PARSE_OP_OBJECT_COPY_TO_PREV,
+
+ // Store the last item to the target at items[decode_ops[pc + 1]]
+ UACPI_PARSE_OP_STORE_TO_TARGET,
+
+ /*
+ * Store the item at items[decode_ops[pc + 2]] to target
+ * at items[decode_ops[pc + 1]]
+ */
+ UACPI_PARSE_OP_STORE_TO_TARGET_INDIRECT,
+
+ /*
+ * Error if reached. Should be used for opcodes that are supposed to be
+ * converted at op parse time, e.g. invoking a method or referring to
+ * a named object.
+ */
+ UACPI_PARSE_OP_UNREACHABLE,
+
+ // Invalid opcode, should never be encountered in the stream
+ UACPI_PARSE_OP_BAD_OPCODE,
+
+ // Decrement the current AML instruction pointer
+ UACPI_PARSE_OP_AML_PC_DECREMENT,
+
+ // Decrement the immediate at decode_ops[pc + 1]
+ UACPI_PARSE_OP_IMM_DECREMENT,
+
+ // Remove the last item off the item stack
+ UACPI_PARSE_OP_ITEM_POP,
+
+ // Dispatch the method call from items[0] and return from current op_exec
+ UACPI_PARSE_OP_DISPATCH_METHOD_CALL,
+
+ /*
+ * Dispatch a table load with scope node at items[0] and method at items[1].
+ * The last item is expected to be an integer object that is set to 0 in
+ * case load fails.
+ */
+ UACPI_PARSE_OP_DISPATCH_TABLE_LOAD,
+
+ /*
+ * Convert the current resolved namestring to either a method call
+ * or a named object reference.
+ */
+ UACPI_PARSE_OP_CONVERT_NAMESTRING,
+
+ /*
+ * Execute the next instruction only if currently tracked package still
+ * has data left, otherwise skip decode_ops[pc + 1] bytes.
+ */
+ UACPI_PARSE_OP_IF_HAS_DATA,
+
+ /*
+ * Execute the next instruction only if the handle at
+ * items[decode_ops[pc + 1]] is null. Otherwise skip
+ * decode_ops[pc + 2] bytes.
+ */
+ UACPI_PARSE_OP_IF_NULL,
+
+ /*
+ * Execute the next instruction only if the handle at
+ * items[-1] is null. Otherwise skip decode_ops[pc + 1] bytes.
+ */
+ UACPI_PARSE_OP_IF_LAST_NULL,
+
+ // The inverse of UACPI_PARSE_OP_IF_NULL
+ UACPI_PARSE_OP_IF_NOT_NULL,
+
+ // The inverse of UACPI_PARSE_OP_IF_LAST_NULL
+ UACPI_PARSE_OP_IF_LAST_NOT_NULL,
+
+ /*
+ * Execute the next instruction only if the last immediate is equal to
+ * decode_ops[pc + 1], otherwise skip decode_ops[pc + 2] bytes.
+ */
+ UACPI_PARSE_OP_IF_LAST_EQUALS,
+
+ /*
+ * Execute the next instruction only if the last object is a false value
+ * (has a value of 0), otherwise skip decode_ops[pc + 1] bytes.
+ */
+ UACPI_PARSE_OP_IF_LAST_FALSE,
+
+ // The inverse of UACPI_PARSE_OP_IF_LAST_FALSE
+ UACPI_PARSE_OP_IF_LAST_TRUE,
+
+ /*
+ * Switch to opcode at decode_ops[pc + 1] only if the next AML instruction
+ * in the stream is equal to it. Note that this looks ahead of the tracked
+ * package if one is active. Switching to the next op also applies the
+ * currently tracked package.
+ */
+ UACPI_PARSE_OP_SWITCH_TO_NEXT_IF_EQUALS,
+
+ /*
+ * Execute the next instruction only if this op was switched to from op at
+ * (decode_ops[pc + 1] | decode_ops[pc + 2] << 8), otherwise skip
+ * decode_ops[pc + 3] bytes.
+ */
+ UACPI_PARSE_OP_IF_SWITCHED_FROM,
+
+ /*
+ * pc = decode_ops[pc + 1]
+ */
+ UACPI_PARSE_OP_JMP,
+ UACPI_PARSE_OP_MAX = UACPI_PARSE_OP_JMP,
+};
+const uacpi_char *uacpi_parse_op_to_string(enum uacpi_parse_op op);
+
+/*
+ * A few notes about op properties:
+ * Technically the spec says that RefOfOp is considered a SuperName, but NT
+ * disagrees about this. For example Store(..., RefOf) fails with
+ * "Invalid SuperName". MethodInvocation could also technically be considered
+ * a SuperName, but NT doesn't allow that either: Store(..., MethodInvocation)
+ * fails with "Invalid Target Method, expected a DataObject" error.
+ */
+
+enum uacpi_op_property {
+ UACPI_OP_PROPERTY_TERM_ARG = 1,
+ UACPI_OP_PROPERTY_SUPERNAME = 2,
+ UACPI_OP_PROPERTY_SIMPLE_NAME = 4,
+ UACPI_OP_PROPERTY_TARGET = 8,
+
+ // The ops to execute are pointed to by indirect_decode_ops
+ UACPI_OP_PROPERTY_OUT_OF_LINE = 16,
+
+ // Error if encountered in the AML byte strem
+ UACPI_OP_PROPERTY_RESERVED = 128,
+};
+
+struct uacpi_op_spec {
+ uacpi_char *name;
+ union {
+ uacpi_u8 decode_ops[16];
+ uacpi_u8 *indirect_decode_ops;
+ };
+ uacpi_u8 properties;
+ uacpi_aml_op code;
+};
+
+const struct uacpi_op_spec *uacpi_get_op_spec(uacpi_aml_op);
+
+#define UACPI_INTERNAL_OP(code) \
+ UACPI_OP(Internal_##code, code, 0, { UACPI_PARSE_OP_UNREACHABLE })
+
+#define UACPI_BAD_OPCODE(code) \
+ UACPI_OP(Reserved_##code, code, 0, { UACPI_PARSE_OP_BAD_OPCODE })
+
+#define UACPI_METHOD_CALL_OPCODE(nargs) \
+ UACPI_OP( \
+ InternalOpMethodCall##nargs##Args, 0xF7 + nargs, \
+ UACPI_OP_PROPERTY_TERM_ARG | \
+ UACPI_OP_PROPERTY_RESERVED, \
+ { \
+ UACPI_PARSE_OP_LOAD_INLINE_IMM, 1, nargs, \
+ UACPI_PARSE_OP_IF_NOT_NULL, 1, 6, \
+ UACPI_PARSE_OP_TERM_ARG_UNWRAP_INTERNAL, \
+ UACPI_PARSE_OP_OBJECT_CONVERT_TO_SHALLOW_COPY, \
+ UACPI_PARSE_OP_IMM_DECREMENT, 1, \
+ UACPI_PARSE_OP_JMP, 3, \
+ UACPI_PARSE_OP_OBJECT_ALLOC, \
+ UACPI_PARSE_OP_DISPATCH_METHOD_CALL, \
+ UACPI_PARSE_OP_OBJECT_TRANSFER_TO_PREV, \
+ } \
+ )
+
+/*
+ * -------------------------------------------------------------
+ * RootChar := ‘\’
+ * ParentPrefixChar := ‘^’
+ * ‘\’ := 0x5C
+ * ‘^’ := 0x5E
+ * MultiNamePrefix := 0x2F
+ * DualNamePrefix := 0x2E
+ * ------------------------------------------------------------
+ * ‘A’-‘Z’ := 0x41 - 0x5A
+ * ‘_’ := 0x5F
+ * LeadNameChar := ‘A’-‘Z’ | ‘_’
+ * NameSeg := <leadnamechar namechar namechar namechar>
+ * NameString := <rootchar namepath> | <prefixpath namepath>
+ * PrefixPath := Nothing | <’^’ prefixpath>
+ * DualNamePath := DualNamePrefix NameSeg NameSeg
+ * MultiNamePath := MultiNamePrefix SegCount NameSeg(SegCount)
+ */
+#define UACPI_UNRESOLVED_NAME_STRING_OP(character, code) \
+ UACPI_OP( \
+ UACPI_InternalOpUnresolvedNameString_##character, code, \
+ UACPI_OP_PROPERTY_SIMPLE_NAME | \
+ UACPI_OP_PROPERTY_SUPERNAME | \
+ UACPI_OP_PROPERTY_TERM_ARG, \
+ { \
+ UACPI_PARSE_OP_AML_PC_DECREMENT, \
+ UACPI_PARSE_OP_EXISTING_NAMESTRING_OR_NULL, \
+ UACPI_PARSE_OP_CONVERT_NAMESTRING, \
+ } \
+ )
+
+#define UACPI_BUILD_LOCAL_OR_ARG_OP(prefix, base, offset) \
+UACPI_OP( \
+ prefix##offset##Op, base + offset, \
+ UACPI_OP_PROPERTY_SUPERNAME | \
+ UACPI_OP_PROPERTY_TERM_ARG | \
+ UACPI_OP_PROPERTY_SIMPLE_NAME, \
+ { \
+ UACPI_PARSE_OP_EMPTY_OBJECT_ALLOC, \
+ UACPI_PARSE_OP_INVOKE_HANDLER, \
+ UACPI_PARSE_OP_OBJECT_TRANSFER_TO_PREV, \
+ } \
+) \
+
+#define UACPI_LOCALX_OP(idx) UACPI_BUILD_LOCAL_OR_ARG_OP(Local, 0x60, idx)
+#define UACPI_ARGX_OP(idx) UACPI_BUILD_LOCAL_OR_ARG_OP(Arg, 0x68, idx)
+
+#define UACPI_BUILD_PACKAGE_OP(name, code, jmp_off, ...) \
+UACPI_OP( \
+ name##Op, code, \
+ UACPI_OP_PROPERTY_TERM_ARG, \
+ { \
+ UACPI_PARSE_OP_TRACKED_PKGLEN, \
+ ##__VA_ARGS__, \
+ UACPI_PARSE_OP_IF_HAS_DATA, 4, \
+ UACPI_PARSE_OP_RECORD_AML_PC, \
+ UACPI_PARSE_OP_TERM_ARG_OR_NAMED_OBJECT_OR_UNRESOLVED, \
+ UACPI_PARSE_OP_JMP, jmp_off, \
+ UACPI_PARSE_OP_OBJECT_ALLOC_TYPED, UACPI_OBJECT_PACKAGE, \
+ UACPI_PARSE_OP_INVOKE_HANDLER, \
+ UACPI_PARSE_OP_OBJECT_TRANSFER_TO_PREV, \
+ } \
+)
+
+#define UACPI_BUILD_BINARY_MATH_OP(prefix, code) \
+UACPI_OP( \
+ prefix##Op, code, \
+ UACPI_OP_PROPERTY_TERM_ARG, \
+ { \
+ UACPI_PARSE_OP_OPERAND, \
+ UACPI_PARSE_OP_OPERAND, \
+ UACPI_PARSE_OP_TARGET, \
+ UACPI_PARSE_OP_OBJECT_ALLOC_TYPED, UACPI_OBJECT_INTEGER, \
+ UACPI_PARSE_OP_INVOKE_HANDLER, \
+ UACPI_PARSE_OP_TRUNCATE_NUMBER, \
+ UACPI_PARSE_OP_STORE_TO_TARGET, 2, \
+ UACPI_PARSE_OP_OBJECT_COPY_TO_PREV, \
+ } \
+)
+
+#define UACPI_BUILD_UNARY_MATH_OP(type, code) \
+UACPI_OP( \
+ type##Op, code, \
+ UACPI_OP_PROPERTY_TERM_ARG, \
+ { \
+ UACPI_PARSE_OP_OPERAND, \
+ UACPI_PARSE_OP_TARGET, \
+ UACPI_PARSE_OP_OBJECT_ALLOC_TYPED, UACPI_OBJECT_INTEGER, \
+ UACPI_PARSE_OP_INVOKE_HANDLER, \
+ UACPI_PARSE_OP_STORE_TO_TARGET, 1, \
+ UACPI_PARSE_OP_OBJECT_TRANSFER_TO_PREV, \
+ } \
+)
+
+#define UACPI_DO_BUILD_BUFFER_FIELD_OP(type, code, node_idx, ...) \
+UACPI_OP( \
+ type##FieldOp, code, 0, \
+ { \
+ UACPI_PARSE_OP_TERM_ARG_UNWRAP_INTERNAL, \
+ UACPI_PARSE_OP_TYPECHECK, UACPI_OBJECT_BUFFER, \
+ UACPI_PARSE_OP_OPERAND, \
+ ##__VA_ARGS__, \
+ UACPI_PARSE_OP_CREATE_NAMESTRING_OR_NULL_IF_LOAD, \
+ UACPI_PARSE_OP_SKIP_WITH_WARN_IF_NULL, node_idx, \
+ UACPI_PARSE_OP_OBJECT_ALLOC_TYPED, UACPI_OBJECT_BUFFER_FIELD, \
+ UACPI_PARSE_OP_INVOKE_HANDLER, \
+ UACPI_PARSE_OP_INSTALL_NAMESPACE_NODE, node_idx, \
+ } \
+)
+
+#define UACPI_BUILD_BUFFER_FIELD_OP(type, code) \
+ UACPI_DO_BUILD_BUFFER_FIELD_OP(Create##type, code, 2)
+
+#define UACPI_INTEGER_LITERAL_OP(type, code, bytes) \
+UACPI_OP( \
+ type##Prefix, code, \
+ UACPI_OP_PROPERTY_TERM_ARG, \
+ { \
+ UACPI_PARSE_OP_LOAD_IMM_AS_OBJECT, bytes, \
+ UACPI_PARSE_OP_OBJECT_TRANSFER_TO_PREV, \
+ } \
+) \
+
+#define UACPI_BUILD_BINARY_LOGIC_OP(type, code) \
+UACPI_OP( \
+ type##Op, code, \
+ UACPI_OP_PROPERTY_TERM_ARG, \
+ { \
+ UACPI_PARSE_OP_COMPUTATIONAL_DATA, \
+ UACPI_PARSE_OP_COMPUTATIONAL_DATA, \
+ UACPI_PARSE_OP_OBJECT_ALLOC_TYPED, UACPI_OBJECT_INTEGER, \
+ UACPI_PARSE_OP_INVOKE_HANDLER, \
+ UACPI_PARSE_OP_OBJECT_TRANSFER_TO_PREV, \
+ } \
+)
+
+#define UACPI_BUILD_TO_OP(kind, code, dst_type) \
+UACPI_OP( \
+ To##kind##Op, code, \
+ UACPI_OP_PROPERTY_TERM_ARG, \
+ { \
+ UACPI_PARSE_OP_COMPUTATIONAL_DATA, \
+ UACPI_PARSE_OP_TARGET, \
+ UACPI_PARSE_OP_OBJECT_ALLOC_TYPED, dst_type, \
+ UACPI_PARSE_OP_INVOKE_HANDLER, \
+ UACPI_PARSE_OP_STORE_TO_TARGET, 1, \
+ UACPI_PARSE_OP_OBJECT_TRANSFER_TO_PREV, \
+ } \
+)
+
+#define UACPI_BUILD_INC_DEC_OP(prefix, code) \
+UACPI_OP( \
+ prefix##Op, code, \
+ UACPI_OP_PROPERTY_TERM_ARG, \
+ { \
+ UACPI_PARSE_OP_SUPERNAME, \
+ UACPI_PARSE_OP_OBJECT_ALLOC_TYPED, UACPI_OBJECT_INTEGER, \
+ UACPI_PARSE_OP_INVOKE_HANDLER, \
+ UACPI_PARSE_OP_TRUNCATE_NUMBER, \
+ UACPI_PARSE_OP_STORE_TO_TARGET, 0, \
+ UACPI_PARSE_OP_OBJECT_TRANSFER_TO_PREV, \
+ } \
+) \
+
+#define UACPI_ENUMERATE_OPCODES \
+UACPI_OP( \
+ ZeroOp, 0x00, \
+ UACPI_OP_PROPERTY_TARGET | \
+ UACPI_OP_PROPERTY_TERM_ARG, \
+ { \
+ UACPI_PARSE_OP_LOAD_INLINE_IMM_AS_OBJECT, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ UACPI_PARSE_OP_OBJECT_TRANSFER_TO_PREV, \
+ } \
+) \
+UACPI_OP( \
+ OneOp, 0x01, \
+ UACPI_OP_PROPERTY_TERM_ARG, \
+ { \
+ UACPI_PARSE_OP_LOAD_INLINE_IMM_AS_OBJECT, \
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ UACPI_PARSE_OP_OBJECT_TRANSFER_TO_PREV, \
+ } \
+) \
+UACPI_BAD_OPCODE(0x02) \
+UACPI_BAD_OPCODE(0x03) \
+UACPI_BAD_OPCODE(0x04) \
+UACPI_BAD_OPCODE(0x05) \
+UACPI_OP( \
+ AliasOp, 0x06, 0, \
+ { \
+ UACPI_PARSE_OP_EXISTING_NAMESTRING_OR_NULL_IF_LOAD, \
+ UACPI_PARSE_OP_CREATE_NAMESTRING_OR_NULL_IF_LOAD, \
+ UACPI_PARSE_OP_SKIP_WITH_WARN_IF_NULL, 0, \
+ UACPI_PARSE_OP_SKIP_WITH_WARN_IF_NULL, 1, \
+ UACPI_PARSE_OP_INVOKE_HANDLER, \
+ UACPI_PARSE_OP_INSTALL_NAMESPACE_NODE, 1, \
+ } \
+) \
+UACPI_BAD_OPCODE(0x07) \
+UACPI_OP( \
+ NameOp, 0x08, 0, \
+ { \
+ UACPI_PARSE_OP_CREATE_NAMESTRING_OR_NULL_IF_LOAD, \
+ UACPI_PARSE_OP_TERM_ARG_UNWRAP_INTERNAL, \
+ UACPI_PARSE_OP_SKIP_WITH_WARN_IF_NULL, 0, \
+ UACPI_PARSE_OP_OBJECT_CONVERT_TO_DEEP_COPY, \
+ UACPI_PARSE_OP_INVOKE_HANDLER, \
+ UACPI_PARSE_OP_INSTALL_NAMESPACE_NODE, 0, \
+ } \
+) \
+UACPI_BAD_OPCODE(0x09) \
+UACPI_INTEGER_LITERAL_OP(Byte, 0x0A, 1) \
+UACPI_INTEGER_LITERAL_OP(Word, 0x0B, 2) \
+UACPI_INTEGER_LITERAL_OP(DWord, 0x0C, 4) \
+UACPI_OP( \
+ StringPrefix, 0x0D, \
+ UACPI_OP_PROPERTY_TERM_ARG, \
+ { \
+ UACPI_PARSE_OP_OBJECT_ALLOC_TYPED, UACPI_OBJECT_STRING, \
+ UACPI_PARSE_OP_INVOKE_HANDLER, \
+ UACPI_PARSE_OP_OBJECT_TRANSFER_TO_PREV, \
+ } \
+) \
+UACPI_INTEGER_LITERAL_OP(QWord, 0x0E, 8) \
+UACPI_BAD_OPCODE(0x0F) \
+UACPI_OP( \
+ ScopeOp, 0x10, 0, \
+ { \
+ UACPI_PARSE_OP_TRACKED_PKGLEN, \
+ UACPI_PARSE_OP_EXISTING_NAMESTRING_OR_NULL_IF_LOAD, \
+ UACPI_PARSE_OP_SKIP_WITH_WARN_IF_NULL, 1, \
+ UACPI_PARSE_OP_INVOKE_HANDLER, \
+ } \
+) \
+UACPI_OP( \
+ BufferOp, 0x11, \
+ UACPI_OP_PROPERTY_TERM_ARG, \
+ { \
+ UACPI_PARSE_OP_TRACKED_PKGLEN, \
+ UACPI_PARSE_OP_OPERAND, \
+ UACPI_PARSE_OP_RECORD_AML_PC, \
+ UACPI_PARSE_OP_OBJECT_ALLOC_TYPED, UACPI_OBJECT_BUFFER, \
+ UACPI_PARSE_OP_INVOKE_HANDLER, \
+ UACPI_PARSE_OP_OBJECT_TRANSFER_TO_PREV, \
+ } \
+) \
+UACPI_BUILD_PACKAGE_OP( \
+ Package, 0x12, 3, \
+ UACPI_PARSE_OP_LOAD_IMM, 1 \
+) \
+UACPI_BUILD_PACKAGE_OP( \
+ VarPackage, 0x13, 2, \
+ UACPI_PARSE_OP_OPERAND \
+) \
+UACPI_OP( \
+ MethodOp, 0x14, 0, \
+ { \
+ UACPI_PARSE_OP_TRACKED_PKGLEN, \
+ UACPI_PARSE_OP_CREATE_NAMESTRING_OR_NULL_IF_LOAD, \
+ UACPI_PARSE_OP_LOAD_IMM, 1, \
+ UACPI_PARSE_OP_SKIP_WITH_WARN_IF_NULL, 1, \
+ UACPI_PARSE_OP_RECORD_AML_PC, \
+ UACPI_PARSE_OP_OBJECT_ALLOC_TYPED, UACPI_OBJECT_METHOD, \
+ UACPI_PARSE_OP_INVOKE_HANDLER, \
+ UACPI_PARSE_OP_INSTALL_NAMESPACE_NODE, 1, \
+ } \
+) \
+UACPI_OP( \
+ ExternalOp, 0x15, 0, \
+ { \
+ UACPI_PARSE_OP_EXISTING_NAMESTRING_OR_NULL, \
+ UACPI_PARSE_OP_LOAD_IMM, 1, \
+ UACPI_PARSE_OP_LOAD_IMM, 1, \
+ } \
+) \
+UACPI_BAD_OPCODE(0x16) \
+UACPI_BAD_OPCODE(0x17) \
+UACPI_BAD_OPCODE(0x18) \
+UACPI_BAD_OPCODE(0x19) \
+UACPI_BAD_OPCODE(0x1A) \
+UACPI_BAD_OPCODE(0x1B) \
+UACPI_BAD_OPCODE(0x1C) \
+UACPI_BAD_OPCODE(0x1D) \
+UACPI_BAD_OPCODE(0x1E) \
+UACPI_BAD_OPCODE(0x1F) \
+UACPI_BAD_OPCODE(0x20) \
+UACPI_BAD_OPCODE(0x21) \
+UACPI_BAD_OPCODE(0x22) \
+UACPI_BAD_OPCODE(0x23) \
+UACPI_BAD_OPCODE(0x24) \
+UACPI_BAD_OPCODE(0x25) \
+UACPI_BAD_OPCODE(0x26) \
+UACPI_BAD_OPCODE(0x27) \
+UACPI_BAD_OPCODE(0x28) \
+UACPI_BAD_OPCODE(0x29) \
+UACPI_BAD_OPCODE(0x2A) \
+UACPI_BAD_OPCODE(0x2B) \
+UACPI_BAD_OPCODE(0x2C) \
+UACPI_BAD_OPCODE(0x2D) \
+UACPI_UNRESOLVED_NAME_STRING_OP(DualNamePrefix, 0x2E) \
+UACPI_UNRESOLVED_NAME_STRING_OP(MultiNamePrefix, 0x2F) \
+UACPI_INTERNAL_OP(0x30) \
+UACPI_INTERNAL_OP(0x31) \
+UACPI_INTERNAL_OP(0x32) \
+UACPI_INTERNAL_OP(0x33) \
+UACPI_INTERNAL_OP(0x34) \
+UACPI_INTERNAL_OP(0x35) \
+UACPI_INTERNAL_OP(0x36) \
+UACPI_INTERNAL_OP(0x37) \
+UACPI_INTERNAL_OP(0x38) \
+UACPI_INTERNAL_OP(0x39) \
+UACPI_BAD_OPCODE(0x3A) \
+UACPI_BAD_OPCODE(0x3B) \
+UACPI_BAD_OPCODE(0x3C) \
+UACPI_BAD_OPCODE(0x3D) \
+UACPI_BAD_OPCODE(0x3E) \
+UACPI_BAD_OPCODE(0x3F) \
+UACPI_BAD_OPCODE(0x40) \
+UACPI_UNRESOLVED_NAME_STRING_OP(A, 0x41) \
+UACPI_UNRESOLVED_NAME_STRING_OP(B, 0x42) \
+UACPI_UNRESOLVED_NAME_STRING_OP(C, 0x43) \
+UACPI_UNRESOLVED_NAME_STRING_OP(D, 0x44) \
+UACPI_UNRESOLVED_NAME_STRING_OP(E, 0x45) \
+UACPI_UNRESOLVED_NAME_STRING_OP(F, 0x46) \
+UACPI_UNRESOLVED_NAME_STRING_OP(G, 0x47) \
+UACPI_UNRESOLVED_NAME_STRING_OP(H, 0x48) \
+UACPI_UNRESOLVED_NAME_STRING_OP(I, 0x49) \
+UACPI_UNRESOLVED_NAME_STRING_OP(J, 0x4A) \
+UACPI_UNRESOLVED_NAME_STRING_OP(K, 0x4B) \
+UACPI_UNRESOLVED_NAME_STRING_OP(L, 0x4C) \
+UACPI_UNRESOLVED_NAME_STRING_OP(M, 0x4D) \
+UACPI_UNRESOLVED_NAME_STRING_OP(N, 0x4E) \
+UACPI_UNRESOLVED_NAME_STRING_OP(O, 0x4F) \
+UACPI_UNRESOLVED_NAME_STRING_OP(P, 0x50) \
+UACPI_UNRESOLVED_NAME_STRING_OP(Q, 0x51) \
+UACPI_UNRESOLVED_NAME_STRING_OP(R, 0x52) \
+UACPI_UNRESOLVED_NAME_STRING_OP(S, 0x53) \
+UACPI_UNRESOLVED_NAME_STRING_OP(T, 0x54) \
+UACPI_UNRESOLVED_NAME_STRING_OP(U, 0x55) \
+UACPI_UNRESOLVED_NAME_STRING_OP(V, 0x56) \
+UACPI_UNRESOLVED_NAME_STRING_OP(W, 0x57) \
+UACPI_UNRESOLVED_NAME_STRING_OP(X, 0x58) \
+UACPI_UNRESOLVED_NAME_STRING_OP(Y, 0x59) \
+UACPI_UNRESOLVED_NAME_STRING_OP(Z, 0x5A) \
+UACPI_INTERNAL_OP(0x5B) \
+UACPI_UNRESOLVED_NAME_STRING_OP(RootChar, 0x5C) \
+UACPI_BAD_OPCODE(0x5D) \
+UACPI_UNRESOLVED_NAME_STRING_OP(ParentPrefixChar, 0x5E) \
+UACPI_UNRESOLVED_NAME_STRING_OP(Underscore, 0x5F) \
+UACPI_LOCALX_OP(0) \
+UACPI_LOCALX_OP(1) \
+UACPI_LOCALX_OP(2) \
+UACPI_LOCALX_OP(3) \
+UACPI_LOCALX_OP(4) \
+UACPI_LOCALX_OP(5) \
+UACPI_LOCALX_OP(6) \
+UACPI_LOCALX_OP(7) \
+UACPI_ARGX_OP(0) \
+UACPI_ARGX_OP(1) \
+UACPI_ARGX_OP(2) \
+UACPI_ARGX_OP(3) \
+UACPI_ARGX_OP(4) \
+UACPI_ARGX_OP(5) \
+UACPI_ARGX_OP(6) \
+UACPI_BAD_OPCODE(0x6F) \
+UACPI_OP( \
+ StoreOp, 0x70, \
+ UACPI_OP_PROPERTY_TERM_ARG, \
+ { \
+ UACPI_PARSE_OP_TERM_ARG, \
+ UACPI_PARSE_OP_SUPERNAME, \
+ UACPI_PARSE_OP_INVOKE_HANDLER, \
+ UACPI_PARSE_OP_ITEM_POP, \
+ UACPI_PARSE_OP_OBJECT_COPY_TO_PREV, \
+ } \
+) \
+UACPI_OP( \
+ RefOfOp, 0x71, \
+ UACPI_OP_PROPERTY_TERM_ARG, \
+ { \
+ UACPI_PARSE_OP_SUPERNAME, \
+ UACPI_PARSE_OP_OBJECT_ALLOC, \
+ UACPI_PARSE_OP_INVOKE_HANDLER, \
+ UACPI_PARSE_OP_OBJECT_TRANSFER_TO_PREV, \
+ } \
+) \
+UACPI_BUILD_BINARY_MATH_OP(Add, 0x72) \
+UACPI_OP( \
+ ConcatOp, 0x73, \
+ UACPI_OP_PROPERTY_TERM_ARG, \
+ { \
+ UACPI_PARSE_OP_COMPUTATIONAL_DATA, \
+ UACPI_PARSE_OP_COMPUTATIONAL_DATA, \
+ UACPI_PARSE_OP_TARGET, \
+ UACPI_PARSE_OP_OBJECT_ALLOC_TYPED, UACPI_OBJECT_BUFFER, \
+ UACPI_PARSE_OP_INVOKE_HANDLER, \
+ UACPI_PARSE_OP_STORE_TO_TARGET, 2, \
+ UACPI_PARSE_OP_OBJECT_COPY_TO_PREV, \
+ } \
+) \
+UACPI_BUILD_BINARY_MATH_OP(Subtract, 0x74) \
+UACPI_BUILD_INC_DEC_OP(Increment, 0x75) \
+UACPI_BUILD_INC_DEC_OP(Decrement, 0x76) \
+UACPI_BUILD_BINARY_MATH_OP(Multiply, 0x77) \
+UACPI_OP( \
+ DivideOp, 0x78, \
+ UACPI_OP_PROPERTY_TERM_ARG, \
+ { \
+ UACPI_PARSE_OP_OPERAND, \
+ UACPI_PARSE_OP_OPERAND, \
+ UACPI_PARSE_OP_TARGET, \
+ UACPI_PARSE_OP_TARGET, \
+ UACPI_PARSE_OP_OBJECT_ALLOC_TYPED, UACPI_OBJECT_INTEGER, \
+ UACPI_PARSE_OP_OBJECT_ALLOC_TYPED, UACPI_OBJECT_INTEGER, \
+ UACPI_PARSE_OP_INVOKE_HANDLER, \
+ UACPI_PARSE_OP_STORE_TO_TARGET, 3, \
+ UACPI_PARSE_OP_OBJECT_COPY_TO_PREV, \
+ UACPI_PARSE_OP_STORE_TO_TARGET_INDIRECT, 2, 4, \
+ } \
+) \
+UACPI_BUILD_BINARY_MATH_OP(ShiftLeft, 0x79) \
+UACPI_BUILD_BINARY_MATH_OP(ShiftRight, 0x7A) \
+UACPI_BUILD_BINARY_MATH_OP(And, 0x7B) \
+UACPI_BUILD_BINARY_MATH_OP(Nand, 0x7C) \
+UACPI_BUILD_BINARY_MATH_OP(Or, 0x7D) \
+UACPI_BUILD_BINARY_MATH_OP(Nor, 0x7E) \
+UACPI_BUILD_BINARY_MATH_OP(Xor, 0x7F) \
+UACPI_BUILD_UNARY_MATH_OP(Not, 0x80) \
+UACPI_BUILD_UNARY_MATH_OP(FindSetLeftBit, 0x81) \
+UACPI_BUILD_UNARY_MATH_OP(FindSetRightBit, 0x82) \
+UACPI_OP( \
+ DerefOfOp, 0x83, \
+ UACPI_OP_PROPERTY_TERM_ARG, \
+ { \
+ UACPI_PARSE_OP_TERM_ARG_UNWRAP_INTERNAL, \
+ UACPI_PARSE_OP_OBJECT_ALLOC, \
+ UACPI_PARSE_OP_INVOKE_HANDLER, \
+ UACPI_PARSE_OP_OBJECT_TRANSFER_TO_PREV, \
+ } \
+) \
+UACPI_OP( \
+ ConcatResOp, 0x84, \
+ UACPI_OP_PROPERTY_TERM_ARG, \
+ { \
+ UACPI_PARSE_OP_TERM_ARG_UNWRAP_INTERNAL, \
+ UACPI_PARSE_OP_TYPECHECK, UACPI_OBJECT_BUFFER, \
+ UACPI_PARSE_OP_TERM_ARG_UNWRAP_INTERNAL, \
+ UACPI_PARSE_OP_TYPECHECK, UACPI_OBJECT_BUFFER, \
+ UACPI_PARSE_OP_TARGET, \
+ UACPI_PARSE_OP_OBJECT_ALLOC_TYPED, UACPI_OBJECT_BUFFER, \
+ UACPI_PARSE_OP_INVOKE_HANDLER, \
+ UACPI_PARSE_OP_STORE_TO_TARGET, 2, \
+ UACPI_PARSE_OP_OBJECT_TRANSFER_TO_PREV, \
+ } \
+) \
+UACPI_BUILD_BINARY_MATH_OP(Mod, 0x85) \
+UACPI_OP( \
+ NotifyOp, 0x86, 0, \
+ { \
+ /* This is technically wrong according to spec but I was */ \
+ /* unable to find any examples of anything else after */ \
+ /* inspecting about 500 AML dumps. Spec says this is a */ \
+ /* SuperName that must evaluate to Device/ThermalZone or */ \
+ /* Processor, just ignore for now. */ \
+ UACPI_PARSE_OP_EXISTING_NAMESTRING_OR_NULL_IF_LOAD, \
+ UACPI_PARSE_OP_OPERAND, \
+ UACPI_PARSE_OP_SKIP_WITH_WARN_IF_NULL, 0, \
+ UACPI_PARSE_OP_INVOKE_HANDLER, \
+ } \
+) \
+UACPI_OP( \
+ SizeOfOp, 0x87, \
+ UACPI_OP_PROPERTY_TERM_ARG, \
+ { \
+ UACPI_PARSE_OP_SUPERNAME, \
+ UACPI_PARSE_OP_OBJECT_ALLOC_TYPED, UACPI_OBJECT_INTEGER, \
+ UACPI_PARSE_OP_INVOKE_HANDLER, \
+ UACPI_PARSE_OP_OBJECT_TRANSFER_TO_PREV, \
+ } \
+) \
+UACPI_OP( \
+ IndexOp, 0x88, \
+ UACPI_OP_PROPERTY_TERM_ARG | \
+ UACPI_OP_PROPERTY_SUPERNAME | \
+ UACPI_OP_PROPERTY_SIMPLE_NAME, \
+ { \
+ UACPI_PARSE_OP_TERM_ARG_UNWRAP_INTERNAL, \
+ UACPI_PARSE_OP_OPERAND, \
+ UACPI_PARSE_OP_TARGET, \
+ UACPI_PARSE_OP_EMPTY_OBJECT_ALLOC, \
+ UACPI_PARSE_OP_INVOKE_HANDLER, \
+ UACPI_PARSE_OP_STORE_TO_TARGET, 2, \
+ UACPI_PARSE_OP_OBJECT_TRANSFER_TO_PREV, \
+ } \
+) \
+UACPI_OP( \
+ MatchOp, 0x89, \
+ UACPI_OP_PROPERTY_TERM_ARG, \
+ { \
+ UACPI_PARSE_OP_TERM_ARG_UNWRAP_INTERNAL, \
+ UACPI_PARSE_OP_TYPECHECK, UACPI_OBJECT_PACKAGE, \
+ UACPI_PARSE_OP_LOAD_IMM, 1, \
+ UACPI_PARSE_OP_OPERAND, \
+ UACPI_PARSE_OP_LOAD_IMM, 1, \
+ UACPI_PARSE_OP_OPERAND, \
+ UACPI_PARSE_OP_OPERAND, \
+ UACPI_PARSE_OP_OBJECT_ALLOC_TYPED, UACPI_OBJECT_INTEGER, \
+ UACPI_PARSE_OP_INVOKE_HANDLER, \
+ UACPI_PARSE_OP_OBJECT_TRANSFER_TO_PREV, \
+ } \
+) \
+UACPI_BUILD_BUFFER_FIELD_OP(DWord, 0x8A) \
+UACPI_BUILD_BUFFER_FIELD_OP(Word, 0x8B) \
+UACPI_BUILD_BUFFER_FIELD_OP(Byte, 0x8C) \
+UACPI_BUILD_BUFFER_FIELD_OP(Bit, 0x8D) \
+UACPI_OP( \
+ ObjectTypeOp, 0x8E, \
+ UACPI_OP_PROPERTY_TERM_ARG, \
+ { \
+ UACPI_PARSE_OP_TERM_ARG_OR_NAMED_OBJECT, \
+ UACPI_PARSE_OP_OBJECT_ALLOC_TYPED, UACPI_OBJECT_INTEGER, \
+ UACPI_PARSE_OP_INVOKE_HANDLER, \
+ UACPI_PARSE_OP_OBJECT_TRANSFER_TO_PREV, \
+ } \
+) \
+UACPI_BUILD_BUFFER_FIELD_OP(QWord, 0x8F) \
+UACPI_BUILD_BINARY_LOGIC_OP(Land, 0x90) \
+UACPI_BUILD_BINARY_LOGIC_OP(Lor, 0x91) \
+UACPI_OP( \
+ LnotOp, 0x92, \
+ UACPI_OP_PROPERTY_TERM_ARG, \
+ { \
+ UACPI_PARSE_OP_OPERAND, \
+ UACPI_PARSE_OP_OBJECT_ALLOC, \
+ UACPI_PARSE_OP_INVOKE_HANDLER, \
+ UACPI_PARSE_OP_OBJECT_TRANSFER_TO_PREV, \
+ } \
+) \
+UACPI_BUILD_BINARY_LOGIC_OP(LEqual, 0x93) \
+UACPI_BUILD_BINARY_LOGIC_OP(LGreater, 0x94) \
+UACPI_BUILD_BINARY_LOGIC_OP(LLess, 0x95) \
+UACPI_BUILD_TO_OP(Buffer, 0x96, UACPI_OBJECT_BUFFER) \
+UACPI_BUILD_TO_OP(DecimalString, 0x97, UACPI_OBJECT_STRING) \
+UACPI_BUILD_TO_OP(HexString, 0x98, UACPI_OBJECT_STRING) \
+UACPI_BUILD_TO_OP(Integer, 0x99, UACPI_OBJECT_INTEGER) \
+UACPI_BAD_OPCODE(0x9A) \
+UACPI_BAD_OPCODE(0x9B) \
+UACPI_OP( \
+ ToStringOp, 0x9C, \
+ UACPI_OP_PROPERTY_TERM_ARG, \
+ { \
+ UACPI_PARSE_OP_TERM_ARG_UNWRAP_INTERNAL, \
+ UACPI_PARSE_OP_TYPECHECK, UACPI_OBJECT_BUFFER, \
+ UACPI_PARSE_OP_OPERAND, \
+ UACPI_PARSE_OP_TARGET, \
+ UACPI_PARSE_OP_OBJECT_ALLOC_TYPED, UACPI_OBJECT_STRING, \
+ UACPI_PARSE_OP_INVOKE_HANDLER, \
+ UACPI_PARSE_OP_STORE_TO_TARGET, 2, \
+ UACPI_PARSE_OP_OBJECT_TRANSFER_TO_PREV, \
+ } \
+) \
+UACPI_OP( \
+ CopyObjectOp, 0x9D, \
+ UACPI_OP_PROPERTY_TERM_ARG, \
+ { \
+ UACPI_PARSE_OP_TERM_ARG, \
+ UACPI_PARSE_OP_OBJECT_COPY_TO_PREV, \
+ UACPI_PARSE_OP_SIMPLE_NAME, \
+ UACPI_PARSE_OP_INVOKE_HANDLER, \
+ } \
+) \
+UACPI_OP( \
+ MidOp, 0x9E, \
+ UACPI_OP_PROPERTY_TERM_ARG, \
+ { \
+ UACPI_PARSE_OP_TERM_ARG_UNWRAP_INTERNAL, \
+ UACPI_PARSE_OP_OPERAND, \
+ UACPI_PARSE_OP_OPERAND, \
+ UACPI_PARSE_OP_TARGET, \
+ UACPI_PARSE_OP_OBJECT_ALLOC_TYPED, UACPI_OBJECT_BUFFER, \
+ UACPI_PARSE_OP_INVOKE_HANDLER, \
+ UACPI_PARSE_OP_STORE_TO_TARGET, 3, \
+ UACPI_PARSE_OP_OBJECT_TRANSFER_TO_PREV, \
+ } \
+) \
+UACPI_OP( \
+ ContinueOp, 0x9F, 0, \
+ { \
+ UACPI_PARSE_OP_INVOKE_HANDLER, \
+ } \
+) \
+UACPI_OP( \
+ IfOp, 0xA0, 0, \
+ { \
+ UACPI_PARSE_OP_TRACKED_PKGLEN, \
+ UACPI_PARSE_OP_OPERAND, \
+ UACPI_PARSE_OP_IF_LAST_NULL, 3, \
+ UACPI_PARSE_OP_EMIT_SKIP_WARN, \
+ UACPI_PARSE_OP_JMP, 9, \
+ UACPI_PARSE_OP_IF_LAST_FALSE, 4, \
+ UACPI_PARSE_OP_SWITCH_TO_NEXT_IF_EQUALS, 0xA1, 0x00, \
+ UACPI_PARSE_OP_END, \
+ UACPI_PARSE_OP_INVOKE_HANDLER, \
+ } \
+) \
+UACPI_OP( \
+ ElseOp, 0xA1, 0, \
+ { \
+ UACPI_PARSE_OP_IF_SWITCHED_FROM, 0xA0, 0x00, 10, \
+ UACPI_PARSE_OP_IF_LAST_NULL, 3, \
+ UACPI_PARSE_OP_TRACKED_PKGLEN, \
+ UACPI_PARSE_OP_EMIT_SKIP_WARN, \
+ UACPI_PARSE_OP_END, \
+ UACPI_PARSE_OP_ITEM_POP, \
+ UACPI_PARSE_OP_ITEM_POP, \
+ UACPI_PARSE_OP_PKGLEN, \
+ UACPI_PARSE_OP_INVOKE_HANDLER, \
+ UACPI_PARSE_OP_END, \
+ UACPI_PARSE_OP_TRACKED_PKGLEN, \
+ } \
+) \
+UACPI_OP( \
+ WhileOp, 0xA2, 0, \
+ { \
+ UACPI_PARSE_OP_TRACKED_PKGLEN, \
+ UACPI_PARSE_OP_OPERAND, \
+ UACPI_PARSE_OP_SKIP_WITH_WARN_IF_NULL, 1, \
+ UACPI_PARSE_OP_IF_LAST_TRUE, 1, \
+ UACPI_PARSE_OP_INVOKE_HANDLER, \
+ } \
+) \
+UACPI_OP( \
+ NoopOp, 0xA3, 0, \
+ { \
+ UACPI_PARSE_OP_END, \
+ } \
+) \
+UACPI_OP( \
+ ReturnOp, 0xA4, 0, \
+ { \
+ UACPI_PARSE_OP_TERM_ARG_UNWRAP_INTERNAL, \
+ UACPI_PARSE_OP_INVOKE_HANDLER, \
+ } \
+) \
+UACPI_OP( \
+ BreakOp, 0xA5, 0, \
+ { \
+ UACPI_PARSE_OP_INVOKE_HANDLER, \
+ } \
+) \
+UACPI_BAD_OPCODE(0xA6) \
+UACPI_BAD_OPCODE(0xA7) \
+UACPI_BAD_OPCODE(0xA8) \
+UACPI_BAD_OPCODE(0xA9) \
+UACPI_BAD_OPCODE(0xAA) \
+UACPI_BAD_OPCODE(0xAB) \
+UACPI_BAD_OPCODE(0xAC) \
+UACPI_BAD_OPCODE(0xAD) \
+UACPI_BAD_OPCODE(0xAE) \
+UACPI_BAD_OPCODE(0xAF) \
+UACPI_BAD_OPCODE(0xB0) \
+UACPI_BAD_OPCODE(0xB1) \
+UACPI_BAD_OPCODE(0xB2) \
+UACPI_BAD_OPCODE(0xB3) \
+UACPI_BAD_OPCODE(0xB4) \
+UACPI_BAD_OPCODE(0xB5) \
+UACPI_BAD_OPCODE(0xB6) \
+UACPI_BAD_OPCODE(0xB7) \
+UACPI_BAD_OPCODE(0xB8) \
+UACPI_BAD_OPCODE(0xB9) \
+UACPI_BAD_OPCODE(0xBA) \
+UACPI_BAD_OPCODE(0xBB) \
+UACPI_BAD_OPCODE(0xBC) \
+UACPI_BAD_OPCODE(0xBD) \
+UACPI_BAD_OPCODE(0xBE) \
+UACPI_BAD_OPCODE(0xBF) \
+UACPI_BAD_OPCODE(0xC0) \
+UACPI_BAD_OPCODE(0xC1) \
+UACPI_BAD_OPCODE(0xC2) \
+UACPI_BAD_OPCODE(0xC3) \
+UACPI_BAD_OPCODE(0xC4) \
+UACPI_BAD_OPCODE(0xC5) \
+UACPI_BAD_OPCODE(0xC6) \
+UACPI_BAD_OPCODE(0xC7) \
+UACPI_BAD_OPCODE(0xC8) \
+UACPI_BAD_OPCODE(0xC9) \
+UACPI_BAD_OPCODE(0xCA) \
+UACPI_BAD_OPCODE(0xCB) \
+UACPI_OP( \
+ BreakPointOp, 0xCC, 0, \
+ { \
+ UACPI_PARSE_OP_INVOKE_HANDLER, \
+ } \
+) \
+UACPI_BAD_OPCODE(0xCD) \
+UACPI_BAD_OPCODE(0xCE) \
+UACPI_BAD_OPCODE(0xCF) \
+UACPI_BAD_OPCODE(0xD0) \
+UACPI_BAD_OPCODE(0xD1) \
+UACPI_BAD_OPCODE(0xD2) \
+UACPI_BAD_OPCODE(0xD3) \
+UACPI_BAD_OPCODE(0xD4) \
+UACPI_BAD_OPCODE(0xD5) \
+UACPI_BAD_OPCODE(0xD6) \
+UACPI_BAD_OPCODE(0xD7) \
+UACPI_BAD_OPCODE(0xD8) \
+UACPI_BAD_OPCODE(0xD9) \
+UACPI_BAD_OPCODE(0xDA) \
+UACPI_BAD_OPCODE(0xDB) \
+UACPI_BAD_OPCODE(0xDC) \
+UACPI_BAD_OPCODE(0xDD) \
+UACPI_BAD_OPCODE(0xDE) \
+UACPI_BAD_OPCODE(0xDF) \
+UACPI_BAD_OPCODE(0xE0) \
+UACPI_BAD_OPCODE(0xE1) \
+UACPI_BAD_OPCODE(0xE2) \
+UACPI_BAD_OPCODE(0xE3) \
+UACPI_BAD_OPCODE(0xE4) \
+UACPI_BAD_OPCODE(0xE5) \
+UACPI_BAD_OPCODE(0xE6) \
+UACPI_BAD_OPCODE(0xE7) \
+UACPI_BAD_OPCODE(0xE8) \
+UACPI_BAD_OPCODE(0xE9) \
+UACPI_BAD_OPCODE(0xEA) \
+UACPI_BAD_OPCODE(0xEB) \
+UACPI_BAD_OPCODE(0xEC) \
+UACPI_BAD_OPCODE(0xED) \
+UACPI_BAD_OPCODE(0xEE) \
+UACPI_BAD_OPCODE(0xEF) \
+UACPI_BAD_OPCODE(0xF0) \
+UACPI_BAD_OPCODE(0xF1) \
+UACPI_BAD_OPCODE(0xF2) \
+UACPI_BAD_OPCODE(0xF3) \
+UACPI_OP( \
+ InternalOpReadFieldAsBuffer, 0xF4, \
+ UACPI_OP_PROPERTY_TERM_ARG | \
+ UACPI_OP_PROPERTY_RESERVED, \
+ { \
+ UACPI_PARSE_OP_OBJECT_ALLOC_TYPED, UACPI_OBJECT_BUFFER, \
+ UACPI_PARSE_OP_INVOKE_HANDLER, \
+ UACPI_PARSE_OP_OBJECT_TRANSFER_TO_PREV, \
+ } \
+) \
+UACPI_OP( \
+ InternalOpReadFieldAsInteger, 0xF5, \
+ UACPI_OP_PROPERTY_TERM_ARG | \
+ UACPI_OP_PROPERTY_RESERVED, \
+ { \
+ UACPI_PARSE_OP_OBJECT_ALLOC_TYPED, UACPI_OBJECT_INTEGER, \
+ UACPI_PARSE_OP_INVOKE_HANDLER, \
+ UACPI_PARSE_OP_OBJECT_TRANSFER_TO_PREV, \
+ } \
+) \
+UACPI_OP( \
+ InternalOpNamedObject, 0xF6, \
+ UACPI_OP_PROPERTY_SIMPLE_NAME | \
+ UACPI_OP_PROPERTY_SUPERNAME | \
+ UACPI_OP_PROPERTY_TERM_ARG | \
+ UACPI_OP_PROPERTY_RESERVED, \
+ { \
+ UACPI_PARSE_OP_EMPTY_OBJECT_ALLOC, \
+ UACPI_PARSE_OP_INVOKE_HANDLER, \
+ UACPI_PARSE_OP_OBJECT_TRANSFER_TO_PREV, \
+ } \
+) \
+UACPI_METHOD_CALL_OPCODE(0) \
+UACPI_METHOD_CALL_OPCODE(1) \
+UACPI_METHOD_CALL_OPCODE(2) \
+UACPI_METHOD_CALL_OPCODE(3) \
+UACPI_METHOD_CALL_OPCODE(4) \
+UACPI_METHOD_CALL_OPCODE(5) \
+UACPI_METHOD_CALL_OPCODE(6) \
+UACPI_METHOD_CALL_OPCODE(7) \
+UACPI_OP( \
+ OnesOp, 0xFF, \
+ UACPI_OP_PROPERTY_TERM_ARG, \
+ { \
+ UACPI_PARSE_OP_LOAD_INLINE_IMM_AS_OBJECT, \
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, \
+ UACPI_PARSE_OP_TRUNCATE_NUMBER, \
+ UACPI_PARSE_OP_OBJECT_TRANSFER_TO_PREV, \
+ } \
+)
+
+extern uacpi_u8 uacpi_field_op_decode_ops[];
+extern uacpi_u8 uacpi_index_field_op_decode_ops[];
+extern uacpi_u8 uacpi_bank_field_op_decode_ops[];
+extern uacpi_u8 uacpi_load_op_decode_ops[];
+extern uacpi_u8 uacpi_load_table_op_decode_ops[];
+
+#define UACPI_BUILD_NAMED_SCOPE_OBJECT_OP(name, code, type, ...) \
+UACPI_OP( \
+ name##Op, UACPI_EXT_OP(code), 0, \
+ { \
+ UACPI_PARSE_OP_TRACKED_PKGLEN, \
+ UACPI_PARSE_OP_CREATE_NAMESTRING_OR_NULL_IF_LOAD, \
+ ##__VA_ARGS__, \
+ UACPI_PARSE_OP_SKIP_WITH_WARN_IF_NULL, 1, \
+ UACPI_PARSE_OP_OBJECT_ALLOC_TYPED, type, \
+ UACPI_PARSE_OP_INVOKE_HANDLER, \
+ UACPI_PARSE_OP_INSTALL_NAMESPACE_NODE, 1, \
+ } \
+)
+
+#define UACPI_BUILD_TO_FROM_BCD(type, code) \
+UACPI_OP( \
+ type##BCDOp, UACPI_EXT_OP(code), \
+ UACPI_OP_PROPERTY_TERM_ARG, \
+ { \
+ UACPI_PARSE_OP_OPERAND, \
+ UACPI_PARSE_OP_TARGET, \
+ UACPI_PARSE_OP_OBJECT_ALLOC_TYPED, \
+ UACPI_OBJECT_INTEGER, \
+ UACPI_PARSE_OP_INVOKE_HANDLER, \
+ UACPI_PARSE_OP_STORE_TO_TARGET, 1, \
+ UACPI_PARSE_OP_OBJECT_TRANSFER_TO_PREV, \
+ } \
+)
+
+#define UACPI_ENUMERATE_EXT_OPCODES \
+UACPI_OP( \
+ ReservedExtOp, UACPI_EXT_OP(0x00), 0, \
+ { \
+ UACPI_PARSE_OP_BAD_OPCODE, \
+ } \
+) \
+UACPI_OP( \
+ MutexOp, UACPI_EXT_OP(0x01), 0, \
+ { \
+ UACPI_PARSE_OP_CREATE_NAMESTRING_OR_NULL_IF_LOAD, \
+ UACPI_PARSE_OP_LOAD_IMM, 1, \
+ UACPI_PARSE_OP_SKIP_WITH_WARN_IF_NULL, 0, \
+ UACPI_PARSE_OP_OBJECT_ALLOC_TYPED, \
+ UACPI_OBJECT_MUTEX, \
+ UACPI_PARSE_OP_INVOKE_HANDLER, \
+ UACPI_PARSE_OP_INSTALL_NAMESPACE_NODE, 0, \
+ } \
+) \
+UACPI_OP( \
+ EventOp, UACPI_EXT_OP(0x02), 0, \
+ { \
+ UACPI_PARSE_OP_CREATE_NAMESTRING_OR_NULL_IF_LOAD, \
+ UACPI_PARSE_OP_SKIP_WITH_WARN_IF_NULL, 0, \
+ UACPI_PARSE_OP_OBJECT_ALLOC_TYPED, \
+ UACPI_OBJECT_EVENT, \
+ UACPI_PARSE_OP_INVOKE_HANDLER, \
+ UACPI_PARSE_OP_INSTALL_NAMESPACE_NODE, 0, \
+ } \
+) \
+UACPI_OP( \
+ CondRefOfOp, UACPI_EXT_OP(0x12), \
+ UACPI_OP_PROPERTY_TERM_ARG, \
+ { \
+ UACPI_PARSE_OP_SUPERNAME_OR_UNRESOLVED, \
+ UACPI_PARSE_OP_TARGET, \
+ UACPI_PARSE_OP_IF_NULL, 0, 3, \
+ UACPI_PARSE_OP_LOAD_FALSE_OBJECT, \
+ UACPI_PARSE_OP_OBJECT_TRANSFER_TO_PREV, \
+ UACPI_PARSE_OP_END, \
+ UACPI_PARSE_OP_OBJECT_ALLOC, \
+ UACPI_PARSE_OP_INVOKE_HANDLER, \
+ UACPI_PARSE_OP_STORE_TO_TARGET, 1, \
+ UACPI_PARSE_OP_LOAD_TRUE_OBJECT, \
+ UACPI_PARSE_OP_OBJECT_TRANSFER_TO_PREV, \
+ } \
+) \
+UACPI_DO_BUILD_BUFFER_FIELD_OP( \
+ Create, UACPI_EXT_OP(0x13), 3, \
+ UACPI_PARSE_OP_OPERAND \
+) \
+UACPI_OUT_OF_LINE_OP( \
+ LoadTableOp, UACPI_EXT_OP(0x1F), \
+ uacpi_load_table_op_decode_ops, \
+ UACPI_OP_PROPERTY_TERM_ARG | \
+ UACPI_OP_PROPERTY_OUT_OF_LINE \
+) \
+UACPI_OUT_OF_LINE_OP( \
+ LoadOp, UACPI_EXT_OP(0x20), \
+ uacpi_load_op_decode_ops, \
+ UACPI_OP_PROPERTY_TERM_ARG | \
+ UACPI_OP_PROPERTY_OUT_OF_LINE \
+) \
+UACPI_OP( \
+ StallOp, UACPI_EXT_OP(0x21), 0, \
+ { \
+ UACPI_PARSE_OP_OPERAND, \
+ UACPI_PARSE_OP_INVOKE_HANDLER, \
+ } \
+) \
+UACPI_OP( \
+ SleepOp, UACPI_EXT_OP(0x22), 0, \
+ { \
+ UACPI_PARSE_OP_OPERAND, \
+ UACPI_PARSE_OP_INVOKE_HANDLER, \
+ } \
+) \
+UACPI_OP( \
+ AcquireOp, UACPI_EXT_OP(0x23), \
+ UACPI_OP_PROPERTY_TERM_ARG, \
+ { \
+ UACPI_PARSE_OP_SUPERNAME, \
+ UACPI_PARSE_OP_LOAD_IMM, 2, \
+ UACPI_PARSE_OP_LOAD_TRUE_OBJECT, \
+ UACPI_PARSE_OP_INVOKE_HANDLER, \
+ UACPI_PARSE_OP_OBJECT_TRANSFER_TO_PREV, \
+ } \
+) \
+UACPI_OP( \
+ SignalOp, UACPI_EXT_OP(0x24), 0, \
+ { \
+ UACPI_PARSE_OP_SUPERNAME, \
+ UACPI_PARSE_OP_INVOKE_HANDLER, \
+ } \
+) \
+UACPI_OP( \
+ WaitOp, UACPI_EXT_OP(0x25), \
+ UACPI_OP_PROPERTY_TERM_ARG, \
+ { \
+ UACPI_PARSE_OP_SUPERNAME, \
+ UACPI_PARSE_OP_OPERAND, \
+ UACPI_PARSE_OP_LOAD_TRUE_OBJECT, \
+ UACPI_PARSE_OP_INVOKE_HANDLER, \
+ UACPI_PARSE_OP_OBJECT_TRANSFER_TO_PREV, \
+ } \
+) \
+UACPI_OP( \
+ ResetOp, UACPI_EXT_OP(0x26), 0, \
+ { \
+ UACPI_PARSE_OP_SUPERNAME, \
+ UACPI_PARSE_OP_INVOKE_HANDLER, \
+ } \
+) \
+UACPI_OP( \
+ ReleaseOp, UACPI_EXT_OP(0x27), 0, \
+ { \
+ UACPI_PARSE_OP_SUPERNAME, \
+ UACPI_PARSE_OP_INVOKE_HANDLER, \
+ } \
+) \
+UACPI_BUILD_TO_FROM_BCD(From, 0x28) \
+UACPI_BUILD_TO_FROM_BCD(To, 0x29) \
+UACPI_OP( \
+ UnloadOp, UACPI_EXT_OP(0x2A), 0, \
+ { \
+ UACPI_PARSE_OP_SUPERNAME, \
+ UACPI_PARSE_OP_INVOKE_HANDLER, \
+ } \
+) \
+UACPI_OP( \
+ RevisionOp, UACPI_EXT_OP(0x30), \
+ UACPI_OP_PROPERTY_TERM_ARG, \
+ { \
+ UACPI_PARSE_OP_LOAD_INLINE_IMM_AS_OBJECT, \
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ UACPI_PARSE_OP_OBJECT_TRANSFER_TO_PREV, \
+ } \
+) \
+UACPI_OP( \
+ DebugOp, UACPI_EXT_OP(0x31), \
+ UACPI_OP_PROPERTY_TERM_ARG | \
+ UACPI_OP_PROPERTY_SUPERNAME | \
+ UACPI_OP_PROPERTY_TARGET, \
+ { \
+ UACPI_PARSE_OP_OBJECT_ALLOC_TYPED, \
+ UACPI_OBJECT_DEBUG, \
+ UACPI_PARSE_OP_OBJECT_TRANSFER_TO_PREV, \
+ } \
+) \
+UACPI_OP( \
+ FatalOp, UACPI_EXT_OP(0x32), 0, \
+ { \
+ UACPI_PARSE_OP_LOAD_IMM, 1, \
+ UACPI_PARSE_OP_LOAD_IMM, 4, \
+ UACPI_PARSE_OP_OPERAND, \
+ UACPI_PARSE_OP_INVOKE_HANDLER, \
+ } \
+) \
+UACPI_OP( \
+ TimerOp, UACPI_EXT_OP(0x33), \
+ UACPI_OP_PROPERTY_TERM_ARG, \
+ { \
+ UACPI_PARSE_OP_OBJECT_ALLOC_TYPED, \
+ UACPI_OBJECT_INTEGER, \
+ UACPI_PARSE_OP_INVOKE_HANDLER, \
+ UACPI_PARSE_OP_OBJECT_TRANSFER_TO_PREV, \
+ } \
+) \
+UACPI_OP( \
+ OpRegionOp, UACPI_EXT_OP(0x80), 0, \
+ { \
+ UACPI_PARSE_OP_CREATE_NAMESTRING_OR_NULL_IF_LOAD, \
+ UACPI_PARSE_OP_LOAD_IMM, 1, \
+ UACPI_PARSE_OP_OPERAND, \
+ UACPI_PARSE_OP_OPERAND, \
+ UACPI_PARSE_OP_SKIP_WITH_WARN_IF_NULL, 0, \
+ UACPI_PARSE_OP_OBJECT_ALLOC_TYPED, \
+ UACPI_OBJECT_OPERATION_REGION, \
+ UACPI_PARSE_OP_INVOKE_HANDLER, \
+ UACPI_PARSE_OP_INSTALL_NAMESPACE_NODE, 0, \
+ } \
+) \
+UACPI_OUT_OF_LINE_OP( \
+ FieldOp, UACPI_EXT_OP(0x81), \
+ uacpi_field_op_decode_ops, \
+ UACPI_OP_PROPERTY_OUT_OF_LINE \
+) \
+UACPI_BUILD_NAMED_SCOPE_OBJECT_OP( \
+ Device, 0x82, UACPI_OBJECT_DEVICE \
+) \
+UACPI_BUILD_NAMED_SCOPE_OBJECT_OP( \
+ Processor, 0x83, UACPI_OBJECT_PROCESSOR, \
+ UACPI_PARSE_OP_LOAD_IMM, 1, \
+ UACPI_PARSE_OP_LOAD_IMM, 4, \
+ UACPI_PARSE_OP_LOAD_IMM, 1 \
+) \
+UACPI_BUILD_NAMED_SCOPE_OBJECT_OP( \
+ PowerRes, 0x84, UACPI_OBJECT_POWER_RESOURCE, \
+ UACPI_PARSE_OP_LOAD_IMM, 1, \
+ UACPI_PARSE_OP_LOAD_IMM, 2 \
+) \
+UACPI_BUILD_NAMED_SCOPE_OBJECT_OP( \
+ ThermalZone, 0x85, UACPI_OBJECT_THERMAL_ZONE \
+) \
+UACPI_OUT_OF_LINE_OP( \
+ IndexFieldOp, UACPI_EXT_OP(0x86), \
+ uacpi_index_field_op_decode_ops, \
+ UACPI_OP_PROPERTY_OUT_OF_LINE \
+) \
+UACPI_OUT_OF_LINE_OP( \
+ BankFieldOp, UACPI_EXT_OP(0x87), \
+ uacpi_bank_field_op_decode_ops, \
+ UACPI_OP_PROPERTY_OUT_OF_LINE \
+) \
+UACPI_OP( \
+ DataRegionOp, UACPI_EXT_OP(0x88), 0, \
+ { \
+ UACPI_PARSE_OP_CREATE_NAMESTRING_OR_NULL_IF_LOAD, \
+ UACPI_PARSE_OP_STRING, \
+ UACPI_PARSE_OP_STRING, \
+ UACPI_PARSE_OP_STRING, \
+ UACPI_PARSE_OP_SKIP_WITH_WARN_IF_NULL, 0, \
+ UACPI_PARSE_OP_OBJECT_ALLOC_TYPED, \
+ UACPI_OBJECT_OPERATION_REGION, \
+ UACPI_PARSE_OP_INVOKE_HANDLER, \
+ UACPI_PARSE_OP_INSTALL_NAMESPACE_NODE, 0, \
+ } \
+)
+
+enum uacpi_aml_op {
+#define UACPI_OP(name, code, ...) UACPI_AML_OP_##name = code,
+#define UACPI_OUT_OF_LINE_OP(name, code, ...) UACPI_AML_OP_##name = code,
+ UACPI_ENUMERATE_OPCODES
+ UACPI_ENUMERATE_EXT_OPCODES
+#undef UACPI_OP
+#undef UACPI_OUT_OF_LINE_OP
+};
diff --git a/sys/include/dev/acpi/uacpi/uacpi/internal/opregion.h b/sys/include/dev/acpi/uacpi/uacpi/internal/opregion.h
new file mode 100644
index 0000000..a1173f4
--- /dev/null
+++ b/sys/include/dev/acpi/uacpi/uacpi/internal/opregion.h
@@ -0,0 +1,49 @@
+#pragma once
+
+#include <uacpi/internal/types.h>
+#include <uacpi/opregion.h>
+
+#ifndef UACPI_BAREBONES_MODE
+
+uacpi_status uacpi_initialize_opregion(void);
+void uacpi_deinitialize_opregion(void);
+
+void uacpi_trace_region_error(
+ uacpi_namespace_node *node, uacpi_char *message, uacpi_status ret
+);
+
+uacpi_status uacpi_install_address_space_handler_with_flags(
+ uacpi_namespace_node *device_node, enum uacpi_address_space space,
+ uacpi_region_handler handler, uacpi_handle handler_context,
+ uacpi_u16 flags
+);
+
+void uacpi_opregion_uninstall_handler(uacpi_namespace_node *node);
+
+uacpi_bool uacpi_address_space_handler_is_default(
+ uacpi_address_space_handler *handler
+);
+
+uacpi_address_space_handlers *uacpi_node_get_address_space_handlers(
+ uacpi_namespace_node *node
+);
+
+uacpi_status uacpi_initialize_opregion_node(uacpi_namespace_node *node);
+
+uacpi_status uacpi_opregion_attach(uacpi_namespace_node *node);
+
+void uacpi_install_default_address_space_handlers(void);
+
+uacpi_bool uacpi_is_buffer_access_address_space(uacpi_address_space space);
+
+union uacpi_opregion_io_data {
+ uacpi_u64 *integer;
+ uacpi_data_view buffer;
+};
+
+uacpi_status uacpi_dispatch_opregion_io(
+ uacpi_field_unit *field, uacpi_u32 offset,
+ uacpi_region_op op, union uacpi_opregion_io_data data
+);
+
+#endif // !UACPI_BAREBONES_MODE
diff --git a/sys/include/dev/acpi/uacpi/uacpi/internal/osi.h b/sys/include/dev/acpi/uacpi/uacpi/internal/osi.h
new file mode 100644
index 0000000..6d7b0db
--- /dev/null
+++ b/sys/include/dev/acpi/uacpi/uacpi/internal/osi.h
@@ -0,0 +1,8 @@
+#pragma once
+
+#include <uacpi/osi.h>
+
+uacpi_status uacpi_initialize_interfaces(void);
+void uacpi_deinitialize_interfaces(void);
+
+uacpi_status uacpi_handle_osi(const uacpi_char *string, uacpi_bool *out_value);
diff --git a/sys/include/dev/acpi/uacpi/uacpi/internal/registers.h b/sys/include/dev/acpi/uacpi/uacpi/internal/registers.h
new file mode 100644
index 0000000..84694ac
--- /dev/null
+++ b/sys/include/dev/acpi/uacpi/uacpi/internal/registers.h
@@ -0,0 +1,7 @@
+#pragma once
+
+#include <uacpi/types.h>
+#include <uacpi/registers.h>
+
+uacpi_status uacpi_initialize_registers(void);
+void uacpi_deinitialize_registers(void);
diff --git a/sys/include/dev/acpi/uacpi/uacpi/internal/resources.h b/sys/include/dev/acpi/uacpi/uacpi/internal/resources.h
new file mode 100644
index 0000000..4c4a1ff
--- /dev/null
+++ b/sys/include/dev/acpi/uacpi/uacpi/internal/resources.h
@@ -0,0 +1,327 @@
+#pragma once
+
+#include <uacpi/internal/types.h>
+#include <uacpi/resources.h>
+
+#ifndef UACPI_BAREBONES_MODE
+
+enum uacpi_aml_resource {
+ UACPI_AML_RESOURCE_TYPE_INVALID = 0,
+
+ // Small resources
+ UACPI_AML_RESOURCE_IRQ,
+ UACPI_AML_RESOURCE_DMA,
+ UACPI_AML_RESOURCE_START_DEPENDENT,
+ UACPI_AML_RESOURCE_END_DEPENDENT,
+ UACPI_AML_RESOURCE_IO,
+ UACPI_AML_RESOURCE_FIXED_IO,
+ UACPI_AML_RESOURCE_FIXED_DMA,
+ UACPI_AML_RESOURCE_VENDOR_TYPE0,
+ UACPI_AML_RESOURCE_END_TAG,
+
+ // Large resources
+ UACPI_AML_RESOURCE_MEMORY24,
+ UACPI_AML_RESOURCE_GENERIC_REGISTER,
+ UACPI_AML_RESOURCE_VENDOR_TYPE1,
+ UACPI_AML_RESOURCE_MEMORY32,
+ UACPI_AML_RESOURCE_FIXED_MEMORY32,
+ UACPI_AML_RESOURCE_ADDRESS32,
+ UACPI_AML_RESOURCE_ADDRESS16,
+ UACPI_AML_RESOURCE_EXTENDED_IRQ,
+ UACPI_AML_RESOURCE_ADDRESS64,
+ UACPI_AML_RESOURCE_ADDRESS64_EXTENDED,
+ UACPI_AML_RESOURCE_GPIO_CONNECTION,
+ UACPI_AML_RESOURCE_PIN_FUNCTION,
+ UACPI_AML_RESOURCE_SERIAL_CONNECTION,
+ UACPI_AML_RESOURCE_PIN_CONFIGURATION,
+ UACPI_AML_RESOURCE_PIN_GROUP,
+ UACPI_AML_RESOURCE_PIN_GROUP_FUNCTION,
+ UACPI_AML_RESOURCE_PIN_GROUP_CONFIGURATION,
+ UACPI_AML_RESOURCE_CLOCK_INPUT,
+ UACPI_AML_RESOURCE_MAX = UACPI_AML_RESOURCE_CLOCK_INPUT,
+};
+
+enum uacpi_aml_resource_size_kind {
+ UACPI_AML_RESOURCE_SIZE_KIND_FIXED,
+ UACPI_AML_RESOURCE_SIZE_KIND_FIXED_OR_ONE_LESS,
+ UACPI_AML_RESOURCE_SIZE_KIND_VARIABLE,
+};
+
+enum uacpi_aml_resource_kind {
+ UACPI_AML_RESOURCE_KIND_SMALL = 0,
+ UACPI_AML_RESOURCE_KIND_LARGE,
+};
+
+enum uacpi_resource_convert_opcode {
+ UACPI_RESOURCE_CONVERT_OPCODE_END = 0,
+
+ /*
+ * AML -> native:
+ * Take the mask at 'aml_offset' and convert to an array of uacpi_u8
+ * at 'native_offset' with the value corresponding to the bit index.
+ * The array size is written to the byte at offset 'arg2'.
+ *
+ * native -> AML:
+ * Walk each element of the array at 'native_offset' and set the
+ * corresponding bit in the mask at 'aml_offset' to 1. The array size is
+ * read from the byte at offset 'arg2'.
+ */
+ UACPI_RESOURCE_CONVERT_OPCODE_PACKED_ARRAY_8,
+ UACPI_RESOURCE_CONVERT_OPCODE_PACKED_ARRAY_16,
+
+ /*
+ * AML -> native:
+ * Grab the bits at the byte at 'aml_offset' + 'bit_index', and copy its
+ * value into the byte at 'native_offset'.
+ *
+ * native -> AML:
+ * Grab first N bits at 'native_offset' and copy to 'aml_offset' starting
+ * at the 'bit_index'.
+ *
+ * NOTE:
+ * These must be contiguous in this order.
+ */
+ UACPI_RESOURCE_CONVERT_OPCODE_BIT_FIELD_1,
+ UACPI_RESOURCE_CONVERT_OPCODE_BIT_FIELD_2,
+ UACPI_RESOURCE_CONVERT_OPCODE_BIT_FIELD_3,
+ UACPI_RESOURCE_CONVERT_OPCODE_BIT_FIELD_6 =
+ UACPI_RESOURCE_CONVERT_OPCODE_BIT_FIELD_3 + 3,
+
+ /*
+ * AML -> native:
+ * Copy N bytes at 'aml_offset' to 'native_offset'.
+ *
+ * native -> AML:
+ * Copy N bytes at 'native_offset' to 'aml_offset'.
+ *
+ * 'imm' is added to the accumulator.
+ *
+ * NOTE: These are affected by the current value in the accumulator. If it's
+ * set to 0 at the time of evalution, this is executed once, N times
+ * otherwise. 0xFF is considered a special value, which resets the
+ * accumulator to 0 unconditionally.
+ */
+ UACPI_RESOURCE_CONVERT_OPCODE_FIELD_8,
+ UACPI_RESOURCE_CONVERT_OPCODE_FIELD_16,
+ UACPI_RESOURCE_CONVERT_OPCODE_FIELD_32,
+ UACPI_RESOURCE_CONVERT_OPCODE_FIELD_64,
+
+ /*
+ * If the length of the current resource is less than 'arg0', then skip
+ * 'imm' instructions.
+ */
+ UACPI_RESOURCE_CONVERT_OPCODE_SKIP_IF_AML_SIZE_LESS_THAN,
+
+ /*
+ * Skip 'imm' instructions if 'arg0' is not equal to the value in the
+ * accumulator.
+ */
+ UACPI_RESOURCE_CONVERT_OPCODE_SKIP_IF_NOT_EQUALS,
+
+ /*
+ * AML -> native:
+ * Set the byte at 'native_offset' to 'imm'.
+ *
+ * native -> AML:
+ * Set the byte at 'aml_offset' to 'imm'.
+ */
+ UACPI_RESOURCE_CONVERT_OPCODE_SET_TO_IMM,
+
+ /*
+ * AML -> native:
+ * Load the AML resoruce length into the accumulator as well as the field at
+ * 'native_offset' of width N.
+ *
+ * native -> AML:
+ * Load the resource length into the accumulator.
+ */
+ UACPI_RESOURCE_CONVERT_OPCODE_LOAD_AML_SIZE_32,
+
+ /*
+ * AML -> native:
+ * Load the 8 bit field at 'aml_offset' into the accumulator and store at
+ * 'native_offset'.
+ *
+ * native -> AML:
+ * Load the 8 bit field at 'native_offset' into the accumulator and store
+ * at 'aml_offset'.
+ *
+ * The accumulator is multiplied by 'imm' unless it's set to zero.
+ */
+ UACPI_RESOURCE_CONVERT_OPCODE_LOAD_8_STORE,
+
+ /*
+ * Load the N bit field at 'native_offset' into the accumulator
+ */
+ UACPI_RESOURCE_CONVERT_OPCODE_LOAD_8_NATIVE,
+ UACPI_RESOURCE_CONVERT_OPCODE_LOAD_16_NATIVE,
+
+ /*
+ * Load 'imm' into the accumulator.
+ */
+ UACPI_RESOURCE_CONVERT_OPCODE_LOAD_IMM,
+
+ /*
+ * AML -> native:
+ * Load the resource source at offset = aml size + accumulator into the
+ * uacpi_resource_source struct at 'native_offset'. The string bytes are
+ * written to the offset at resource size + accumulator. The presence is
+ * detected by comparing the length of the resource to the offset,
+ * 'arg2' optionally specifies the offset to the upper bound of the string.
+ *
+ * native -> AML:
+ * Load the resource source from the uacpi_resource_source struct at
+ * 'native_offset' to aml_size + accumulator. aml_size + accumulator is
+ * optionally written to 'aml_offset' if it's specified.
+ */
+ UACPI_RESOURCE_CONVERT_OPCODE_RESOURCE_SOURCE,
+ UACPI_RESOURCE_CONVERT_OPCODE_RESOURCE_SOURCE_NO_INDEX,
+ UACPI_RESOURCE_CONVERT_OPCODE_RESOURCE_LABEL,
+
+ /*
+ * AML -> native:
+ * Load the pin table with upper bound specified at 'aml_offset'.
+ * The table length is calculated by subtracting the upper bound from
+ * aml_size and is written into the accumulator.
+ *
+ * native -> AML:
+ * Load the pin table length from 'native_offset' and multiply by 2, store
+ * the result in the accumulator.
+ */
+ UACPI_RESOURCE_CONVERT_OPCODE_LOAD_PIN_TABLE_LENGTH,
+
+ /*
+ * AML -> native:
+ * Store the accumulator divided by 2 at 'native_offset'.
+ * The table is copied to the offset at resource size from offset at
+ * aml_size with the pointer written to the offset at 'arg2'.
+ *
+ * native -> AML:
+ * Read the pin table from resource size offset, write aml_size to
+ * 'aml_offset'. Copy accumulator bytes to the offset at aml_size.
+ */
+ UACPI_RESOURCE_CONVERT_OPCODE_PIN_TABLE,
+
+ /*
+ * AML -> native:
+ * Load vendor data with offset stored at 'aml_offset'. The length is
+ * calculated as aml_size - aml_offset and is written to 'native_offset'.
+ * The data is written to offset - aml_size with the pointer written back
+ * to the offset at 'arg2'.
+ *
+ * native -> AML:
+ * Read vendor data from the pointer at offset 'arg2' and size at
+ * 'native_offset', the offset to write to is calculated as the difference
+ * between the data pointer and the native resource end pointer.
+ * offset + aml_size is written to 'aml_offset' and the data is copied
+ * there as well.
+ */
+ UACPI_RESOURCE_CONVERT_OPCODE_VENDOR_DATA,
+
+ /*
+ * AML -> native:
+ * Read the serial type from the byte at 'aml_offset' and write it to the
+ * type field of the uacpi_resource_serial_bus_common structure. Convert
+ * the serial type to native and set the resource type to it. Copy the
+ * vendor data to the offset at native size, the length is calculated
+ * as type_data_length - extra-type-specific-size, and is written to
+ * vendor_data_length, as well as the accumulator. The data pointer is
+ * written to vendor_data.
+ *
+ * native -> AML:
+ * Set the serial type at 'aml_offset' to the value stored at
+ * 'native_offset'. Load the vendor data to the offset at aml_size,
+ * the length is read from 'vendor_data_length', and the data is copied from
+ * 'vendor_data'.
+ */
+ UACPI_RESOURCE_CONVERT_OPCODE_SERIAL_TYPE_SPECIFIC,
+
+ /*
+ * Produces an error if encountered in the instruction stream.
+ * Used to trap invalid/unexpected code flow.
+ */
+ UACPI_RESOURCE_CONVERT_OPCODE_UNREACHABLE,
+};
+
+struct uacpi_resource_convert_instruction {
+ uacpi_u8 code;
+
+ union {
+ uacpi_u8 aml_offset;
+ uacpi_u8 arg0;
+ } f1;
+
+ union {
+ uacpi_u8 native_offset;
+ uacpi_u8 arg1;
+ } f2;
+
+ union {
+ uacpi_u8 imm;
+ uacpi_u8 bit_index;
+ uacpi_u8 arg2;
+ } f3;
+};
+
+struct uacpi_resource_spec {
+ uacpi_u8 type : 5;
+ uacpi_u8 native_type : 5;
+ uacpi_u8 resource_kind : 1;
+ uacpi_u8 size_kind : 2;
+
+ /*
+ * Size of the resource as appears in the AML byte stream, for variable
+ * length resources this is the minimum.
+ */
+ uacpi_u16 aml_size;
+
+ /*
+ * Size of the native human-readable uacpi resource, for variable length
+ * resources this is the minimum. The final length is this field plus the
+ * result of extra_size_for_native().
+ */
+ uacpi_u16 native_size;
+
+ /*
+ * Calculate the amount of extra bytes that must be allocated for a specific
+ * native resource given the AML counterpart. This being NULL means no extra
+ * bytes are needed, aka native resources is always the same size.
+ */
+ uacpi_size (*extra_size_for_native)(
+ const struct uacpi_resource_spec*, void*, uacpi_size
+ );
+
+ /*
+ * Calculate the number of bytes needed to represent a native resource as
+ * AML. The 'aml_size' field is used if this is NULL.
+ */
+ uacpi_size (*size_for_aml)(
+ const struct uacpi_resource_spec*, uacpi_resource*
+ );
+
+ const struct uacpi_resource_convert_instruction *to_native;
+ const struct uacpi_resource_convert_instruction *to_aml;
+};
+
+typedef uacpi_iteration_decision (*uacpi_aml_resource_iteration_callback)(
+ void*, uacpi_u8 *data, uacpi_u16 resource_size,
+ const struct uacpi_resource_spec*
+);
+
+uacpi_status uacpi_for_each_aml_resource(
+ uacpi_data_view, uacpi_aml_resource_iteration_callback cb, void *user
+);
+
+uacpi_status uacpi_find_aml_resource_end_tag(
+ uacpi_data_view, uacpi_size *out_offset
+);
+
+uacpi_status uacpi_native_resources_from_aml(
+ uacpi_data_view, uacpi_resources **out_resources
+);
+
+uacpi_status uacpi_native_resources_to_aml(
+ uacpi_resources *resources, uacpi_object **out_template
+);
+
+#endif // !UACPI_BAREBONES_MODE
diff --git a/sys/include/dev/acpi/uacpi/uacpi/internal/shareable.h b/sys/include/dev/acpi/uacpi/uacpi/internal/shareable.h
new file mode 100644
index 0000000..e00d850
--- /dev/null
+++ b/sys/include/dev/acpi/uacpi/uacpi/internal/shareable.h
@@ -0,0 +1,21 @@
+#pragma once
+
+#include <uacpi/types.h>
+
+struct uacpi_shareable {
+ uacpi_u32 reference_count;
+};
+
+void uacpi_shareable_init(uacpi_handle);
+
+uacpi_bool uacpi_bugged_shareable(uacpi_handle);
+void uacpi_make_shareable_bugged(uacpi_handle);
+
+uacpi_u32 uacpi_shareable_ref(uacpi_handle);
+uacpi_u32 uacpi_shareable_unref(uacpi_handle);
+
+void uacpi_shareable_unref_and_delete_if_last(
+ uacpi_handle, void (*do_free)(uacpi_handle)
+);
+
+uacpi_u32 uacpi_shareable_refcount(uacpi_handle);
diff --git a/sys/include/dev/acpi/uacpi/uacpi/internal/stdlib.h b/sys/include/dev/acpi/uacpi/uacpi/internal/stdlib.h
new file mode 100644
index 0000000..853c1bc
--- /dev/null
+++ b/sys/include/dev/acpi/uacpi/uacpi/internal/stdlib.h
@@ -0,0 +1,131 @@
+#pragma once
+
+#include <uacpi/internal/types.h>
+#include <uacpi/internal/helpers.h>
+#include <uacpi/platform/libc.h>
+#include <uacpi/platform/config.h>
+#include <uacpi/kernel_api.h>
+
+#define offsetof(st, m) \
+ ((size_t)&(((st *)0)->m))
+
+#ifdef UACPI_USE_BUILTIN_STRING
+
+#ifndef uacpi_memcpy
+void *uacpi_memcpy(void *dest, const void *src, uacpi_size count);
+#endif
+
+#ifndef uacpi_memmove
+void *uacpi_memmove(void *dest, const void *src, uacpi_size count);
+#endif
+
+#ifndef uacpi_memset
+void *uacpi_memset(void *dest, uacpi_i32 ch, uacpi_size count);
+#endif
+
+#ifndef uacpi_memcmp
+uacpi_i32 uacpi_memcmp(const void *lhs, const void *rhs, uacpi_size count);
+#endif
+
+#else
+
+#ifndef uacpi_memcpy
+ #ifdef UACPI_COMPILER_HAS_BUILTIN_MEMCPY
+ #define uacpi_memcpy __builtin_memcpy
+ #else
+ extern void *memcpy(void *dest, const void *src, uacpi_size count);
+ #define uacpi_memcpy memcpy
+ #endif
+#endif
+
+#ifndef uacpi_memmove
+ #ifdef UACPI_COMPILER_HAS_BUILTIN_MEMMOVE
+ #define uacpi_memmove __builtin_memmove
+ #else
+ extern void *memmove(void *dest, const void *src, uacpi_size count);
+ #define uacpi_memmove memmove
+ #endif
+#endif
+
+#ifndef uacpi_memset
+ #ifdef UACPI_COMPILER_HAS_BUILTIN_MEMSET
+ #define uacpi_memset __builtin_memset
+ #else
+ extern void *memset(void *dest, int ch, uacpi_size count);
+ #define uacpi_memset memset
+ #endif
+#endif
+
+#ifndef uacpi_memcmp
+ #ifdef UACPI_COMPILER_HAS_BUILTIN_MEMCMP
+ #define uacpi_memcmp __builtin_memcmp
+ #else
+ extern int memcmp(const void *lhs, const void *rhs, uacpi_size count);
+ #define uacpi_memcmp memcmp
+ #endif
+#endif
+
+#endif
+
+#ifndef uacpi_strlen
+uacpi_size uacpi_strlen(const uacpi_char *str);
+#endif
+
+#ifndef uacpi_strnlen
+uacpi_size uacpi_strnlen(const uacpi_char *str, uacpi_size max);
+#endif
+
+#ifndef uacpi_strcmp
+uacpi_i32 uacpi_strcmp(const uacpi_char *lhs, const uacpi_char *rhs);
+#endif
+
+#ifndef uacpi_snprintf
+UACPI_PRINTF_DECL(3, 4)
+uacpi_i32 uacpi_snprintf(
+ uacpi_char *buffer, uacpi_size capacity, const uacpi_char *fmt, ...
+);
+#endif
+
+#ifndef uacpi_vsnprintf
+uacpi_i32 uacpi_vsnprintf(
+ uacpi_char *buffer, uacpi_size capacity, const uacpi_char *fmt,
+ uacpi_va_list vlist
+);
+#endif
+
+#ifdef UACPI_SIZED_FREES
+#define uacpi_free(mem, size) uacpi_kernel_free(mem, size)
+#else
+#define uacpi_free(mem, _) uacpi_kernel_free(mem)
+#endif
+
+#define uacpi_memzero(ptr, size) uacpi_memset(ptr, 0, size)
+
+#define UACPI_COMPARE(x, y, op) ((x) op (y) ? (x) : (y))
+#define UACPI_MIN(x, y) UACPI_COMPARE(x, y, <)
+#define UACPI_MAX(x, y) UACPI_COMPARE(x, y, >)
+
+#define UACPI_ALIGN_UP_MASK(x, mask) (((x) + (mask)) & ~(mask))
+#define UACPI_ALIGN_UP(x, val, type) UACPI_ALIGN_UP_MASK(x, (type)(val) - 1)
+
+#define UACPI_ALIGN_DOWN_MASK(x, mask) ((x) & ~(mask))
+#define UACPI_ALIGN_DOWN(x, val, type) UACPI_ALIGN_DOWN_MASK(x, (type)(val) - 1)
+
+#define UACPI_IS_ALIGNED_MASK(x, mask) (((x) & (mask)) == 0)
+#define UACPI_IS_ALIGNED(x, val, type) UACPI_IS_ALIGNED_MASK(x, (type)(val) - 1)
+
+#define UACPI_IS_POWER_OF_TWO(x, type) UACPI_IS_ALIGNED(x, x, type)
+
+void uacpi_memcpy_zerout(void *dst, const void *src,
+ uacpi_size dst_size, uacpi_size src_size);
+
+// Returns the one-based bit location of LSb or 0
+uacpi_u8 uacpi_bit_scan_forward(uacpi_u64);
+
+// Returns the one-based bit location of MSb or 0
+uacpi_u8 uacpi_bit_scan_backward(uacpi_u64);
+
+#ifndef UACPI_NATIVE_ALLOC_ZEROED
+void *uacpi_builtin_alloc_zeroed(uacpi_size size);
+#define uacpi_kernel_alloc_zeroed uacpi_builtin_alloc_zeroed
+#endif
diff --git a/sys/include/dev/acpi/uacpi/uacpi/internal/tables.h b/sys/include/dev/acpi/uacpi/uacpi/internal/tables.h
new file mode 100644
index 0000000..8a5345f
--- /dev/null
+++ b/sys/include/dev/acpi/uacpi/uacpi/internal/tables.h
@@ -0,0 +1,70 @@
+#pragma once
+
+#include <uacpi/internal/context.h>
+#include <uacpi/internal/interpreter.h>
+#include <uacpi/types.h>
+#include <uacpi/status.h>
+#include <uacpi/tables.h>
+
+enum uacpi_table_origin {
+#ifndef UACPI_BAREBONES_MODE
+ UACPI_TABLE_ORIGIN_FIRMWARE_VIRTUAL = 0,
+#endif
+ UACPI_TABLE_ORIGIN_FIRMWARE_PHYSICAL = 1,
+
+ UACPI_TABLE_ORIGIN_HOST_VIRTUAL,
+ UACPI_TABLE_ORIGIN_HOST_PHYSICAL,
+};
+
+struct uacpi_installed_table {
+ uacpi_phys_addr phys_addr;
+ struct acpi_sdt_hdr hdr;
+ void *ptr;
+
+ uacpi_u16 reference_count;
+
+#define UACPI_TABLE_LOADED (1 << 0)
+#define UACPI_TABLE_CSUM_VERIFIED (1 << 1)
+#define UACPI_TABLE_INVALID (1 << 2)
+ uacpi_u8 flags;
+ uacpi_u8 origin;
+};
+
+uacpi_status uacpi_initialize_tables(void);
+void uacpi_deinitialize_tables(void);
+
+uacpi_bool uacpi_signatures_match(const void *const lhs, const void *const rhs);
+uacpi_status uacpi_check_table_signature(void *table, const uacpi_char *expect);
+uacpi_status uacpi_verify_table_checksum(void *table, uacpi_size size);
+
+uacpi_status uacpi_table_install_physical_with_origin(
+ uacpi_phys_addr phys, enum uacpi_table_origin origin, uacpi_table *out_table
+);
+uacpi_status uacpi_table_install_with_origin(
+ void *virt, enum uacpi_table_origin origin, uacpi_table *out_table
+);
+
+#ifndef UACPI_BAREBONES_MODE
+void uacpi_table_mark_as_loaded(uacpi_size idx);
+
+uacpi_status uacpi_table_load_with_cause(
+ uacpi_size idx, enum uacpi_table_load_cause cause
+);
+#endif // !UACPI_BAREBONES_MODE
+
+typedef uacpi_iteration_decision (*uacpi_table_iteration_callback)
+ (void *user, struct uacpi_installed_table *tbl, uacpi_size idx);
+
+uacpi_status uacpi_for_each_table(
+ uacpi_size base_idx, uacpi_table_iteration_callback, void *user
+);
+
+typedef uacpi_bool (*uacpi_table_match_callback)
+ (struct uacpi_installed_table *tbl);
+
+uacpi_status uacpi_table_match(
+ uacpi_size base_idx, uacpi_table_match_callback, uacpi_table *out_table
+);
+
+#define UACPI_PRI_TBL_HDR "'%.4s' (OEM ID '%.6s' OEM Table ID '%.8s')"
+#define UACPI_FMT_TBL_HDR(hdr) (hdr)->signature, (hdr)->oemid, (hdr)->oem_table_id
diff --git a/sys/include/dev/acpi/uacpi/uacpi/internal/types.h b/sys/include/dev/acpi/uacpi/uacpi/internal/types.h
new file mode 100644
index 0000000..b994a27
--- /dev/null
+++ b/sys/include/dev/acpi/uacpi/uacpi/internal/types.h
@@ -0,0 +1,310 @@
+#pragma once
+
+#include <uacpi/status.h>
+#include <uacpi/types.h>
+#include <uacpi/internal/shareable.h>
+
+#ifndef UACPI_BAREBONES_MODE
+
+// object->flags field if object->type == UACPI_OBJECT_REFERENCE
+enum uacpi_reference_kind {
+ UACPI_REFERENCE_KIND_REFOF = 0,
+ UACPI_REFERENCE_KIND_LOCAL = 1,
+ UACPI_REFERENCE_KIND_ARG = 2,
+ UACPI_REFERENCE_KIND_NAMED = 3,
+ UACPI_REFERENCE_KIND_PKG_INDEX = 4,
+};
+
+// object->flags field if object->type == UACPI_OBJECT_STRING
+enum uacpi_string_kind {
+ UACPI_STRING_KIND_NORMAL = 0,
+ UACPI_STRING_KIND_PATH,
+};
+
+typedef struct uacpi_buffer {
+ struct uacpi_shareable shareable;
+ union {
+ void *data;
+ uacpi_u8 *byte_data;
+ uacpi_char *text;
+ };
+ uacpi_size size;
+} uacpi_buffer;
+
+typedef struct uacpi_package {
+ struct uacpi_shareable shareable;
+ uacpi_object **objects;
+ uacpi_size count;
+} uacpi_package;
+
+typedef struct uacpi_buffer_field {
+ uacpi_buffer *backing;
+ uacpi_size bit_index;
+ uacpi_u32 bit_length;
+ uacpi_bool force_buffer;
+} uacpi_buffer_field;
+
+typedef struct uacpi_buffer_index {
+ uacpi_size idx;
+ uacpi_buffer *buffer;
+} uacpi_buffer_index;
+
+typedef struct uacpi_mutex {
+ struct uacpi_shareable shareable;
+ uacpi_handle handle;
+ uacpi_thread_id owner;
+ uacpi_u16 depth;
+ uacpi_u8 sync_level;
+} uacpi_mutex;
+
+typedef struct uacpi_event {
+ struct uacpi_shareable shareable;
+ uacpi_handle handle;
+} uacpi_event;
+
+typedef struct uacpi_address_space_handler {
+ struct uacpi_shareable shareable;
+ uacpi_region_handler callback;
+ uacpi_handle user_context;
+ struct uacpi_address_space_handler *next;
+ struct uacpi_operation_region *regions;
+ uacpi_u16 space;
+
+#define UACPI_ADDRESS_SPACE_HANDLER_DEFAULT (1 << 0)
+ uacpi_u16 flags;
+} uacpi_address_space_handler;
+
+/*
+ * NOTE: These are common object headers.
+ * Any changes to these structs must be propagated to all objects.
+ * ==============================================================
+ * Common for the following objects:
+ * - UACPI_OBJECT_OPERATION_REGION
+ * - UACPI_OBJECT_PROCESSOR
+ * - UACPI_OBJECT_DEVICE
+ * - UACPI_OBJECT_THERMAL_ZONE
+ */
+typedef struct uacpi_address_space_handlers {
+ struct uacpi_shareable shareable;
+ uacpi_address_space_handler *head;
+} uacpi_address_space_handlers;
+
+typedef struct uacpi_device_notify_handler {
+ uacpi_notify_handler callback;
+ uacpi_handle user_context;
+ struct uacpi_device_notify_handler *next;
+} uacpi_device_notify_handler;
+
+/*
+ * Common for the following objects:
+ * - UACPI_OBJECT_PROCESSOR
+ * - UACPI_OBJECT_DEVICE
+ * - UACPI_OBJECT_THERMAL_ZONE
+ */
+typedef struct uacpi_handlers {
+ struct uacpi_shareable shareable;
+ uacpi_address_space_handler *address_space_head;
+ uacpi_device_notify_handler *notify_head;
+} uacpi_handlers;
+
+// This region has a corresponding _REG method that was succesfully executed
+#define UACPI_OP_REGION_STATE_REG_EXECUTED (1 << 0)
+
+// This region was successfully attached to a handler
+#define UACPI_OP_REGION_STATE_ATTACHED (1 << 1)
+
+typedef struct uacpi_operation_region {
+ struct uacpi_shareable shareable;
+ uacpi_address_space_handler *handler;
+ uacpi_handle user_context;
+ uacpi_u16 space;
+ uacpi_u8 state_flags;
+ uacpi_u64 offset;
+ uacpi_u64 length;
+
+ union {
+ // If space == TABLE_DATA
+ uacpi_u64 table_idx;
+
+ // If space == PCC
+ uacpi_u8 *internal_buffer;
+ };
+
+ // Used to link regions sharing the same handler
+ struct uacpi_operation_region *next;
+} uacpi_operation_region;
+
+typedef struct uacpi_device {
+ struct uacpi_shareable shareable;
+ uacpi_address_space_handler *address_space_handlers;
+ uacpi_device_notify_handler *notify_handlers;
+} uacpi_device;
+
+typedef struct uacpi_processor {
+ struct uacpi_shareable shareable;
+ uacpi_address_space_handler *address_space_handlers;
+ uacpi_device_notify_handler *notify_handlers;
+ uacpi_u8 id;
+ uacpi_u32 block_address;
+ uacpi_u8 block_length;
+} uacpi_processor;
+
+typedef struct uacpi_thermal_zone {
+ struct uacpi_shareable shareable;
+ uacpi_address_space_handler *address_space_handlers;
+ uacpi_device_notify_handler *notify_handlers;
+} uacpi_thermal_zone;
+
+typedef struct uacpi_power_resource {
+ uacpi_u8 system_level;
+ uacpi_u16 resource_order;
+} uacpi_power_resource;
+
+typedef uacpi_status (*uacpi_native_call_handler)(
+ uacpi_handle ctx, uacpi_object *retval
+);
+
+typedef struct uacpi_control_method {
+ struct uacpi_shareable shareable;
+ union {
+ uacpi_u8 *code;
+ uacpi_native_call_handler handler;
+ };
+ uacpi_mutex *mutex;
+ uacpi_u32 size;
+ uacpi_u8 sync_level : 4;
+ uacpi_u8 args : 3;
+ uacpi_u8 is_serialized : 1;
+ uacpi_u8 named_objects_persist: 1;
+ uacpi_u8 native_call : 1;
+ uacpi_u8 owns_code : 1;
+} uacpi_control_method;
+
+typedef enum uacpi_access_type {
+ UACPI_ACCESS_TYPE_ANY = 0,
+ UACPI_ACCESS_TYPE_BYTE = 1,
+ UACPI_ACCESS_TYPE_WORD = 2,
+ UACPI_ACCESS_TYPE_DWORD = 3,
+ UACPI_ACCESS_TYPE_QWORD = 4,
+ UACPI_ACCESS_TYPE_BUFFER = 5,
+} uacpi_access_type;
+
+typedef enum uacpi_lock_rule {
+ UACPI_LOCK_RULE_NO_LOCK = 0,
+ UACPI_LOCK_RULE_LOCK = 1,
+} uacpi_lock_rule;
+
+typedef enum uacpi_update_rule {
+ UACPI_UPDATE_RULE_PRESERVE = 0,
+ UACPI_UPDATE_RULE_WRITE_AS_ONES = 1,
+ UACPI_UPDATE_RULE_WRITE_AS_ZEROES = 2,
+} uacpi_update_rule;
+
+typedef enum uacpi_field_unit_kind {
+ UACPI_FIELD_UNIT_KIND_NORMAL = 0,
+ UACPI_FIELD_UNIT_KIND_INDEX = 1,
+ UACPI_FIELD_UNIT_KIND_BANK = 2,
+} uacpi_field_unit_kind;
+
+typedef struct uacpi_field_unit {
+ struct uacpi_shareable shareable;
+
+ union {
+ // UACPI_FIELD_UNIT_KIND_NORMAL
+ struct {
+ uacpi_namespace_node *region;
+ };
+
+ // UACPI_FIELD_UNIT_KIND_INDEX
+ struct {
+ struct uacpi_field_unit *index;
+ struct uacpi_field_unit *data;
+ };
+
+ // UACPI_FIELD_UNIT_KIND_BANK
+ struct {
+ uacpi_namespace_node *bank_region;
+ struct uacpi_field_unit *bank_selection;
+ uacpi_u64 bank_value;
+ };
+ };
+
+ uacpi_object *connection;
+
+ uacpi_u32 byte_offset;
+ uacpi_u32 bit_length;
+ uacpi_u32 pin_offset;
+ uacpi_u8 bit_offset_within_first_byte;
+ uacpi_u8 access_width_bytes;
+ uacpi_u8 access_length;
+
+ uacpi_u8 attributes : 4;
+ uacpi_u8 update_rule : 2;
+ uacpi_u8 kind : 2;
+ uacpi_u8 lock_rule : 1;
+} uacpi_field_unit;
+
+typedef struct uacpi_object {
+ struct uacpi_shareable shareable;
+ uacpi_u8 type;
+ uacpi_u8 flags;
+
+ union {
+ uacpi_u64 integer;
+ uacpi_package *package;
+ uacpi_buffer_field buffer_field;
+ uacpi_object *inner_object;
+ uacpi_control_method *method;
+ uacpi_buffer *buffer;
+ uacpi_mutex *mutex;
+ uacpi_event *event;
+ uacpi_buffer_index buffer_index;
+ uacpi_operation_region *op_region;
+ uacpi_device *device;
+ uacpi_processor *processor;
+ uacpi_thermal_zone *thermal_zone;
+ uacpi_address_space_handlers *address_space_handlers;
+ uacpi_handlers *handlers;
+ uacpi_power_resource power_resource;
+ uacpi_field_unit *field_unit;
+ };
+} uacpi_object;
+
+uacpi_object *uacpi_create_object(uacpi_object_type type);
+
+enum uacpi_assign_behavior {
+ UACPI_ASSIGN_BEHAVIOR_DEEP_COPY,
+ UACPI_ASSIGN_BEHAVIOR_SHALLOW_COPY,
+};
+
+uacpi_status uacpi_object_assign(uacpi_object *dst, uacpi_object *src,
+ enum uacpi_assign_behavior);
+
+void uacpi_object_attach_child(uacpi_object *parent, uacpi_object *child);
+void uacpi_object_detach_child(uacpi_object *parent);
+
+struct uacpi_object *uacpi_create_internal_reference(
+ enum uacpi_reference_kind kind, uacpi_object *child
+);
+uacpi_object *uacpi_unwrap_internal_reference(uacpi_object *object);
+
+enum uacpi_prealloc_objects {
+ UACPI_PREALLOC_OBJECTS_NO,
+ UACPI_PREALLOC_OBJECTS_YES,
+};
+
+uacpi_bool uacpi_package_fill(
+ uacpi_package *pkg, uacpi_size num_elements,
+ enum uacpi_prealloc_objects prealloc_objects
+);
+
+uacpi_mutex *uacpi_create_mutex(void);
+void uacpi_mutex_unref(uacpi_mutex*);
+
+void uacpi_method_unref(uacpi_control_method*);
+
+void uacpi_address_space_handler_unref(uacpi_address_space_handler *handler);
+
+void uacpi_buffer_to_view(uacpi_buffer*, uacpi_data_view*);
+
+#endif // !UACPI_BAREBONES_MODE
diff --git a/sys/include/dev/acpi/uacpi/uacpi/internal/utilities.h b/sys/include/dev/acpi/uacpi/uacpi/internal/utilities.h
new file mode 100644
index 0000000..606ec92
--- /dev/null
+++ b/sys/include/dev/acpi/uacpi/uacpi/internal/utilities.h
@@ -0,0 +1,45 @@
+#pragma once
+
+#include <uacpi/types.h>
+#include <uacpi/utilities.h>
+#include <uacpi/internal/log.h>
+#include <uacpi/internal/stdlib.h>
+
+static inline uacpi_phys_addr uacpi_truncate_phys_addr_with_warn(uacpi_u64 large_addr)
+{
+ if (sizeof(uacpi_phys_addr) < 8 && large_addr > 0xFFFFFFFF) {
+ uacpi_warn(
+ "truncating a physical address 0x%"UACPI_PRIX64
+ " outside of address space\n", UACPI_FMT64(large_addr)
+ );
+ }
+
+ return (uacpi_phys_addr)large_addr;
+}
+
+#define UACPI_PTR_TO_VIRT_ADDR(ptr) ((uacpi_virt_addr)(ptr))
+#define UACPI_VIRT_ADDR_TO_PTR(vaddr) ((void*)(vaddr))
+
+#define UACPI_PTR_ADD(ptr, value) ((void*)(((uacpi_u8*)(ptr)) + value))
+
+/*
+ * Target buffer must have a length of at least 8 bytes.
+ */
+void uacpi_eisa_id_to_string(uacpi_u32, uacpi_char *out_string);
+
+enum uacpi_base {
+ UACPI_BASE_AUTO,
+ UACPI_BASE_OCT = 8,
+ UACPI_BASE_DEC = 10,
+ UACPI_BASE_HEX = 16,
+};
+uacpi_status uacpi_string_to_integer(
+ const uacpi_char *str, uacpi_size max_chars, enum uacpi_base base,
+ uacpi_u64 *out_value
+);
+
+uacpi_bool uacpi_is_valid_nameseg(uacpi_u8 *nameseg);
+
+void uacpi_free_dynamic_string(const uacpi_char *str);
+
+#define UACPI_NANOSECONDS_PER_SEC (1000ull * 1000ull * 1000ull)
diff --git a/sys/include/dev/acpi/uacpi/uacpi/io.h b/sys/include/dev/acpi/uacpi/uacpi/io.h
new file mode 100644
index 0000000..6535a06
--- /dev/null
+++ b/sys/include/dev/acpi/uacpi/uacpi/io.h
@@ -0,0 +1,36 @@
+#pragma once
+
+#include <uacpi/types.h>
+#include <uacpi/acpi.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef UACPI_BAREBONES_MODE
+
+uacpi_status uacpi_gas_read(const struct acpi_gas *gas, uacpi_u64 *value);
+uacpi_status uacpi_gas_write(const struct acpi_gas *gas, uacpi_u64 value);
+
+typedef struct uacpi_mapped_gas uacpi_mapped_gas;
+
+/*
+ * Map a GAS for faster access in the future. The handle returned via
+ * 'out_mapped' must be freed & unmapped using uacpi_unmap_gas() when
+ * no longer needed.
+ */
+uacpi_status uacpi_map_gas(const struct acpi_gas *gas, uacpi_mapped_gas **out_mapped);
+void uacpi_unmap_gas(uacpi_mapped_gas*);
+
+/*
+ * Same as uacpi_gas_{read,write} but operates on a pre-mapped handle for faster
+ * access and/or ability to use in critical sections/irq contexts.
+ */
+uacpi_status uacpi_gas_read_mapped(const uacpi_mapped_gas *gas, uacpi_u64 *value);
+uacpi_status uacpi_gas_write_mapped(const uacpi_mapped_gas *gas, uacpi_u64 value);
+
+#endif // !UACPI_BAREBONES_MODE
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/sys/include/dev/acpi/uacpi/uacpi/kernel_api.h b/sys/include/dev/acpi/uacpi/uacpi/kernel_api.h
new file mode 100644
index 0000000..2a370de
--- /dev/null
+++ b/sys/include/dev/acpi/uacpi/uacpi/kernel_api.h
@@ -0,0 +1,375 @@
+#pragma once
+
+#include <uacpi/types.h>
+#include <uacpi/platform/arch_helpers.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Returns the PHYSICAL address of the RSDP structure via *out_rsdp_address.
+uacpi_status uacpi_kernel_get_rsdp(uacpi_phys_addr *out_rsdp_address);
+
+/*
+ * Map a physical memory range starting at 'addr' with length 'len', and return
+ * a virtual address that can be used to access it.
+ *
+ * NOTE: 'addr' may be misaligned, in this case the host is expected to round it
+ * down to the nearest page-aligned boundary and map that, while making
+ * sure that at least 'len' bytes are still mapped starting at 'addr'. The
+ * return value preserves the misaligned offset.
+ *
+ * Example for uacpi_kernel_map(0x1ABC, 0xF00):
+ * 1. Round down the 'addr' we got to the nearest page boundary.
+ * Considering a PAGE_SIZE of 4096 (or 0x1000), 0x1ABC rounded down
+ * is 0x1000, offset within the page is 0x1ABC - 0x1000 => 0xABC
+ * 2. Requested 'len' is 0xF00 bytes, but we just rounded the address
+ * down by 0xABC bytes, so add those on top. 0xF00 + 0xABC => 0x19BC
+ * 3. Round up the final 'len' to the nearest PAGE_SIZE boundary, in
+ * this case 0x19BC is 0x2000 bytes (2 pages if PAGE_SIZE is 4096)
+ * 4. Call the VMM to map the aligned address 0x1000 (from step 1)
+ * with length 0x2000 (from step 3). Let's assume the returned
+ * virtual address for the mapping is 0xF000.
+ * 5. Add the original offset within page 0xABC (from step 1) to the
+ * resulting virtual address 0xF000 + 0xABC => 0xFABC. Return it
+ * to uACPI.
+ */
+void *uacpi_kernel_map(uacpi_phys_addr addr, uacpi_size len);
+
+/*
+ * Unmap a virtual memory range at 'addr' with a length of 'len' bytes.
+ *
+ * NOTE: 'addr' may be misaligned, see the comment above 'uacpi_kernel_map'.
+ * Similar steps to uacpi_kernel_map can be taken to retrieve the
+ * virtual address originally returned by the VMM for this mapping
+ * as well as its true length.
+ */
+void uacpi_kernel_unmap(void *addr, uacpi_size len);
+
+#ifndef UACPI_FORMATTED_LOGGING
+void uacpi_kernel_log(uacpi_log_level, const uacpi_char*);
+#else
+UACPI_PRINTF_DECL(2, 3)
+void uacpi_kernel_log(uacpi_log_level, const uacpi_char*, ...);
+void uacpi_kernel_vlog(uacpi_log_level, const uacpi_char*, uacpi_va_list);
+#endif
+
+/*
+ * Only the above ^^^ API may be used by early table access and
+ * UACPI_BAREBONES_MODE.
+ */
+#ifndef UACPI_BAREBONES_MODE
+
+/*
+ * Convenience initialization/deinitialization hooks that will be called by
+ * uACPI automatically when appropriate if compiled-in.
+ */
+#ifdef UACPI_KERNEL_INITIALIZATION
+/*
+ * This API is invoked for each initialization level so that appropriate parts
+ * of the host kernel and/or glue code can be initialized at different stages.
+ *
+ * uACPI API that triggers calls to uacpi_kernel_initialize and the respective
+ * 'current_init_lvl' passed to the hook at that stage:
+ * 1. uacpi_initialize() -> UACPI_INIT_LEVEL_EARLY
+ * 2. uacpi_namespace_load() -> UACPI_INIT_LEVEL_SUBSYSTEM_INITIALIZED
+ * 3. (start of) uacpi_namespace_initialize() -> UACPI_INIT_LEVEL_NAMESPACE_LOADED
+ * 4. (end of) uacpi_namespace_initialize() -> UACPI_INIT_LEVEL_NAMESPACE_INITIALIZED
+ */
+uacpi_status uacpi_kernel_initialize(uacpi_init_level current_init_lvl);
+void uacpi_kernel_deinitialize(void);
+#endif
+
+/*
+ * Open a PCI device at 'address' for reading & writing.
+ *
+ * Note that this must be able to open any arbitrary PCI device, not just those
+ * detected during kernel PCI enumeration, since the following pattern is
+ * relatively common in AML firmware:
+ * Device (THC0)
+ * {
+ * // Device at 00:10.06
+ * Name (_ADR, 0x00100006) // _ADR: Address
+ *
+ * OperationRegion (THCR, PCI_Config, Zero, 0x0100)
+ * Field (THCR, ByteAcc, NoLock, Preserve)
+ * {
+ * // Vendor ID field in the PCI configuration space
+ * VDID, 32
+ * }
+ *
+ * // Check if the device at 00:10.06 actually exists, that is reading
+ * // from its configuration space returns something other than 0xFFs.
+ * If ((VDID != 0xFFFFFFFF))
+ * {
+ * // Actually create the rest of the device's body if it's present
+ * // in the system, otherwise skip it.
+ * }
+ * }
+ *
+ * The handle returned via 'out_handle' is used to perform IO on the
+ * configuration space of the device.
+ */
+uacpi_status uacpi_kernel_pci_device_open(
+ uacpi_pci_address address, uacpi_handle *out_handle
+);
+void uacpi_kernel_pci_device_close(uacpi_handle);
+
+/*
+ * Read & write the configuration space of a previously open PCI device.
+ */
+uacpi_status uacpi_kernel_pci_read8(
+ uacpi_handle device, uacpi_size offset, uacpi_u8 *value
+);
+uacpi_status uacpi_kernel_pci_read16(
+ uacpi_handle device, uacpi_size offset, uacpi_u16 *value
+);
+uacpi_status uacpi_kernel_pci_read32(
+ uacpi_handle device, uacpi_size offset, uacpi_u32 *value
+);
+
+uacpi_status uacpi_kernel_pci_write8(
+ uacpi_handle device, uacpi_size offset, uacpi_u8 value
+);
+uacpi_status uacpi_kernel_pci_write16(
+ uacpi_handle device, uacpi_size offset, uacpi_u16 value
+);
+uacpi_status uacpi_kernel_pci_write32(
+ uacpi_handle device, uacpi_size offset, uacpi_u32 value
+);
+
+/*
+ * Map a SystemIO address at [base, base + len) and return a kernel-implemented
+ * handle that can be used for reading and writing the IO range.
+ *
+ * NOTE: The x86 architecture uses the in/out family of instructions
+ * to access the SystemIO address space.
+ */
+uacpi_status uacpi_kernel_io_map(
+ uacpi_io_addr base, uacpi_size len, uacpi_handle *out_handle
+);
+void uacpi_kernel_io_unmap(uacpi_handle handle);
+
+/*
+ * Read/Write the IO range mapped via uacpi_kernel_io_map
+ * at a 0-based 'offset' within the range.
+ *
+ * NOTE:
+ * The x86 architecture uses the in/out family of instructions
+ * to access the SystemIO address space.
+ *
+ * You are NOT allowed to break e.g. a 4-byte access into four 1-byte accesses.
+ * Hardware ALWAYS expects accesses to be of the exact width.
+ */
+uacpi_status uacpi_kernel_io_read8(
+ uacpi_handle, uacpi_size offset, uacpi_u8 *out_value
+);
+uacpi_status uacpi_kernel_io_read16(
+ uacpi_handle, uacpi_size offset, uacpi_u16 *out_value
+);
+uacpi_status uacpi_kernel_io_read32(
+ uacpi_handle, uacpi_size offset, uacpi_u32 *out_value
+);
+
+uacpi_status uacpi_kernel_io_write8(
+ uacpi_handle, uacpi_size offset, uacpi_u8 in_value
+);
+uacpi_status uacpi_kernel_io_write16(
+ uacpi_handle, uacpi_size offset, uacpi_u16 in_value
+);
+uacpi_status uacpi_kernel_io_write32(
+ uacpi_handle, uacpi_size offset, uacpi_u32 in_value
+);
+
+/*
+ * Allocate a block of memory of 'size' bytes.
+ * The contents of the allocated memory are unspecified.
+ */
+void *uacpi_kernel_alloc(uacpi_size size);
+
+#ifdef UACPI_NATIVE_ALLOC_ZEROED
+/*
+ * Allocate a block of memory of 'size' bytes.
+ * The returned memory block is expected to be zero-filled.
+ */
+void *uacpi_kernel_alloc_zeroed(uacpi_size size);
+#endif
+
+/*
+ * Free a previously allocated memory block.
+ *
+ * 'mem' might be a NULL pointer. In this case, the call is assumed to be a
+ * no-op.
+ *
+ * An optionally enabled 'size_hint' parameter contains the size of the original
+ * allocation. Note that in some scenarios this incurs additional cost to
+ * calculate the object size.
+ */
+#ifndef UACPI_SIZED_FREES
+void uacpi_kernel_free(void *mem);
+#else
+void uacpi_kernel_free(void *mem, uacpi_size size_hint);
+#endif
+
+/*
+ * Returns the number of nanosecond ticks elapsed since boot,
+ * strictly monotonic.
+ */
+uacpi_u64 uacpi_kernel_get_nanoseconds_since_boot(void);
+
+/*
+ * Spin for N microseconds.
+ */
+void uacpi_kernel_stall(uacpi_u8 usec);
+
+/*
+ * Sleep for N milliseconds.
+ */
+void uacpi_kernel_sleep(uacpi_u64 msec);
+
+/*
+ * Create/free an opaque non-recursive kernel mutex object.
+ */
+uacpi_handle uacpi_kernel_create_mutex(void);
+void uacpi_kernel_free_mutex(uacpi_handle);
+
+/*
+ * Create/free an opaque kernel (semaphore-like) event object.
+ */
+uacpi_handle uacpi_kernel_create_event(void);
+void uacpi_kernel_free_event(uacpi_handle);
+
+/*
+ * Returns a unique identifier of the currently executing thread.
+ *
+ * The returned thread id cannot be UACPI_THREAD_ID_NONE.
+ */
+uacpi_thread_id uacpi_kernel_get_thread_id(void);
+
+/*
+ * Try to acquire the mutex with a millisecond timeout.
+ *
+ * The timeout value has the following meanings:
+ * 0x0000 - Attempt to acquire the mutex once, in a non-blocking manner
+ * 0x0001...0xFFFE - Attempt to acquire the mutex for at least 'timeout'
+ * milliseconds
+ * 0xFFFF - Infinite wait, block until the mutex is acquired
+ *
+ * The following are possible return values:
+ * 1. UACPI_STATUS_OK - successful acquire operation
+ * 2. UACPI_STATUS_TIMEOUT - timeout reached while attempting to acquire (or the
+ * single attempt to acquire was not successful for
+ * calls with timeout=0)
+ * 3. Any other value - signifies a host internal error and is treated as such
+ */
+uacpi_status uacpi_kernel_acquire_mutex(uacpi_handle, uacpi_u16);
+void uacpi_kernel_release_mutex(uacpi_handle);
+
+/*
+ * Try to wait for an event (counter > 0) with a millisecond timeout.
+ * A timeout value of 0xFFFF implies infinite wait.
+ *
+ * The internal counter is decremented by 1 if wait was successful.
+ *
+ * A successful wait is indicated by returning UACPI_TRUE.
+ */
+uacpi_bool uacpi_kernel_wait_for_event(uacpi_handle, uacpi_u16);
+
+/*
+ * Signal the event object by incrementing its internal counter by 1.
+ *
+ * This function may be used in interrupt contexts.
+ */
+void uacpi_kernel_signal_event(uacpi_handle);
+
+/*
+ * Reset the event counter to 0.
+ */
+void uacpi_kernel_reset_event(uacpi_handle);
+
+/*
+ * Handle a firmware request.
+ *
+ * Currently either a Breakpoint or Fatal operators.
+ */
+uacpi_status uacpi_kernel_handle_firmware_request(uacpi_firmware_request*);
+
+/*
+ * Install an interrupt handler at 'irq', 'ctx' is passed to the provided
+ * handler for every invocation.
+ *
+ * 'out_irq_handle' is set to a kernel-implemented value that can be used to
+ * refer to this handler from other API.
+ */
+uacpi_status uacpi_kernel_install_interrupt_handler(
+ uacpi_u32 irq, uacpi_interrupt_handler, uacpi_handle ctx,
+ uacpi_handle *out_irq_handle
+);
+
+/*
+ * Uninstall an interrupt handler. 'irq_handle' is the value returned via
+ * 'out_irq_handle' during installation.
+ */
+uacpi_status uacpi_kernel_uninstall_interrupt_handler(
+ uacpi_interrupt_handler, uacpi_handle irq_handle
+);
+
+/*
+ * Create/free a kernel spinlock object.
+ *
+ * Unlike other types of locks, spinlocks may be used in interrupt contexts.
+ */
+uacpi_handle uacpi_kernel_create_spinlock(void);
+void uacpi_kernel_free_spinlock(uacpi_handle);
+
+/*
+ * Lock/unlock helpers for spinlocks.
+ *
+ * These are expected to disable interrupts, returning the previous state of cpu
+ * flags, that can be used to possibly re-enable interrupts if they were enabled
+ * before.
+ *
+ * Note that lock is infalliable.
+ */
+uacpi_cpu_flags uacpi_kernel_lock_spinlock(uacpi_handle);
+void uacpi_kernel_unlock_spinlock(uacpi_handle, uacpi_cpu_flags);
+
+typedef enum uacpi_work_type {
+ /*
+ * Schedule a GPE handler method for execution.
+ * This should be scheduled to run on CPU0 to avoid potential SMI-related
+ * firmware bugs.
+ */
+ UACPI_WORK_GPE_EXECUTION,
+
+ /*
+ * Schedule a Notify(device) firmware request for execution.
+ * This can run on any CPU.
+ */
+ UACPI_WORK_NOTIFICATION,
+} uacpi_work_type;
+
+typedef void (*uacpi_work_handler)(uacpi_handle);
+
+/*
+ * Schedules deferred work for execution.
+ * Might be invoked from an interrupt context.
+ */
+uacpi_status uacpi_kernel_schedule_work(
+ uacpi_work_type, uacpi_work_handler, uacpi_handle ctx
+);
+
+/*
+ * Waits for two types of work to finish:
+ * 1. All in-flight interrupts installed via uacpi_kernel_install_interrupt_handler
+ * 2. All work scheduled via uacpi_kernel_schedule_work
+ *
+ * Note that the waits must be done in this order specifically.
+ */
+uacpi_status uacpi_kernel_wait_for_work_completion(void);
+
+#endif // !UACPI_BAREBONES_MODE
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/sys/include/dev/acpi/uacpi/uacpi/log.h b/sys/include/dev/acpi/uacpi/uacpi/log.h
new file mode 100644
index 0000000..4fb5457
--- /dev/null
+++ b/sys/include/dev/acpi/uacpi/uacpi/log.h
@@ -0,0 +1,40 @@
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum uacpi_log_level {
+ /*
+ * Super verbose logging, every op & uop being processed is logged.
+ * Mostly useful for tracking down hangs/lockups.
+ */
+ UACPI_LOG_DEBUG = 5,
+
+ /*
+ * A little verbose, every operation region access is traced with a bit of
+ * extra information on top.
+ */
+ UACPI_LOG_TRACE = 4,
+
+ /*
+ * Only logs the bare minimum information about state changes and/or
+ * initialization progress.
+ */
+ UACPI_LOG_INFO = 3,
+
+ /*
+ * Logs recoverable errors and/or non-important aborts.
+ */
+ UACPI_LOG_WARN = 2,
+
+ /*
+ * Logs only critical errors that might affect the ability to initialize or
+ * prevent stable runtime.
+ */
+ UACPI_LOG_ERROR = 1,
+} uacpi_log_level;
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/sys/include/dev/acpi/uacpi/uacpi/namespace.h b/sys/include/dev/acpi/uacpi/uacpi/namespace.h
new file mode 100644
index 0000000..5ef23af
--- /dev/null
+++ b/sys/include/dev/acpi/uacpi/uacpi/namespace.h
@@ -0,0 +1,186 @@
+#pragma once
+
+#include <uacpi/types.h>
+#include <uacpi/status.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef UACPI_BAREBONES_MODE
+
+typedef struct uacpi_namespace_node uacpi_namespace_node;
+
+uacpi_namespace_node *uacpi_namespace_root(void);
+
+typedef enum uacpi_predefined_namespace {
+ UACPI_PREDEFINED_NAMESPACE_ROOT = 0,
+ UACPI_PREDEFINED_NAMESPACE_GPE,
+ UACPI_PREDEFINED_NAMESPACE_PR,
+ UACPI_PREDEFINED_NAMESPACE_SB,
+ UACPI_PREDEFINED_NAMESPACE_SI,
+ UACPI_PREDEFINED_NAMESPACE_TZ,
+ UACPI_PREDEFINED_NAMESPACE_GL,
+ UACPI_PREDEFINED_NAMESPACE_OS,
+ UACPI_PREDEFINED_NAMESPACE_OSI,
+ UACPI_PREDEFINED_NAMESPACE_REV,
+ UACPI_PREDEFINED_NAMESPACE_MAX = UACPI_PREDEFINED_NAMESPACE_REV,
+} uacpi_predefined_namespace;
+uacpi_namespace_node *uacpi_namespace_get_predefined(
+ uacpi_predefined_namespace
+);
+
+/*
+ * Returns UACPI_TRUE if the provided 'node' is an alias.
+ */
+uacpi_bool uacpi_namespace_node_is_alias(uacpi_namespace_node *node);
+
+uacpi_object_name uacpi_namespace_node_name(const uacpi_namespace_node *node);
+
+/*
+ * Returns the type of object stored at the namespace node.
+ *
+ * NOTE: due to the existance of the CopyObject operator in AML, the
+ * return value of this function is subject to TOCTOU bugs.
+ */
+uacpi_status uacpi_namespace_node_type(
+ const uacpi_namespace_node *node, uacpi_object_type *out_type
+);
+
+/*
+ * Returns UACPI_TRUE via 'out' if the type of the object stored at the
+ * namespace node matches the provided value, UACPI_FALSE otherwise.
+ *
+ * NOTE: due to the existance of the CopyObject operator in AML, the
+ * return value of this function is subject to TOCTOU bugs.
+ */
+uacpi_status uacpi_namespace_node_is(
+ const uacpi_namespace_node *node, uacpi_object_type type, uacpi_bool *out
+);
+
+/*
+ * Returns UACPI_TRUE via 'out' if the type of the object stored at the
+ * namespace node matches any of the type bits in the provided value,
+ * UACPI_FALSE otherwise.
+ *
+ * NOTE: due to the existance of the CopyObject operator in AML, the
+ * return value of this function is subject to TOCTOU bugs.
+ */
+uacpi_status uacpi_namespace_node_is_one_of(
+ const uacpi_namespace_node *node, uacpi_object_type_bits type_mask,
+ uacpi_bool *out
+);
+
+uacpi_size uacpi_namespace_node_depth(const uacpi_namespace_node *node);
+
+uacpi_namespace_node *uacpi_namespace_node_parent(
+ uacpi_namespace_node *node
+);
+
+uacpi_status uacpi_namespace_node_find(
+ uacpi_namespace_node *parent,
+ const uacpi_char *path,
+ uacpi_namespace_node **out_node
+);
+
+/*
+ * Same as uacpi_namespace_node_find, except the search recurses upwards when
+ * the namepath consists of only a single nameseg. Usually, this behavior is
+ * only desired if resolving a namepath specified in an aml-provided object,
+ * such as a package element.
+ */
+uacpi_status uacpi_namespace_node_resolve_from_aml_namepath(
+ uacpi_namespace_node *scope,
+ const uacpi_char *path,
+ uacpi_namespace_node **out_node
+);
+
+typedef uacpi_iteration_decision (*uacpi_iteration_callback) (
+ void *user, uacpi_namespace_node *node, uacpi_u32 node_depth
+);
+
+#define UACPI_MAX_DEPTH_ANY 0xFFFFFFFF
+
+/*
+ * Depth-first iterate the namespace starting at the first child of 'parent'.
+ */
+uacpi_status uacpi_namespace_for_each_child_simple(
+ uacpi_namespace_node *parent, uacpi_iteration_callback callback, void *user
+);
+
+/*
+ * Depth-first iterate the namespace starting at the first child of 'parent'.
+ *
+ * 'descending_callback' is invoked the first time a node is visited when
+ * walking down. 'ascending_callback' is invoked the second time a node is
+ * visited after we reach the leaf node without children and start walking up.
+ * Either of the callbacks may be NULL, but not both at the same time.
+ *
+ * Only nodes matching 'type_mask' are passed to the callbacks.
+ *
+ * 'max_depth' is used to limit the maximum reachable depth from 'parent',
+ * where 1 is only direct children of 'parent', 2 is children of first-level
+ * children etc. Use UACPI_MAX_DEPTH_ANY or -1 to specify infinite depth.
+ */
+uacpi_status uacpi_namespace_for_each_child(
+ uacpi_namespace_node *parent, uacpi_iteration_callback descending_callback,
+ uacpi_iteration_callback ascending_callback,
+ uacpi_object_type_bits type_mask, uacpi_u32 max_depth, void *user
+);
+
+/*
+ * Retrieve the next peer namespace node of '*iter', or, if '*iter' is
+ * UACPI_NULL, retrieve the first child of 'parent' instead. The resulting
+ * namespace node is stored at '*iter'.
+ *
+ * This API can be used to implement an "iterator" version of the
+ * for_each_child helpers.
+ *
+ * Example usage:
+ * void recurse(uacpi_namespace_node *parent) {
+ * uacpi_namespace_node *iter = UACPI_NULL;
+ *
+ * while (uacpi_namespace_node_next(parent, &iter) == UACPI_STATUS_OK) {
+ * // Do something with iter...
+ * descending_callback(iter);
+ *
+ * // Recurse down to walk over the children of iter
+ * recurse(iter);
+ * }
+ * }
+ *
+ * Prefer the for_each_child family of helpers if possible instead of this API
+ * as they avoid recursion and/or the need to use dynamic data structures
+ * entirely.
+ */
+uacpi_status uacpi_namespace_node_next(
+ uacpi_namespace_node *parent, uacpi_namespace_node **iter
+);
+
+/*
+ * Retrieve the next peer namespace node of '*iter', or, if '*iter' is
+ * UACPI_NULL, retrieve the first child of 'parent' instead. The resulting
+ * namespace node is stored at '*iter'. Only nodes which type matches one
+ * of the types set in 'type_mask' are returned.
+ *
+ * See comment above 'uacpi_namespace_node_next' for usage examples.
+ *
+ * Prefer the for_each_child family of helpers if possible instead of this API
+ * as they avoid recursion and/or the need to use dynamic data structures
+ * entirely.
+ */
+uacpi_status uacpi_namespace_node_next_typed(
+ uacpi_namespace_node *parent, uacpi_namespace_node **iter,
+ uacpi_object_type_bits type_mask
+);
+
+const uacpi_char *uacpi_namespace_node_generate_absolute_path(
+ const uacpi_namespace_node *node
+);
+void uacpi_free_absolute_path(const uacpi_char *path);
+
+#endif // !UACPI_BAREBONES_MODE
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/sys/include/dev/acpi/uacpi/uacpi/notify.h b/sys/include/dev/acpi/uacpi/uacpi/notify.h
new file mode 100644
index 0000000..3b66757
--- /dev/null
+++ b/sys/include/dev/acpi/uacpi/uacpi/notify.h
@@ -0,0 +1,30 @@
+#pragma once
+
+#include <uacpi/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef UACPI_BAREBONES_MODE
+
+/*
+ * Install a Notify() handler to a device node.
+ * A handler installed to the root node will receive all notifications, even if
+ * a device already has a dedicated Notify handler.
+ * 'handler_context' is passed to the handler on every invocation.
+ */
+uacpi_status uacpi_install_notify_handler(
+ uacpi_namespace_node *node, uacpi_notify_handler handler,
+ uacpi_handle handler_context
+);
+
+uacpi_status uacpi_uninstall_notify_handler(
+ uacpi_namespace_node *node, uacpi_notify_handler handler
+);
+
+#endif // !UACPI_BAREBONES_MODE
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/sys/include/dev/acpi/uacpi/uacpi/opregion.h b/sys/include/dev/acpi/uacpi/uacpi/opregion.h
new file mode 100644
index 0000000..1eee4f0
--- /dev/null
+++ b/sys/include/dev/acpi/uacpi/uacpi/opregion.h
@@ -0,0 +1,47 @@
+#pragma once
+
+#include <uacpi/types.h>
+#include <uacpi/status.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef UACPI_BAREBONES_MODE
+
+/*
+ * Install an address space handler to a device node.
+ * The handler is recursively connected to all of the operation regions of
+ * type 'space' underneath 'device_node'. Note that this recursion stops as
+ * soon as another device node that already has an address space handler of
+ * this type installed is encountered.
+ */
+uacpi_status uacpi_install_address_space_handler(
+ uacpi_namespace_node *device_node, enum uacpi_address_space space,
+ uacpi_region_handler handler, uacpi_handle handler_context
+);
+
+/*
+ * Uninstall the handler of type 'space' from a given device node.
+ */
+uacpi_status uacpi_uninstall_address_space_handler(
+ uacpi_namespace_node *device_node,
+ enum uacpi_address_space space
+);
+
+/*
+ * Execute _REG(space, ACPI_REG_CONNECT) for all of the opregions with this
+ * address space underneath this device. This should only be called manually
+ * if you want to register an early handler that must be available before the
+ * call to uacpi_namespace_initialize().
+ */
+uacpi_status uacpi_reg_all_opregions(
+ uacpi_namespace_node *device_node,
+ enum uacpi_address_space space
+);
+
+#endif // !UACPI_BAREBONES_MODE
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/sys/include/dev/acpi/uacpi/uacpi/osi.h b/sys/include/dev/acpi/uacpi/uacpi/osi.h
new file mode 100644
index 0000000..5330138
--- /dev/null
+++ b/sys/include/dev/acpi/uacpi/uacpi/osi.h
@@ -0,0 +1,125 @@
+#pragma once
+
+#include <uacpi/types.h>
+#include <uacpi/status.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef UACPI_BAREBONES_MODE
+
+typedef enum uacpi_vendor_interface {
+ UACPI_VENDOR_INTERFACE_NONE = 0,
+ UACPI_VENDOR_INTERFACE_WINDOWS_2000,
+ UACPI_VENDOR_INTERFACE_WINDOWS_XP,
+ UACPI_VENDOR_INTERFACE_WINDOWS_XP_SP1,
+ UACPI_VENDOR_INTERFACE_WINDOWS_SERVER_2003,
+ UACPI_VENDOR_INTERFACE_WINDOWS_XP_SP2,
+ UACPI_VENDOR_INTERFACE_WINDOWS_SERVER_2003_SP1,
+ UACPI_VENDOR_INTERFACE_WINDOWS_VISTA,
+ UACPI_VENDOR_INTERFACE_WINDOWS_SERVER_2008,
+ UACPI_VENDOR_INTERFACE_WINDOWS_VISTA_SP1,
+ UACPI_VENDOR_INTERFACE_WINDOWS_VISTA_SP2,
+ UACPI_VENDOR_INTERFACE_WINDOWS_7,
+ UACPI_VENDOR_INTERFACE_WINDOWS_8,
+ UACPI_VENDOR_INTERFACE_WINDOWS_8_1,
+ UACPI_VENDOR_INTERFACE_WINDOWS_10,
+ UACPI_VENDOR_INTERFACE_WINDOWS_10_RS1,
+ UACPI_VENDOR_INTERFACE_WINDOWS_10_RS2,
+ UACPI_VENDOR_INTERFACE_WINDOWS_10_RS3,
+ UACPI_VENDOR_INTERFACE_WINDOWS_10_RS4,
+ UACPI_VENDOR_INTERFACE_WINDOWS_10_RS5,
+ UACPI_VENDOR_INTERFACE_WINDOWS_10_19H1,
+ UACPI_VENDOR_INTERFACE_WINDOWS_10_20H1,
+ UACPI_VENDOR_INTERFACE_WINDOWS_11,
+ UACPI_VENDOR_INTERFACE_WINDOWS_11_22H2,
+} uacpi_vendor_interface;
+
+/*
+ * Returns the "latest" AML-queried _OSI vendor interface.
+ *
+ * E.g. for the following AML code:
+ * _OSI("Windows 2021")
+ * _OSI("Windows 2000")
+ *
+ * This function will return UACPI_VENDOR_INTERFACE_WINDOWS_11, since this is
+ * the latest version of the interface the code queried, even though the
+ * "Windows 2000" query came after "Windows 2021".
+ */
+uacpi_vendor_interface uacpi_latest_queried_vendor_interface(void);
+
+typedef enum uacpi_interface_kind {
+ UACPI_INTERFACE_KIND_VENDOR = (1 << 0),
+ UACPI_INTERFACE_KIND_FEATURE = (1 << 1),
+ UACPI_INTERFACE_KIND_ALL = UACPI_INTERFACE_KIND_VENDOR |
+ UACPI_INTERFACE_KIND_FEATURE,
+} uacpi_interface_kind;
+
+/*
+ * Install or uninstall an interface.
+ *
+ * The interface kind is used for matching during interface enumeration in
+ * uacpi_bulk_configure_interfaces().
+ *
+ * After installing an interface, all _OSI queries report it as supported.
+ */
+uacpi_status uacpi_install_interface(
+ const uacpi_char *name, uacpi_interface_kind
+);
+uacpi_status uacpi_uninstall_interface(const uacpi_char *name);
+
+typedef enum uacpi_host_interface {
+ UACPI_HOST_INTERFACE_MODULE_DEVICE = 1,
+ UACPI_HOST_INTERFACE_PROCESSOR_DEVICE,
+ UACPI_HOST_INTERFACE_3_0_THERMAL_MODEL,
+ UACPI_HOST_INTERFACE_3_0_SCP_EXTENSIONS,
+ UACPI_HOST_INTERFACE_PROCESSOR_AGGREGATOR_DEVICE,
+} uacpi_host_interface;
+
+/*
+ * Same as install/uninstall interface, but comes with an enum of known
+ * interfaces defined by the ACPI specification. These are disabled by default
+ * as they depend on the host kernel support.
+ */
+uacpi_status uacpi_enable_host_interface(uacpi_host_interface);
+uacpi_status uacpi_disable_host_interface(uacpi_host_interface);
+
+typedef uacpi_bool (*uacpi_interface_handler)
+ (const uacpi_char *name, uacpi_bool supported);
+
+/*
+ * Set a custom interface query (_OSI) handler.
+ *
+ * This callback will be invoked for each _OSI query with the value
+ * passed in the _OSI, as well as whether the interface was detected as
+ * supported. The callback is able to override the return value dynamically
+ * or leave it untouched if desired (e.g. if it simply wants to log something or
+ * do internal bookkeeping of some kind).
+ */
+uacpi_status uacpi_set_interface_query_handler(uacpi_interface_handler);
+
+typedef enum uacpi_interface_action {
+ UACPI_INTERFACE_ACTION_DISABLE = 0,
+ UACPI_INTERFACE_ACTION_ENABLE,
+} uacpi_interface_action;
+
+/*
+ * Bulk interface configuration, used to disable or enable all interfaces that
+ * match 'kind'.
+ *
+ * This is generally only needed to work around buggy hardware, for example if
+ * requested from the kernel command line.
+ *
+ * By default, all vendor strings (like "Windows 2000") are enabled, and all
+ * host features (like "3.0 Thermal Model") are disabled.
+ */
+uacpi_status uacpi_bulk_configure_interfaces(
+ uacpi_interface_action action, uacpi_interface_kind kind
+);
+
+#endif // !UACPI_BAREBONES_MODE
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/sys/include/dev/acpi/uacpi/uacpi/platform/arch_helpers.h b/sys/include/dev/acpi/uacpi/uacpi/platform/arch_helpers.h
new file mode 100644
index 0000000..2e566c4
--- /dev/null
+++ b/sys/include/dev/acpi/uacpi/uacpi/platform/arch_helpers.h
@@ -0,0 +1,38 @@
+#pragma once
+
+#ifdef UACPI_OVERRIDE_ARCH_HELPERS
+#include "uacpi_arch_helpers.h"
+#else
+
+#include <uacpi/platform/atomic.h>
+
+#ifndef UACPI_ARCH_FLUSH_CPU_CACHE
+#define UACPI_ARCH_FLUSH_CPU_CACHE() do {} while (0)
+#endif
+
+typedef unsigned long uacpi_cpu_flags;
+
+typedef void *uacpi_thread_id;
+
+/*
+ * Replace as needed depending on your platform's way to represent thread ids.
+ * uACPI offers a few more helpers like uacpi_atomic_{load,store}{8,16,32,64,ptr}
+ * (or you could provide your own helpers)
+ */
+#ifndef UACPI_ATOMIC_LOAD_THREAD_ID
+#define UACPI_ATOMIC_LOAD_THREAD_ID(ptr) ((uacpi_thread_id)uacpi_atomic_load_ptr(ptr))
+#endif
+
+#ifndef UACPI_ATOMIC_STORE_THREAD_ID
+#define UACPI_ATOMIC_STORE_THREAD_ID(ptr, value) uacpi_atomic_store_ptr(ptr, value)
+#endif
+
+/*
+ * A sentinel value that the kernel promises to NEVER return from
+ * uacpi_kernel_get_current_thread_id or this will break
+ */
+#ifndef UACPI_THREAD_ID_NONE
+#define UACPI_THREAD_ID_NONE ((uacpi_thread_id)-1)
+#endif
+
+#endif
diff --git a/sys/include/dev/acpi/uacpi/uacpi/platform/atomic.h b/sys/include/dev/acpi/uacpi/uacpi/platform/atomic.h
new file mode 100644
index 0000000..1d1b570
--- /dev/null
+++ b/sys/include/dev/acpi/uacpi/uacpi/platform/atomic.h
@@ -0,0 +1,347 @@
+#pragma once
+
+/*
+ * Most of this header is a giant workaround for MSVC to make atomics into a
+ * somewhat unified interface with how GCC and Clang handle them.
+ *
+ * We don't use the absolutely disgusting C11 stdatomic.h header because it is
+ * unable to operate on non _Atomic types, which enforce implicit sequential
+ * consistency and alter the behavior of the standard C binary/unary operators.
+ *
+ * The strictness of the atomic helpers defined here is assumed to be at least
+ * acquire for loads and release for stores. Cmpxchg uses the standard acq/rel
+ * for success, acq for failure, and is assumed to be strong.
+ */
+
+#ifdef UACPI_OVERRIDE_ATOMIC
+#include "uacpi_atomic.h"
+#else
+
+#include <uacpi/platform/compiler.h>
+
+#if defined(_MSC_VER) && !defined(__clang__)
+
+#include <intrin.h>
+
+// mimic __atomic_compare_exchange_n that doesn't exist on MSVC
+#define UACPI_MAKE_MSVC_CMPXCHG(width, type, suffix) \
+ static inline int uacpi_do_atomic_cmpxchg##width( \
+ type volatile *ptr, type volatile *expected, type desired \
+ ) \
+ { \
+ type current; \
+ \
+ current = _InterlockedCompareExchange##suffix(ptr, *expected, desired); \
+ if (current != *expected) { \
+ *expected = current; \
+ return 0; \
+ } \
+ return 1; \
+ }
+
+#define UACPI_MSVC_CMPXCHG_INVOKE(ptr, expected, desired, width, type) \
+ uacpi_do_atomic_cmpxchg##width( \
+ (type volatile*)ptr, (type volatile*)expected, desired \
+ )
+
+#define UACPI_MSVC_ATOMIC_STORE(ptr, value, type, width) \
+ _InterlockedExchange##width((type volatile*)(ptr), (type)(value))
+
+#define UACPI_MSVC_ATOMIC_LOAD(ptr, type, width) \
+ _InterlockedOr##width((type volatile*)(ptr), 0)
+
+#define UACPI_MSVC_ATOMIC_INC(ptr, type, width) \
+ _InterlockedIncrement##width((type volatile*)(ptr))
+
+#define UACPI_MSVC_ATOMIC_DEC(ptr, type, width) \
+ _InterlockedDecrement##width((type volatile*)(ptr))
+
+UACPI_MAKE_MSVC_CMPXCHG(64, __int64, 64)
+UACPI_MAKE_MSVC_CMPXCHG(32, long,)
+UACPI_MAKE_MSVC_CMPXCHG(16, short, 16)
+
+#define uacpi_atomic_cmpxchg16(ptr, expected, desired) \
+ UACPI_MSVC_CMPXCHG_INVOKE(ptr, expected, desired, 16, short)
+
+#define uacpi_atomic_cmpxchg32(ptr, expected, desired) \
+ UACPI_MSVC_CMPXCHG_INVOKE(ptr, expected, desired, 32, long)
+
+#define uacpi_atomic_cmpxchg64(ptr, expected, desired) \
+ UACPI_MSVC_CMPXCHG_INVOKE(ptr, expected, desired, 64, __int64)
+
+#define uacpi_atomic_load8(ptr) UACPI_MSVC_ATOMIC_LOAD(ptr, char, 8)
+#define uacpi_atomic_load16(ptr) UACPI_MSVC_ATOMIC_LOAD(ptr, short, 16)
+#define uacpi_atomic_load32(ptr) UACPI_MSVC_ATOMIC_LOAD(ptr, long,)
+#define uacpi_atomic_load64(ptr) UACPI_MSVC_ATOMIC_LOAD(ptr, __int64, 64)
+
+#define uacpi_atomic_store8(ptr, value) UACPI_MSVC_ATOMIC_STORE(ptr, value, char, 8)
+#define uacpi_atomic_store16(ptr, value) UACPI_MSVC_ATOMIC_STORE(ptr, value, short, 16)
+#define uacpi_atomic_store32(ptr, value) UACPI_MSVC_ATOMIC_STORE(ptr, value, long,)
+#define uacpi_atomic_store64(ptr, value) UACPI_MSVC_ATOMIC_STORE(ptr, value, __int64, 64)
+
+#define uacpi_atomic_inc16(ptr) UACPI_MSVC_ATOMIC_INC(ptr, short, 16)
+#define uacpi_atomic_inc32(ptr) UACPI_MSVC_ATOMIC_INC(ptr, long,)
+#define uacpi_atomic_inc64(ptr) UACPI_MSVC_ATOMIC_INC(ptr, __int64, 64)
+
+#define uacpi_atomic_dec16(ptr) UACPI_MSVC_ATOMIC_DEC(ptr, short, 16)
+#define uacpi_atomic_dec32(ptr) UACPI_MSVC_ATOMIC_DEC(ptr, long,)
+#define uacpi_atomic_dec64(ptr) UACPI_MSVC_ATOMIC_DEC(ptr, __int64, 64)
+#elif defined(__WATCOMC__)
+
+#include <stdint.h>
+
+static int uacpi_do_atomic_cmpxchg16(volatile uint16_t *ptr, volatile uint16_t *expected, uint16_t desired);
+#pragma aux uacpi_do_atomic_cmpxchg16 = \
+ ".486" \
+ "mov ax, [esi]" \
+ "lock cmpxchg [edi], bx" \
+ "mov [esi], ax" \
+ "setz al" \
+ "movzx eax, al" \
+ parm [ edi ] [ esi ] [ ebx ] \
+ value [ eax ]
+
+static int uacpi_do_atomic_cmpxchg32(volatile uint32_t *ptr, volatile uint32_t *expected, uint32_t desired);
+#pragma aux uacpi_do_atomic_cmpxchg32 = \
+ ".486" \
+ "mov eax, [esi]" \
+ "lock cmpxchg [edi], ebx" \
+ "mov [esi], eax" \
+ "setz al" \
+ "movzx eax, al" \
+ parm [ edi ] [ esi ] [ ebx ] \
+ value [ eax ]
+
+static int uacpi_do_atomic_cmpxchg64_asm(volatile uint64_t *ptr, volatile uint64_t *expected, uint32_t low, uint32_t high);
+#pragma aux uacpi_do_atomic_cmpxchg64_asm = \
+ ".586" \
+ "mov eax, [esi]" \
+ "mov edx, [esi + 4]" \
+ "lock cmpxchg8b [edi]" \
+ "mov [esi], eax" \
+ "mov [esi + 4], edx" \
+ "setz al" \
+ "movzx eax, al" \
+ modify [ edx ] \
+ parm [ edi ] [ esi ] [ ebx ] [ ecx ] \
+ value [ eax ]
+
+static inline int uacpi_do_atomic_cmpxchg64(volatile uint64_t *ptr, volatile uint64_t *expected, uint64_t desired) {
+ return uacpi_do_atomic_cmpxchg64_asm(ptr, expected, desired, desired >> 32);
+}
+
+#define uacpi_atomic_cmpxchg16(ptr, expected, desired) \
+ uacpi_do_atomic_cmpxchg16((volatile uint16_t*)ptr, (volatile uint16_t*)expected, (uint16_t)desired)
+#define uacpi_atomic_cmpxchg32(ptr, expected, desired) \
+ uacpi_do_atomic_cmpxchg32((volatile uint32_t*)ptr, (volatile uint32_t*)expected, (uint32_t)desired)
+#define uacpi_atomic_cmpxchg64(ptr, expected, desired) \
+ uacpi_do_atomic_cmpxchg64((volatile uint64_t*)ptr, (volatile uint64_t*)expected, (uint64_t)desired)
+
+static uint8_t uacpi_do_atomic_load8(volatile uint8_t *ptr);
+#pragma aux uacpi_do_atomic_load8 = \
+ "mov al, [esi]" \
+ parm [ esi ] \
+ value [ al ]
+
+static uint16_t uacpi_do_atomic_load16(volatile uint16_t *ptr);
+#pragma aux uacpi_do_atomic_load16 = \
+ "mov ax, [esi]" \
+ parm [ esi ] \
+ value [ ax ]
+
+static uint32_t uacpi_do_atomic_load32(volatile uint32_t *ptr);
+#pragma aux uacpi_do_atomic_load32 = \
+ "mov eax, [esi]" \
+ parm [ esi ] \
+ value [ eax ]
+
+static void uacpi_do_atomic_load64_asm(volatile uint64_t *ptr, uint64_t *out);
+#pragma aux uacpi_do_atomic_load64_asm = \
+ ".586" \
+ "xor eax, eax" \
+ "xor ebx, ebx" \
+ "xor ecx, ecx" \
+ "xor edx, edx" \
+ "lock cmpxchg8b [esi]" \
+ "mov [edi], eax" \
+ "mov [edi + 4], edx" \
+ modify [ eax ebx ecx edx ] \
+ parm [ esi ] [ edi ]
+
+static inline uint64_t uacpi_do_atomic_load64(volatile uint64_t *ptr) {
+ uint64_t value;
+ uacpi_do_atomic_load64_asm(ptr, &value);
+ return value;
+}
+
+#define uacpi_atomic_load8(ptr) uacpi_do_atomic_load8((volatile uint8_t*)ptr)
+#define uacpi_atomic_load16(ptr) uacpi_do_atomic_load16((volatile uint16_t*)ptr)
+#define uacpi_atomic_load32(ptr) uacpi_do_atomic_load32((volatile uint32_t*)ptr)
+#define uacpi_atomic_load64(ptr) uacpi_do_atomic_load64((volatile uint64_t*)ptr)
+
+static void uacpi_do_atomic_store8(volatile uint8_t *ptr, uint8_t value);
+#pragma aux uacpi_do_atomic_store8 = \
+ "mov [edi], al" \
+ parm [ edi ] [ eax ]
+
+static void uacpi_do_atomic_store16(volatile uint16_t *ptr, uint16_t value);
+#pragma aux uacpi_do_atomic_store16 = \
+ "mov [edi], ax" \
+ parm [ edi ] [ eax ]
+
+static void uacpi_do_atomic_store32(volatile uint32_t *ptr, uint32_t value);
+#pragma aux uacpi_do_atomic_store32 = \
+ "mov [edi], eax" \
+ parm [ edi ] [ eax ]
+
+static void uacpi_do_atomic_store64_asm(volatile uint64_t *ptr, uint32_t low, uint32_t high);
+#pragma aux uacpi_do_atomic_store64_asm = \
+ ".586" \
+ "xor eax, eax" \
+ "xor edx, edx" \
+ "retry: lock cmpxchg8b [edi]" \
+ "jnz retry" \
+ modify [ eax edx ] \
+ parm [ edi ] [ ebx ] [ ecx ]
+
+static inline void uacpi_do_atomic_store64(volatile uint64_t *ptr, uint64_t value) {
+ uacpi_do_atomic_store64_asm(ptr, value, value >> 32);
+}
+
+#define uacpi_atomic_store8(ptr, value) uacpi_do_atomic_store8((volatile uint8_t*)ptr, (uint8_t)value)
+#define uacpi_atomic_store16(ptr, value) uacpi_do_atomic_store16((volatile uint16_t*)ptr, (uint16_t)value)
+#define uacpi_atomic_store32(ptr, value) uacpi_do_atomic_store32((volatile uint32_t*)ptr, (uint32_t)value)
+#define uacpi_atomic_store64(ptr, value) uacpi_do_atomic_store64((volatile uint64_t*)ptr, (uint64_t)value)
+
+static uint16_t uacpi_do_atomic_inc16(volatile uint16_t *ptr);
+#pragma aux uacpi_do_atomic_inc16 = \
+ ".486" \
+ "mov ax, 1" \
+ "lock xadd [edi], ax" \
+ "add ax, 1" \
+ parm [ edi ] \
+ value [ ax ]
+
+static uint32_t uacpi_do_atomic_inc32(volatile uint32_t *ptr);
+#pragma aux uacpi_do_atomic_inc32 = \
+ ".486" \
+ "mov eax, 1" \
+ "lock xadd [edi], eax" \
+ "add eax, 1" \
+ parm [ edi ] \
+ value [ eax ]
+
+static void uacpi_do_atomic_inc64_asm(volatile uint64_t *ptr, uint64_t *out);
+#pragma aux uacpi_do_atomic_inc64_asm = \
+ ".586" \
+ "xor eax, eax" \
+ "xor edx, edx" \
+ "mov ebx, 1" \
+ "mov ecx, 1" \
+ "retry: lock cmpxchg8b [esi]" \
+ "mov ebx, eax" \
+ "mov ecx, edx" \
+ "add ebx, 1" \
+ "adc ecx, 0" \
+ "jnz retry" \
+ "mov [edi], ebx" \
+ "mov [edi + 4], ecx" \
+ modify [ eax ebx ecx edx ] \
+ parm [ esi ] [ edi ]
+
+static inline uint64_t uacpi_do_atomic_inc64(volatile uint64_t *ptr) {
+ uint64_t value;
+ uacpi_do_atomic_inc64_asm(ptr, &value);
+ return value;
+}
+
+#define uacpi_atomic_inc16(ptr) uacpi_do_atomic_inc16((volatile uint16_t*)ptr)
+#define uacpi_atomic_inc32(ptr) uacpi_do_atomic_inc32((volatile uint32_t*)ptr)
+#define uacpi_atomic_inc64(ptr) uacpi_do_atomic_inc64((volatile uint64_t*)ptr)
+
+static uint16_t uacpi_do_atomic_dec16(volatile uint16_t *ptr);
+#pragma aux uacpi_do_atomic_dec16 = \
+ ".486" \
+ "mov ax, -1" \
+ "lock xadd [edi], ax" \
+ "add ax, -1" \
+ parm [ edi ] \
+ value [ ax ]
+
+static uint32_t uacpi_do_atomic_dec32(volatile uint32_t *ptr);
+#pragma aux uacpi_do_atomic_dec32 = \
+ ".486" \
+ "mov eax, -1" \
+ "lock xadd [edi], eax" \
+ "add eax, -1" \
+ parm [ edi ] \
+ value [ eax ]
+
+static void uacpi_do_atomic_dec64_asm(volatile uint64_t *ptr, uint64_t *out);
+#pragma aux uacpi_do_atomic_dec64_asm = \
+ ".586" \
+ "xor eax, eax" \
+ "xor edx, edx" \
+ "mov ebx, -1" \
+ "mov ecx, -1" \
+ "retry: lock cmpxchg8b [esi]" \
+ "mov ebx, eax" \
+ "mov ecx, edx" \
+ "sub ebx, 1" \
+ "sbb ecx, 0" \
+ "jnz retry" \
+ "mov [edi], ebx" \
+ "mov [edi + 4], ecx" \
+ modify [ eax ebx ecx edx ] \
+ parm [ esi ] [ edi ]
+
+static inline uint64_t uacpi_do_atomic_dec64(volatile uint64_t *ptr) {
+ uint64_t value;
+ uacpi_do_atomic_dec64_asm(ptr, &value);
+ return value;
+}
+
+#define uacpi_atomic_dec16(ptr) uacpi_do_atomic_dec16((volatile uint16_t*)ptr)
+#define uacpi_atomic_dec32(ptr) uacpi_do_atomic_dec32((volatile uint32_t*)ptr)
+#define uacpi_atomic_dec64(ptr) uacpi_do_atomic_dec64((volatile uint64_t*)ptr)
+#else
+
+#define UACPI_DO_CMPXCHG(ptr, expected, desired) \
+ __atomic_compare_exchange_n(ptr, expected, desired, 0, \
+ __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE)
+
+#define uacpi_atomic_cmpxchg16(ptr, expected, desired) \
+ UACPI_DO_CMPXCHG(ptr, expected, desired)
+#define uacpi_atomic_cmpxchg32(ptr, expected, desired) \
+ UACPI_DO_CMPXCHG(ptr, expected, desired)
+#define uacpi_atomic_cmpxchg64(ptr, expected, desired) \
+ UACPI_DO_CMPXCHG(ptr, expected, desired)
+
+#define uacpi_atomic_load8(ptr) __atomic_load_n(ptr, __ATOMIC_ACQUIRE)
+#define uacpi_atomic_load16(ptr) __atomic_load_n(ptr, __ATOMIC_ACQUIRE)
+#define uacpi_atomic_load32(ptr) __atomic_load_n(ptr, __ATOMIC_ACQUIRE)
+#define uacpi_atomic_load64(ptr) __atomic_load_n(ptr, __ATOMIC_ACQUIRE)
+
+#define uacpi_atomic_store8(ptr, value) __atomic_store_n(ptr, value, __ATOMIC_RELEASE)
+#define uacpi_atomic_store16(ptr, value) __atomic_store_n(ptr, value, __ATOMIC_RELEASE)
+#define uacpi_atomic_store32(ptr, value) __atomic_store_n(ptr, value, __ATOMIC_RELEASE)
+#define uacpi_atomic_store64(ptr, value) __atomic_store_n(ptr, value, __ATOMIC_RELEASE)
+
+#define uacpi_atomic_inc16(ptr) __atomic_add_fetch(ptr, 1, __ATOMIC_ACQ_REL)
+#define uacpi_atomic_inc32(ptr) __atomic_add_fetch(ptr, 1, __ATOMIC_ACQ_REL)
+#define uacpi_atomic_inc64(ptr) __atomic_add_fetch(ptr, 1, __ATOMIC_ACQ_REL)
+
+#define uacpi_atomic_dec16(ptr) __atomic_sub_fetch(ptr, 1, __ATOMIC_ACQ_REL)
+#define uacpi_atomic_dec32(ptr) __atomic_sub_fetch(ptr, 1, __ATOMIC_ACQ_REL)
+#define uacpi_atomic_dec64(ptr) __atomic_sub_fetch(ptr, 1, __ATOMIC_ACQ_REL)
+#endif
+
+#if UACPI_POINTER_SIZE == 4
+#define uacpi_atomic_load_ptr(ptr_to_ptr) uacpi_atomic_load32(ptr_to_ptr)
+#define uacpi_atomic_store_ptr(ptr_to_ptr, value) uacpi_atomic_store32(ptr_to_ptr, value)
+#else
+#define uacpi_atomic_load_ptr(ptr_to_ptr) uacpi_atomic_load64(ptr_to_ptr)
+#define uacpi_atomic_store_ptr(ptr_to_ptr, value) uacpi_atomic_store64(ptr_to_ptr, value)
+#endif
+
+#endif
diff --git a/sys/include/dev/acpi/uacpi/uacpi/platform/compiler.h b/sys/include/dev/acpi/uacpi/uacpi/platform/compiler.h
new file mode 100644
index 0000000..78aab08
--- /dev/null
+++ b/sys/include/dev/acpi/uacpi/uacpi/platform/compiler.h
@@ -0,0 +1,123 @@
+#pragma once
+
+/*
+ * Compiler-specific attributes/macros go here. This is the default placeholder
+ * that should work for MSVC/GCC/clang.
+ */
+
+#ifdef UACPI_OVERRIDE_COMPILER
+#include "uacpi_compiler.h"
+#else
+
+#define UACPI_ALIGN(x) __declspec(align(x))
+
+#if defined(__WATCOMC__)
+#define UACPI_STATIC_ASSERT(expr, msg)
+#elif defined(__cplusplus)
+#define UACPI_STATIC_ASSERT static_assert
+#else
+#define UACPI_STATIC_ASSERT _Static_assert
+#endif
+
+#ifdef _MSC_VER
+ #include <intrin.h>
+
+ #define UACPI_ALWAYS_INLINE __forceinline
+
+ #define UACPI_PACKED(decl) \
+ __pragma(pack(push, 1)) \
+ decl; \
+ __pragma(pack(pop))
+#elif defined(__WATCOMC__)
+ #define UACPI_ALWAYS_INLINE inline
+ #define UACPI_PACKED(decl) _Packed decl;
+#else
+ #define UACPI_ALWAYS_INLINE inline __attribute__((always_inline))
+ #define UACPI_PACKED(decl) decl __attribute__((packed));
+#endif
+
+#if defined(__GNUC__) || defined(__clang__)
+ #define uacpi_unlikely(expr) __builtin_expect(!!(expr), 0)
+ #define uacpi_likely(expr) __builtin_expect(!!(expr), 1)
+
+ #if __has_attribute(__fallthrough__)
+ #define UACPI_FALLTHROUGH __attribute__((__fallthrough__))
+ #endif
+
+ #define UACPI_MAYBE_UNUSED __attribute__ ((unused))
+
+ #define UACPI_NO_UNUSED_PARAMETER_WARNINGS_BEGIN \
+ _Pragma("GCC diagnostic push") \
+ _Pragma("GCC diagnostic ignored \"-Wunused-parameter\"")
+
+ #define UACPI_NO_UNUSED_PARAMETER_WARNINGS_END \
+ _Pragma("GCC diagnostic pop")
+
+ #ifdef __clang__
+ #define UACPI_PRINTF_DECL(fmt_idx, args_idx) \
+ __attribute__((format(printf, fmt_idx, args_idx)))
+ #else
+ #define UACPI_PRINTF_DECL(fmt_idx, args_idx) \
+ __attribute__((format(gnu_printf, fmt_idx, args_idx)))
+ #endif
+
+ #define UACPI_COMPILER_HAS_BUILTIN_MEMCPY
+ #define UACPI_COMPILER_HAS_BUILTIN_MEMMOVE
+ #define UACPI_COMPILER_HAS_BUILTIN_MEMSET
+ #define UACPI_COMPILER_HAS_BUILTIN_MEMCMP
+#elif defined(__WATCOMC__)
+ #define uacpi_unlikely(expr) expr
+ #define uacpi_likely(expr) expr
+
+ /*
+ * The OpenWatcom documentation suggests this should be done using
+ * _Pragma("off (unreferenced)") and _Pragma("pop (unreferenced)"),
+ * but these pragmas appear to be no-ops. Use inline as the next best thing.
+ * Note that OpenWatcom accepts redundant modifiers without a warning,
+ * so UACPI_MAYBE_UNUSED inline still works.
+ */
+ #define UACPI_MAYBE_UNUSED inline
+
+ #define UACPI_NO_UNUSED_PARAMETER_WARNINGS_BEGIN
+ #define UACPI_NO_UNUSED_PARAMETER_WARNINGS_END
+
+ #define UACPI_PRINTF_DECL(fmt_idx, args_idx)
+#else
+ #define uacpi_unlikely(expr) expr
+ #define uacpi_likely(expr) expr
+
+ #define UACPI_MAYBE_UNUSED
+
+ #define UACPI_NO_UNUSED_PARAMETER_WARNINGS_BEGIN
+ #define UACPI_NO_UNUSED_PARAMETER_WARNINGS_END
+
+ #define UACPI_PRINTF_DECL(fmt_idx, args_idx)
+#endif
+
+#ifndef UACPI_FALLTHROUGH
+ #define UACPI_FALLTHROUGH do {} while (0)
+#endif
+
+#ifndef UACPI_POINTER_SIZE
+ #ifdef _WIN32
+ #ifdef _WIN64
+ #define UACPI_POINTER_SIZE 8
+ #else
+ #define UACPI_POINTER_SIZE 4
+ #endif
+ #elif defined(__GNUC__)
+ #define UACPI_POINTER_SIZE __SIZEOF_POINTER__
+ #elif defined(__WATCOMC__)
+ #ifdef __386__
+ #define UACPI_POINTER_SIZE 4
+ #elif defined(__I86__)
+ #error uACPI does not support 16-bit mode compilation
+ #else
+ #error Unknown target architecture
+ #endif
+ #else
+ #error Failed to detect pointer size
+ #endif
+#endif
+
+#endif
diff --git a/sys/include/dev/acpi/uacpi/uacpi/platform/config.h b/sys/include/dev/acpi/uacpi/uacpi/platform/config.h
new file mode 100644
index 0000000..dff043f
--- /dev/null
+++ b/sys/include/dev/acpi/uacpi/uacpi/platform/config.h
@@ -0,0 +1,162 @@
+#pragma once
+
+#ifdef UACPI_OVERRIDE_CONFIG
+#include "uacpi_config.h"
+#else
+
+#include <uacpi/helpers.h>
+#include <uacpi/log.h>
+
+/*
+ * =======================
+ * Context-related options
+ * =======================
+ */
+#ifndef UACPI_DEFAULT_LOG_LEVEL
+ #define UACPI_DEFAULT_LOG_LEVEL UACPI_LOG_INFO
+#endif
+
+UACPI_BUILD_BUG_ON_WITH_MSG(
+ UACPI_DEFAULT_LOG_LEVEL < UACPI_LOG_ERROR ||
+ UACPI_DEFAULT_LOG_LEVEL > UACPI_LOG_DEBUG,
+ "configured default log level is invalid"
+);
+
+#ifndef UACPI_DEFAULT_LOOP_TIMEOUT_SECONDS
+ #define UACPI_DEFAULT_LOOP_TIMEOUT_SECONDS 30
+#endif
+
+UACPI_BUILD_BUG_ON_WITH_MSG(
+ UACPI_DEFAULT_LOOP_TIMEOUT_SECONDS < 1,
+ "configured default loop timeout is invalid (expecting at least 1 second)"
+);
+
+#ifndef UACPI_DEFAULT_MAX_CALL_STACK_DEPTH
+ #define UACPI_DEFAULT_MAX_CALL_STACK_DEPTH 256
+#endif
+
+UACPI_BUILD_BUG_ON_WITH_MSG(
+ UACPI_DEFAULT_MAX_CALL_STACK_DEPTH < 4,
+ "configured default max call stack depth is invalid "
+ "(expecting at least 4 frames)"
+);
+
+/*
+ * ===================
+ * Kernel-api options
+ * ===================
+ */
+
+/*
+ * Convenience initialization/deinitialization hooks that will be called by
+ * uACPI automatically when appropriate if compiled-in.
+ */
+// #define UACPI_KERNEL_INITIALIZATION
+
+/*
+ * Makes kernel api logging callbacks work with unformatted printf-style
+ * strings and va_args instead of a pre-formatted string. Can be useful if
+ * your native logging is implemented in terms of this format as well.
+ */
+// #define UACPI_FORMATTED_LOGGING
+
+/*
+ * Makes uacpi_kernel_free take in an additional 'size_hint' parameter, which
+ * contains the size of the original allocation. Note that this comes with a
+ * performance penalty in some cases.
+ */
+// #define UACPI_SIZED_FREES
+
+
+/*
+ * Makes uacpi_kernel_alloc_zeroed mandatory to implement by the host, uACPI
+ * will not provide a default implementation if this is enabled.
+ */
+// #define UACPI_NATIVE_ALLOC_ZEROED
+
+/*
+ * =========================
+ * Platform-specific options
+ * =========================
+ */
+
+/*
+ * Makes uACPI use the internal versions of mem{cpy,move,set,cmp} instead of
+ * relying on the host to provide them. Note that compilers like clang and GCC
+ * rely on these being available by default, even in freestanding mode, so
+ * compiling uACPI may theoretically generate implicit dependencies on them
+ * even if this option is defined.
+ */
+// #define UACPI_USE_BUILTIN_STRING
+
+/*
+ * Turns uacpi_phys_addr and uacpi_io_addr into a 32-bit type, and adds extra
+ * code for address truncation. Needed for e.g. i686 platforms without PAE
+ * support.
+ */
+// #define UACPI_PHYS_ADDR_IS_32BITS
+
+/*
+ * Switches uACPI into reduced-hardware-only mode. Strips all full-hardware
+ * ACPI support code at compile-time, including the event subsystem, the global
+ * lock, and other full-hardware features.
+ */
+// #define UACPI_REDUCED_HARDWARE
+
+/*
+ * Switches uACPI into tables-subsystem-only mode and strips all other code.
+ * This means only the table API will be usable, no other subsystems are
+ * compiled in. In this mode, uACPI only depends on the following kernel APIs:
+ * - uacpi_kernel_get_rsdp
+ * - uacpi_kernel_{map,unmap}
+ * - uacpi_kernel_log
+ *
+ * Use uacpi_setup_early_table_access to initialize, uacpi_state_reset to
+ * deinitialize.
+ *
+ * This mode is primarily designed for these three use-cases:
+ * - Bootloader/pre-kernel environments that need to parse ACPI tables, but
+ * don't actually need a fully-featured AML interpreter, and everything else
+ * that a full APCI implementation entails.
+ * - A micro-kernel that has the full AML interpreter running in userspace, but
+ * still needs to parse ACPI tables to bootstrap allocators, timers, SMP etc.
+ * - A WIP kernel that needs to parse ACPI tables for bootrapping SMP/timers,
+ * ECAM, etc., but doesn't yet have enough subsystems implemented in order
+ * to run a fully-featured AML interpreter.
+ */
+// #define UACPI_BAREBONES_MODE
+
+/*
+ * =============
+ * Misc. options
+ * =============
+ */
+
+/*
+ * If UACPI_FORMATTED_LOGGING is not enabled, this is the maximum length of the
+ * pre-formatted message that is passed to the logging callback.
+ */
+#ifndef UACPI_PLAIN_LOG_BUFFER_SIZE
+ #define UACPI_PLAIN_LOG_BUFFER_SIZE 128
+#endif
+
+UACPI_BUILD_BUG_ON_WITH_MSG(
+ UACPI_PLAIN_LOG_BUFFER_SIZE < 16,
+ "configured log buffer size is too small (expecting at least 16 bytes)"
+);
+
+/*
+ * The size of the table descriptor inline storage. All table descriptors past
+ * this length will be stored in a dynamically allocated heap array. The size
+ * of one table descriptor is approximately 56 bytes.
+ */
+#ifndef UACPI_STATIC_TABLE_ARRAY_LEN
+ #define UACPI_STATIC_TABLE_ARRAY_LEN 16
+#endif
+
+UACPI_BUILD_BUG_ON_WITH_MSG(
+ UACPI_STATIC_TABLE_ARRAY_LEN < 1,
+ "configured static table array length is too small (expecting at least 1)"
+);
+
+#endif
diff --git a/sys/include/dev/acpi/uacpi/uacpi/platform/libc.h b/sys/include/dev/acpi/uacpi/uacpi/platform/libc.h
new file mode 100644
index 0000000..44c9013
--- /dev/null
+++ b/sys/include/dev/acpi/uacpi/uacpi/platform/libc.h
@@ -0,0 +1,28 @@
+#pragma once
+
+#ifdef UACPI_OVERRIDE_LIBC
+#include "uacpi_libc.h"
+#else
+/*
+ * The following libc functions are used internally by uACPI and have a default
+ * (sub-optimal) implementation:
+ * - strcmp
+ * - strnlen
+ * - strlen
+ * - snprintf
+ * - vsnprintf
+ *
+ * The following use a builtin implementation only if UACPI_USE_BUILTIN_STRING
+ * is defined (more information can be found in the config.h header):
+ * - memcpy
+ * - memmove
+ * - memset
+ * - memcmp
+ *
+ * In case your platform happens to implement optimized verisons of the helpers
+ * above, you are able to make uACPI use those instead by overriding them like so:
+ *
+ * #define uacpi_memcpy my_fast_memcpy
+ * #define uacpi_snprintf my_fast_snprintf
+ */
+#endif
diff --git a/sys/include/dev/acpi/uacpi/uacpi/platform/types.h b/sys/include/dev/acpi/uacpi/uacpi/platform/types.h
new file mode 100644
index 0000000..f4a7cf9
--- /dev/null
+++ b/sys/include/dev/acpi/uacpi/uacpi/platform/types.h
@@ -0,0 +1,64 @@
+#pragma once
+
+/*
+ * Platform-specific types go here. This is the default placeholder using
+ * types from the standard headers.
+ */
+
+#ifdef UACPI_OVERRIDE_TYPES
+#include "uacpi_types.h"
+#else
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stddef.h>
+#include <stdarg.h>
+
+#include <uacpi/helpers.h>
+
+typedef uint8_t uacpi_u8;
+typedef uint16_t uacpi_u16;
+typedef uint32_t uacpi_u32;
+typedef uint64_t uacpi_u64;
+
+typedef int8_t uacpi_i8;
+typedef int16_t uacpi_i16;
+typedef int32_t uacpi_i32;
+typedef int64_t uacpi_i64;
+
+#define UACPI_TRUE true
+#define UACPI_FALSE false
+typedef bool uacpi_bool;
+
+#define UACPI_NULL NULL
+
+typedef uintptr_t uacpi_uintptr;
+typedef uacpi_uintptr uacpi_virt_addr;
+typedef size_t uacpi_size;
+
+typedef va_list uacpi_va_list;
+#define uacpi_va_start va_start
+#define uacpi_va_end va_end
+#define uacpi_va_arg va_arg
+
+typedef char uacpi_char;
+
+#define uacpi_offsetof offsetof
+
+/*
+ * We use unsignd long long for 64-bit number formatting because 64-bit types
+ * don't have a standard way to format them. The inttypes.h header is not
+ * freestanding therefore it's not practical to force the user to define the
+ * corresponding PRI macros. Moreover, unsignd long long is required to be
+ * at least 64-bits as per C99.
+ */
+UACPI_BUILD_BUG_ON_WITH_MSG(
+ sizeof(unsigned long long) < 8,
+ "unsigned long long must be at least 64 bits large as per C99"
+);
+#define UACPI_PRIu64 "llu"
+#define UACPI_PRIx64 "llx"
+#define UACPI_PRIX64 "llX"
+#define UACPI_FMT64(val) ((unsigned long long)(val))
+
+#endif
diff --git a/sys/include/dev/acpi/uacpi/uacpi/registers.h b/sys/include/dev/acpi/uacpi/uacpi/registers.h
new file mode 100644
index 0000000..cdffb97
--- /dev/null
+++ b/sys/include/dev/acpi/uacpi/uacpi/registers.h
@@ -0,0 +1,105 @@
+#include <uacpi/types.h>
+
+/*
+ * BEFORE YOU USE THIS API:
+ * uACPI manages FADT registers on its own entirely, you should only use this
+ * API directly if there's absolutely no other way for your use case, e.g.
+ * implementing a CPU idle state driver that does C state switching or similar.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef UACPI_BAREBONES_MODE
+
+typedef enum uacpi_register {
+ UACPI_REGISTER_PM1_STS = 0,
+ UACPI_REGISTER_PM1_EN,
+ UACPI_REGISTER_PM1_CNT,
+ UACPI_REGISTER_PM_TMR,
+ UACPI_REGISTER_PM2_CNT,
+ UACPI_REGISTER_SLP_CNT,
+ UACPI_REGISTER_SLP_STS,
+ UACPI_REGISTER_RESET,
+ UACPI_REGISTER_SMI_CMD,
+ UACPI_REGISTER_MAX = UACPI_REGISTER_SMI_CMD,
+} uacpi_register;
+
+/*
+ * Read a register from FADT
+ *
+ * NOTE: write-only bits (if any) are cleared automatically
+ */
+uacpi_status uacpi_read_register(uacpi_register, uacpi_u64*);
+
+/*
+ * Write a register from FADT
+ *
+ * NOTE:
+ * - Preserved bits (if any) are preserved automatically
+ * - If a register is made up of two (e.g. PM1a and PM1b) parts, the input
+ * is written to both at the same time
+ */
+uacpi_status uacpi_write_register(uacpi_register, uacpi_u64);
+
+/*
+ * Write a register from FADT
+ *
+ * NOTE:
+ * - Preserved bits (if any) are preserved automatically
+ * - For registers that are made up of two (e.g. PM1a and PM1b) parts, the
+ * provided values are written to their respective physical register
+ */
+uacpi_status uacpi_write_registers(uacpi_register, uacpi_u64, uacpi_u64);
+
+typedef enum uacpi_register_field {
+ UACPI_REGISTER_FIELD_TMR_STS = 0,
+ UACPI_REGISTER_FIELD_BM_STS,
+ UACPI_REGISTER_FIELD_GBL_STS,
+ UACPI_REGISTER_FIELD_PWRBTN_STS,
+ UACPI_REGISTER_FIELD_SLPBTN_STS,
+ UACPI_REGISTER_FIELD_RTC_STS,
+ UACPI_REGISTER_FIELD_PCIEX_WAKE_STS,
+ UACPI_REGISTER_FIELD_HWR_WAK_STS,
+ UACPI_REGISTER_FIELD_WAK_STS,
+ UACPI_REGISTER_FIELD_TMR_EN,
+ UACPI_REGISTER_FIELD_GBL_EN,
+ UACPI_REGISTER_FIELD_PWRBTN_EN,
+ UACPI_REGISTER_FIELD_SLPBTN_EN,
+ UACPI_REGISTER_FIELD_RTC_EN,
+ UACPI_REGISTER_FIELD_PCIEXP_WAKE_DIS,
+ UACPI_REGISTER_FIELD_SCI_EN,
+ UACPI_REGISTER_FIELD_BM_RLD,
+ UACPI_REGISTER_FIELD_GBL_RLS,
+ UACPI_REGISTER_FIELD_SLP_TYP,
+ UACPI_REGISTER_FIELD_HWR_SLP_TYP,
+ UACPI_REGISTER_FIELD_SLP_EN,
+ UACPI_REGISTER_FIELD_HWR_SLP_EN,
+ UACPI_REGISTER_FIELD_ARB_DIS,
+ UACPI_REGISTER_FIELD_MAX = UACPI_REGISTER_FIELD_ARB_DIS,
+} uacpi_register_field;
+
+/*
+ * Read a field from a FADT register
+ *
+ * NOTE: The value is automatically masked and shifted down as appropriate,
+ * the client code doesn't have to do any bit manipulation. E.g. for
+ * a field at 0b???XX??? the returned value will contain just the 0bXX
+ */
+uacpi_status uacpi_read_register_field(uacpi_register_field, uacpi_u64*);
+
+/*
+ * Write to a field of a FADT register
+ *
+ * NOTE: The value is automatically masked and shifted up as appropriate,
+ * the client code doesn't have to do any bit manipulation. E.g. for
+ * a field at 0b???XX??? the passed value should be just 0bXX
+ */
+uacpi_status uacpi_write_register_field(uacpi_register_field, uacpi_u64);
+
+#endif // !UACPI_BAREBONES_MODE
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/sys/include/dev/acpi/uacpi/uacpi/resources.h b/sys/include/dev/acpi/uacpi/uacpi/resources.h
new file mode 100644
index 0000000..f929f1d
--- /dev/null
+++ b/sys/include/dev/acpi/uacpi/uacpi/resources.h
@@ -0,0 +1,740 @@
+#pragma once
+
+#include <uacpi/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef UACPI_BAREBONES_MODE
+
+typedef enum uacpi_resource_type {
+ UACPI_RESOURCE_TYPE_IRQ,
+ UACPI_RESOURCE_TYPE_EXTENDED_IRQ,
+
+ UACPI_RESOURCE_TYPE_DMA,
+ UACPI_RESOURCE_TYPE_FIXED_DMA,
+
+ UACPI_RESOURCE_TYPE_IO,
+ UACPI_RESOURCE_TYPE_FIXED_IO,
+
+ UACPI_RESOURCE_TYPE_ADDRESS16,
+ UACPI_RESOURCE_TYPE_ADDRESS32,
+ UACPI_RESOURCE_TYPE_ADDRESS64,
+ UACPI_RESOURCE_TYPE_ADDRESS64_EXTENDED,
+
+ UACPI_RESOURCE_TYPE_MEMORY24,
+ UACPI_RESOURCE_TYPE_MEMORY32,
+ UACPI_RESOURCE_TYPE_FIXED_MEMORY32,
+
+ UACPI_RESOURCE_TYPE_START_DEPENDENT,
+ UACPI_RESOURCE_TYPE_END_DEPENDENT,
+
+ // Up to 7 bytes
+ UACPI_RESOURCE_TYPE_VENDOR_SMALL,
+
+ // Up to 2^16 - 1 bytes
+ UACPI_RESOURCE_TYPE_VENDOR_LARGE,
+
+ UACPI_RESOURCE_TYPE_GENERIC_REGISTER,
+ UACPI_RESOURCE_TYPE_GPIO_CONNECTION,
+
+ // These must always be contiguous in this order
+ UACPI_RESOURCE_TYPE_SERIAL_I2C_CONNECTION,
+ UACPI_RESOURCE_TYPE_SERIAL_SPI_CONNECTION,
+ UACPI_RESOURCE_TYPE_SERIAL_UART_CONNECTION,
+ UACPI_RESOURCE_TYPE_SERIAL_CSI2_CONNECTION,
+
+ UACPI_RESOURCE_TYPE_PIN_FUNCTION,
+ UACPI_RESOURCE_TYPE_PIN_CONFIGURATION,
+ UACPI_RESOURCE_TYPE_PIN_GROUP,
+ UACPI_RESOURCE_TYPE_PIN_GROUP_FUNCTION,
+ UACPI_RESOURCE_TYPE_PIN_GROUP_CONFIGURATION,
+
+ UACPI_RESOURCE_TYPE_CLOCK_INPUT,
+
+ UACPI_RESOURCE_TYPE_END_TAG,
+ UACPI_RESOURCE_TYPE_MAX = UACPI_RESOURCE_TYPE_END_TAG,
+} uacpi_resource_type;
+
+typedef struct uacpi_resource_source {
+ uacpi_u8 index;
+ uacpi_bool index_present;
+ uacpi_u16 length;
+ uacpi_char *string;
+} uacpi_resource_source;
+
+/*
+ * This applies to IRQ & StartDependent resources only. The DONT_CARE value is
+ * used for deserialization into the AML format to signify that the serializer
+ * is allowed to optimize the length down if possible. Note that this is
+ * generally not allowed unless the resource is generated by the caller:
+ *
+ * -- ACPI 6.5 ------------------------------------------------------------
+ * The resource descriptors in the byte stream argument must be specified
+ * exactly as listed in the _CRS byte stream - meaning that the identical
+ * resource descriptors must appear in the identical order, resulting in a
+ * buffer of exactly the same length. Optimizations such as changing an
+ * IRQ descriptor to an IRQNoFlags descriptor (or vice-versa) must not be
+ * performed. Similarly, changing StartDependentFn to StartDependentFnNoPri
+ * is not allowed.
+ * ------------------------------------------------------------------------
+ */
+enum uacpi_resource_length_kind {
+ UACPI_RESOURCE_LENGTH_KIND_DONT_CARE = 0,
+ UACPI_RESOURCE_LENGTH_KIND_ONE_LESS,
+ UACPI_RESOURCE_LENGTH_KIND_FULL,
+};
+
+// triggering fields
+#define UACPI_TRIGGERING_EDGE 1
+#define UACPI_TRIGGERING_LEVEL 0
+
+// polarity
+#define UACPI_POLARITY_ACTIVE_HIGH 0
+#define UACPI_POLARITY_ACTIVE_LOW 1
+#define UACPI_POLARITY_ACTIVE_BOTH 2
+
+// sharing
+#define UACPI_EXCLUSIVE 0
+#define UACPI_SHARED 1
+
+// wake_capability
+#define UACPI_WAKE_CAPABLE 1
+#define UACPI_NOT_WAKE_CAPABLE 0
+
+typedef struct uacpi_resource_irq {
+ uacpi_u8 length_kind;
+ uacpi_u8 triggering;
+ uacpi_u8 polarity;
+ uacpi_u8 sharing;
+ uacpi_u8 wake_capability;
+ uacpi_u8 num_irqs;
+ uacpi_u8 irqs[];
+} uacpi_resource_irq;
+
+typedef struct uacpi_resource_extended_irq {
+ uacpi_u8 direction;
+ uacpi_u8 triggering;
+ uacpi_u8 polarity;
+ uacpi_u8 sharing;
+ uacpi_u8 wake_capability;
+ uacpi_u8 num_irqs;
+ uacpi_resource_source source;
+ uacpi_u32 irqs[];
+} uacpi_resource_extended_irq;
+
+// transfer_type
+#define UACPI_TRANSFER_TYPE_8_BIT 0b00
+#define UACPI_TRANSFER_TYPE_8_AND_16_BIT 0b01
+#define UACPI_TRANSFER_TYPE_16_BIT 0b10
+
+// bus_master_status
+#define UACPI_BUS_MASTER 0b1
+
+// channel_speed
+#define UACPI_DMA_COMPATIBILITY 0b00
+#define UACPI_DMA_TYPE_A 0b01
+#define UACPI_DMA_TYPE_B 0b10
+#define UACPI_DMA_TYPE_F 0b11
+
+// transfer_width
+#define UACPI_TRANSFER_WIDTH_8 0x00
+#define UACPI_TRANSFER_WIDTH_16 0x01
+#define UACPI_TRANSFER_WIDTH_32 0x02
+#define UACPI_TRANSFER_WIDTH_64 0x03
+#define UACPI_TRANSFER_WIDTH_128 0x04
+#define UACPI_TRANSFER_WIDTH_256 0x05
+
+typedef struct uacpi_resource_dma {
+ uacpi_u8 transfer_type;
+ uacpi_u8 bus_master_status;
+ uacpi_u8 channel_speed;
+ uacpi_u8 num_channels;
+ uacpi_u8 channels[];
+} uacpi_resource_dma;
+
+typedef struct uacpi_resource_fixed_dma {
+ uacpi_u16 request_line;
+ uacpi_u16 channel;
+ uacpi_u8 transfer_width;
+} uacpi_resource_fixed_dma;
+
+// decode_type
+#define UACPI_DECODE_16 0b1
+#define UACPI_DECODE_10 0b0
+
+typedef struct uacpi_resource_io {
+ uacpi_u8 decode_type;
+ uacpi_u16 minimum;
+ uacpi_u16 maximum;
+ uacpi_u8 alignment;
+ uacpi_u8 length;
+} uacpi_resource_io;
+
+typedef struct uacpi_resource_fixed_io {
+ uacpi_u16 address;
+ uacpi_u8 length;
+} uacpi_resource_fixed_io;
+
+// write_status
+#define UACPI_NON_WRITABLE 0
+#define UACPI_WRITABLE 1
+
+// caching
+#define UACPI_NON_CACHEABLE 0
+#define UACPI_CACHEABLE 1
+#define UACPI_CACHEABLE_WRITE_COMBINING 2
+#define UACPI_PREFETCHABLE 3
+
+// range_type
+#define UACPI_RANGE_TYPE_MEMORY 0
+#define UACPI_RANGE_TYPE_RESERVED 1
+#define UACPI_RANGE_TYPE_ACPI 2
+#define UACPI_RANGE_TYPE_NVS 3
+
+// address_common->type
+#define UACPI_RANGE_MEMORY 0
+#define UACPI_RANGE_IO 1
+#define UACPI_RANGE_BUS 2
+
+// translation
+#define UACPI_IO_MEM_TRANSLATION 1
+#define UACPI_IO_MEM_STATIC 0
+
+// translation_type
+#define UACPI_TRANSLATION_DENSE 0
+#define UACPI_TRANSLATION_SPARSE 1
+
+// direction
+#define UACPI_PRODUCER 0
+#define UACPI_CONSUMER 1
+
+// decode_type
+#define UACPI_POSITIVE_DECODE 0
+#define UACPI_SUBTRACTIVE_DECODE 1
+
+/*
+ * DO NOT USE! SLATED FOR REMOVAL AT 3.0
+ * See the version without the typo above (UACPI_POSITIVE_DECODE)
+ */
+#define UACPI_POISITIVE_DECODE 0
+
+// fixed_min_address & fixed_max_address
+#define UACPI_ADDRESS_NOT_FIXED 0
+#define UACPI_ADDRESS_FIXED 1
+
+typedef struct uacpi_memory_attribute {
+ uacpi_u8 write_status;
+ uacpi_u8 caching;
+ uacpi_u8 range_type;
+ uacpi_u8 translation;
+} uacpi_memory_attribute;
+
+typedef struct uacpi_io_attribute {
+ uacpi_u8 range_type;
+ uacpi_u8 translation;
+ uacpi_u8 translation_type;
+} uacpi_io_attribute;
+
+typedef union uacpi_address_attribute {
+ uacpi_memory_attribute memory;
+ uacpi_io_attribute io;
+ uacpi_u8 type_specific;
+} uacpi_address_attribute;
+
+typedef struct uacpi_resource_address_common {
+ uacpi_address_attribute attribute;
+ uacpi_u8 type;
+ uacpi_u8 direction;
+ uacpi_u8 decode_type;
+ uacpi_u8 fixed_min_address;
+ uacpi_u8 fixed_max_address;
+} uacpi_resource_address_common;
+
+typedef struct uacpi_resource_address16 {
+ uacpi_resource_address_common common;
+ uacpi_u16 granularity;
+ uacpi_u16 minimum;
+ uacpi_u16 maximum;
+ uacpi_u16 translation_offset;
+ uacpi_u16 address_length;
+ uacpi_resource_source source;
+} uacpi_resource_address16;
+
+typedef struct uacpi_resource_address32 {
+ uacpi_resource_address_common common;
+ uacpi_u32 granularity;
+ uacpi_u32 minimum;
+ uacpi_u32 maximum;
+ uacpi_u32 translation_offset;
+ uacpi_u32 address_length;
+ uacpi_resource_source source;
+} uacpi_resource_address32;
+
+typedef struct uacpi_resource_address64 {
+ uacpi_resource_address_common common;
+ uacpi_u64 granularity;
+ uacpi_u64 minimum;
+ uacpi_u64 maximum;
+ uacpi_u64 translation_offset;
+ uacpi_u64 address_length;
+ uacpi_resource_source source;
+} uacpi_resource_address64;
+
+typedef struct uacpi_resource_address64_extended {
+ uacpi_resource_address_common common;
+ uacpi_u8 revision_id;
+ uacpi_u64 granularity;
+ uacpi_u64 minimum;
+ uacpi_u64 maximum;
+ uacpi_u64 translation_offset;
+ uacpi_u64 address_length;
+ uacpi_u64 attributes;
+} uacpi_resource_address64_extended;
+
+typedef struct uacpi_resource_memory24 {
+ uacpi_u8 write_status;
+ uacpi_u16 minimum;
+ uacpi_u16 maximum;
+ uacpi_u16 alignment;
+ uacpi_u16 length;
+} uacpi_resource_memory24;
+
+typedef struct uacpi_resource_memory32 {
+ uacpi_u8 write_status;
+ uacpi_u32 minimum;
+ uacpi_u32 maximum;
+ uacpi_u32 alignment;
+ uacpi_u32 length;
+} uacpi_resource_memory32;
+
+typedef struct uacpi_resource_fixed_memory32 {
+ uacpi_u8 write_status;
+ uacpi_u32 address;
+ uacpi_u32 length;
+} uacpi_resource_fixed_memory32;
+
+// compatibility & performance
+#define UACPI_GOOD 0
+#define UACPI_ACCEPTABLE 1
+#define UACPI_SUB_OPTIMAL 2
+
+typedef struct uacpi_resource_start_dependent {
+ uacpi_u8 length_kind;
+ uacpi_u8 compatibility;
+ uacpi_u8 performance;
+} uacpi_resource_start_dependent;
+
+typedef struct uacpi_resource_vendor_defined {
+ uacpi_u8 length;
+ uacpi_u8 data[];
+} uacpi_resource_vendor;
+
+typedef struct uacpi_resource_vendor_typed {
+ uacpi_u16 length;
+ uacpi_u8 sub_type;
+ uacpi_u8 uuid[16];
+ uacpi_u8 data[];
+} uacpi_resource_vendor_typed;
+
+typedef struct uacpi_resource_generic_register {
+ uacpi_u8 address_space_id;
+ uacpi_u8 bit_width;
+ uacpi_u8 bit_offset;
+ uacpi_u8 access_size;
+ uacpi_u64 address;
+} uacpi_resource_generic_register;
+
+// type
+#define UACPI_GPIO_CONNECTION_INTERRUPT 0x00
+#define UACPI_GPIO_CONNECTION_IO 0x01
+
+typedef struct uacpi_interrupt_connection_flags {
+ uacpi_u8 triggering;
+ uacpi_u8 polarity;
+ uacpi_u8 sharing;
+ uacpi_u8 wake_capability;
+} uacpi_interrupt_connection_flags;
+
+// restriction
+#define UACPI_IO_RESTRICTION_NONE 0x0
+#define UACPI_IO_RESTRICTION_INPUT 0x1
+#define UACPI_IO_RESTRICTION_OUTPUT 0x2
+#define UACPI_IO_RESTRICTION_NONE_PRESERVE 0x3
+
+typedef struct uacpi_io_connection_flags {
+ uacpi_u8 restriction;
+ uacpi_u8 sharing;
+} uacpi_io_connection_flags;
+
+// pull_configuration
+#define UACPI_PIN_CONFIG_DEFAULT 0x00
+#define UACPI_PIN_CONFIG_PULL_UP 0x01
+#define UACPI_PIN_CONFIG_PULL_DOWN 0x02
+#define UACPI_PIN_CONFIG_NO_PULL 0x03
+
+typedef struct uacpi_resource_gpio_connection {
+ uacpi_u8 revision_id;
+ uacpi_u8 type;
+ uacpi_u8 direction;
+
+ union {
+ uacpi_interrupt_connection_flags intr;
+ uacpi_io_connection_flags io;
+ uacpi_u16 type_specific;
+ };
+
+ uacpi_u8 pull_configuration;
+ uacpi_u16 drive_strength;
+ uacpi_u16 debounce_timeout;
+ uacpi_u16 vendor_data_length;
+ uacpi_u16 pin_table_length;
+ uacpi_resource_source source;
+ uacpi_u16 *pin_table;
+ uacpi_u8 *vendor_data;
+} uacpi_resource_gpio_connection;
+
+// mode
+#define UACPI_MODE_CONTROLLER_INITIATED 0x0
+#define UACPI_MODE_DEVICE_INITIATED 0x1
+
+typedef struct uacpi_resource_serial_bus_common {
+ uacpi_u8 revision_id;
+ uacpi_u8 type;
+ uacpi_u8 mode;
+ uacpi_u8 direction;
+ uacpi_u8 sharing;
+ uacpi_u8 type_revision_id;
+ uacpi_u16 type_data_length;
+ uacpi_u16 vendor_data_length;
+ uacpi_resource_source source;
+ uacpi_u8 *vendor_data;
+} uacpi_resource_serial_bus_common;
+
+// addressing_mode
+#define UACPI_I2C_7BIT 0x0
+#define UACPI_I2C_10BIT 0x1
+
+typedef struct uacpi_resource_i2c_connection {
+ uacpi_resource_serial_bus_common common;
+ uacpi_u8 addressing_mode;
+ uacpi_u16 slave_address;
+ uacpi_u32 connection_speed;
+} uacpi_resource_i2c_connection;
+
+// wire_mode
+#define UACPI_SPI_4_WIRES 0
+#define UACPI_SPI_3_WIRES 1
+
+// device_polarity
+#define UACPI_SPI_ACTIVE_LOW 0
+#define UACPI_SPI_ACTIVE_HIGH 1
+
+// phase
+#define UACPI_SPI_PHASE_FIRST 0
+#define UACPI_SPI_PHASE_SECOND 1
+
+// polarity
+#define UACPI_SPI_START_LOW 0
+#define UACPI_SPI_START_HIGH 1
+
+typedef struct uacpi_resource_spi_connection {
+ uacpi_resource_serial_bus_common common;
+ uacpi_u8 wire_mode;
+ uacpi_u8 device_polarity;
+ uacpi_u8 data_bit_length;
+ uacpi_u8 phase;
+ uacpi_u8 polarity;
+ uacpi_u16 device_selection;
+ uacpi_u32 connection_speed;
+} uacpi_resource_spi_connection;
+
+// stop_bits
+#define UACPI_UART_STOP_BITS_NONE 0b00
+#define UACPI_UART_STOP_BITS_1 0b01
+#define UACPI_UART_STOP_BITS_1_5 0b10
+#define UACPI_UART_STOP_BITS_2 0b11
+
+// data_bits
+#define UACPI_UART_DATA_5BITS 0b000
+#define UACPI_UART_DATA_6BITS 0b001
+#define UACPI_UART_DATA_7BITS 0b010
+#define UACPI_UART_DATA_8BITS 0b011
+#define UACPI_UART_DATA_9BITS 0b100
+
+// endianness
+#define UACPI_UART_LITTLE_ENDIAN 0
+#define UACPI_UART_BIG_ENDIAN 1
+
+// parity
+#define UACPI_UART_PARITY_NONE 0x00
+#define UACPI_UART_PARITY_EVEN 0x01
+#define UACPI_UART_PARITY_ODD 0x02
+#define UACPI_UART_PARITY_MARK 0x03
+#define UACPI_UART_PARITY_SPACE 0x04
+
+// lines_enabled
+#define UACPI_UART_DATA_CARRIER_DETECT (1 << 2)
+#define UACPI_UART_RING_INDICATOR (1 << 3)
+#define UACPI_UART_DATA_SET_READY (1 << 4)
+#define UACPI_UART_DATA_TERMINAL_READY (1 << 5)
+#define UACPI_UART_CLEAR_TO_SEND (1 << 6)
+#define UACPI_UART_REQUEST_TO_SEND (1 << 7)
+
+// flow_control
+#define UACPI_UART_FLOW_CONTROL_NONE 0b00
+#define UACPI_UART_FLOW_CONTROL_HW 0b01
+#define UACPI_UART_FLOW_CONTROL_XON_XOFF 0b10
+
+typedef struct uacpi_resource_uart_connection {
+ uacpi_resource_serial_bus_common common;
+ uacpi_u8 stop_bits;
+ uacpi_u8 data_bits;
+ uacpi_u8 endianness;
+ uacpi_u8 parity;
+ uacpi_u8 lines_enabled;
+ uacpi_u8 flow_control;
+ uacpi_u32 baud_rate;
+ uacpi_u16 rx_fifo;
+ uacpi_u16 tx_fifo;
+} uacpi_resource_uart_connection;
+
+// phy_type
+#define UACPI_CSI2_PHY_C 0b00
+#define UACPI_CSI2_PHY_D 0b01
+
+typedef struct uacpi_resource_csi2_connection {
+ uacpi_resource_serial_bus_common common;
+ uacpi_u8 phy_type;
+ uacpi_u8 local_port;
+} uacpi_resource_csi2_connection;
+
+typedef struct uacpi_resource_pin_function {
+ uacpi_u8 revision_id;
+ uacpi_u8 sharing;
+ uacpi_u8 pull_configuration;
+ uacpi_u16 function_number;
+ uacpi_u16 pin_table_length;
+ uacpi_u16 vendor_data_length;
+ uacpi_resource_source source;
+ uacpi_u16 *pin_table;
+ uacpi_u8 *vendor_data;
+} uacpi_resource_pin_function;
+
+// type
+#define UACPI_PIN_CONFIG_DEFAULT 0x00
+#define UACPI_PIN_CONFIG_BIAS_PULL_UP 0x01
+#define UACPI_PIN_CONFIG_BIAS_PULL_DOWN 0x02
+#define UACPI_PIN_CONFIG_BIAS_DEFAULT 0x03
+#define UACPI_PIN_CONFIG_BIAS_DISABLE 0x04
+#define UACPI_PIN_CONFIG_BIAS_HIGH_IMPEDANCE 0x05
+#define UACPI_PIN_CONFIG_BIAS_BUS_HOLD 0x06
+#define UACPI_PIN_CONFIG_DRIVE_OPEN_DRAIN 0x07
+#define UACPI_PIN_CONFIG_DRIVE_OPEN_SOURCE 0x08
+#define UACPI_PIN_CONFIG_DRIVE_PUSH_PULL 0x09
+#define UACPI_PIN_CONFIG_DRIVE_STRENGTH 0x0A
+#define UACPI_PIN_CONFIG_SLEW_RATE 0x0B
+#define UACPI_PIN_CONFIG_INPUT_DEBOUNCE 0x0C
+#define UACPI_PIN_CONFIG_INPUT_SCHMITT_TRIGGER 0x0D
+
+typedef struct uacpi_resource_pin_configuration {
+ uacpi_u8 revision_id;
+ uacpi_u8 sharing;
+ uacpi_u8 direction;
+ uacpi_u8 type;
+ uacpi_u32 value;
+ uacpi_u16 pin_table_length;
+ uacpi_u16 vendor_data_length;
+ uacpi_resource_source source;
+ uacpi_u16 *pin_table;
+ uacpi_u8 *vendor_data;
+} uacpi_resource_pin_configuration;
+
+typedef struct uacpi_resource_label {
+ uacpi_u16 length;
+ const uacpi_char *string;
+} uacpi_resource_label;
+
+typedef struct uacpi_resource_pin_group {
+ uacpi_u8 revision_id;
+ uacpi_u8 direction;
+ uacpi_u16 pin_table_length;
+ uacpi_u16 vendor_data_length;
+ uacpi_resource_label label;
+ uacpi_u16 *pin_table;
+ uacpi_u8 *vendor_data;
+} uacpi_resource_pin_group;
+
+typedef struct uacpi_resource_pin_group_function {
+ uacpi_u8 revision_id;
+ uacpi_u8 sharing;
+ uacpi_u8 direction;
+ uacpi_u16 function;
+ uacpi_u16 vendor_data_length;
+ uacpi_resource_source source;
+ uacpi_resource_label label;
+ uacpi_u8 *vendor_data;
+} uacpi_resource_pin_group_function;
+
+typedef struct uacpi_resource_pin_group_configuration {
+ uacpi_u8 revision_id;
+ uacpi_u8 sharing;
+ uacpi_u8 direction;
+ uacpi_u8 type;
+ uacpi_u32 value;
+ uacpi_u16 vendor_data_length;
+ uacpi_resource_source source;
+ uacpi_resource_label label;
+ uacpi_u8 *vendor_data;
+} uacpi_resource_pin_group_configuration;
+
+// scale
+#define UACPI_SCALE_HZ 0b00
+#define UACPI_SCALE_KHZ 0b01
+#define UACPI_SCALE_MHZ 0b10
+
+// frequency
+#define UACPI_FREQUENCY_FIXED 0x0
+#define UACPI_FREQUENCY_VARIABLE 0x1
+
+typedef struct uacpi_resource_clock_input {
+ uacpi_u8 revision_id;
+ uacpi_u8 frequency;
+ uacpi_u8 scale;
+ uacpi_u16 divisor;
+ uacpi_u32 numerator;
+ uacpi_resource_source source;
+} uacpi_resource_clock_input;
+
+typedef struct uacpi_resource {
+ uacpi_u32 type;
+ uacpi_u32 length;
+
+ union {
+ uacpi_resource_irq irq;
+ uacpi_resource_extended_irq extended_irq;
+ uacpi_resource_dma dma;
+ uacpi_resource_fixed_dma fixed_dma;
+ uacpi_resource_io io;
+ uacpi_resource_fixed_io fixed_io;
+ uacpi_resource_address16 address16;
+ uacpi_resource_address32 address32;
+ uacpi_resource_address64 address64;
+ uacpi_resource_address64_extended address64_extended;
+ uacpi_resource_memory24 memory24;
+ uacpi_resource_memory32 memory32;
+ uacpi_resource_fixed_memory32 fixed_memory32;
+ uacpi_resource_start_dependent start_dependent;
+ uacpi_resource_vendor vendor;
+ uacpi_resource_vendor_typed vendor_typed;
+ uacpi_resource_generic_register generic_register;
+ uacpi_resource_gpio_connection gpio_connection;
+ uacpi_resource_serial_bus_common serial_bus_common;
+ uacpi_resource_i2c_connection i2c_connection;
+ uacpi_resource_spi_connection spi_connection;
+ uacpi_resource_uart_connection uart_connection;
+ uacpi_resource_csi2_connection csi2_connection;
+ uacpi_resource_pin_function pin_function;
+ uacpi_resource_pin_configuration pin_configuration;
+ uacpi_resource_pin_group pin_group;
+ uacpi_resource_pin_group_function pin_group_function;
+ uacpi_resource_pin_group_configuration pin_group_configuration;
+ uacpi_resource_clock_input clock_input;
+ };
+} uacpi_resource;
+
+#define UACPI_NEXT_RESOURCE(cur) \
+ ((uacpi_resource*)((uacpi_u8*)(cur) + (cur)->length))
+
+typedef struct uacpi_resources {
+ uacpi_size length;
+ uacpi_resource *entries;
+} uacpi_resources;
+void uacpi_free_resources(uacpi_resources*);
+
+typedef uacpi_iteration_decision (*uacpi_resource_iteration_callback)
+ (void *user, uacpi_resource *resource);
+
+/*
+ * Evaluate the _CRS method for a 'device' and get the returned resource list
+ * via 'out_resources'.
+ *
+ * NOTE: the returned buffer must be released via a uacpi_free_resources()
+ */
+uacpi_status uacpi_get_current_resources(
+ uacpi_namespace_node *device, uacpi_resources **out_resources
+);
+
+/*
+ * Evaluate the _PRS method for a 'device' and get the returned resource list
+ * via 'out_resources'.
+ *
+ * NOTE: the returned buffer must be released via uacpi_free_resources()
+ */
+uacpi_status uacpi_get_possible_resources(
+ uacpi_namespace_node *device, uacpi_resources **out_resources
+);
+
+/*
+ * Evaluate an arbitrary method that is expected to return an AML resource
+ * buffer for a 'device' and get the returned resource list via 'out_resources'.
+ *
+ * NOTE: the returned buffer must be released via uacpi_free_resources()
+ */
+uacpi_status uacpi_get_device_resources(
+ uacpi_namespace_node *device, const uacpi_char *method,
+ uacpi_resources **out_resources
+);
+
+/*
+ * Set the configuration to be used by the 'device' by calling its _SRS method.
+ *
+ * Note that this expects 'resources' in the normal 'uacpi_resources' format,
+ * and not the raw AML resources bytestream, the conversion to the latter is
+ * done automatically by this API. If you want to _SRS a raw AML resources
+ * bytestream, use 'uacpi_execute' or similar API directly.
+ */
+uacpi_status uacpi_set_resources(
+ uacpi_namespace_node *device, uacpi_resources *resources
+);
+
+/*
+ * A convenience helper for iterating over the resource list returned by any
+ * of the uacpi_get_*_resources functions.
+ */
+uacpi_status uacpi_for_each_resource(
+ uacpi_resources *resources, uacpi_resource_iteration_callback cb, void *user
+);
+
+/*
+ * A shorthand for uacpi_get_device_resources() + uacpi_for_each_resource().
+ *
+ * Use if you don't actually want to save the 'resources' list, but simply want
+ * to iterate it once to extract the resources you care about and then free it
+ * right away.
+ */
+uacpi_status uacpi_for_each_device_resource(
+ uacpi_namespace_node *device, const uacpi_char *method,
+ uacpi_resource_iteration_callback cb, void *user
+);
+
+/*
+ * Convert a single AML-encoded resource to native format.
+ *
+ * This should be used for converting Connection() fields (passed during IO on
+ * GeneralPurposeIO or GenericSerialBus operation regions) or other similar
+ * buffers with only one resource to native format.
+ *
+ * NOTE: the returned buffer must be released via uacpi_free_resource()
+ */
+uacpi_status uacpi_get_resource_from_buffer(
+ uacpi_data_view aml_buffer, uacpi_resource **out_resource
+);
+void uacpi_free_resource(uacpi_resource*);
+
+#endif // !UACPI_BAREBONES_MODE
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/sys/include/dev/acpi/uacpi/uacpi/sleep.h b/sys/include/dev/acpi/uacpi/uacpi/sleep.h
new file mode 100644
index 0000000..3fd9bf3
--- /dev/null
+++ b/sys/include/dev/acpi/uacpi/uacpi/sleep.h
@@ -0,0 +1,67 @@
+#pragma once
+
+#include <uacpi/types.h>
+#include <uacpi/status.h>
+#include <uacpi/uacpi.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef UACPI_BAREBONES_MODE
+
+/*
+ * Set the firmware waking vector in FACS.
+ *
+ * 'addr32' is the real mode entry-point address
+ * 'addr64' is the protected mode entry-point address
+ */
+UACPI_ALWAYS_ERROR_FOR_REDUCED_HARDWARE(
+uacpi_status uacpi_set_waking_vector(
+ uacpi_phys_addr addr32, uacpi_phys_addr addr64
+))
+
+typedef enum uacpi_sleep_state {
+ UACPI_SLEEP_STATE_S0 = 0,
+ UACPI_SLEEP_STATE_S1,
+ UACPI_SLEEP_STATE_S2,
+ UACPI_SLEEP_STATE_S3,
+ UACPI_SLEEP_STATE_S4,
+ UACPI_SLEEP_STATE_S5,
+ UACPI_SLEEP_STATE_MAX = UACPI_SLEEP_STATE_S5,
+} uacpi_sleep_state;
+
+/*
+ * Prepare for a given sleep state.
+ * Must be caled with interrupts ENABLED.
+ */
+uacpi_status uacpi_prepare_for_sleep_state(uacpi_sleep_state);
+
+/*
+ * Enter the given sleep state after preparation.
+ * Must be called with interrupts DISABLED.
+ */
+uacpi_status uacpi_enter_sleep_state(uacpi_sleep_state);
+
+/*
+ * Prepare to leave the given sleep state.
+ * Must be called with interrupts DISABLED.
+ */
+uacpi_status uacpi_prepare_for_wake_from_sleep_state(uacpi_sleep_state);
+
+/*
+ * Wake from the given sleep state.
+ * Must be called with interrupts ENABLED.
+ */
+uacpi_status uacpi_wake_from_sleep_state(uacpi_sleep_state);
+
+/*
+ * Attempt reset via the FADT reset register.
+ */
+uacpi_status uacpi_reboot(void);
+
+#endif // !UACPI_BAREBONES_MODE
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/sys/include/dev/acpi/uacpi/uacpi/status.h b/sys/include/dev/acpi/uacpi/uacpi/status.h
new file mode 100644
index 0000000..5c09508
--- /dev/null
+++ b/sys/include/dev/acpi/uacpi/uacpi/status.h
@@ -0,0 +1,57 @@
+#pragma once
+
+#include <uacpi/internal/compiler.h>
+#include <uacpi/platform/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum uacpi_status {
+ UACPI_STATUS_OK = 0,
+ UACPI_STATUS_MAPPING_FAILED = 1,
+ UACPI_STATUS_OUT_OF_MEMORY = 2,
+ UACPI_STATUS_BAD_CHECKSUM = 3,
+ UACPI_STATUS_INVALID_SIGNATURE = 4,
+ UACPI_STATUS_INVALID_TABLE_LENGTH = 5,
+ UACPI_STATUS_NOT_FOUND = 6,
+ UACPI_STATUS_INVALID_ARGUMENT = 7,
+ UACPI_STATUS_UNIMPLEMENTED = 8,
+ UACPI_STATUS_ALREADY_EXISTS = 9,
+ UACPI_STATUS_INTERNAL_ERROR = 10,
+ UACPI_STATUS_TYPE_MISMATCH = 11,
+ UACPI_STATUS_INIT_LEVEL_MISMATCH = 12,
+ UACPI_STATUS_NAMESPACE_NODE_DANGLING = 13,
+ UACPI_STATUS_NO_HANDLER = 14,
+ UACPI_STATUS_NO_RESOURCE_END_TAG = 15,
+ UACPI_STATUS_COMPILED_OUT = 16,
+ UACPI_STATUS_HARDWARE_TIMEOUT = 17,
+ UACPI_STATUS_TIMEOUT = 18,
+ UACPI_STATUS_OVERRIDDEN = 19,
+ UACPI_STATUS_DENIED = 20,
+
+ // All errors that have bytecode-related origin should go here
+ UACPI_STATUS_AML_UNDEFINED_REFERENCE = 0x0EFF0000,
+ UACPI_STATUS_AML_INVALID_NAMESTRING = 0x0EFF0001,
+ UACPI_STATUS_AML_OBJECT_ALREADY_EXISTS = 0x0EFF0002,
+ UACPI_STATUS_AML_INVALID_OPCODE = 0x0EFF0003,
+ UACPI_STATUS_AML_INCOMPATIBLE_OBJECT_TYPE = 0x0EFF0004,
+ UACPI_STATUS_AML_BAD_ENCODING = 0x0EFF0005,
+ UACPI_STATUS_AML_OUT_OF_BOUNDS_INDEX = 0x0EFF0006,
+ UACPI_STATUS_AML_SYNC_LEVEL_TOO_HIGH = 0x0EFF0007,
+ UACPI_STATUS_AML_INVALID_RESOURCE = 0x0EFF0008,
+ UACPI_STATUS_AML_LOOP_TIMEOUT = 0x0EFF0009,
+ UACPI_STATUS_AML_CALL_STACK_DEPTH_LIMIT = 0x0EFF000A,
+} uacpi_status;
+
+const uacpi_char *uacpi_status_to_string(uacpi_status);
+
+#define uacpi_unlikely_error(expr) uacpi_unlikely((expr) != UACPI_STATUS_OK)
+#define uacpi_likely_error(expr) uacpi_likely((expr) != UACPI_STATUS_OK)
+
+#define uacpi_unlikely_success(expr) uacpi_unlikely((expr) == UACPI_STATUS_OK)
+#define uacpi_likely_success(expr) uacpi_likely((expr) == UACPI_STATUS_OK)
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/sys/include/dev/acpi/uacpi/uacpi/tables.h b/sys/include/dev/acpi/uacpi/uacpi/tables.h
new file mode 100644
index 0000000..5fbecee
--- /dev/null
+++ b/sys/include/dev/acpi/uacpi/uacpi/tables.h
@@ -0,0 +1,141 @@
+#pragma once
+
+#include <uacpi/types.h>
+#include <uacpi/status.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Forward-declared to avoid including the entire acpi.h here
+struct acpi_fadt;
+
+typedef struct uacpi_table_identifiers {
+ uacpi_object_name signature;
+
+ // if oemid[0] == 0 this field is ignored
+ char oemid[6];
+
+ // if oem_table_id[0] == 0 this field is ignored
+ char oem_table_id[8];
+} uacpi_table_identifiers;
+
+typedef struct uacpi_table {
+ union {
+ uacpi_virt_addr virt_addr;
+ void *ptr;
+ struct acpi_sdt_hdr *hdr;
+ };
+
+ // Index number used to identify this table internally
+ uacpi_size index;
+} uacpi_table;
+
+/*
+ * Install a table from either a virtual or a physical address.
+ * The table is simply stored in the internal table array, and not loaded by
+ * the interpreter (see uacpi_table_load).
+ *
+ * The table is optionally returned via 'out_table'.
+ *
+ * Manual calls to uacpi_table_install are not subject to filtering via the
+ * table installation callback (if any).
+ */
+uacpi_status uacpi_table_install(
+ void*, uacpi_table *out_table
+);
+uacpi_status uacpi_table_install_physical(
+ uacpi_phys_addr, uacpi_table *out_table
+);
+
+#ifndef UACPI_BAREBONES_MODE
+/*
+ * Load a previously installed table by feeding it to the interpreter.
+ */
+uacpi_status uacpi_table_load(uacpi_size index);
+#endif // !UACPI_BAREBONES_MODE
+
+/*
+ * Helpers for finding tables.
+ *
+ * for find_by_signature:
+ * 'signature' is an array of 4 characters, a null terminator is not
+ * necessary and can be omitted (especially useful for non-C language
+ * bindings)
+ *
+ * 'out_table' is a pointer to a caller allocated uacpi_table structure that
+ * receives the table pointer & its internal index in case the call was
+ * successful.
+ *
+ * NOTE:
+ * The returned table's reference count is incremented by 1, which keeps its
+ * mapping alive forever unless uacpi_table_unref() is called for this table
+ * later on. Calling uacpi_table_find_next_with_same_signature() on a table also
+ * drops its reference count by 1, so if you want to keep it mapped you must
+ * manually call uacpi_table_ref() beforehand.
+ */
+uacpi_status uacpi_table_find_by_signature(
+ const uacpi_char *signature, uacpi_table *out_table
+);
+uacpi_status uacpi_table_find_next_with_same_signature(
+ uacpi_table *in_out_table
+);
+uacpi_status uacpi_table_find(
+ const uacpi_table_identifiers *id, uacpi_table *out_table
+);
+
+/*
+ * Increment/decrement a table's reference count.
+ * The table is unmapped when the reference count drops to 0.
+ */
+uacpi_status uacpi_table_ref(uacpi_table*);
+uacpi_status uacpi_table_unref(uacpi_table*);
+
+/*
+ * Returns the pointer to a sanitized internal version of FADT.
+ *
+ * The revision is guaranteed to be correct. All of the registers are converted
+ * to GAS format. Fields that might contain garbage are cleared.
+ */
+uacpi_status uacpi_table_fadt(struct acpi_fadt**);
+
+typedef enum uacpi_table_installation_disposition {
+ // Allow the table to be installed as-is
+ UACPI_TABLE_INSTALLATION_DISPOSITON_ALLOW = 0,
+
+ /*
+ * Deny the table from being installed completely. This is useful for
+ * debugging various problems, e.g. AML loading bad SSDTs that cause the
+ * system to hang or enter an undesired state.
+ */
+ UACPI_TABLE_INSTALLATION_DISPOSITON_DENY,
+
+ /*
+ * Override the table being installed with the table at the virtual address
+ * returned in 'out_override_address'.
+ */
+ UACPI_TABLE_INSTALLATION_DISPOSITON_VIRTUAL_OVERRIDE,
+
+ /*
+ * Override the table being installed with the table at the physical address
+ * returned in 'out_override_address'.
+ */
+ UACPI_TABLE_INSTALLATION_DISPOSITON_PHYSICAL_OVERRIDE,
+} uacpi_table_installation_disposition;
+
+typedef uacpi_table_installation_disposition (*uacpi_table_installation_handler)
+ (struct acpi_sdt_hdr *hdr, uacpi_u64 *out_override_address);
+
+/*
+ * Set a handler that is invoked for each table before it gets installed.
+ *
+ * Depending on the return value, the table is either allowed to be installed
+ * as-is, denied, or overriden with a new one.
+ */
+uacpi_status uacpi_set_table_installation_handler(
+ uacpi_table_installation_handler handler
+);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/sys/include/dev/acpi/uacpi/uacpi/types.h b/sys/include/dev/acpi/uacpi/uacpi/types.h
new file mode 100644
index 0000000..240cfdc
--- /dev/null
+++ b/sys/include/dev/acpi/uacpi/uacpi/types.h
@@ -0,0 +1,547 @@
+#pragma once
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wpedantic"
+#pragma GCC diagnostic ignored "-Wformat"
+
+#include <uacpi/status.h>
+#include <uacpi/platform/types.h>
+#include <uacpi/platform/compiler.h>
+#include <uacpi/platform/arch_helpers.h>
+#include <uacpi/platform/config.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if UACPI_POINTER_SIZE == 4 && defined(UACPI_PHYS_ADDR_IS_32BITS)
+typedef uacpi_u32 uacpi_phys_addr;
+typedef uacpi_u32 uacpi_io_addr;
+#else
+typedef uacpi_u64 uacpi_phys_addr;
+typedef uacpi_u64 uacpi_io_addr;
+#endif
+
+typedef void *uacpi_handle;
+
+typedef union uacpi_object_name {
+ uacpi_char text[4];
+ uacpi_u32 id;
+} uacpi_object_name;
+
+typedef enum uacpi_iteration_decision {
+ UACPI_ITERATION_DECISION_CONTINUE = 0,
+ UACPI_ITERATION_DECISION_BREAK,
+
+ // Only applicable for uacpi_namespace_for_each_child
+ UACPI_ITERATION_DECISION_NEXT_PEER,
+} uacpi_iteration_decision;
+
+typedef enum uacpi_address_space {
+ UACPI_ADDRESS_SPACE_SYSTEM_MEMORY = 0,
+ UACPI_ADDRESS_SPACE_SYSTEM_IO = 1,
+ UACPI_ADDRESS_SPACE_PCI_CONFIG = 2,
+ UACPI_ADDRESS_SPACE_EMBEDDED_CONTROLLER = 3,
+ UACPI_ADDRESS_SPACE_SMBUS = 4,
+ UACPI_ADDRESS_SPACE_SYSTEM_CMOS = 5,
+ UACPI_ADDRESS_SPACE_PCI_BAR_TARGET = 6,
+ UACPI_ADDRESS_SPACE_IPMI = 7,
+ UACPI_ADDRESS_SPACE_GENERAL_PURPOSE_IO = 8,
+ UACPI_ADDRESS_SPACE_GENERIC_SERIAL_BUS = 9,
+ UACPI_ADDRESS_SPACE_PCC = 0x0A,
+ UACPI_ADDRESS_SPACE_PRM = 0x0B,
+ UACPI_ADDRESS_SPACE_FFIXEDHW = 0x7F,
+
+ // Internal type
+ UACPI_ADDRESS_SPACE_TABLE_DATA = 0xDA1A,
+} uacpi_address_space;
+const uacpi_char *uacpi_address_space_to_string(uacpi_address_space space);
+
+#ifndef UACPI_BAREBONES_MODE
+
+typedef enum uacpi_init_level {
+ // Reboot state, nothing is available
+ UACPI_INIT_LEVEL_EARLY = 0,
+
+ /*
+ * State after a successfull call to uacpi_initialize. Table API and
+ * other helpers that don't depend on the ACPI namespace may be used.
+ */
+ UACPI_INIT_LEVEL_SUBSYSTEM_INITIALIZED = 1,
+
+ /*
+ * State after a successfull call to uacpi_namespace_load. Most API may be
+ * used, namespace can be iterated, etc.
+ */
+ UACPI_INIT_LEVEL_NAMESPACE_LOADED = 2,
+
+ /*
+ * The final initialization stage, this is entered after the call to
+ * uacpi_namespace_initialize. All API is available to use.
+ */
+ UACPI_INIT_LEVEL_NAMESPACE_INITIALIZED = 3,
+} uacpi_init_level;
+
+typedef struct uacpi_pci_address {
+ uacpi_u16 segment;
+ uacpi_u8 bus;
+ uacpi_u8 device;
+ uacpi_u8 function;
+} uacpi_pci_address;
+
+typedef struct uacpi_data_view {
+ union {
+ uacpi_u8 *bytes;
+ const uacpi_u8 *const_bytes;
+
+ uacpi_char *text;
+ const uacpi_char *const_text;
+
+ void *data;
+ const void *const_data;
+ };
+ uacpi_size length;
+} uacpi_data_view;
+
+typedef struct uacpi_namespace_node uacpi_namespace_node;
+
+typedef enum uacpi_object_type {
+ UACPI_OBJECT_UNINITIALIZED = 0,
+ UACPI_OBJECT_INTEGER = 1,
+ UACPI_OBJECT_STRING = 2,
+ UACPI_OBJECT_BUFFER = 3,
+ UACPI_OBJECT_PACKAGE = 4,
+ UACPI_OBJECT_FIELD_UNIT = 5,
+ UACPI_OBJECT_DEVICE = 6,
+ UACPI_OBJECT_EVENT = 7,
+ UACPI_OBJECT_METHOD = 8,
+ UACPI_OBJECT_MUTEX = 9,
+ UACPI_OBJECT_OPERATION_REGION = 10,
+ UACPI_OBJECT_POWER_RESOURCE = 11,
+ UACPI_OBJECT_PROCESSOR = 12,
+ UACPI_OBJECT_THERMAL_ZONE = 13,
+ UACPI_OBJECT_BUFFER_FIELD = 14,
+ UACPI_OBJECT_DEBUG = 16,
+
+ UACPI_OBJECT_REFERENCE = 20,
+ UACPI_OBJECT_BUFFER_INDEX = 21,
+ UACPI_OBJECT_MAX_TYPE_VALUE = UACPI_OBJECT_BUFFER_INDEX
+} uacpi_object_type;
+
+// Type bits for API requiring a bit mask, e.g. uacpi_eval_typed
+typedef enum uacpi_object_type_bits {
+ UACPI_OBJECT_INTEGER_BIT = (1 << UACPI_OBJECT_INTEGER),
+ UACPI_OBJECT_STRING_BIT = (1 << UACPI_OBJECT_STRING),
+ UACPI_OBJECT_BUFFER_BIT = (1 << UACPI_OBJECT_BUFFER),
+ UACPI_OBJECT_PACKAGE_BIT = (1 << UACPI_OBJECT_PACKAGE),
+ UACPI_OBJECT_FIELD_UNIT_BIT = (1 << UACPI_OBJECT_FIELD_UNIT),
+ UACPI_OBJECT_DEVICE_BIT = (1 << UACPI_OBJECT_DEVICE),
+ UACPI_OBJECT_EVENT_BIT = (1 << UACPI_OBJECT_EVENT),
+ UACPI_OBJECT_METHOD_BIT = (1 << UACPI_OBJECT_METHOD),
+ UACPI_OBJECT_MUTEX_BIT = (1 << UACPI_OBJECT_MUTEX),
+ UACPI_OBJECT_OPERATION_REGION_BIT = (1 << UACPI_OBJECT_OPERATION_REGION),
+ UACPI_OBJECT_POWER_RESOURCE_BIT = (1 << UACPI_OBJECT_POWER_RESOURCE),
+ UACPI_OBJECT_PROCESSOR_BIT = (1 << UACPI_OBJECT_PROCESSOR),
+ UACPI_OBJECT_THERMAL_ZONE_BIT = (1 << UACPI_OBJECT_THERMAL_ZONE),
+ UACPI_OBJECT_BUFFER_FIELD_BIT = (1 << UACPI_OBJECT_BUFFER_FIELD),
+ UACPI_OBJECT_DEBUG_BIT = (1 << UACPI_OBJECT_DEBUG),
+ UACPI_OBJECT_REFERENCE_BIT = (1 << UACPI_OBJECT_REFERENCE),
+ UACPI_OBJECT_BUFFER_INDEX_BIT = (1 << UACPI_OBJECT_BUFFER_INDEX),
+ UACPI_OBJECT_ANY_BIT = 0xFFFFFFFF,
+} uacpi_object_type_bits;
+
+typedef struct uacpi_object uacpi_object;
+
+void uacpi_object_ref(uacpi_object *obj);
+void uacpi_object_unref(uacpi_object *obj);
+
+uacpi_object_type uacpi_object_get_type(uacpi_object*);
+uacpi_object_type_bits uacpi_object_get_type_bit(uacpi_object*);
+
+/*
+ * Returns UACPI_TRUE if the provided object's type matches this type.
+ */
+uacpi_bool uacpi_object_is(uacpi_object*, uacpi_object_type);
+
+/*
+ * Returns UACPI_TRUE if the provided object's type is one of the values
+ * specified in the 'type_mask' of UACPI_OBJECT_*_BIT.
+ */
+uacpi_bool uacpi_object_is_one_of(
+ uacpi_object*, uacpi_object_type_bits type_mask
+);
+
+const uacpi_char *uacpi_object_type_to_string(uacpi_object_type);
+
+/*
+ * Create an uninitialized object. The object can be further overwritten via
+ * uacpi_object_assign_* to anything.
+ */
+uacpi_object *uacpi_object_create_uninitialized(void);
+
+/*
+ * Create an integer object with the value provided.
+ */
+uacpi_object *uacpi_object_create_integer(uacpi_u64);
+
+typedef enum uacpi_overflow_behavior {
+ UACPI_OVERFLOW_ALLOW = 0,
+ UACPI_OVERFLOW_TRUNCATE,
+ UACPI_OVERFLOW_DISALLOW,
+} uacpi_overflow_behavior;
+
+/*
+ * Same as uacpi_object_create_integer, but introduces additional ways to
+ * control what happens if the provided integer is larger than 32-bits, and the
+ * AML code expects 32-bit integers.
+ *
+ * - UACPI_OVERFLOW_ALLOW -> do nothing, same as the vanilla helper
+ * - UACPI_OVERFLOW_TRUNCATE -> truncate the integer to 32-bits if it happens to
+ * be larger than allowed by the DSDT
+ * - UACPI_OVERFLOW_DISALLOW -> fail object creation with
+ * UACPI_STATUS_INVALID_ARGUMENT if the provided
+ * value happens to be too large
+ */
+uacpi_status uacpi_object_create_integer_safe(
+ uacpi_u64, uacpi_overflow_behavior, uacpi_object **out_obj
+);
+
+uacpi_status uacpi_object_assign_integer(uacpi_object*, uacpi_u64 value);
+uacpi_status uacpi_object_get_integer(uacpi_object*, uacpi_u64 *out);
+
+/*
+ * Create a string/buffer object. Takes in a constant view of the data.
+ *
+ * NOTE: The data is copied to a separately allocated buffer and is not taken
+ * ownership of.
+ */
+uacpi_object *uacpi_object_create_string(uacpi_data_view);
+uacpi_object *uacpi_object_create_cstring(const uacpi_char*);
+uacpi_object *uacpi_object_create_buffer(uacpi_data_view);
+
+/*
+ * Returns a writable view of the data stored in the string or buffer type
+ * object.
+ */
+uacpi_status uacpi_object_get_string_or_buffer(
+ uacpi_object*, uacpi_data_view *out
+);
+uacpi_status uacpi_object_get_string(uacpi_object*, uacpi_data_view *out);
+uacpi_status uacpi_object_get_buffer(uacpi_object*, uacpi_data_view *out);
+
+/*
+ * Returns UACPI_TRUE if the provided string object is actually an AML namepath.
+ *
+ * This can only be the case for package elements. If a package element is
+ * specified as a path to an object in AML, it's not resolved by the interpreter
+ * right away as it might not have been defined at that point yet, and is
+ * instead stored as a special string object to be resolved by client code
+ * when needed.
+ *
+ * Example usage:
+ * uacpi_namespace_node *target_node = UACPI_NULL;
+ *
+ * uacpi_object *obj = UACPI_NULL;
+ * uacpi_eval(scope, path, UACPI_NULL, &obj);
+ *
+ * uacpi_object_array arr;
+ * uacpi_object_get_package(obj, &arr);
+ *
+ * if (uacpi_object_is_aml_namepath(arr.objects[0])) {
+ * uacpi_object_resolve_as_aml_namepath(
+ * arr.objects[0], scope, &target_node
+ * );
+ * }
+ */
+uacpi_bool uacpi_object_is_aml_namepath(uacpi_object*);
+
+/*
+ * Resolve an AML namepath contained in a string object.
+ *
+ * This is only applicable to objects that are package elements. See an
+ * explanation of how this works in the comment above the declaration of
+ * uacpi_object_is_aml_namepath.
+ *
+ * This is a shorthand for:
+ * uacpi_data_view view;
+ * uacpi_object_get_string(object, &view);
+ *
+ * target_node = uacpi_namespace_node_resolve_from_aml_namepath(
+ * scope, view.text
+ * );
+ */
+uacpi_status uacpi_object_resolve_as_aml_namepath(
+ uacpi_object*, uacpi_namespace_node *scope, uacpi_namespace_node **out_node
+);
+
+/*
+ * Make the provided object a string/buffer.
+ * Takes in a constant view of the data to be stored in the object.
+ *
+ * NOTE: The data is copied to a separately allocated buffer and is not taken
+ * ownership of.
+ */
+uacpi_status uacpi_object_assign_string(uacpi_object*, uacpi_data_view in);
+uacpi_status uacpi_object_assign_buffer(uacpi_object*, uacpi_data_view in);
+
+typedef struct uacpi_object_array {
+ uacpi_object **objects;
+ uacpi_size count;
+} uacpi_object_array;
+
+/*
+ * Create a package object and store all of the objects in the array inside.
+ * The array is allowed to be empty.
+ *
+ * NOTE: the reference count of each object is incremented before being stored
+ * in the object. Client code must remove all of the locally created
+ * references at its own discretion.
+ */
+uacpi_object *uacpi_object_create_package(uacpi_object_array in);
+
+/*
+ * Returns the list of objects stored in a package object.
+ *
+ * NOTE: the reference count of the objects stored inside is not incremented,
+ * which means destorying/overwriting the object also potentially destroys
+ * all of the objects stored inside unless the reference count is
+ * incremented by the client via uacpi_object_ref.
+ */
+uacpi_status uacpi_object_get_package(uacpi_object*, uacpi_object_array *out);
+
+/*
+ * Make the provided object a package and store all of the objects in the array
+ * inside. The array is allowed to be empty.
+ *
+ * NOTE: the reference count of each object is incremented before being stored
+ * in the object. Client code must remove all of the locally created
+ * references at its own discretion.
+ */
+uacpi_status uacpi_object_assign_package(uacpi_object*, uacpi_object_array in);
+
+/*
+ * Create a reference object and make it point to 'child'.
+ *
+ * NOTE: child's reference count is incremented by one. Client code must remove
+ * all of the locally created references at its own discretion.
+ */
+uacpi_object *uacpi_object_create_reference(uacpi_object *child);
+
+/*
+ * Make the provided object a reference and make it point to 'child'.
+ *
+ * NOTE: child's reference count is incremented by one. Client code must remove
+ * all of the locally created references at its own discretion.
+ */
+uacpi_status uacpi_object_assign_reference(uacpi_object*, uacpi_object *child);
+
+/*
+ * Retrieve the object pointed to by a reference object.
+ *
+ * NOTE: the reference count of the returned object is incremented by one and
+ * must be uacpi_object_unref'ed by the client when no longer needed.
+ */
+uacpi_status uacpi_object_get_dereferenced(uacpi_object*, uacpi_object **out);
+
+typedef struct uacpi_processor_info {
+ uacpi_u8 id;
+ uacpi_u32 block_address;
+ uacpi_u8 block_length;
+} uacpi_processor_info;
+
+/*
+ * Returns the information about the provided processor object.
+ */
+uacpi_status uacpi_object_get_processor_info(
+ uacpi_object*, uacpi_processor_info *out
+);
+
+typedef struct uacpi_power_resource_info {
+ uacpi_u8 system_level;
+ uacpi_u16 resource_order;
+} uacpi_power_resource_info;
+
+/*
+ * Returns the information about the provided power resource object.
+ */
+uacpi_status uacpi_object_get_power_resource_info(
+ uacpi_object*, uacpi_power_resource_info *out
+);
+
+typedef enum uacpi_region_op {
+ // data => uacpi_region_attach_data
+ UACPI_REGION_OP_ATTACH = 0,
+ // data => uacpi_region_detach_data
+ UACPI_REGION_OP_DETACH,
+
+ // data => uacpi_region_rw_data
+ UACPI_REGION_OP_READ,
+ UACPI_REGION_OP_WRITE,
+
+ // data => uacpi_region_pcc_send_data
+ UACPI_REGION_OP_PCC_SEND,
+
+ // data => uacpi_region_gpio_rw_data
+ UACPI_REGION_OP_GPIO_READ,
+ UACPI_REGION_OP_GPIO_WRITE,
+
+ // data => uacpi_region_ipmi_rw_data
+ UACPI_REGION_OP_IPMI_COMMAND,
+
+ // data => uacpi_region_ffixedhw_rw_data
+ UACPI_REGION_OP_FFIXEDHW_COMMAND,
+
+ // data => uacpi_region_prm_rw_data
+ UACPI_REGION_OP_PRM_COMMAND,
+
+ // data => uacpi_region_serial_rw_data
+ UACPI_REGION_OP_SERIAL_READ,
+ UACPI_REGION_OP_SERIAL_WRITE,
+} uacpi_region_op;
+
+typedef struct uacpi_generic_region_info {
+ uacpi_u64 base;
+ uacpi_u64 length;
+} uacpi_generic_region_info;
+
+typedef struct uacpi_pcc_region_info {
+ uacpi_data_view buffer;
+ uacpi_u8 subspace_id;
+} uacpi_pcc_region_info;
+
+typedef struct uacpi_gpio_region_info
+{
+ uacpi_u64 num_pins;
+} uacpi_gpio_region_info;
+
+typedef struct uacpi_region_attach_data {
+ void *handler_context;
+ uacpi_namespace_node *region_node;
+ union {
+ uacpi_generic_region_info generic_info;
+ uacpi_pcc_region_info pcc_info;
+ uacpi_gpio_region_info gpio_info;
+ };
+ void *out_region_context;
+} uacpi_region_attach_data;
+
+typedef struct uacpi_region_rw_data {
+ void *handler_context;
+ void *region_context;
+ union {
+ uacpi_phys_addr address;
+ uacpi_u64 offset;
+ };
+ uacpi_u64 value;
+ uacpi_u8 byte_width;
+} uacpi_region_rw_data;
+
+typedef struct uacpi_region_pcc_send_data {
+ void *handler_context;
+ void *region_context;
+ uacpi_data_view buffer;
+} uacpi_region_pcc_send_data;
+
+typedef struct uacpi_region_gpio_rw_data
+{
+ void *handler_context;
+ void *region_context;
+ uacpi_data_view connection;
+ uacpi_u32 pin_offset;
+ uacpi_u32 num_pins;
+ uacpi_u64 value;
+} uacpi_region_gpio_rw_data;
+
+typedef struct uacpi_region_ipmi_rw_data
+{
+ void *handler_context;
+ void *region_context;
+ uacpi_data_view in_out_message;
+ uacpi_u64 command;
+} uacpi_region_ipmi_rw_data;
+
+typedef uacpi_region_ipmi_rw_data uacpi_region_ffixedhw_rw_data;
+
+typedef struct uacpi_region_prm_rw_data
+{
+ void *handler_context;
+ void *region_context;
+ uacpi_data_view in_out_message;
+} uacpi_region_prm_rw_data;
+
+typedef enum uacpi_access_attribute {
+ UACPI_ACCESS_ATTRIBUTE_QUICK = 0x02,
+ UACPI_ACCESS_ATTRIBUTE_SEND_RECEIVE = 0x04,
+ UACPI_ACCESS_ATTRIBUTE_BYTE = 0x06,
+ UACPI_ACCESS_ATTRIBUTE_WORD = 0x08,
+ UACPI_ACCESS_ATTRIBUTE_BLOCK = 0x0A,
+ UACPI_ACCESS_ATTRIBUTE_BYTES = 0x0B,
+ UACPI_ACCESS_ATTRIBUTE_PROCESS_CALL = 0x0C,
+ UACPI_ACCESS_ATTRIBUTE_BLOCK_PROCESS_CALL = 0x0D,
+ UACPI_ACCESS_ATTRIBUTE_RAW_BYTES = 0x0E,
+ UACPI_ACCESS_ATTRIBUTE_RAW_PROCESS_BYTES = 0x0F,
+} uacpi_access_attribute;
+
+typedef struct uacpi_region_serial_rw_data {
+ void *handler_context;
+ void *region_context;
+ uacpi_u64 command;
+ uacpi_data_view connection;
+ uacpi_data_view in_out_buffer;
+ uacpi_access_attribute access_attribute;
+
+ /*
+ * Applicable if access_attribute is one of:
+ * - UACPI_ACCESS_ATTRIBUTE_BYTES
+ * - UACPI_ACCESS_ATTRIBUTE_RAW_BYTES
+ * - UACPI_ACCESS_ATTRIBUTE_RAW_PROCESS_BYTES
+ */
+ uacpi_u8 access_length;
+} uacpi_region_serial_rw_data;
+
+typedef struct uacpi_region_detach_data {
+ void *handler_context;
+ void *region_context;
+ uacpi_namespace_node *region_node;
+} uacpi_region_detach_data;
+
+typedef uacpi_status (*uacpi_region_handler)
+ (uacpi_region_op op, uacpi_handle op_data);
+
+typedef uacpi_status (*uacpi_notify_handler)
+ (uacpi_handle context, uacpi_namespace_node *node, uacpi_u64 value);
+
+typedef enum uacpi_firmware_request_type {
+ UACPI_FIRMWARE_REQUEST_TYPE_BREAKPOINT,
+ UACPI_FIRMWARE_REQUEST_TYPE_FATAL,
+} uacpi_firmware_request_type;
+
+typedef struct uacpi_firmware_request {
+ uacpi_u8 type;
+
+ union {
+ // UACPI_FIRMWARE_REQUEST_BREAKPOINT
+ struct {
+ // The context of the method currently being executed
+ uacpi_handle ctx;
+ } breakpoint;
+
+ // UACPI_FIRMWARE_REQUEST_FATAL
+ struct {
+ uacpi_u8 type;
+ uacpi_u32 code;
+ uacpi_u64 arg;
+ } fatal;
+ };
+} uacpi_firmware_request;
+
+#define UACPI_INTERRUPT_NOT_HANDLED 0
+#define UACPI_INTERRUPT_HANDLED 1
+typedef uacpi_u32 uacpi_interrupt_ret;
+
+typedef uacpi_interrupt_ret (*uacpi_interrupt_handler)(uacpi_handle);
+
+#endif // !UACPI_BAREBONES_MODE
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/sys/include/dev/acpi/uacpi/uacpi/uacpi.h b/sys/include/dev/acpi/uacpi/uacpi/uacpi.h
new file mode 100644
index 0000000..a37836c
--- /dev/null
+++ b/sys/include/dev/acpi/uacpi/uacpi/uacpi.h
@@ -0,0 +1,269 @@
+#pragma once
+
+#include <uacpi/types.h>
+#include <uacpi/status.h>
+#include <uacpi/kernel_api.h>
+#include <uacpi/namespace.h>
+
+#define UACPI_MAJOR 2
+#define UACPI_MINOR 1
+#define UACPI_PATCH 1
+
+#ifdef UACPI_REDUCED_HARDWARE
+#define UACPI_MAKE_STUB_FOR_REDUCED_HARDWARE(fn, ret) \
+ UACPI_NO_UNUSED_PARAMETER_WARNINGS_BEGIN \
+ static inline fn { return ret; } \
+ UACPI_NO_UNUSED_PARAMETER_WARNINGS_END
+
+#define UACPI_STUB_IF_REDUCED_HARDWARE(fn) \
+ UACPI_MAKE_STUB_FOR_REDUCED_HARDWARE(fn,)
+#define UACPI_ALWAYS_ERROR_FOR_REDUCED_HARDWARE(fn) \
+ UACPI_MAKE_STUB_FOR_REDUCED_HARDWARE(fn, UACPI_STATUS_COMPILED_OUT)
+#define UACPI_ALWAYS_OK_FOR_REDUCED_HARDWARE(fn) \
+ UACPI_MAKE_STUB_FOR_REDUCED_HARDWARE(fn, UACPI_STATUS_OK)
+#else
+
+#define UACPI_STUB_IF_REDUCED_HARDWARE(fn) fn;
+#define UACPI_ALWAYS_ERROR_FOR_REDUCED_HARDWARE(fn) fn;
+#define UACPI_ALWAYS_OK_FOR_REDUCED_HARDWARE(fn) fn;
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Set up early access to the table subsystem. What this means is:
+ * - uacpi_table_find() and similar API becomes usable before the call to
+ * uacpi_initialize().
+ * - No kernel API besides logging and map/unmap will be invoked at this stage,
+ * allowing for heap and scheduling to still be fully offline.
+ * - The provided 'temporary_buffer' will be used as a temporary storage for the
+ * internal metadata about the tables (list, reference count, addresses,
+ * sizes, etc).
+ * - The 'temporary_buffer' is replaced with a normal heap buffer allocated via
+ * uacpi_kernel_alloc() after the call to uacpi_initialize() and can therefore
+ * be reclaimed by the kernel.
+ *
+ * The approximate overhead per table is 56 bytes, so a buffer of 4096 bytes
+ * yields about 73 tables in terms of capacity. uACPI also has an internal
+ * static buffer for tables, "UACPI_STATIC_TABLE_ARRAY_LEN", which is configured
+ * as 16 descriptors in length by default.
+ *
+ * This function is used to initialize the barebones mode, see
+ * UACPI_BAREBONES_MODE in config.h for more information.
+ */
+uacpi_status uacpi_setup_early_table_access(
+ void *temporary_buffer, uacpi_size buffer_size
+);
+
+/*
+ * Bad table checksum should be considered a fatal error
+ * (table load is fully aborted in this case)
+ */
+#define UACPI_FLAG_BAD_CSUM_FATAL (1ull << 0)
+
+/*
+ * Unexpected table signature should be considered a fatal error
+ * (table load is fully aborted in this case)
+ */
+#define UACPI_FLAG_BAD_TBL_SIGNATURE_FATAL (1ull << 1)
+
+/*
+ * Force uACPI to use RSDT even for later revisions
+ */
+#define UACPI_FLAG_BAD_XSDT (1ull << 2)
+
+/*
+ * If this is set, ACPI mode is not entered during the call to
+ * uacpi_initialize. The caller is expected to enter it later at their own
+ * discretion by using uacpi_enter_acpi_mode().
+ */
+#define UACPI_FLAG_NO_ACPI_MODE (1ull << 3)
+
+/*
+ * Don't create the \_OSI method when building the namespace.
+ * Only enable this if you're certain that having this method breaks your AML
+ * blob, a more atomic/granular interface management is available via osi.h
+ */
+#define UACPI_FLAG_NO_OSI (1ull << 4)
+
+/*
+ * Validate table checksums at installation time instead of first use.
+ * Note that this makes uACPI map the entire table at once, which not all
+ * hosts are able to handle at early init.
+ */
+#define UACPI_FLAG_PROACTIVE_TBL_CSUM (1ull << 5)
+
+#ifndef UACPI_BAREBONES_MODE
+
+/*
+ * Initializes the uACPI subsystem, iterates & records all relevant RSDT/XSDT
+ * tables. Enters ACPI mode.
+ *
+ * 'flags' is any combination of UACPI_FLAG_* above
+ */
+uacpi_status uacpi_initialize(uacpi_u64 flags);
+
+/*
+ * Parses & executes all of the DSDT/SSDT tables.
+ * Initializes the event subsystem.
+ */
+uacpi_status uacpi_namespace_load(void);
+
+/*
+ * Initializes all the necessary objects in the namespaces by calling
+ * _STA/_INI etc.
+ */
+uacpi_status uacpi_namespace_initialize(void);
+
+// Returns the current subsystem initialization level
+uacpi_init_level uacpi_get_current_init_level(void);
+
+/*
+ * Evaluate an object within the namespace and get back its value.
+ * Either root or path must be valid.
+ * A value of NULL for 'parent' implies uacpi_namespace_root() relative
+ * lookups, unless 'path' is already absolute.
+ */
+uacpi_status uacpi_eval(
+ uacpi_namespace_node *parent, const uacpi_char *path,
+ const uacpi_object_array *args, uacpi_object **ret
+);
+uacpi_status uacpi_eval_simple(
+ uacpi_namespace_node *parent, const uacpi_char *path, uacpi_object **ret
+);
+
+/*
+ * Same as uacpi_eval() but without a return value.
+ */
+uacpi_status uacpi_execute(
+ uacpi_namespace_node *parent, const uacpi_char *path,
+ const uacpi_object_array *args
+);
+uacpi_status uacpi_execute_simple(
+ uacpi_namespace_node *parent, const uacpi_char *path
+);
+
+/*
+ * Same as uacpi_eval, but the return value type is validated against
+ * the 'ret_mask'. UACPI_STATUS_TYPE_MISMATCH is returned on error.
+ */
+uacpi_status uacpi_eval_typed(
+ uacpi_namespace_node *parent, const uacpi_char *path,
+ const uacpi_object_array *args, uacpi_object_type_bits ret_mask,
+ uacpi_object **ret
+);
+uacpi_status uacpi_eval_simple_typed(
+ uacpi_namespace_node *parent, const uacpi_char *path,
+ uacpi_object_type_bits ret_mask, uacpi_object **ret
+);
+
+/*
+ * A shorthand for uacpi_eval_typed with UACPI_OBJECT_INTEGER_BIT.
+ */
+uacpi_status uacpi_eval_integer(
+ uacpi_namespace_node *parent, const uacpi_char *path,
+ const uacpi_object_array *args, uacpi_u64 *out_value
+);
+uacpi_status uacpi_eval_simple_integer(
+ uacpi_namespace_node *parent, const uacpi_char *path, uacpi_u64 *out_value
+);
+
+/*
+ * A shorthand for uacpi_eval_typed with
+ * UACPI_OBJECT_BUFFER_BIT | UACPI_OBJECT_STRING_BIT
+ *
+ * Use uacpi_object_get_string_or_buffer to retrieve the resulting buffer data.
+ */
+uacpi_status uacpi_eval_buffer_or_string(
+ uacpi_namespace_node *parent, const uacpi_char *path,
+ const uacpi_object_array *args, uacpi_object **ret
+);
+uacpi_status uacpi_eval_simple_buffer_or_string(
+ uacpi_namespace_node *parent, const uacpi_char *path, uacpi_object **ret
+);
+
+/*
+ * A shorthand for uacpi_eval_typed with UACPI_OBJECT_STRING_BIT.
+ *
+ * Use uacpi_object_get_string to retrieve the resulting buffer data.
+ */
+uacpi_status uacpi_eval_string(
+ uacpi_namespace_node *parent, const uacpi_char *path,
+ const uacpi_object_array *args, uacpi_object **ret
+);
+uacpi_status uacpi_eval_simple_string(
+ uacpi_namespace_node *parent, const uacpi_char *path, uacpi_object **ret
+);
+
+/*
+ * A shorthand for uacpi_eval_typed with UACPI_OBJECT_BUFFER_BIT.
+ *
+ * Use uacpi_object_get_buffer to retrieve the resulting buffer data.
+ */
+uacpi_status uacpi_eval_buffer(
+ uacpi_namespace_node *parent, const uacpi_char *path,
+ const uacpi_object_array *args, uacpi_object **ret
+);
+uacpi_status uacpi_eval_simple_buffer(
+ uacpi_namespace_node *parent, const uacpi_char *path, uacpi_object **ret
+);
+
+/*
+ * A shorthand for uacpi_eval_typed with UACPI_OBJECT_PACKAGE_BIT.
+ *
+ * Use uacpi_object_get_package to retrieve the resulting object array.
+ */
+uacpi_status uacpi_eval_package(
+ uacpi_namespace_node *parent, const uacpi_char *path,
+ const uacpi_object_array *args, uacpi_object **ret
+);
+uacpi_status uacpi_eval_simple_package(
+ uacpi_namespace_node *parent, const uacpi_char *path, uacpi_object **ret
+);
+
+/*
+ * Get the bitness of the currently loaded AML code according to the DSDT.
+ *
+ * Returns either 32 or 64.
+ */
+uacpi_status uacpi_get_aml_bitness(uacpi_u8 *out_bitness);
+
+/*
+ * Helpers for entering & leaving ACPI mode. Note that ACPI mode is entered
+ * automatically during the call to uacpi_initialize().
+ */
+UACPI_ALWAYS_OK_FOR_REDUCED_HARDWARE(
+ uacpi_status uacpi_enter_acpi_mode(void)
+)
+UACPI_ALWAYS_ERROR_FOR_REDUCED_HARDWARE(
+ uacpi_status uacpi_leave_acpi_mode(void)
+)
+
+/*
+ * Attempt to acquire the global lock for 'timeout' milliseconds.
+ * 0xFFFF implies infinite wait.
+ *
+ * On success, 'out_seq' is set to a unique sequence number for the current
+ * acquire transaction. This number is used for validation during release.
+ */
+uacpi_status uacpi_acquire_global_lock(uacpi_u16 timeout, uacpi_u32 *out_seq);
+uacpi_status uacpi_release_global_lock(uacpi_u32 seq);
+
+#endif // !UACPI_BAREBONES_MODE
+
+/*
+ * Reset the global uACPI state by freeing all internally allocated data
+ * structures & resetting any global variables. After this call, uACPI must be
+ * re-initialized from scratch to be used again.
+ *
+ * This is called by uACPI automatically if a fatal error occurs during a call
+ * to uacpi_initialize/uacpi_namespace_load etc. in order to prevent accidental
+ * use of partially uninitialized subsystems.
+ */
+void uacpi_state_reset(void);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/sys/include/dev/acpi/uacpi/uacpi/utilities.h b/sys/include/dev/acpi/uacpi/uacpi/utilities.h
new file mode 100644
index 0000000..dfc41c3
--- /dev/null
+++ b/sys/include/dev/acpi/uacpi/uacpi/utilities.h
@@ -0,0 +1,188 @@
+#pragma once
+
+#include <uacpi/status.h>
+#include <uacpi/types.h>
+#include <uacpi/namespace.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef UACPI_BAREBONES_MODE
+
+/*
+ * Checks whether the device at 'node' matches any of the PNP ids provided in
+ * 'list' (terminated by a UACPI_NULL). This is done by first attempting to
+ * match the value returned from _HID and then the value(s) from _CID.
+ *
+ * Note that the presence of the device (_STA) is not verified here.
+ */
+uacpi_bool uacpi_device_matches_pnp_id(
+ uacpi_namespace_node *node,
+ const uacpi_char *const *list
+);
+
+/*
+ * Find all the devices in the namespace starting at 'parent' matching the
+ * specified 'hids' (terminated by a UACPI_NULL) against any value from _HID or
+ * _CID. Only devices reported as present via _STA are checked. Any matching
+ * devices are then passed to the 'cb'.
+ */
+uacpi_status uacpi_find_devices_at(
+ uacpi_namespace_node *parent,
+ const uacpi_char *const *hids,
+ uacpi_iteration_callback cb,
+ void *user
+);
+
+/*
+ * Same as uacpi_find_devices_at, except this starts at the root and only
+ * matches one hid.
+ */
+uacpi_status uacpi_find_devices(
+ const uacpi_char *hid,
+ uacpi_iteration_callback cb,
+ void *user
+);
+
+typedef enum uacpi_interrupt_model {
+ UACPI_INTERRUPT_MODEL_PIC = 0,
+ UACPI_INTERRUPT_MODEL_IOAPIC = 1,
+ UACPI_INTERRUPT_MODEL_IOSAPIC = 2,
+} uacpi_interrupt_model;
+
+uacpi_status uacpi_set_interrupt_model(uacpi_interrupt_model);
+
+typedef struct uacpi_pci_routing_table_entry {
+ uacpi_u32 address;
+ uacpi_u32 index;
+ uacpi_namespace_node *source;
+ uacpi_u8 pin;
+} uacpi_pci_routing_table_entry;
+
+typedef struct uacpi_pci_routing_table {
+ uacpi_size num_entries;
+ uacpi_pci_routing_table_entry entries[];
+} uacpi_pci_routing_table;
+void uacpi_free_pci_routing_table(uacpi_pci_routing_table*);
+
+uacpi_status uacpi_get_pci_routing_table(
+ uacpi_namespace_node *parent, uacpi_pci_routing_table **out_table
+);
+
+typedef struct uacpi_id_string {
+ // size of the string including the null byte
+ uacpi_u32 size;
+ uacpi_char *value;
+} uacpi_id_string;
+void uacpi_free_id_string(uacpi_id_string *id);
+
+/*
+ * Evaluate a device's _HID method and get its value.
+ * The returned struture must be freed using uacpi_free_id_string.
+ */
+uacpi_status uacpi_eval_hid(uacpi_namespace_node*, uacpi_id_string **out_id);
+
+typedef struct uacpi_pnp_id_list {
+ // number of 'ids' in the list
+ uacpi_u32 num_ids;
+
+ // size of the 'ids' list including the string lengths
+ uacpi_u32 size;
+
+ // list of PNP ids
+ uacpi_id_string ids[];
+} uacpi_pnp_id_list;
+void uacpi_free_pnp_id_list(uacpi_pnp_id_list *list);
+
+/*
+ * Evaluate a device's _CID method and get its value.
+ * The returned structure must be freed using uacpi_free_pnp_id_list.
+ */
+uacpi_status uacpi_eval_cid(uacpi_namespace_node*, uacpi_pnp_id_list **out_list);
+
+/*
+ * Evaluate a device's _STA method and get its value.
+ * If this method is not found, the value of 'flags' is set to all ones.
+ */
+uacpi_status uacpi_eval_sta(uacpi_namespace_node*, uacpi_u32 *flags);
+
+/*
+ * Evaluate a device's _ADR method and get its value.
+ */
+uacpi_status uacpi_eval_adr(uacpi_namespace_node*, uacpi_u64 *out);
+
+/*
+ * Evaluate a device's _CLS method and get its value.
+ * The format of returned string is BBSSPP where:
+ * BB => Base Class (e.g. 01 => Mass Storage)
+ * SS => Sub-Class (e.g. 06 => SATA)
+ * PP => Programming Interface (e.g. 01 => AHCI)
+ * The returned struture must be freed using uacpi_free_id_string.
+ */
+uacpi_status uacpi_eval_cls(uacpi_namespace_node*, uacpi_id_string **out_id);
+
+/*
+ * Evaluate a device's _UID method and get its value.
+ * The returned struture must be freed using uacpi_free_id_string.
+ */
+uacpi_status uacpi_eval_uid(uacpi_namespace_node*, uacpi_id_string **out_uid);
+
+
+// uacpi_namespace_node_info->flags
+#define UACPI_NS_NODE_INFO_HAS_ADR (1 << 0)
+#define UACPI_NS_NODE_INFO_HAS_HID (1 << 1)
+#define UACPI_NS_NODE_INFO_HAS_UID (1 << 2)
+#define UACPI_NS_NODE_INFO_HAS_CID (1 << 3)
+#define UACPI_NS_NODE_INFO_HAS_CLS (1 << 4)
+#define UACPI_NS_NODE_INFO_HAS_SXD (1 << 5)
+#define UACPI_NS_NODE_INFO_HAS_SXW (1 << 6)
+
+typedef struct uacpi_namespace_node_info {
+ // Size of the entire structure
+ uacpi_u32 size;
+
+ // Object information
+ uacpi_object_name name;
+ uacpi_object_type type;
+ uacpi_u8 num_params;
+
+ // UACPI_NS_NODE_INFO_HAS_*
+ uacpi_u8 flags;
+
+ /*
+ * A mapping of [S1..S4] to the shallowest D state supported by the device
+ * in that S state.
+ */
+ uacpi_u8 sxd[4];
+
+ /*
+ * A mapping of [S0..S4] to the deepest D state supported by the device
+ * in that S state to be able to wake itself.
+ */
+ uacpi_u8 sxw[5];
+
+ uacpi_u64 adr;
+ uacpi_id_string hid;
+ uacpi_id_string uid;
+ uacpi_id_string cls;
+ uacpi_pnp_id_list cid;
+} uacpi_namespace_node_info;
+void uacpi_free_namespace_node_info(uacpi_namespace_node_info*);
+
+/*
+ * Retrieve information about a namespace node. This includes the attached
+ * object's type, name, number of parameters (if it's a method), the result of
+ * evaluating _ADR, _UID, _CLS, _HID, _CID, as well as _SxD and _SxW.
+ *
+ * The returned structure must be freed with uacpi_free_namespace_node_info.
+ */
+uacpi_status uacpi_get_namespace_node_info(
+ uacpi_namespace_node *node, uacpi_namespace_node_info **out_info
+);
+
+#endif // !UACPI_BAREBONES_MODE
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/sys/include/dev/cons/ansi.h b/sys/include/dev/cons/ansi.h
new file mode 100644
index 0000000..7a336d1
--- /dev/null
+++ b/sys/include/dev/cons/ansi.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _CONS_ANSI_H_
+#define _CONS_ANSI_H_
+
+#include <sys/types.h>
+#include <sys/cdefs.h>
+#include <sys/param.h>
+
+/* ANSI colors */
+#define ANSI_BLACK 0x000000
+#define ANSI_RED 0xAA0000
+#define ANSI_GREEN 0x00AA00
+#define ANSI_BLUE 0x00007F
+#define ANSI_YELLOW 0xAA5500
+#define ANSI_MAGENTA 0xAA00AA
+#define ANSI_CYAN 0x00AAAA
+#define ANSI_WHITE 0xAAAAAA
+
+/* ANSI_FEED update codes */
+#define ANSI_UPDATE_COLOR -1
+#define ANSI_UPDATE_CURSOR -2
+
+/*
+ * ANSI parser state machine.
+ *
+ * @prev: Previous char
+ * @csi: Encountered control seq introducer
+ * @reset_color: 1 if color is to be reset
+ * @set_fg: 1 if fg is being set
+ * @set_bg: 1 if bg is being set
+ * @fg: Foreground color
+ * @bg: Background color
+ * @flags: State flags
+ */
+struct ansi_state {
+ char prev;
+ uint8_t csi : 2;
+ uint8_t reset_color : 1;
+ uint8_t set_fg : 1;
+ uint8_t set_bg : 1;
+ uint32_t fg;
+ uint32_t bg;
+};
+
+int ansi_feed(struct ansi_state *statep, char c);
+
+#endif /* !_CONS_ANSI_H_ */
diff --git a/sys/include/dev/cons/cons.h b/sys/include/dev/cons/cons.h
index 3569c52..7c4e41a 100644
--- a/sys/include/dev/cons/cons.h
+++ b/sys/include/dev/cons/cons.h
@@ -32,8 +32,12 @@
#include <sys/types.h>
#include <sys/spinlock.h>
+#include <sys/proc.h>
+#include <sys/mutex.h>
+#include <sys/console.h>
#include <dev/video/fbdev.h>
#include <dev/cons/consvar.h>
+#include <dev/cons/ansi.h>
struct cons_char {
char c;
@@ -45,6 +49,11 @@ struct cons_char {
struct cons_screen {
struct fbdev fbdev;
+ struct ansi_state ansi_s;
+ struct console_feat feat; /* Features */
+ struct console_attr attr; /* Attributes */
+ struct proc *atproc; /* Attached proc */
+ struct mutex *atproc_lock;
uint32_t fg;
uint32_t bg;
@@ -54,17 +63,25 @@ struct cons_screen {
uint32_t ncols;
uint32_t ch_col; /* Current col */
uint32_t ch_row; /* Current row */
- uint32_t curs_col; /* Cursor col */
- uint32_t curs_row; /* Cursor row */
struct cons_buf *ib; /* Input buffer */
struct cons_buf **ob; /* Output buffers */
struct cons_char last_chr;
struct spinlock lock;
};
+#define curs_col attr.cursor_x
+#define curs_row attr.cursor_y
+
void cons_init(void);
void cons_expose(void);
+void cons_update_color(struct cons_screen *scr, uint32_t fg, uint32_t bg);
+void cons_clear_scr(struct cons_screen *scr, uint32_t bg);
+void cons_reset_color(struct cons_screen *scr);
+void cons_reset_cursor(struct cons_screen *scr);
+int cons_attach(void);
+int cons_detach(void);
int cons_putch(struct cons_screen *scr, char c);
+int cons_putstr(struct cons_screen *scr, const char *s, size_t len);
extern struct cons_screen g_root_scr;
diff --git a/sys/include/dev/cons/consvar.h b/sys/include/dev/cons/consvar.h
index 483d5f1..253176b 100644
--- a/sys/include/dev/cons/consvar.h
+++ b/sys/include/dev/cons/consvar.h
@@ -32,6 +32,7 @@
#include <sys/types.h>
#include <sys/param.h>
+#include <sys/spinlock.h>
/* Buffer types */
#define CONS_BUF_INPUT 0
@@ -62,6 +63,7 @@ struct cons_input {
* keyboard input or console output.
*/
struct cons_buf {
+ struct spinlock lock;
union {
struct cons_input *ibuf;
struct cons_char *obuf;
diff --git a/sys/include/dev/dmi/dmi.h b/sys/include/dev/dmi/dmi.h
new file mode 100644
index 0000000..8b7030c
--- /dev/null
+++ b/sys/include/dev/dmi/dmi.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _DMI_DMI_H_
+#define _DMI_DMI_H_
+
+#include <sys/types.h>
+
+const char *dmi_vendor(void);
+const char *dmi_prodver(void);
+const char *dmi_prodfam(void);
+const char *dmi_product(void);
+const char *dmi_cpu_manufact(void);
+const char *dmi_cpu_version(void);
+
+#endif /* !_DMI_DMI_H_ */
diff --git a/sys/include/dev/dmi/dmivar.h b/sys/include/dev/dmi/dmivar.h
new file mode 100644
index 0000000..e5da92f
--- /dev/null
+++ b/sys/include/dev/dmi/dmivar.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _DEV_DMIVAR_H_
+#define _DEV_DMIVAR_H_
+
+#include <sys/types.h>
+#include <sys/sio.h>
+#include <fs/ctlfs.h>
+
+extern struct ctlops g_ctl_board_ident;
+
+int dmi_board_ctl_read(struct ctlfs_dev *cdp, struct sio_txn *sio);
+
+#endif /* !_DEV_DMIVAR_H_ */
diff --git a/sys/include/dev/ic/ahciregs.h b/sys/include/dev/ic/ahciregs.h
index 4a4dc65..232b41e 100644
--- a/sys/include/dev/ic/ahciregs.h
+++ b/sys/include/dev/ic/ahciregs.h
@@ -34,8 +34,10 @@
#include <sys/param.h>
struct hba_port {
- volatile uint64_t clb; /* Command list base (1k-byte aligned) */
- volatile uint64_t fb; /* FIS base (256-byte aligned) */
+ volatile uint32_t clb; /* Command list base low (1k-byte aligned) */
+ volatile uint32_t clbu; /* Command list base upper */
+ volatile uint32_t fb; /* FIS base (256-byte aligned) */
+ volatile uint32_t fbu; /* FIS base upper */
volatile uint32_t is; /* Interrupt status */
volatile uint32_t ie; /* Interrupt enable */
volatile uint32_t cmd; /* Command and status */
@@ -86,6 +88,7 @@ struct hba_memspace {
*/
#define AHCI_PXSSTS_DET(SSTS) (SSTS & 0xF)
#define AHCI_PXSSTS_IPM(SSTS) ((SSTS >> 8) & 0xF)
+#define AHCI_PXSSTS_SPD(SSTS) ((SSTS >> 4) & 0xF)
/*
* Port SATA control bits
@@ -98,6 +101,7 @@ struct hba_memspace {
* See section 3.3.7 of the AHCI spec.
*/
#define AHCI_PXCMD_ST BIT(0) /* Start */
+#define AHCI_PXCMD_SUD BIT(1) /* Spin-up device */
#define AHCI_PXCMD_FRE BIT(4) /* FIS Receive Enable */
#define AHCI_PXCMD_FR BIT(14) /* FIS Receive Running */
#define AHCI_PXCMD_CR BIT(15) /* Command List Running */
@@ -122,6 +126,9 @@ struct hba_memspace {
*/
#define AHCI_CAP_NP(CAP) (CAP & 0x1F) /* Number of ports */
#define AHCI_CAP_NCS(CAP) ((CAP >> 8) & 0x1F) /* Number of command slots */
+#define AHCI_CAP_EMS(CAP) ((CAP >> 6) & 1) /* Enclosure management support */
+#define AHCI_CAP_SAL(CAP) ((CAP >> 25) & 1) /* Supports activity LED */
+#define AHCI_CAP_SSS(CAP) ((CAP >> 27) & 1) /* Supports staggered spin up */
/*
* Device detection (DET) and Interface power
@@ -132,6 +139,31 @@ struct hba_memspace {
#define AHCI_DET_PRESENT 1 /* Device present (no PHY comm) */
#define AHCI_DET_COMM 3 /* Device present and phy comm established */
#define AHCI_IPM_ACTIVE 1
+#define AHCI_SPD_GEN1 1 /* 1.5 Gb/s */
+#define AHCI_SPD_GEN2 2 /* 3 Gb/s */
+#define AHCI_SPD_GEN3 3 /* 6 Gb/s */
+
+/*
+ * PxSERR bits
+ * See section 3.3.12 of the AHCI spec
+ */
+#define AHCI_SERR_I BIT(0) /* Recovered data integrity error */
+#define AHCI_SERR_M BIT(1) /* Recovered comms error */
+#define AHCI_SERR_T BIT(8) /* Transient data integrity error */
+#define AHCI_SERR_C BIT(9) /* Persistent comms error */
+#define AHCI_SERR_P BIT(10) /* Protocol error ("oh fuck!" bit) */
+#define AHCI_SERR_E BIT(11) /* Internal error (only God knows, just pray) */
+#define AHCI_DIAG_N BIT(16) /* PhyRdy change */
+#define AHCI_DIAG_I BIT(17) /* PHY internal error */
+#define AHCI_DIAG_W BIT(18) /* Comm wake */
+#define AHCI_DIAG_B BIT(19) /* 10B to 8B decode error */
+#define AHCI_DIAG_C BIT(21) /* CRC error */
+#define AHCI_DIAG_H BIT(22) /* Handshake error */
+#define AHCI_DIAG_S BIT(23) /* Link sequence error */
+#define AHCI_DIAG_T BIT(24) /* Transport state transition error */
+#define AHCI_DIAG_F BIT(25) /* Unknown FIS type */
+
+#define ATAPI_SIG 0xEB140101
/*
* Device detection initialization values
diff --git a/sys/include/dev/ic/ahcivar.h b/sys/include/dev/ic/ahcivar.h
index 0d307cd..67f2efe 100644
--- a/sys/include/dev/ic/ahcivar.h
+++ b/sys/include/dev/ic/ahcivar.h
@@ -30,12 +30,210 @@
#ifndef _IC_AHCIVAR_H_
#define _IC_AHCIVAR_H_
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/device.h>
+#include <dev/dcdr/cache.h>
#include <dev/ic/ahciregs.h>
+#include <fs/ctlfs.h>
+#define AHCI_DCDR_CAP 16
+
+struct ahci_cmd_hdr;
+extern const struct ctlops g_sata_bsize_ops;
+
+struct ata_identity {
+ uint16_t rsvd0 : 1;
+ uint16_t unused0 : 1;
+ uint16_t incomplete : 1;
+ uint16_t unused1 : 3;
+ uint16_t fixed_dev : 1;
+ uint16_t removable : 1;
+ uint16_t unused2 : 7;
+ uint16_t device_type : 1;
+ uint16_t ncylinders;
+ uint16_t specific_config;
+ uint16_t nheads;
+ uint16_t unused3[2];
+ uint16_t sectors_per_track;
+ uint16_t vendor[3];
+ char serial_number[20];
+ uint16_t unused4[2];
+ uint16_t unused5;
+ char firmware_rev[8];
+ char model_number[40];
+ char pad[256];
+};
+
+/*
+ * AHCI Host Bus Adapter
+ *
+ * @io: HBA MMIO
+ * @maxports: Max number of HBA ports
+ * @nports: Number of implemented HBA ports.
+ * @nslots: Number of command slots
+ * @ems: Enclosure management support
+ * @sal: Supports activity LED
+ * @sss: Supports staggered spin up
+ */
struct ahci_hba {
struct hba_memspace *io;
+ uint32_t maxports;
+ uint32_t nports;
+ uint32_t nslots;
+ uint8_t ems : 1;
+ uint8_t sal : 1;
+ uint8_t sss : 1;
+ devmajor_t major;
+};
+
+/*
+ * A device attached to a physical HBA port.
+ *
+ * [d]: Dynalloc'd memory
+ * [p]: Paged memory (allocated pageframe)
+ *
+ * @io: Memory mapped port registers
+ * @hba: HBA descriptor
+ * @cmdlist: Command list [p]
+ * @nlba: Max number of addressable blocks
+ * @fra: FIS receive area [p]
+ * @dev: Device minor number.
+ */
+struct hba_device {
+ struct hba_port *io;
+ struct ahci_hba *hba;
+ struct ahci_cmd_hdr *cmdlist;
+ struct dcdr *dcdr;
+ uint32_t nlba;
+ void *fra;
+ dev_t dev;
+};
+
+/*
+ * Command header
+ *
+ * @cfl: Command FIS length
+ * @a: ATAPI
+ * @w: Write
+ * @p: Prefetchable
+ * @r: Reset
+ * @c: Clear busy upon R_OK
+ * @rsvd0: Reserved
+ * @pmp: Port multiplier port
+ * @prdtl: PRDT length (in entries)
+ * @prdbc: PRDT bytes transferred count
+ * @ctba: Command table descriptor base addr
+ * @rsvd1: Reserved
+ */
+struct ahci_cmd_hdr {
+ uint8_t cfl : 5;
+ uint8_t a : 1;
+ uint8_t w : 1;
+ uint8_t p : 1;
+ uint8_t r : 1;
+ uint8_t c : 1;
+ uint8_t rsvd0 : 1;
+ uint8_t pmp : 4;
+ uint16_t prdtl;
+ volatile uint32_t prdbc;
+ uintptr_t ctba;
+ uint32_t rsvd1[4];
+};
+
+/*
+ * Physical region descriptor
+ *
+ * @dba: Data base address
+ * @rsvd0: Reserved
+ * @dbc: Count
+ * @rsvd1: Reserved
+ * @i: Interrupt on completion
+ */
+struct ahci_prdt_entry {
+ uintptr_t dba;
+ uint32_t rsvd0;
+ uint32_t dbc : 22;
+ uint16_t rsvd1 : 9;
+ uint8_t i : 1;
+};
+
+/*
+ * Command table
+ *
+ * @cfis: Command FIS
+ * @acmd: ATAPI command
+ * @rsvd: Reserved
+ * @prdt: Physical region descriptors
+ */
+struct ahci_cmdtab {
+ uint8_t cfis[64];
+ uint8_t acmd[16];
+ uint8_t rsvd[48];
+ struct ahci_prdt_entry prdt[1];
+};
+
+/*
+ * Host to device FIS
+ *
+ * [h]: Set by host
+ * [d]: Set by device
+ * [srb]: Shadow register block
+ *
+ * @type: Must be 0x27 for H2D [h]
+ * @pmp: Port multiplier port [h]
+ * @c: Set to denote command FIS [h]
+ * @command: Command type [h/srb]
+ * @feature1: Features register (7:0) [h/srb]
+ * @lba0: LBA low [h/srb]
+ * @lba1: LBA mid [h/srb]
+ * @lba2: LBA hi [h/srb]
+ * @device: Set bit 7 for LBA [h/srb]
+ * @lba3: LBA (31:24) [h/srb]
+ * @lba4: LBA (39:32) [h/srb]
+ * @lba5: LBA (47:40) [h/srb]
+ * @featureh: Features high [h/srb]
+ * @countl: Count low (block aligned) [h/srb]
+ * @counth: Count high (block aligned) [h/srb]
+ */
+struct ahci_fis_h2d {
+ uint8_t type;
+ uint8_t pmp : 4;
+ uint8_t rsvd0 : 3;
+ uint8_t c : 1;
+ uint8_t command;
+ uint8_t featurel;
+ uint8_t lba0;
+ uint8_t lba1;
+ uint8_t lba2;
+ uint8_t device;
+ uint8_t lba3;
+ uint8_t lba4;
+ uint8_t lba5;
+ uint8_t featureh;
+ uint8_t countl;
+ uint8_t counth;
+ uint8_t icc;
+ uint8_t control;
+ uint8_t rsvd1[4];
};
#define AHCI_TIMEOUT 500 /* In ms */
+/* AHCI size constants */
+#define AHCI_FIS_SIZE 256
+#define AHCI_CMDTAB_SIZE 256
+#define AHCI_CMDENTRY_SIZE 32
+#define AHCI_SECTOR_SIZE 512
+
+/* AHCI FIS types */
+#define FIS_TYPE_H2D 0x27
+#define FIS_TYPE_D2H 0x34
+
+/* ATA commands */
+#define ATA_CMD_NOP 0x00
+#define ATA_CMD_IDENTIFY 0xEC
+#define ATA_CMD_READ_DMA 0x25
+#define ATA_CMD_WRITE_DMA 0x35
+
#endif /* !_IC_AHCIVAR_H_ */
diff --git a/sys/include/dev/ic/nvmevar.h b/sys/include/dev/ic/nvmevar.h
index eab8b52..8dbc7b9 100644
--- a/sys/include/dev/ic/nvmevar.h
+++ b/sys/include/dev/ic/nvmevar.h
@@ -31,9 +31,11 @@
#define _IC_NVMEVAR_H_
#include <sys/types.h>
+#include <sys/cdefs.h>
/* Admin commands */
#define NVME_OP_CREATE_IOSQ 0x01
+#define NVME_OP_GET_LOGPAGE 0x02
#define NVME_OP_CREATE_IOCQ 0x05
#define NVME_OP_IDENTIFY 0x06
@@ -45,6 +47,67 @@
#define NVME_OP_WRITE 0x01
#define NVME_OP_READ 0x02
+/* Log page identifiers */
+#define NVME_LOGPAGE_SMART 0x02
+
+/*
+ * S.M.A.R.T health / information log
+ *
+ * See section 5.16.1.3, figure 207 of the
+ * NVMe base spec (rev 2.0a)
+ *
+ * @cwarn: Critical warning
+ * @temp: Composite tempature (kelvin)
+ * @avail_spare: Available spare (in percentage)
+ * @avail_spare_thr: Available spare threshold
+ * @percent_used: Estimate NVMe life used percentage
+ * @end_cwarn: Endurance group critical warning summary
+ * @data_units_read: Number of 512 byte data units read
+ * @data_units_written: Number of 512 byte data units written
+ * @host_reads: Number of host read commands completed
+ * @host_writes: Number of host write commands completed
+ * @ctrl_busy_time: Controller busy time
+ * @power_cycles: Number of power cycles
+ * @power_on_hours: Number of power on hours
+ * @unsafe_shutdowns: Number of unsafe shutdowns
+ * @media_errors: Media and data integrity errors
+ * @n_errlog_entries: Number of error log info entries
+ * @warning_temp_time: Warning composite tempature time
+ * @critical_comp_time: Critical composite tempature time
+ * @temp_sensor: Tempature sensor <n> data
+ * @temp1_trans_cnt: Tempature 1 transition count
+ * @temp2_trans_cnt: Tempature 2 transition count
+ * @temp1_total_time: Total time for tempature 1
+ * @temp2_total_time: Total time for tempature 2
+ */
+struct __packed nvme_smart_data {
+ uint8_t cwarn;
+ uint16_t temp;
+ uint8_t avail_spare;
+ uint8_t avail_spare_thr;
+ uint8_t percent_used;
+ uint8_t end_cwarn;
+ uint8_t reserved[25];
+ uint8_t data_units_read[16];
+ uint8_t data_units_written[16];
+ uint8_t host_reads[16];
+ uint8_t host_writes[16];
+ uint8_t ctrl_busy_time[16];
+ uint8_t power_cycles[16];
+ uint8_t power_on_hours[16];
+ uint8_t unsafe_shutdowns[16];
+ uint8_t media_errors[16];
+ uint8_t n_errlog_entries[16];
+ uint32_t warning_temp_time;
+ uint32_t critical_comp_time;
+ uint16_t temp_sensor[8];
+ uint32_t temp1_trans_cnt;
+ uint32_t temp2_trans_cnt;
+ uint32_t temp1_total_time;
+ uint32_t temp2_total_time;
+ uint8_t reserved1[280];
+};
+
struct nvme_identify_cmd {
uint8_t opcode;
uint8_t flags;
@@ -98,6 +161,26 @@ struct nvme_create_iosq_cmd {
uint64_t unused3[2];
};
+/* Get log page */
+struct nvme_get_logpage_cmd {
+ uint8_t opcode;
+ uint8_t flags;
+ uint16_t cid;
+ uint32_t nsid;
+ uint64_t unused[2];
+ uint64_t prp1;
+ uint64_t prp2;
+ uint8_t lid;
+ uint8_t lsp;
+ uint16_t numdl;
+ uint16_t numdu;
+ uint16_t lsi;
+ uint64_t lpo;
+ uint8_t unused1[3];
+ uint8_t csi;
+ uint32_t unused2;
+};
+
/* Read/write */
struct nvme_rw_cmd {
uint8_t opcode;
@@ -123,6 +206,7 @@ struct nvme_cmd {
struct nvme_identify_cmd identify;
struct nvme_create_iocq_cmd create_iocq;
struct nvme_create_iosq_cmd create_iosq;
+ struct nvme_get_logpage_cmd get_logpage;
struct nvme_rw_cmd rw;
};
};
diff --git a/sys/include/dev/mii/mii.h b/sys/include/dev/mii/mii.h
new file mode 100644
index 0000000..5d77281
--- /dev/null
+++ b/sys/include/dev/mii/mii.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _DEV_MII_H_
+#define _DEV_MII_H_
+
+#include <sys/param.h>
+
+/*
+ * MII registers
+ */
+#define MII_BMCR 0x00 /* Basic Mode Config */
+#define MII_BMSR 0x01 /* Basic Mode Status */
+#define MII_PHYID 0x02 /* MII PHY identifier 1 */
+#define MII_PHYID2 0x03 /* MII PHY identifier 2 */
+#define MII_ADVER 0x04 /* Auto-negotiation advertisement */
+#define MII_LPA 0x05 /* Link parter abilities */
+#define MII_EXPAN 0x06 /* Auto-negotiation expansion */
+#define MII_ESTATUS 0x0F /* Extended status register */
+#define MII_IRQ 0x1B /* Interrupt control/status */
+
+/*
+ * MII BMCR bits
+ */
+#define MII_BMCR_RST BIT(15) /* PHY reset */
+#define MII_BCMR_LOOP BIT(14) /* Loopback mode enable */
+#define MII_BMCR_ANEN BIT(12) /* Auto-negotiation enable */
+#define MII_PWR_DOWN BIT(11) /* Power down PHY */
+#define MII_ISOLATE BIT(10) /* Electrically isolate PHY from MII */
+
+#endif /* !_DEV_MII_H_ */
diff --git a/sys/include/dev/pci/pci.h b/sys/include/dev/pci/pci.h
index 4bfacdd..144b500 100644
--- a/sys/include/dev/pci/pci.h
+++ b/sys/include/dev/pci/pci.h
@@ -54,6 +54,7 @@ struct pci_device {
uint8_t slot;
uint8_t func;
+ uint16_t segment;
uint16_t msix_capoff;
uint16_t device_id;
uint16_t vendor_id;
@@ -61,6 +62,7 @@ struct pci_device {
uint8_t pci_subclass;
uint8_t prog_if;
uint8_t hdr_type;
+ uint8_t pci_express : 1;
uint8_t pri_bus;
uint8_t sec_bus;
@@ -74,7 +76,7 @@ struct pci_device {
struct msi_intr {
const char *name;
- void(*handler)(void *);
+ int(*handler)(void *);
};
pcireg_t pci_readl(struct pci_device *dev, uint32_t offset);
@@ -84,6 +86,8 @@ int pci_map_bar(struct pci_device *dev, uint8_t barno, void **vap);
void pci_writel(struct pci_device *dev, uint32_t offset, pcireg_t val);
int pci_enable_msix(struct pci_device *dev, const struct msi_intr *intr);
+void pci_add_device(struct pci_device *dev);
+
void pci_msix_eoi(void);
int pci_init(void);
diff --git a/sys/include/dev/phy/e1000regs.h b/sys/include/dev/phy/e1000regs.h
new file mode 100644
index 0000000..7caceee
--- /dev/null
+++ b/sys/include/dev/phy/e1000regs.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _PHY_E1000_REGS_H_
+#define _PHY_E1000_REGS_H_
+
+#include <sys/types.h>
+#include <sys/param.h>
+
+/*
+ * E1000 register offsets
+ *
+ * XXX: Notes about reserve fields:
+ *
+ * - The `EERD' register is reserved and should NOT be touched
+ * for the 82544GC/EI card.
+ *
+ * - The `FLA' register is only usable for the 82541xx and
+ * 82547GI/EI cards, this is reserved and should NOT be
+ * touched on any other cards.
+ *
+ * - The `TXCW' and `RXCW' registers are reserved and should NOT
+ * be touched for the 82540EP/EM, 82541xx and 82547GI/EI cards.
+ *
+ * - The `LEDCTL' register is reserved and should NOT be touched
+ * for the 82544GC/EI card.
+ */
+#define E1000_CTL 0x00000 /* Control register */
+#define E1000_STATUS 0x00008 /* Status register */
+#define E1000_EECD 0x00010 /* EEPROM/flash control and data register */
+#define E1000_EERD 0x00014 /* EEPROM/flash read register */
+#define E1000_FLA 0x0001C /* EEPROM/flash read register */
+#define E1000_CTRL_EXT 0x00018 /* Extended device control register */
+#define E1000_MDIC 0x00020 /* PHY management data interface control register */
+#define E1000_FCAL 0x00028 /* Flow control low register */
+#define E1000_FCAH 0x0002C /* Flow control high register */
+#define E1000_FCT 0x00030 /* Flow control type register */
+#define E1000_VET 0x00038 /* VLAN ethertype register */
+#define E1000_FCTTV 0x00170 /* Flow control transmit timer value register */
+#define E1000_TXCW 0x00178 /* Transmit config word register */
+#define E1000_RXCW 0x00180 /* Receive config word register */
+#define E1000_LEDCTL 0x00E00 /* LED control register */
+
+/*
+ * Device control register (`ctl') bits
+ *
+ * See section 13.4.1 of the PCI/PCI-X Intel Gigabit
+ * Ethernet Controllers spec
+ *
+ * XXX: Notes about reserved bits:
+ *
+ * - The CTL.LRST bit is reserved and should NOT be touched
+ * for the 82540EP/EM, 82541xx, or 82547GI/EI cards.
+ */
+#define E1000_CTL_FD BIT(0) /* Full-duplex */
+#define E1000_CTL_LRST BIT(3) /* Link-reset */
+#define E1000_CTL_RST BIT(26) /* Device reset */
+
+/*
+ * EEPROM/flash control and data register (`eecd')
+ * bits
+ *
+ * See section 13.4.3 of the PCI/PCI-X Intel Gigabit
+ * Ethernet controller spec
+ */
+#define E1000_EECD_SK BIT(0) /* EEPROM clock input */
+#define E1000_EECD_CS BIT(1) /* EEPROM chip select */
+#define E1000_EECD_DI BIT(2) /* EEPROM data input */
+#define E1000_EECD_DO BIT(3) /* EEPROM data output */
+#define E1000_EECD_FWE BIT(4) /* EEPROM flash write enable ctl (4:5) */
+#define E1000_EECD_REQ BIT(6) /* Request EEPROM access */
+#define E1000_EECD_GNT BIT(7) /* Grant EEPROM access */
+#define E1000_EECD_PRES BIT(8) /* EEPROM present */
+#define E1000_EECD_SIZE BIT(9) /* EEPROM size (1024-bit [0], 4096-bit [1]) */
+#define E1000_EECD_TYPE BIT(13) /* EEPROM type (microwire [0], SPI [1]) */
+
+/*
+ * EEPROM read (`eerd') register bits
+ *
+ * See section 13.4.4 of the PCI/PCI-X Intel Gigabit
+ * Ethernet controller spec
+ */
+#define E1000_EERD_START BIT(0) /* Start read */
+#define E1000_EERD_DONE BIT(4) /* EEPROM read finished */
+
+/*
+ * EEPROM word addresses
+ */
+#define E1000_HWADDR0 0x00 /* Word 0 */
+#define E1000_HWADDR1 0x01 /* Word 1 */
+#define E1000_HWADDR2 0x02 /* Word 2 */
+
+#endif /* !_PHY_E1000_REGS_H_ */
diff --git a/sys/include/dev/phy/et131xregs.h b/sys/include/dev/phy/et131xregs.h
new file mode 100644
index 0000000..1f8bfcb
--- /dev/null
+++ b/sys/include/dev/phy/et131xregs.h
@@ -0,0 +1,275 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Please refer to share/docs/hw/et131x.txt
+ */
+
+#ifndef _PHYS_ET131XREGS_H_
+#define _PHYS_ET131XREGS_H_
+
+#include <sys/types.h>
+
+#define MAC_CFG1_SOFTRST 0x80000000 /* Soft reset */
+#define MAC_CFG1_SIMRST 0x40000000 /* SIM reset */
+#define MAC_CFG1_RESET_RXMC 0x00080000 /* RX MC reset */
+#define MAC_CFG1_RESET_TXMC 0x00040000 /* TX MC reset */
+#define MAC_CFG1_RESET_RXFUNC 0x00020000 /* RX func reset */
+#define MAC_CFG1_RESET_TXFUNC 0x00010000 /* TX func reset */
+
+#define PAD_N(N, NAME) uint8_t (NAME)[(N)]
+
+/*
+ * ET131X global registers
+ */
+struct global_regs {
+ uint32_t txq_start;
+ uint32_t txq_end;
+ uint32_t rxq_start;
+ uint32_t rxq_end;
+ uint32_t pm_csr;
+ uint32_t unused;
+ uint32_t istat;
+ uint32_t imask;
+ uint32_t ialias_clr_en;
+ uint32_t istat_alias;
+ uint32_t sw_reset;
+ uint32_t slv_timer;
+ uint32_t msi_config;
+ uint32_t loopback;
+ uint32_t watchdog_timer;
+};
+
+/*
+ * ET131X TX DMA registers
+ */
+struct txdma_regs {
+ uint32_t csr;
+ uint32_t pr_base_hi;
+ uint32_t pr_base_lo;
+ uint32_t pr_num_des;
+ uint32_t txq_wr_addr;
+ uint32_t txq_wr_addr_ext;
+ uint32_t txq_rd_addr;
+ uint32_t dma_wb_base_hi;
+ uint32_t dma_wb_base_lo;
+ uint32_t service_request;
+ uint32_t service_complete;
+ uint32_t cache_rd_index;
+ uint32_t cache_wr_index;
+ uint32_t tx_dma_error;
+ uint32_t des_abort_cnt;
+ uint32_t payload_abort_cnt;
+ uint32_t wb_abort_cnt;
+ uint32_t des_timeout_cnt;
+ uint32_t payload_timeout_cnt;
+ uint32_t wb_timeout_cnt;
+ uint32_t des_error_cnt;
+ uint32_t payload_err_cnt;
+ uint32_t wb_error_cnt;
+ uint32_t dropped_tlp_cnt;
+ uint32_t new_service_complete;
+ uint32_t ether_pkt_cnt;
+};
+
+/*
+ * ET131X RX DMA registers
+ */
+struct rxdma_regs {
+ uint32_t csr;
+ uint32_t dma_wb_base_lo;
+ uint32_t dma_wb_base_hi;
+ uint32_t num_pkt_done;
+ uint32_t max_pkt_time;
+ uint32_t rxq_rd_addr;
+ uint32_t rxq_rd_addr_ext;
+ uint32_t rxq_wr_addr;
+ uint32_t psr_base_lo;
+ uint32_t psr_base_hi;
+ uint32_t psr_num_des;
+ uint32_t psr_avail_offset;
+ uint32_t psr_full_offset;
+ uint32_t psr_access_index;
+ uint32_t psr_min_des;
+ uint32_t fbr0_base_lo;
+ uint32_t fbr0_base_hi;
+ uint32_t fbr0_num_des;
+ uint32_t fbr0_avail_offset;
+ uint32_t fbr0_full_offset;
+ uint32_t fbr0_rd_index;
+ uint32_t fbr0_min_des;
+ uint32_t fbr1_base_lo;
+ uint32_t fbr1_base_hi;
+ uint32_t fbr1_num_des;
+ uint32_t fbr1_avail_offset;
+ uint32_t fbr1_full_offset;
+ uint32_t fbr1_rd_index;
+ uint32_t fbr1_min_des;
+};
+
+/*
+ * ET131X TX MAC registers
+ */
+struct txmac_regs {
+ uint32_t ctl;
+ uint32_t shadow_ptr;
+ uint32_t err_cnt;
+ uint32_t max_fill;
+ uint32_t cf_param;
+ uint32_t tx_test;
+ uint32_t err;
+ uint32_t err_int;
+ uint32_t bp_ctrl;
+};
+
+/*
+ * ET131X RX MAC registers
+ */
+struct rxmac_regs {
+ uint32_t ctrl;
+ uint32_t crc0;
+ uint32_t crc12;
+ uint32_t crc34;
+ uint32_t sa_lo;
+ uint32_t sa_hi;
+ uint32_t mask0_word0;
+ uint32_t mask0_word1;
+ uint32_t mask0_word2;
+ uint32_t mask0_word3;
+ uint32_t mask1_word0;
+ uint32_t mask1_word1;
+ uint32_t mask1_word2;
+ uint32_t mask1_word3;
+ uint32_t mask2_word0;
+ uint32_t mask2_word1;
+ uint32_t mask2_word2;
+ uint32_t mask2_word3;
+ uint32_t mask3_word0;
+ uint32_t mask3_word1;
+ uint32_t mask3_word2;
+ uint32_t mask3_word3;
+ uint32_t mask4_word0;
+ uint32_t mask4_word1;
+ uint32_t mask4_word2;
+ uint32_t mask4_word3;
+ uint32_t uni_pf_addr1;
+ uint32_t uni_pf_addr2;
+ uint32_t uni_pf_addr3;
+ uint32_t multi_hash1;
+ uint32_t multi_hash2;
+ uint32_t multi_hash3;
+ uint32_t multi_hash4;
+ uint32_t pf_ctrl;
+ uint32_t mcif_ctrl_max_seg;
+ uint32_t mcif_water_mark;
+ uint32_t rxq_diag;
+ uint32_t space_avail;
+ uint32_t mif_ctrl;
+ uint32_t err_reg;
+};
+
+struct mac_regs {
+ uint32_t cfg1;
+ uint32_t cfg2;
+ uint32_t ipg;
+ uint32_t hfdp;
+ uint32_t max_fm_len;
+ uint32_t rsv1;
+ uint32_t rsv2;
+ uint32_t mac_test;
+ uint32_t mii_mgmt_cfg;
+ uint32_t mii_mgmt_cmd;
+ uint32_t mii_mgmt_addr;
+ uint32_t mii_mgmt_ctrl;
+ uint32_t mii_mgmt_stat;
+ uint32_t mii_mgmt_indicator;
+ uint32_t if_ctrl;
+ uint32_t if_stat;
+ uint32_t station_addr_1;
+ uint32_t station_addr_2;
+};
+
+/* Global reset */
+#define GBL_RESET_ALL 0x007F
+
+/* MII management address */
+#define MAC_MII_ADDR(PHY, REG) ((PHY) << 8 | (REG))
+
+/* MAC management indications */
+#define MAC_MGMT_BUSY 0x00000001
+#define MAC_MGMT_WAIT 0x00000005
+
+/* MAC management config values */
+#define MAC_MIIMGMT_CLK_RST 0x00007
+
+/* LED register defines */
+#define PHY_LED2 0x1C
+
+/* PCI config space offsets */
+#define PCI_EEPROM_STATUS 0xB2
+#define PCI_MAC_ADDRESS 0xA4
+
+/*
+ * LED control register 2 values
+ */
+#define LED_BLINK 0xD
+#define LED_ON 0xE
+#define LED_OFF 0xF
+#define LED_ALL_OFF 0xFFFF
+
+/*
+ * LED register bit-shift constants
+ *
+ * Bits [3:0]: 100BASE-T LED
+ * Bits [7:4]: 100BASE-TX LED
+ * Bits [11:8]: TX/RX LED
+ * Bits [15:12]: Link LED
+ */
+#define LED_TXRX_SHIFT 8
+#define LED_LINK_SHIFT 12
+
+struct et131x_iospace {
+#define _IO_PAD(NAME, REGSET) uint8_t NAME[4096 - sizeof(struct REGSET)]
+ struct global_regs global;
+ _IO_PAD(global_pad, global_regs);
+ struct txdma_regs txdma;
+ _IO_PAD(txdma_pad, txdma_regs);
+ struct rxdma_regs rxdma;
+ _IO_PAD(rxdma_pad, rxdma_regs);
+ struct txmac_regs txmac;
+ _IO_PAD(txmac_pad, txmac_regs);
+ struct rxmac_regs rxmac;
+ _IO_PAD(rxmac_pad, rxmac_regs);
+ struct mac_regs mac;
+ _IO_PAD(mac_pad, mac_regs);
+ /* ... TODO - add more */
+#undef _IO_PAD
+};
+
+#endif /* !_PHYS_ET131XREGS_H_ */
diff --git a/sys/include/dev/phy/rt8139.h b/sys/include/dev/phy/rtl.h
index ef7b127..f3178d0 100644
--- a/sys/include/dev/phy/rt8139.h
+++ b/sys/include/dev/phy/rtl.h
@@ -33,7 +33,14 @@
#include <sys/types.h>
#include <sys/param.h>
-#define RT_IDR0 0x00 /* MAC address */
+/* MAC address */
+#define RT_IDR0 0x00
+#define RT_IDR1 0x00
+#define RT_IDR2 0x02
+#define RT_IDR3 0x03
+#define RT_IDR4 0x04
+#define RT_IDR5 0x05
+
#define RT_MAR0 0x08 /* Multicast filter */
#define RT_TXSTATUS0 0x10 /* Transmit status (4 32bit regs) */
#define RT_TXADDR0 0x20 /* Tx descriptors (also 4 32bit) */
@@ -64,12 +71,19 @@
#define RT_AS_LPAR 0x68 /* Auto-negotiation link partner reg (16 bits) */
#define RT_AS_EXPANSION 0x6A /* Auto-negotiation expansion reg (16 bits) */
+#define RT_TXAD_N(N) (RT_TXADDR0 + (N))
+#define RT_TXSTATUS_N(N) (RT_TXSTATUS0 + ((N)))
+
/* Command register bits */
#define RT_BUFEN BIT(0) /* Buffer empty */
#define RT_TE BIT(2) /* Transmitter enable */
#define RT_RE BIT(3) /* Receiver enable */
#define RT_RST BIT(4) /* Reset */
+/* 93C46 EEPROM mode bits */
+#define RT_EEM0 BIT(6)
+#define RT_EEM1 BIT(7)
+
/* Receive register bits */
#define RT_AAP BIT(0) /* Accept all packets */
#define RT_APM BIT(1) /* Accept physical match packets */
diff --git a/sys/include/dev/random/entropy.h b/sys/include/dev/random/entropy.h
new file mode 100644
index 0000000..34d86df
--- /dev/null
+++ b/sys/include/dev/random/entropy.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdint.h>
+
+#define ENTROPY_POOL_SIZE 32
+
+struct entropy_pool {
+ uint8_t pool[ENTROPY_POOL_SIZE];
+ uint32_t entropy_bits;
+};
+
+void mix_entropy(struct entropy_pool *ep, const uint8_t *input,
+ size_t input_len, uint32_t input_entropy_bits);
diff --git a/sys/include/dev/timer.h b/sys/include/dev/timer.h
index e54adcc..2ca6d62 100644
--- a/sys/include/dev/timer.h
+++ b/sys/include/dev/timer.h
@@ -31,11 +31,15 @@
#define _DEV_TIMER_H_
#include <sys/types.h>
+#include <sys/param.h>
/* Timer IDs */
#define TIMER_SCHED 0x00000000U /* Scheduler reserved timer */
#define TIMER_GP 0x00000001U /* General purpose timer */
+/* Timer flags */
+#define TIMER_MONOTONIC BIT(0)
+
/* Number of timer IDs, adjust when adding timer IDs */
#define TIMER_ID_COUNT 2
@@ -69,6 +73,7 @@ struct timer {
const char *name; /* e.g "HPET" */
size_t(*calibrate)(void); /* Returns frequency, 0 for unspecified */
size_t(*get_time_usec)(void); /* Time since init (microseconds) */
+ size_t(*get_time_nsec)(void); /* Time since init (nanoseconds) */
size_t(*get_time_sec)(void); /* Time since init (seconds) */
int(*msleep)(size_t ms);
int(*usleep)(size_t us);
@@ -78,6 +83,7 @@ struct timer {
void(*oneshot_ms)(size_t ms);
void(*oneshot_us)(size_t ms);
void(*stop)(void);
+ uint8_t flags;
};
tmrr_status_t register_timer(timer_id_t id, const struct timer *tmr);
diff --git a/sys/include/dev/usb/xhciregs.h b/sys/include/dev/usb/xhciregs.h
index 69515e4..cafd7c9 100644
--- a/sys/include/dev/usb/xhciregs.h
+++ b/sys/include/dev/usb/xhciregs.h
@@ -77,6 +77,7 @@ struct xhci_opregs {
/* USBSTS bits */
#define USBSTS_HCH BIT(0) /* HC halted */
+#define USBSTS_CNR BIT(11) /* Controller not ready */
/* CAPS.HCSPARAMS1 fields */
#define XHCI_MAXSLOTS(HCSPARAMS1) (HCSPARAMS1 & 0xFF)
@@ -98,6 +99,13 @@ struct xhci_opregs {
#define XHCI_RTS(BASE, RTSOFF) PTR_OFFSET(BASE, RTSOFF)
#define XHCI_CMD_DB(BASE, DBOFF) PTR_OFFSET(BASE, DBOFF)
+/* Runtime register offsets */
+#define XHCI_RT_IMAN 0x20
+#define XHCI_RT_IMOD 0x24
+#define XHCI_RT_ERSTSZ 0x28
+#define XHCI_RT_ERSTBA 0x30
+#define XHCI_RT_ERDP 0x38
+
/* Support protocol cap fields */
#define XHCI_PROTO_ID(PROTO) (PROTO & 0xFF)
#define XHCI_PROTO_MINOR(PROTO) ((PROTO >> 16) & 0xFF)
diff --git a/sys/include/dev/usb/xhcivar.h b/sys/include/dev/usb/xhcivar.h
index 0488ad8..a9a8fc1 100644
--- a/sys/include/dev/usb/xhcivar.h
+++ b/sys/include/dev/usb/xhcivar.h
@@ -32,6 +32,7 @@
#include <sys/types.h>
#include <sys/types.h>
+#include <sys/param.h>
#include <dev/usb/xhciregs.h>
#define XHCI_TIMEOUT 500 /* In ms */
@@ -41,6 +42,9 @@
#define XHCI_MAX_PROTOS 4
#define XHCI_IMOD_DEFAULT 0
+/* Quirks */
+#define XHCI_QUIRK_HANDOFF BIT(0)
+
/*
* USB proto (USB 2.0 or 3.0)
*/
@@ -108,6 +112,7 @@ struct xhci_hc {
uint32_t *evring;
uint8_t maxslots;
uint8_t cr_cycle : 1;
+ uint16_t quirks;
size_t maxports;
size_t protocnt;
struct xhci_caps *caps;
diff --git a/sys/include/dev/video/fbdev.h b/sys/include/dev/video/fbdev.h
index c80fd92..c9fec94 100644
--- a/sys/include/dev/video/fbdev.h
+++ b/sys/include/dev/video/fbdev.h
@@ -52,5 +52,6 @@ fbdev_get_index(const struct fbdev *fbdev, uint32_t x, uint32_t y)
}
struct fbdev fbdev_get(void);
+void fbdev_init_dev(void);
#endif /* !_DEV_FBDEV_H_ */
diff --git a/sys/include/fs/ctlfs.h b/sys/include/fs/ctlfs.h
new file mode 100644
index 0000000..29ae358
--- /dev/null
+++ b/sys/include/fs/ctlfs.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _FS_CTLFS_H_
+#define _FS_CTLFS_H_
+
+#include <sys/sio.h>
+
+struct ctlfs_dev;
+
+struct ctlops {
+ int(*read)(struct ctlfs_dev *cdp, struct sio_txn *sio);
+ int(*write)(struct ctlfs_dev *cdp, struct sio_txn *sio);
+};
+
+/*
+ * Ctlfs op arguments
+ *
+ * @devname [1]: Device name (node name)
+ * @ctlname: [1]: Control name (node entry name)
+ * @ops: Callbacks / fs hooks
+ * @mode: Access flags.
+ */
+struct ctlfs_dev {
+ union {
+ const char *devname;
+ const char *ctlname;
+ };
+ const struct ctlops *ops;
+ mode_t mode;
+};
+
+int ctlfs_create_node(const char *name, const struct ctlfs_dev *dp);
+int ctlfs_create_entry(const char *name, const struct ctlfs_dev *dp);
+
+#endif /* !_FS_CTLFS_H_ */
diff --git a/sys/include/fs/devfs.h b/sys/include/fs/devfs.h
index 012c2eb..51b0b45 100644
--- a/sys/include/fs/devfs.h
+++ b/sys/include/fs/devfs.h
@@ -33,9 +33,11 @@
#include <sys/vnode.h>
#include <sys/types.h>
#include <sys/device.h>
+#include <sys/devstat.h>
extern const struct vops g_devfs_vops;
int devfs_create_entry(const char *name, devmajor_t major, dev_t dev, mode_t mode);
+int devfs_devstat(struct vnode *vp, struct devstat *res);
#endif /* !_FS_DEVFS_H_ */
diff --git a/sys/include/fs/tmpfs.h b/sys/include/fs/tmpfs.h
new file mode 100644
index 0000000..acb5256
--- /dev/null
+++ b/sys/include/fs/tmpfs.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _FS_TMPFS_H_
+#define _FS_TMPFS_H_
+
+#include <sys/types.h>
+#include <sys/limits.h>
+#include <sys/vnode.h>
+#include <sys/queue.h>
+#include <sys/spinlock.h>
+#include <vm/vm_obj.h>
+
+extern const struct vops g_tmpfs_vops;
+
+/* Tmpfs node types */
+#define TMPFS_NONE (VNON) /* No type */
+#define TMPFS_REG (VREG) /* Regular file [f] */
+#define TMPFS_DIR (VDIR) /* Directory [d] */
+
+struct tmpfs_node;
+
+/*
+ * A tmpfs node represents an object within the
+ * tmpfs namespace such as a file, directory, etc.
+ *
+ * @rpath: /tmp/ relative path (for lookups)
+ * @type: The tmpfs node type [one-to-one to vtype]
+ * @len: Length of buffer
+ * @real_size: Actual size of file
+ * @data: The backing file data
+ * @mode: File permissions
+ * @dirvp: Vnode of the parent node
+ * @vp: Vnode of the current node
+ * @lock: Lock protecting this node
+ */
+struct tmpfs_node {
+ char rpath[PATH_MAX];
+ uint8_t type;
+ size_t len;
+ size_t real_size;
+ void *data;
+ mode_t mode;
+ struct vnode *dirvp;
+ struct vnode *vp;
+ struct spinlock lock;
+ TAILQ_HEAD(, tmpfs_node) dirents;
+ TAILQ_ENTRY(tmpfs_node) link;
+};
+
+#endif /* !_FS_TMPFS_H_ */
diff --git a/sys/include/lib/crc32.h b/sys/include/lib/crc32.h
new file mode 100644
index 0000000..a7e5eeb
--- /dev/null
+++ b/sys/include/lib/crc32.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _LIB_CRC32_H_
+#define _LIB_CRC32_H_
+
+#include <sys/types.h>
+
+uint32_t crc32(const void *data, size_t len);
+
+#endif
diff --git a/sys/include/lib/stdbool.h b/sys/include/lib/stdbool.h
new file mode 100644
index 0000000..a7a35f1
--- /dev/null
+++ b/sys/include/lib/stdbool.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _LIB_STDBOOL_H_
+#define _LIB_STDBOOL_H_
+
+typedef _Bool bool;
+
+#define true 1
+#define false 0
+
+#endif /* !_LIB_STDBOOL_H_ */
diff --git a/sys/include/lib/stddef.h b/sys/include/lib/stddef.h
new file mode 100644
index 0000000..cf23841
--- /dev/null
+++ b/sys/include/lib/stddef.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _LIB_STDDEF_H_
+#define _LIB_STDDEF_H_
+
+/* Compat */
+#include <sys/types.h>
+
+#endif /* !_LIB_STDDEF_H_ */
diff --git a/sys/include/lib/stdint.h b/sys/include/lib/stdint.h
new file mode 100644
index 0000000..6eb99f0
--- /dev/null
+++ b/sys/include/lib/stdint.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _LIB_STDINT_H_
+#define _LIB_STDINT_H_
+
+/* Compat */
+#include <sys/types.h>
+
+#endif /* !_LIB_STDINT_H_ */
diff --git a/sys/include/lib/string.h b/sys/include/lib/string.h
index c138cf1..3255ae5 100644
--- a/sys/include/lib/string.h
+++ b/sys/include/lib/string.h
@@ -35,6 +35,7 @@
size_t strlen(const char *s);
char *itoa(int64_t value, char *buf, int base);
+char *strdup(const char *s);
int vsnprintf(char *s, size_t size, const char *fmt, va_list ap);
int snprintf(char *s, size_t size, const char *fmt, ...);
@@ -47,5 +48,6 @@ int strcmp(const char *s1, const char *s2);
int strncmp(const char *s1, const char *s2, size_t n);
int atoi(char *s);
+void *memmove(void *s1, const void *s2, size_t n);
#endif /* !_LIB_STRING_H_ */
diff --git a/sys/include/net/ethertypes.h b/sys/include/net/ethertypes.h
new file mode 100644
index 0000000..753ea10
--- /dev/null
+++ b/sys/include/net/ethertypes.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _NET_ETHERTYPES_H_
+#define _NET_ETHERTYPES_H_
+
+#define ETHERTYPE_IPV4 0x0800
+#define ETHERTYPE_ARP 0x0806
+
+#endif /* !_NET_ETHERTYPES_H_ */
diff --git a/sys/include/net/if.h b/sys/include/net/if.h
new file mode 100644
index 0000000..bd57509
--- /dev/null
+++ b/sys/include/net/if.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _NET_IF_H_
+#define _NET_IF_H_
+
+#define IFNAMESIZ 16
+
+#endif /* !_NET_IF_H_ */
diff --git a/sys/include/net/if_arp.h b/sys/include/net/if_arp.h
new file mode 100644
index 0000000..cbfb2fe
--- /dev/null
+++ b/sys/include/net/if_arp.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _NETINET_IF_ARP_H_
+#define _NETINET_IF_ARP_H_
+
+#include <sys/types.h>
+#include <net/ethertypes.h>
+
+/* ARP hardware types */
+#define ARP_HWTYPE_ETHER 1
+
+/* ARP operation types */
+#define ARP_REQUEST 1
+#define ARP_REPLY 2
+
+struct arp_hdr {
+ uint16_t hw_type; /* See ARP_HWTYPE_* */
+ uint16_t proto_type; /* See ETHERTYPE_* */
+ uint8_t hw_len; /* See ETHER_ADDR_LEN */
+ uint8_t proto_len; /* Protocol address length */
+ uint16_t op_type; /* See operation types above */
+};
+
+#endif /* !_NETINET_IF_ARP_H_ */
diff --git a/sys/include/net/if_var.h b/sys/include/net/if_var.h
new file mode 100644
index 0000000..e032ff4
--- /dev/null
+++ b/sys/include/net/if_var.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _NET_IF_VAR_H_
+#define _NET_IF_VAR_H_
+
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <net/if.h>
+#include <net/netbuf.h>
+
+#define NETIF_ADDR_LEN 32 /* In bytes */
+
+/* Return values for netif hooks */
+#define NETIF_ENQ_OK 0 /* Enqueued */
+#define NETIF_ENQ_FLUSHED 1 /* Internal queue flushed */
+
+/* Interface types */
+#define NETIF_TYPE_ANY 0 /* Any type */
+#define NETIF_TYPE_WIRE 1 /* Ethernet */
+
+/*
+ * Represents the address of a network
+ * interface.
+ *
+ * @data: Raw address bytes
+ */
+struct netif_addr {
+ uint8_t data[NETIF_ADDR_LEN];
+};
+
+/*
+ * Represents a network interface
+ *
+ * @name: Interface name
+ * @type: Interface type (see NETIF_TYPE*)
+ * @tx_enq: Enqueue a packet
+ * @tx_start: Start a packet
+ *
+ * XXX: tx_enq() returns 0 on success and 1 if a flush was needed
+ * and the packets have been transmitted. Less than zero values
+ * indicate failure.
+ */
+struct netif {
+ char name[IFNAMESIZ];
+ uint8_t type;
+ TAILQ_ENTRY(netif) link;
+ struct netif_addr addr;
+ int(*tx_enq)(struct netif *nifp, struct netbuf *nbp, void *data);
+ void(*tx_start)(struct netif *nifp);
+};
+
+void netif_add(struct netif *nifp);
+int netif_lookup(const char *name, uint8_t type, struct netif **res);
+
+#endif /* !_NET_IF_VAR_H_ */
diff --git a/sys/include/net/netbuf.h b/sys/include/net/netbuf.h
new file mode 100644
index 0000000..7067370
--- /dev/null
+++ b/sys/include/net/netbuf.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _NET_NETBUF_H_
+#define _NET_NETBUF_H_
+
+#include <sys/types.h>
+
+#define NETBUF_LEN 256
+
+struct netbuf {
+ char data[NETBUF_LEN];
+ size_t len;
+};
+
+#endif /* !_NET_NETBUF_H_ */
diff --git a/sys/include/netinet/if_ether.h b/sys/include/netinet/if_ether.h
new file mode 100644
index 0000000..d3dc9b7
--- /dev/null
+++ b/sys/include/netinet/if_ether.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _NETINET_IF_ETHER_H_
+#define _NETINET_IF_ETHER_H_
+
+#include <sys/types.h>
+#include <net/if_arp.h>
+#include <net/if_var.h>
+
+#define ETHER_ADDR_LEN 6
+
+struct ether_arp {
+ struct arp_hdr hdr;
+ uint8_t sha[ETHER_ADDR_LEN];
+ uint8_t spa[4];
+ uint8_t tha[ETHER_ADDR_LEN];
+ uint8_t tpa[4];
+};
+
+struct ether_frame {
+ uint8_t ether_daddr[ETHER_ADDR_LEN];
+ uint8_t ether_saddr[ETHER_ADDR_LEN];
+ uint16_t ether_type;
+};
+
+int arp_request(struct netif *nifp, uint8_t *sproto, uint8_t *tproto);
+int arp_reply(struct netif *netif, uint8_t *sproto, uint8_t *tproto);
+
+#endif /* !_NETINET_IF_ETHER_H_ */
diff --git a/sys/include/sys/atomic.h b/sys/include/sys/atomic.h
index f61bf62..d9b3bde 100644
--- a/sys/include/sys/atomic.h
+++ b/sys/include/sys/atomic.h
@@ -30,6 +30,8 @@
#ifndef _SYS_ATOMIC_H_
#define _SYS_ATOMIC_H_
+#include <sys/types.h>
+
static inline unsigned long
atomic_add_long_nv(volatile unsigned long *p, unsigned long v)
{
@@ -42,6 +44,12 @@ atomic_add_int_nv(volatile unsigned int *p, unsigned int v)
return __sync_add_and_fetch(p, v);
}
+static inline unsigned int
+atomic_add_64_nv(volatile uint64_t *p, unsigned int v)
+{
+ return __sync_add_and_fetch(p, v);
+}
+
static inline unsigned long
atomic_sub_long_nv(volatile unsigned long *p, unsigned long v)
{
@@ -55,6 +63,12 @@ atomic_sub_int_nv(volatile unsigned int *p, unsigned int v)
}
static inline unsigned int
+atomic_sub_64_nv(volatile uint64_t *p, unsigned int v)
+{
+ return __sync_sub_and_fetch(p, v);
+}
+
+static inline unsigned int
atomic_load_int_nv(volatile unsigned int *p, unsigned int v)
{
return __atomic_load_n(p, v);
@@ -66,6 +80,12 @@ atomic_load_long_nv(volatile unsigned long *p, unsigned int v)
return __atomic_load_n(p, v);
}
+static inline unsigned int
+atomic_load_64_nv(volatile uint64_t *p, unsigned int v)
+{
+ return __atomic_load_n(p, v);
+}
+
static inline void
atomic_store_int_nv(volatile unsigned int *p, int nv, unsigned int v)
{
@@ -78,20 +98,30 @@ atomic_store_long_nv(volatile unsigned long *p, long nv, unsigned int v)
__atomic_store_n(p, nv, v);
}
+static inline void
+atomic_store_64_nv(volatile uint64_t *p, long nv, unsigned int v)
+{
+ __atomic_store_n(p, nv, v);
+}
+
/* Atomic increment (and fetch) operations */
#define atomic_inc_long(P) atomic_add_long_nv((P), 1)
#define atomic_inc_int(P) atomic_add_int_nv((P), 1)
+#define atomic_inc_64(P) atomic_add_64_nv((P), 1)
/* Atomic decrement (and fetch) operations */
#define atomic_dec_long(P) atomic_sub_long_nv((P), 1)
#define atomic_dec_int(P) atomic_sub_int_nv((P), 1)
+#define atomic_dec_64(P) atomic_sub_64_nv((P), 1)
/* Atomic load operations */
#define atomic_load_int(P) atomic_load_int_nv((P), __ATOMIC_SEQ_CST)
#define atomic_load_long(P) atomic_load_long_nv((P), __ATOMIC_SEQ_CST)
+#define atomic_load_64(P) atomic_load_64_nv((P), __ATOMIC_SEQ_CST)
/* Atomic store operations */
#define atomic_store_int(P, NV) atomic_store_int_nv((P), (NV), __ATOMIC_SEQ_CST)
#define atomic_store_long(P, NV) atomic_store_long_nv((P), (NV), __ATOMIC_SEQ_CST)
+#define atomic_store_64(P, NV) atomic_store_64_nv((P), (NV), __ATOMIC_SEQ_CST)
#endif /* !_SYS_ATOMIC_H_ */
diff --git a/sys/include/sys/bitops.h b/sys/include/sys/bitops.h
new file mode 100644
index 0000000..e8e9567
--- /dev/null
+++ b/sys/include/sys/bitops.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _SYS_BITOPS_H_
+#define _SYS_BITOPS_H_
+
+#include <sys/cdefs.h>
+
+#define BOPS_M1 0x5555555555555555ULL /* 01010101... */
+#define BOPS_M2 0x3333333333333333ULL /* 00110011... */
+#define BOPS_M4 0x0F0F0F0F0F0F0F0FULL /* 00001111... */
+#define BOPS_M8 0x00FF00FF00FF00FFULL /* x4(0), x4(1) */
+#define BOPS_M16 0x0000FFFF0000FFFFULL /* x16(0), x16(1) */
+#define BOPS_M32 0x00000000FFFFFFFFULL /* x32(0), x32(1) */
+#define BOPS_H0 0x0101010101010101ULL /* sum of 256^{0,1,2,3...} */
+
+__always_inline static inline int
+popcnt(uint64_t x)
+{
+ x -= (x >> 1) & BOPS_M1;
+ x = (x & BOPS_M2) + ((x >> 2) & BOPS_M2);
+ x = (x + (x >> 4)) & BOPS_M4;
+ return (x * BOPS_H0) >> 56;
+}
+
+#endif /* !_SYS_BITOPS_H_ */
diff --git a/sys/include/sys/cdefs.h b/sys/include/sys/cdefs.h
index 61106fa..725193e 100644
--- a/sys/include/sys/cdefs.h
+++ b/sys/include/sys/cdefs.h
@@ -42,7 +42,9 @@
#define __dead __attribute__((__noreturn__))
#define __cold __attribute__((__cold__))
#define __dead_cold __attribute__((__noreturn__, __cold__))
+#define __aligned(n) __attribute__((__aligned__((n))))
#define __unused __attribute__((__unused__))
+#define __used __attribute__((__used__))
#define __nothing ((void)0)
#define __likely(exp) __builtin_expect(((exp) != 0), 1)
#define __unlikely(exp) __builtin_expect(((exp) != 0), 0)
diff --git a/sys/include/sys/console.h b/sys/include/sys/console.h
new file mode 100644
index 0000000..d0b89a8
--- /dev/null
+++ b/sys/include/sys/console.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _SYS_CONSOLE_H_
+#define _SYS_CONSOLE_H_
+
+#include <sys/types.h>
+
+/*
+ * Console features
+ *
+ * @ansi_esc: If 1, ANSI escape codes are enabled
+ * @show_curs: If 1, show the cursor
+ */
+struct console_feat {
+ uint8_t ansi_esc : 1;
+ uint8_t show_curs : 1;
+};
+
+/*
+ * Console attributes
+ *
+ * @cursor_x: Cursor x position
+ * @cursor_y: Cursor y position
+ */
+struct console_attr {
+ uint32_t cursor_x;
+ uint32_t cursor_y;
+};
+
+#endif /* !_SYS_CONSOLE_H_ */
diff --git a/sys/include/sys/device.h b/sys/include/sys/device.h
index f5f92ad..04b66fc 100644
--- a/sys/include/sys/device.h
+++ b/sys/include/sys/device.h
@@ -36,21 +36,28 @@
#include <sys/queue.h>
#include <sys/proc.h>
#include <sys/sio.h>
+#include <vm/vm_obj.h>
typedef uint8_t devmajor_t;
/* Device operation typedefs */
typedef int(*dev_read_t)(dev_t, struct sio_txn *, int);
typedef int(*dev_write_t)(dev_t, struct sio_txn *, int);
+typedef int(*dev_bsize_t)(dev_t);
struct cdevsw {
int(*read)(dev_t dev, struct sio_txn *sio, int flags);
int(*write)(dev_t dev, struct sio_txn *sio, int flags);
+ paddr_t(*mmap)(dev_t dev, size_t size, off_t off, int flags);
+
+ /* Private */
+ struct vm_object vmobj;
};
struct bdevsw {
int(*read)(dev_t dev, struct sio_txn *sio, int flags);
int(*write)(dev_t dev, struct sio_txn *sio, int flags);
+ int(*bsize)(dev_t dev);
};
void *dev_get(devmajor_t major, dev_t dev);
@@ -61,10 +68,12 @@ int dev_register(devmajor_t major, dev_t dev, void *devsw);
int dev_noread(void);
int dev_nowrite(void);
+int dev_nobsize(void);
/* Device operation stubs */
#define noread ((dev_read_t)dev_noread)
#define nowrite ((dev_write_t)dev_nowrite)
+#define nobsize ((dev_bsize_t)dev_nobsize)
#endif /* _KERNEL */
#endif /* !_SYS_DEVICE_H_ */
diff --git a/sys/include/sys/devstat.h b/sys/include/sys/devstat.h
new file mode 100644
index 0000000..91af30f
--- /dev/null
+++ b/sys/include/sys/devstat.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _SYS_DEVSTAT_H_
+#define _SYS_DEVSTAT_H_
+
+#include <sys/types.h>
+
+/*
+ * Stats for block devices
+ *
+ * @nwrites: Number of writes total
+ * @nreads: Number of reads total
+ */
+struct devstat {
+ size_t nwrites;
+ size_t nreads;
+};
+
+#endif /* !_SYS_DEVSTAT_H_ */
diff --git a/sys/include/sys/disk.h b/sys/include/sys/disk.h
new file mode 100644
index 0000000..4ad068b
--- /dev/null
+++ b/sys/include/sys/disk.h
@@ -0,0 +1,211 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _SYS_DISK_H_
+#define _SYS_DISK_H_
+
+#include <sys/syscall.h>
+#include <sys/queue.h>
+#include <sys/device.h>
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <sys/limits.h>
+#include <sys/cdefs.h>
+#if defined(_KERNEL)
+#include <dev/dcdr/cache.h>
+#endif /* _KERNEL */
+
+#define DISK_NAME_MAX 64
+
+/*
+ * V_BSIZE is the virtual block size in bytes used
+ * by the disk framework. The virtual block size is a
+ * multiple of the hardware block size and defines
+ * how many bytes a virtual block is made up of.
+ *
+ * A virtual block is simply a unit specific to
+ * the disk framework that represents multiple
+ * hardware disk blocks.
+ */
+#if defined(__V_BSIZE)
+#define V_BSIZE __V_BSIZE
+#else
+#define V_BSIZE 4096
+#endif /* __V_BSIZE */
+
+/* Sanitize the silly human's input */
+_Static_assert(V_BSIZE > 512, "V_BSIZE must be > 512");
+_Static_assert((V_BSIZE & 1) == 0, "V_BSIZE must be a power of two");
+
+#define DISK_PRIMARY 0 /* ID of primary disk */
+
+/*
+ * To prevent unlikely cases of unintended disk
+ * operations (e.g., read, write, etc), we store
+ * a cookie within each set of parameters.
+ *
+ * Requests whose bundle of parameters have no valid
+ * cookie shall be rejected by us.
+ */
+#define DISK_PARAM_COOKIE 0xD1531001
+
+/* Valid disk operations */
+#define DISK_IO_READ 0x00 /* Read data from the disk */
+#define DISK_IO_WRITE 0x01 /* Write data to disk */
+#define DISK_IO_QUERY 0x02 /* Query disk information */
+
+/*
+ * A disk identifier is a zero-based index into
+ * the disk registry.
+ */
+typedef uint16_t diskid_t;
+
+/*
+ * Block offset / LBA
+ */
+typedef off_t blkoff_t;
+
+/*
+ * Disk operations may be requested by user
+ * programs by using a disk operation code.
+ */
+typedef uint8_t diskop_t;
+
+/*
+ * Describes basic disk information
+ *
+ * @block_size: Hardware block size
+ * @vblock_size: Virtual block size
+ * @n_block: Number of blocks total
+ */
+struct disk_info {
+ uint32_t block_size;
+ uint32_t vblock_size;
+ size_t n_block;
+};
+
+/*
+ * The disk metadata structure contains information
+ * describing the disk. It is used for Hyra's pbuf
+ * (persistent buffers / sls) support. This structure
+ * is to be stored at the very last sector of the drive.
+ *
+ * @canary: Boot canary to verify persistent instance
+ * @info: Disk attributes
+ */
+struct disk_root {
+ uint32_t canary;
+ struct disk_info info;
+};
+
+/*
+ * A disk I/O parameter contains information
+ * that is passed from a user application to
+ * the kernel for specific operations.
+ *
+ * @buf: User-side pointer to data buffer
+ * @size: Size of data buffer in bytes
+ * @cookie: Used to prevent unintended operations
+ * @blk: Disk block offset
+ * @u_buf: Used by the kernel to keep track of user buffer
+ */
+struct disk_param {
+ void *buf;
+ size_t size;
+ uint32_t cookie;
+ blkoff_t blk;
+#if defined(_KERNEL)
+ void *u_buf;
+#endif
+};
+
+/*
+ * Helper used to initialize disk I/O parameters.
+ * This is used by the user to initialize a declared
+ * set of parameters.
+ *
+ * @buf: Buffer to operate on
+ * @blk: Disk block to operate on
+ * @size: Operation size in bytes (block-aligned)
+ * @res: Pointer to params to be initialized
+ */
+__always_inline static inline void
+disk_param_init(void *buf, blkoff_t blk, size_t size, struct disk_param *res)
+{
+ if (res != NULL) {
+ res->buf = buf;
+ res->blk = blk;
+ res->size = size;
+ res->cookie = DISK_PARAM_COOKIE;
+ }
+}
+
+/*
+ * User side disk API
+ */
+#if !defined(_KERNEL)
+ssize_t __disk_io(diskid_t id, diskop_t op, const struct disk_param *param);
+#endif /* !_KERNEL */
+
+/* Common disk operations */
+int disk_query(diskid_t id, struct disk_info *res);
+ssize_t disk_read(diskid_t id, blkoff_t blk, void *buf, size_t len);
+ssize_t disk_write(diskid_t id, blkoff_t blk, const void *buf, size_t len);
+
+#if defined(_KERNEL)
+/*
+ * Represents a block storage device
+ *
+ * @name: Name of disk
+ * @cookie: Used internally to ensure validity
+ * @bsize: Hardware block size (defaults to 512 bytes)
+ * @dev: Device minor
+ * @id: Disk ID (zero-based index)
+ * @bdev: Block device operations
+ * @link: TAILQ link
+ */
+struct disk {
+ char name[DISK_NAME_MAX];
+ uint32_t cookie;
+ uint16_t bsize;
+ dev_t dev;
+ diskid_t id;
+ const struct bdevsw *bdev;
+ TAILQ_ENTRY(disk) link;
+};
+
+void *disk_buf_alloc(diskid_t id, size_t len);
+void disk_buf_free(void *p);
+
+int disk_add(const char *name, dev_t dev, const struct bdevsw *bdev, int flags);
+int disk_get_id(diskid_t id, struct disk **res);
+
+scret_t sys_disk(struct syscall_args *scargs);
+#endif /* _KERNEL */
+#endif /* !_SYS_DISK_H_ */
diff --git a/sys/include/sys/disklabel.h b/sys/include/sys/disklabel.h
new file mode 100644
index 0000000..895c35e
--- /dev/null
+++ b/sys/include/sys/disklabel.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _SYS_DISKLABEL_H_
+#define _SYS_DISKLABEL_H_
+
+#include <sys/types.h>
+
+#define DISK_MAG 0x4F445421UL /* "ODT!" */
+
+/*
+ * Represents a disk table.
+ *
+ * @magic: Magic number (`DISK_MAG')
+ * @sect_size: Disk sector size
+ */
+struct disklabel {
+ uint32_t magic;
+ uint32_t sect_size;
+};
+
+#endif /* !_SYS_DISKLABEL_H_ */
diff --git a/sys/include/sys/dmi.h b/sys/include/sys/dmi.h
new file mode 100644
index 0000000..a21cff6
--- /dev/null
+++ b/sys/include/sys/dmi.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _SYS_DMI_H_
+#define _SYS_DMI_H_
+
+#if defined(_KERNEL)
+#include <sys/types.h>
+#else
+#include <stdint.h>
+#include <stddef.h>
+#endif /* _KERNEL */
+
+/*
+ * Provides board information through
+ * DMI.
+ *
+ * @cpu_version: CPU version string
+ * @cpu_manuf: CPU manufacturer string
+ * @product: Board product string
+ * @vendor: Board vendor string
+ * @version: Product version string
+ *
+ * If index 0 of any of the strings contain
+ * '\0', then they are unsupported/unused.
+ *
+ * XXX: Strings are null terminated
+ */
+struct dmi_board {
+ char cpu_version[64];
+ char cpu_manuf[32];
+ char product[32];
+ char vendor[32];
+ char version[32];
+};
+
+#endif /* !_SYS_DMI_H_ */
diff --git a/sys/include/sys/driver.h b/sys/include/sys/driver.h
index 05c40fa..e10021a 100644
--- a/sys/include/sys/driver.h
+++ b/sys/include/sys/driver.h
@@ -31,27 +31,94 @@
#define _SYS_DRIVER_H_
#include <sys/cdefs.h>
+#include <sys/proc.h>
+#include <sys/types.h>
#if defined(_KERNEL)
+/* Variable driver data */
+struct driver_var {
+ uint8_t deferred : 1;
+};
+
struct driver {
int(*init)(void);
+ const char *name;
+ struct driver_var *data;
};
+extern struct proc g_proc0;
+
+/* Early (high priority) drivers */
extern char __drivers_init_start[];
extern char __drivers_init_end[];
-#define DRIVER_EXPORT(INIT) \
+/* Deferred (low priority) drivers */
+extern char __driversd_init_start[];
+extern char __driversd_init_end[];
+
+#define DRIVER_EXPORT(INIT, NAME) \
+ static struct driver_var __driver_var = { \
+ .deferred = 0 \
+ }; \
+ \
__attribute__((used, section(".drivers"))) \
static struct driver __driver_desc = { \
.init = INIT, \
+ .data = &__driver_var, \
+ .name = NAME \
}
+/*
+ * Some drivers are not required to start up
+ * early for proper system operation and may
+ * be deferred to start at a later time.
+ *
+ * Examples of such (deferrable) drivers include code
+ * that waits for I/O (e.g., disks, network cards,
+ * et cetera). This allows for faster boot times
+ * as only *required* drivers are started before
+ * everything else.
+ *
+ * Drivers that wish to be deferred may export themselves
+ * via the DRIVER_DEFER() macro. The DRIVER_DEFERRED()
+ * macro gives the value of 1 if the current driver
+ * context has yet to be initialized. The driver may
+ * use this to defer requests for I/O.
+ */
+#define DRIVER_DEFER(INIT, NAME) \
+ static struct driver_var __driver_var = { \
+ .deferred = 1 \
+ }; \
+ \
+ __attribute__((used, section(".drivers.defer"))) \
+ static struct driver __driver_desc = { \
+ .init = INIT, \
+ .data = &__driver_var, \
+ .name = NAME \
+ }
+
+#define DRIVER_DEFERRED() __driver_var.deferred
+
#define DRIVERS_INIT() \
for (struct driver *__d = (struct driver *)__drivers_init_start; \
(uintptr_t)__d < (uintptr_t)__drivers_init_end; ++__d) \
{ \
+ if (driver_blacklist_check((__d)->name)) { \
+ continue; \
+ } \
__d->init(); \
}
+
+#define DRIVERS_SCHED() \
+ spawn(&g_proc0, __driver_init_td, NULL, 0, NULL)
+
+/* Driver blacklist framework */
+int driver_blacklist(const char *name);
+int driver_blacklist_check(const char *name);
+void driver_blacklist_init(void);
+
+void __driver_init_td(void);
+
#endif /* _KERNEL */
#endif /* !_SYS_DRIVER_H_ */
diff --git a/sys/include/sys/elf.h b/sys/include/sys/elf.h
index af5f6d6..76c6d43 100644
--- a/sys/include/sys/elf.h
+++ b/sys/include/sys/elf.h
@@ -496,4 +496,70 @@ typedef struct {
Elf64_Xword sh_entsize; /* Entry size if section holds table */
} Elf64_Shdr;
+/* Special section indices. */
+
+#define SHN_UNDEF 0 /* Undefined section */
+#define SHN_LORESERVE 0xff00 /* Start of reserved indices */
+#define SHN_LOPROC 0xff00 /* Start of processor-specific */
+#define SHN_BEFORE 0xff00 /* Order section before all others
+ (Solaris). */
+#define SHN_AFTER 0xff01 /* Order section after all others
+ (Solaris). */
+#define SHN_HIPROC 0xff1f /* End of processor-specific */
+#define SHN_LOOS 0xff20 /* Start of OS-specific */
+#define SHN_HIOS 0xff3f /* End of OS-specific */
+#define SHN_ABS 0xfff1 /* Associated symbol is absolute */
+#define SHN_COMMON 0xfff2 /* Associated symbol is common */
+#define SHN_XINDEX 0xffff /* Index is in extra table. */
+#define SHN_HIRESERVE 0xffff /* End of reserved indices */
+
+/* Legal values for sh_type (section type). */
+
+#define SHT_NULL 0 /* Section header table entry unused */
+#define SHT_PROGBITS 1 /* Program data */
+#define SHT_SYMTAB 2 /* Symbol table */
+#define SHT_STRTAB 3 /* String table */
+#define SHT_RELA 4 /* Relocation entries with addends */
+#define SHT_HASH 5 /* Symbol hash table */
+#define SHT_DYNAMIC 6 /* Dynamic linking information */
+#define SHT_NOTE 7 /* Notes */
+#define SHT_NOBITS 8 /* Program space with no data (bss) */
+#define SHT_REL 9 /* Relocation entries, no addends */
+#define SHT_SHLIB 10 /* Reserved */
+#define SHT_DYNSYM 11 /* Dynamic linker symbol table */
+#define SHT_INIT_ARRAY 14 /* Array of constructors */
+#define SHT_FINI_ARRAY 15 /* Array of destructors */
+#define SHT_PREINIT_ARRAY 16 /* Array of pre-constructors */
+#define SHT_GROUP 17 /* Section group */
+#define SHT_SYMTAB_SHNDX 18 /* Extended section indeces */
+#define SHT_NUM 19 /* Number of defined types. */
+#define SHT_LOOS 0x60000000 /* Start OS-specific. */
+#define SHT_CHECKSUM 0x6ffffff8 /* Checksum for DSO content. */
+#define SHT_LOSUNW 0x6ffffffa /* Sun-specific low bound. */
+#define SHT_SUNW_move 0x6ffffffa
+#define SHT_SUNW_COMDAT 0x6ffffffb
+#define SHT_SUNW_syminfo 0x6ffffffc
+#define SHT_HISUNW 0x6fffffff /* Sun-specific high bound. */
+#define SHT_HIOS 0x6fffffff /* End OS-specific type */
+#define SHT_LOPROC 0x70000000 /* Start of processor-specific */
+#define SHT_HIPROC 0x7fffffff /* End of processor-specific */
+#define SHT_LOUSER 0x80000000 /* Start of application-specific */
+#define SHT_HIUSER 0x8fffffff /* End of application-specific */
+
+/* Legal values for sh_flags (section flags). */
+
+#define SHF_WRITE (1 << 0) /* Writable */
+#define SHF_ALLOC (1 << 1) /* Occupies memory during execution */
+#define SHF_EXECINSTR (1 << 2) /* Executable */
+#define SHF_MERGE (1 << 4) /* Might be merged */
+#define SHF_STRINGS (1 << 5) /* Contains nul-terminated strings */
+#define SHF_INFO_LINK (1 << 6) /* `sh_info' contains SHT index */
+#define SHF_LINK_ORDER (1 << 7) /* Preserve order after combining */
+#define SHF_OS_NONCONFORMING (1 << 8) /* Non-standard OS specific handling
+ required */
+#define SHF_GROUP (1 << 9) /* Section is member of a group. */
+#define SHF_TLS (1 << 10) /* Section hold thread-local data. */
+#define SHF_COMPRESSED (1 << 11) /* Section with compressed data. */
+#define SHF_MASKOS 0x0ff00000 /* OS-specific. */
+#define SHF_MASKPROC 0xf0000000 /* Processor-specific */
#endif /* _SYS_ELF_H_ */
diff --git a/sys/include/sys/endian.h b/sys/include/sys/endian.h
new file mode 100644
index 0000000..5cbc94a
--- /dev/null
+++ b/sys/include/sys/endian.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _SYS_ENDIAN_H_
+#define _SYS_ENDIAN_H_
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+#define swap16(x) __swap16((x))
+#define swap32(x) __swap32((x))
+
+__always_inline static inline uint16_t
+__swap16(uint16_t x)
+{
+ return ((x << 8) & 0xFF00) | ((x >> 8) & 0x00FF);
+}
+
+__always_inline static inline uint32_t
+__swap32(uint32_t x)
+{
+ return ((x << 24) & 0xFF000000) |
+ ((x << 8) & 0x00FF0000) |
+ ((x >> 8) & 0x0000FF00) |
+ ((x >> 24) & 0x000000FF);
+}
+
+#endif /* !_SYS_ENDIAN_H_ */
diff --git a/sys/include/sys/exec.h b/sys/include/sys/exec.h
index 7e720fc..aa2a729 100644
--- a/sys/include/sys/exec.h
+++ b/sys/include/sys/exec.h
@@ -32,7 +32,6 @@
#include <sys/types.h>
-#if defined(_KERNEL)
/* Danger: Do not change these !! */
#define AT_NULL 0
@@ -45,7 +44,9 @@
#define AT_RANDOM 7
#define AT_EXECFN 8
#define AT_PAGESIZE 9
+#define _AT_MAX 16
+#if defined(_KERNEL)
#define MAX_PHDRS 32
#define STACK_PUSH(PTR, VAL) *(--(PTR)) = VAL
#define AUXVAL(PTR, TAG, VAL) \
diff --git a/sys/include/sys/fbdev.h b/sys/include/sys/fbdev.h
new file mode 100644
index 0000000..e206889
--- /dev/null
+++ b/sys/include/sys/fbdev.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _SYS_FBDEV_H_
+#define _SYS_FBDEV_H_
+
+struct fbattr {
+ uint32_t width;
+ uint32_t height;
+ uint32_t pitch;
+ uint32_t bpp;
+};
+
+#endif /* !_SYS_FBDEV_H_ */
diff --git a/sys/include/sys/fcntl.h b/sys/include/sys/fcntl.h
index 122a378..83d38af 100644
--- a/sys/include/sys/fcntl.h
+++ b/sys/include/sys/fcntl.h
@@ -33,6 +33,7 @@
#define O_RDONLY 0x0000
#define O_WRONLY 0x0001
#define O_RDWR 0x0002
+#define O_CREAT 0x0004
/* Makes seal checking easier */
#if defined(_KERNEL)
diff --git a/sys/include/sys/filedesc.h b/sys/include/sys/filedesc.h
index a544811..adbcfa8 100644
--- a/sys/include/sys/filedesc.h
+++ b/sys/include/sys/filedesc.h
@@ -31,8 +31,15 @@
#define _SYS_FILEDESC_H_
#include <sys/types.h>
+#if defined(_KERNEL)
#include <sys/vnode.h>
+#include <sys/syscall.h>
#include <sys/spinlock.h>
+#include <sys/syscall.h>
+
+#define SEEK_SET 0
+#define SEEK_CUR 1
+#define SEEK_END 2
struct filedesc {
int fdno;
@@ -48,10 +55,14 @@ int fd_close(unsigned int fd);
int fd_read(unsigned int fd, void *buf, size_t count);
int fd_write(unsigned int fd, void *buf, size_t count);
-int fd_alloc(struct filedesc **fd_out);
+int fd_alloc(struct proc *td, struct filedesc **fd_out);
int fd_open(const char *pathname, int flags);
+off_t fd_seek(int fildes, off_t offset, int whence);
+
+int fd_dup(struct proc *td, int fd);
+struct filedesc *fd_get(struct proc *td, unsigned int fdno);
-int fd_dup(int fd);
-struct filedesc *fd_get(unsigned int fdno);
+scret_t sys_lseek(struct syscall_args *scargs);
+#endif /* _KERNEL */
#endif /* !_SYS_FILEDESC_H_ */
diff --git a/sys/include/sys/krq.h b/sys/include/sys/krq.h
new file mode 100644
index 0000000..9cb6ec6
--- /dev/null
+++ b/sys/include/sys/krq.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _SYS_KRQ_H_
+#define _SYS_KRQ_H_
+
+#include <sys/syscall.h>
+
+#if defined(_KERNEL)
+scret_t sys_inject(struct syscall_args *scargs);
+#else
+int inject(const char *path);
+#endif /* _KERNEL */
+#endif /* !_SYS_KRQ_H_ */
diff --git a/sys/include/sys/limits.h b/sys/include/sys/limits.h
index 6185719..c0ce5af 100644
--- a/sys/include/sys/limits.h
+++ b/sys/include/sys/limits.h
@@ -31,7 +31,12 @@
#define _SYS_LIMITS_H_
#define PATH_MAX 1024
+#define NAME_MAX 256
#define SSIZE_MAX 32767
+#define ARG_MAX 4096
#define CHAR_BIT 8
-
+#define CPU_MAX 256
+#define VSR_MAX_DOMAIN 16
+#define VSR_MAX_CAPSULE 16
+#define IOVEC_MAX 512
#endif /* !_SYS_LIMITS_H_ */
diff --git a/sys/include/sys/mman.h b/sys/include/sys/mman.h
index 4ead9ba..de360e4 100644
--- a/sys/include/sys/mman.h
+++ b/sys/include/sys/mman.h
@@ -35,6 +35,8 @@
#if defined(_KERNEL)
#include <sys/tree.h>
#include <vm/vm_obj.h>
+#else
+#include <stddef.h>
#endif /* _KERNEL */
/*
@@ -49,10 +51,10 @@
#endif /* !_KERNEL */
/* mmap() flags */
+#define MAP_ANON 0x0000
#define MAP_SHARED 0x0001
#define MAP_PRIVATE 0x0002
#define MAP_FIXED 0x0004
-#define MAP_ANON 0x0008
#if defined(_KERNEL)
/*
@@ -80,19 +82,19 @@ struct mmap_lgdr {
size_t nbytes;
};
-/* Kernel munmap() routine */
-int munmap_at(void *addr, size_t len);
-
-/* Kernel mmap() routine */
-void *mmap_at(void *addr, size_t len, int prot, int flags,
- int fildes, off_t off);
-
int mmap_entrycmp(const struct mmap_entry *a, const struct mmap_entry *b);
RBT_PROTOTYPE(lgdr_entries, mmap_entry, hd, mmap_entrycmp)
-#endif /* _KERNEL */
/* Syscall layer */
-scret_t mmap(struct syscall_args *scargs);
-scret_t munmap(struct syscall_args *scargs);
+scret_t sys_mmap(struct syscall_args *scargs);
+scret_t sys_munmap(struct syscall_args *scargs);
+#endif /* _KERNEL */
+
+/* Kernel munmap() routine */
+int munmap(void *addr, size_t len);
+
+/* Kernel mmap() routine */
+void *mmap(void *addr, size_t len, int prot, int flags,
+ int fildes, off_t off);
#endif /* !_SYS_MMAN_H_ */
diff --git a/sys/include/sys/mount.h b/sys/include/sys/mount.h
index 3b5d89e..636c7bf 100644
--- a/sys/include/sys/mount.h
+++ b/sys/include/sys/mount.h
@@ -46,6 +46,8 @@
*/
#define MOUNT_RAMFS "initramfs"
#define MOUNT_DEVFS "devfs"
+#define MOUNT_CTLFS "ctlfs"
+#define MOUNT_TMPFS "tmpfs"
struct vfsops;
struct mount;
@@ -57,6 +59,8 @@ extern mountlist_t g_mountlist;
/* Filesystem operations */
extern const struct vfsops g_initramfs_vfsops;
extern const struct vfsops g_devfs_vfsops;
+extern const struct vfsops g_ctlfs_vfsops;
+extern const struct vfsops g_tmpfs_vfsops;
struct mount {
char *name;
diff --git a/sys/include/sys/mutex.h b/sys/include/sys/mutex.h
new file mode 100644
index 0000000..8a4d50a
--- /dev/null
+++ b/sys/include/sys/mutex.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _SYS_MUTEX_H_
+#define _SYS_MUTEX_H_
+
+#include <sys/types.h>
+#include <vm/dynalloc.h>
+
+#define MUTEX_NAME_LEN 32
+
+#if defined(_KERNEL)
+
+struct mutex {
+ char name[MUTEX_NAME_LEN];
+ volatile uint8_t lock;
+};
+
+struct mutex *mutex_new(const char *name);
+void mutex_free(struct mutex *mtx);
+
+int mutex_acquire(struct mutex *mtx, int flags);
+void mutex_release(struct mutex *mtx);
+
+#endif /* _KERNEL */
+#endif /* !_SYS_MUTEX_H_ */
diff --git a/sys/include/sys/namei.h b/sys/include/sys/namei.h
index f81f905..ccd7f35 100644
--- a/sys/include/sys/namei.h
+++ b/sys/include/sys/namei.h
@@ -32,6 +32,9 @@
#include <sys/types.h>
#include <sys/vnode.h>
+#include <sys/param.h>
+
+#define NAMEI_WANTPARENT BIT(0) /* Request parent only */
struct nameidata {
const char *path; /* Pathname */
diff --git a/sys/include/sys/param.h b/sys/include/sys/param.h
index c0a5686..2bbbabd 100644
--- a/sys/include/sys/param.h
+++ b/sys/include/sys/param.h
@@ -30,11 +30,20 @@
#ifndef _SYS_PARAM_H_
#define _SYS_PARAM_H_
+#if defined(_KERNEL)
+#include <machine/param.h>
+#endif
+
/* Assumed cache line size */
#ifndef COHERENCY_UNIT
#define COHERENCY_UNIT 64
#endif
+/* Assumed machine word size */
+#ifndef M_WORD_SIZE
+#define M_WORD_SIZE 4
+#endif
+
/* Bit related macros */
#define ISSET(v, f) ((v) & (f))
#define BIT(n) (1ULL << (n))
@@ -47,6 +56,7 @@
/* Align up/down a value */
#define ALIGN_DOWN(value, align) ((value) & ~((align)-1))
#define ALIGN_UP(value, align) (((value) + (align)-1) & ~((align)-1))
+#define MALIGN(value) ALIGN_UP((value), M_WORD_SIZE)
/* Bitmap helper macros */
#define setbit(a, b) ((a)[(b) >> 3] |= BIT(b % 8))
@@ -67,8 +77,12 @@
/* Gives 1 if pointer is aligned */
#define PTR_ALIGNED(PTR, ALIGN) (!((uintptr_t)PTR & (ALIGN - 1)))
-/* Adds a value to a pointer */
+/*
+ * PTR_OFFSET: Adds an offset to the pointer
+ * PTR_NOFFSET: Subtracts a negative offset from the pointer
+ */
#define PTR_OFFSET(PTR, OFF) ((void *)((uintptr_t)PTR + OFF))
+#define PTR_NOFFSET(PTR, NOFF) ((void *)((uintptr_t)PTR - NOFF))
#define NELEM(a) (sizeof(a) / sizeof(a[0]))
diff --git a/sys/include/sys/proc.h b/sys/include/sys/proc.h
index c561e91..809ee23 100644
--- a/sys/include/sys/proc.h
+++ b/sys/include/sys/proc.h
@@ -38,6 +38,9 @@
#include <sys/cdefs.h>
#include <sys/syscall.h>
#include <sys/exec.h>
+#include <sys/ucred.h>
+#include <sys/limits.h>
+#include <sys/vsr.h>
#include <sys/filedesc.h>
#include <sys/signal.h>
#include <sys/vnode.h>
@@ -53,36 +56,99 @@
#define PROC_MAX_FILEDES 256
#define PROC_SIGMAX 64
+/*
+ * The coredump structure, contains information
+ * about crashes.
+ *
+ * @pid: PID of process that has crashed
+ * @fault_addr: Address of faulting memory access
+ * @tf: Copy of the programs trapframe
+ * @checksum: CRC32 checksum of entire coredump
+ *
+ * XXX: DO NOT REORDER (always add to the end before 'checksum')
+ */
+struct __packed coredump {
+ pid_t pid;
+ uintptr_t fault_addr;
+ struct trapframe tf;
+
+ /* XXX: Add entries above the checksum */
+ uint32_t checksum;
+};
+
+/*
+ * Sometimes we may need to pin a process
+ * to a specific CPU. This type represents
+ * the (machine independent) logical processor
+ * ID for a process to be pinned to.
+ */
+typedef int16_t affinity_t;
+
struct proc {
pid_t pid;
struct exec_prog exec;
+ struct ucred cred;
struct ksiginfo *ksig_list[PROC_SIGMAX];
struct filedesc *fds[PROC_MAX_FILEDES];
+ struct vsr_domain *vsr_tab[VSR_MAX_DOMAIN];
struct mmap_lgdr *mlgdr;
struct vcache *vcache;
struct spinlock vcache_lock;
struct trapframe tf;
struct pcb pcb;
+ struct proc *parent;
+ affinity_t affinity;
+ void *data;
size_t priority;
+ int exit_status;
bool rested;
- uint32_t flags;
+ volatile uint32_t flags;
+ uint32_t nleaves;
uintptr_t stack_base;
struct spinlock ksigq_lock;
+ TAILQ_HEAD(, proc) leafq;
+ TAILQ_ENTRY(proc) leaf_link;
TAILQ_HEAD(, ksiginfo) ksigq;
TAILQ_ENTRY(proc) link;
};
#define PROC_EXITING BIT(0) /* Exiting */
#define PROC_EXEC BIT(1) /* Exec called (cleared by sched) */
+#define PROC_ZOMB BIT(2) /* Zombie (dead but not deallocated) */
+#define PROC_LEAFQ BIT(3) /* Leaf queue is active */
+#define PROC_WAITED BIT(4) /* Being waited on by parent */
+#define PROC_KTD BIT(5) /* Kernel thread */
+#define PROC_SLEEP BIT(6) /* Thread execution paused */
+#define PROC_PINNED BIT(7) /* Pinned to CPU */
struct proc *this_td(void);
-int md_fork(struct proc *p, struct proc *parent, uintptr_t ip);
+struct proc *td_copy(struct proc *td);
+struct proc *get_child(struct proc *cur, pid_t pid);
+
+int proc_init(struct proc *td, struct proc *parent);
+void proc_pin(struct proc *td, affinity_t cpu);
+void proc_unpin(struct proc *td);
+
+void proc_reap(struct proc *td);
+void proc_coredump(struct proc *td, uintptr_t fault_addr);
+
+pid_t getpid(void);
+pid_t getppid(void);
+
+scret_t sys_getpid(struct syscall_args *scargs);
+scret_t sys_getppid(struct syscall_args *scargs);
+scret_t sys_waitpid(struct syscall_args *scargs);
+
+int md_spawn(struct proc *p, struct proc *parent, uintptr_t ip);
+
+scret_t sys_spawn(struct syscall_args *scargs);
+pid_t spawn(struct proc *cur, void(*func)(void), void *p, int flags, struct proc **newprocp);
-void md_td_stackinit(struct proc *td, void *stack_top, struct exec_prog *prog);
+uintptr_t md_td_stackinit(struct proc *td, void *stack_top, struct exec_prog *prog);
__dead void md_td_kick(struct proc *td);
int fork1(struct proc *cur, int flags, void(*ip)(void), struct proc **newprocp);
-int exit1(struct proc *td);
+int exit1(struct proc *td, int flags);
__dead scret_t sys_exit(struct syscall_args *scargs);
#endif /* _KERNEL */
diff --git a/sys/include/sys/queue.h b/sys/include/sys/queue.h
index e5d607d..92c1ff2 100644
--- a/sys/include/sys/queue.h
+++ b/sys/include/sys/queue.h
@@ -27,7 +27,12 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
+#if !defined(_KERNEL)
+#include <stdint.h>
+#include <stddef.h>
+#else
#include <sys/types.h>
+#endif /* !_KERNEL */
#ifndef _QUEUE_H_
#define _QUEUE_H_
diff --git a/sys/include/sys/reboot.h b/sys/include/sys/reboot.h
index 86fc45d..846073d 100644
--- a/sys/include/sys/reboot.h
+++ b/sys/include/sys/reboot.h
@@ -32,12 +32,15 @@
#include <sys/param.h>
#include <sys/cdefs.h>
+#include <sys/syscall.h>
-#define REBOOT_HALT BIT(0) /* Halt instead of rebooting */
-
-#if defined(_KERNEL)
+#define REBOOT_RESET 0x00000000
+#define REBOOT_HALT BIT(0) /* Halt instead of rebooting */
+#define REBOOT_POWEROFF BIT(1) /* Power off (needs REBOOT_HALT set too) */
void cpu_reboot(int method);
+#if defined(_KERNEL)
+scret_t sys_reboot(struct syscall_args *scargs);
#endif /* _KERNEL */
#endif /* _SYS_REBOOT_H_ */
diff --git a/sys/include/sys/sched.h b/sys/include/sys/sched.h
index 7f5e65f..7bba9df 100644
--- a/sys/include/sys/sched.h
+++ b/sys/include/sys/sched.h
@@ -32,11 +32,47 @@
#include <sys/proc.h>
#include <sys/cdefs.h>
+#include <sys/limits.h>
+#include <sys/time.h>
+
+/*
+ * Scheduler CPU information
+ *
+ * @nswitch: Number of context switches
+ * @idle: Number of milliseconds idle
+ */
+struct sched_cpu {
+ uint64_t nswitch;
+};
+
+/*
+ * Scheduler statistics
+ *
+ * @nproc: Number processes running
+ * @ncpu: Number of CPU cores
+ * @nhlt: Number of halted CPU cores
+ * @quantum_usec: Scheduler quantum (microseconds)
+ */
+struct sched_stat {
+ size_t nproc;
+ uint16_t ncpu;
+ uint16_t nhlt;
+ uint32_t quantum_usec;
+ struct sched_cpu cpus[CPU_MAX];
+};
#if defined(_KERNEL)
+void sched_stat(struct sched_stat *statp);
void sched_init(void);
+
+void sched_preempt_set(bool enable);
+bool sched_preemptable(void);
+
void sched_yield(void);
+void sched_suspend(struct proc *td, const struct timeval *tv);
+void sched_detach(struct proc *td);
+
__dead void sched_enter(void);
void sched_enqueue_td(struct proc *td);
diff --git a/sys/include/sys/schedvar.h b/sys/include/sys/schedvar.h
index 509e2c9..017fcb7 100644
--- a/sys/include/sys/schedvar.h
+++ b/sys/include/sys/schedvar.h
@@ -36,7 +36,7 @@
#include <machine/cdefs.h>
#if defined(_KERNEL)
-#define DEFAULT_TIMESLICE_USEC 500
+#define DEFAULT_TIMESLICE_USEC 9000
#define SHORT_TIMESLICE_USEC 10
#define SCHED_POLICY_MLFQ 0x00U /* Multilevel feedback queue */
@@ -60,5 +60,11 @@ struct sched_queue {
size_t nthread;
};
+struct proc *sched_dequeue_td(void);
+void mi_sched_switch(struct proc *from);
+
+void md_sched_switch(struct trapframe *tf);
+void sched_oneshot(bool now);
+
#endif /* _KERNEL */
#endif /* !_SYS_SCHEDVAR_H_ */
diff --git a/sys/include/sys/signal.h b/sys/include/sys/signal.h
index 9fc767d..eaf2d41 100644
--- a/sys/include/sys/signal.h
+++ b/sys/include/sys/signal.h
@@ -37,6 +37,7 @@
#define SIGFPE 8 /* Floating point exception */
#define SIGKILL 9 /* Kill */
#define SIGSEGV 11 /* Segmentation violation */
+#define SIGTERM 15 /* Terminate gracefully */
typedef uint32_t sigset_t;
@@ -80,5 +81,6 @@ int sigismember(const sigset_t *set, int signo);
void sigfpe_default(int signo);
void sigkill_default(int signo);
void sigsegv_default(int signo);
+void sigterm_default(int signo);
#endif /* _KERNEL */
#endif /* !_SYS_SIGNAL_H_ */
diff --git a/sys/include/sys/socket.h b/sys/include/sys/socket.h
new file mode 100644
index 0000000..1a33108
--- /dev/null
+++ b/sys/include/sys/socket.h
@@ -0,0 +1,202 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _SYS_SOCKET_H_
+#define _SYS_SOCKET_H_
+
+#include <sys/socketvar.h>
+#include <sys/queue.h>
+#include <sys/param.h>
+#include <sys/uio.h>
+#if defined(_KERNEL)
+#include <sys/types.h>
+#include <sys/proc.h>
+#include <sys/syscall.h>
+#include <sys/mutex.h>
+#else
+#include <stdint.h>
+#include <stddef.h>
+#endif /* _KERNEL */
+
+#ifndef _SA_FAMILY_T_DEFINED_
+#define _SA_FAMILY_T_DEFINED_
+typedef uint32_t sa_family_t;
+#endif /* _SA_FAMILY_T_DEFINED_ */
+
+#ifndef _SOCKLEN_T_DEFINED_
+#define _SOCKLEN_T_DEFINED_
+typedef uint32_t socklen_t;
+#endif /* !_SOCKLEN_T_DEFINED_ */
+
+/*
+ * Socket level number
+ */
+#define SOL_SOCKET 0xFFFF
+
+/*
+ * Address family defines
+ */
+#define AF_UNSPEC 0
+#define AF_UNIX 1
+#define AF_LOCAL AF_UNIX
+
+/* Socket types */
+#define SOCK_STREAM 1
+
+/* Socket option names */
+#define SO_RCVTIMEO 0 /* Max time recv(2) waits */
+#define _SO_MAX 1 /* Max socket options */
+
+struct sockaddr_un {
+ sa_family_t sun_family;
+ char sun_path[108];
+};
+
+struct sockaddr {
+ sa_family_t sa_family;
+ char sa_data[14];
+};
+
+/*
+ * POSIX message header for recvmsg()
+ * and sendmsg() calls.
+ */
+struct msghdr {
+ void *msg_name; /* Optional address */
+ socklen_t msg_namelen; /* Size of address */
+ struct iovec *msg_iov; /* Scatter/gather array */
+ int msg_iovlen; /* Members in msg_iov */
+ void *msg_control; /* Ancillary data, see below */
+ socklen_t msg_controllen; /* Ancillary data buffer len */
+ int msg_flags; /* Message flags */
+};
+
+/*
+ * POSIX control message header for
+ * ancillary data objects.
+ */
+struct cmsghdr {
+ socklen_t cmsg_len;
+ int cmsg_level;
+ int cmsg_type;
+};
+
+#define CMSG_SPACE(len) (MALIGN(sizeof(struct cmsghdr)) + MALIGN(len))
+
+/* Return pointer to cmsg data */
+#define CMSG_DATA(cmsg) PTR_OFFSET(cmsg, sizeof(struct cmsghdr))
+
+/* Return length of control message */
+#define CMSG_LEN(len) (MALIGN(sizeof(struct cmsghdr)) + MALIGN(len))
+
+/* Return pointer to next cmsghdr */
+#define CMSG_NXTHDR(mhdr, cmsg) \
+ PTR_OFFSET(cmsg, MALIGN((cmsg)>cmsg_len)) + \
+ MALIGN(sizeof(struct cmsghdr)) > \
+ PTR_OFFSET((mhdr)->msg_control, (mhdr)->msg_controllen) ? \
+ (struct cmsghdr *)NULL : \
+ (struct cmsghdr *)PTR_OFFSET(cmsg, MALIGN((cmsg)->cmsg_len))
+
+/* Return pointer to first header */
+#define CMSG_FIRSTHDR(mhdr) \
+ ((mhdr)->msg_controllen >= sizeof(struct cmsghdr) ? \
+ (struct cmsghdr *)(mhdr)->msg_control : \
+ (struct cmsghdr *)NULL);
+
+/* Socket level control messages */
+#define SCM_RIGHTS 0x01
+
+#if defined(_KERNEL)
+
+struct cmsg {
+ union {
+ struct cmsghdr hdr;
+ uint8_t buf[CMSG_SPACE(sizeof(int))];
+ };
+
+ size_t control_len;
+ TAILQ_ENTRY(cmsg) link;
+};
+
+/*
+ * List of cmsg headers and data, queued up
+ * during sendmsg()
+ */
+struct cmsg_list {
+ TAILQ_HEAD(, cmsg) list;
+ uint8_t is_init : 1;
+};
+
+/*
+ * Socket option that may be applied to
+ * sockets on the system.
+ */
+struct sockopt {
+ socklen_t len;
+ char data[];
+};
+
+struct ksocket {
+ int sockfd;
+ union {
+ struct sockaddr sockaddr;
+ struct sockaddr_un un;
+ };
+ struct sockopt *opt[_SO_MAX];
+ struct proc *owner;
+ struct cmsg_list cmsg_list;
+ struct sockbuf buf;
+ struct mutex *mtx;
+};
+
+scret_t sys_socket(struct syscall_args *scargs);
+scret_t sys_bind(struct syscall_args *scargs);
+scret_t sys_connect(struct syscall_args *scargs);
+
+scret_t sys_recv(struct syscall_args *scargs);
+scret_t sys_send(struct syscall_args *scargs);
+
+scret_t sys_recvmsg(struct syscall_args *scargs);
+scret_t sys_sendmsg(struct syscall_args *scargs);
+scret_t sys_setsockopt(struct syscall_args *scargs);
+#endif /* _KERNEL */
+
+int socket(int domain, int type, int protocol);
+int bind(int sockfd, const struct sockaddr *addr, socklen_t len);
+
+int setsockopt(int sockfd, int level, int name, const void *v, socklen_t len);
+int connect(int sockfd, const struct sockaddr *addr, socklen_t len);
+
+ssize_t send(int sockfd, const void *buf, size_t size, int flags);
+ssize_t recv(int sockfd, void *buf, size_t len, int flags);
+
+ssize_t sendmsg(int socket, const struct msghdr *msg, int flags);
+ssize_t recvmsg(int socket, struct msghdr *msg, int flags);
+
+#endif /* !_SYS_SOCKET_H_ */
diff --git a/sys/include/sys/socketvar.h b/sys/include/sys/socketvar.h
new file mode 100644
index 0000000..e090a70
--- /dev/null
+++ b/sys/include/sys/socketvar.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _SYS_SOCKETVAR_H_
+#define _SYS_SOCKETVAR_H_
+
+#include <sys/types.h>
+#if defined(_KERNEL)
+#include <net/netbuf.h>
+
+/*
+ * Socket buffer
+ *
+ * @buf: Actual data buffer
+ * @head: Buffer head
+ * @tail: Buffer tail
+ * @watermark: Max length
+ */
+struct sockbuf {
+ struct netbuf buf;
+ size_t head;
+ size_t tail;
+ size_t watermark;
+};
+
+#endif /* _KERNEL */
+#endif /* !_SYS_SOCKETVAR_H_ */
diff --git a/sys/include/sys/spawn.h b/sys/include/sys/spawn.h
new file mode 100644
index 0000000..28dbe5b
--- /dev/null
+++ b/sys/include/sys/spawn.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _SYS_SPAWN_H_
+#define _SYS_SPAWN_H_
+
+#include <sys/types.h>
+#include <sys/param.h>
+
+#if !defined(_KERNEL)
+pid_t spawn(const char *pathname, char **argv, char **envp, int flags);
+#endif /* _KERNEL */
+#endif /* !_SYS_SPAWN_H_ */
diff --git a/sys/include/sys/spinlock.h b/sys/include/sys/spinlock.h
index 140addc..b416152 100644
--- a/sys/include/sys/spinlock.h
+++ b/sys/include/sys/spinlock.h
@@ -44,9 +44,6 @@ void spinlock_release(struct spinlock *lock);
int spinlock_try_acquire(struct spinlock *lock);
int spinlock_usleep(struct spinlock *lock, size_t usec_max);
-/* System-wide locking (be careful!!) */
-int syslock(void);
-void sysrel(void);
#endif
#endif /* !_SYS_SPINLOCK_H_ */
diff --git a/sys/include/sys/stat.h b/sys/include/sys/stat.h
index 6303630..5409f2c 100644
--- a/sys/include/sys/stat.h
+++ b/sys/include/sys/stat.h
@@ -32,6 +32,8 @@
#include <sys/types.h>
+#define S_IFBLK 0060000
+
struct stat {
dev_t st_dev;
ino_t st_ino;
@@ -46,4 +48,6 @@ struct stat {
time_t st_ctime;
};
+int stat(const char *path, struct stat *buf);
+
#endif /* _SYS_STAT_H_ */
diff --git a/sys/include/sys/syscall.h b/sys/include/sys/syscall.h
index 3f3a8b3..604f937 100644
--- a/sys/include/sys/syscall.h
+++ b/sys/include/sys/syscall.h
@@ -33,7 +33,9 @@
#if !defined(__ASSEMBLER__)
#include <sys/types.h>
#include <sys/cdefs.h>
+#if defined(_KERNEL) || defined(_OLIBC)
#include <machine/syscall.h>
+#endif /* _KERNEL || _OLIBC */
#endif
#define SYS_none 0
@@ -44,6 +46,28 @@
#define SYS_stat 5
#define SYS_sysctl 6
#define SYS_write 7
+#define SYS_spawn 8
+#define SYS_reboot 9
+#define SYS_mmap 10
+#define SYS_munmap 11
+#define SYS_access 12
+#define SYS_lseek 13
+#define SYS_sleep 14
+#define SYS_inject 15
+#define SYS_getpid 16
+#define SYS_getppid 17
+#define SYS_setuid 18
+#define SYS_getuid 19
+#define SYS_waitpid 20
+#define SYS_socket 21
+#define SYS_bind 22
+#define SYS_recv 23
+#define SYS_send 24
+#define SYS_sendmsg 25
+#define SYS_recvmsg 26
+#define SYS_connect 27
+#define SYS_setsockopt 28
+#define SYS_disk 29
#if defined(_KERNEL)
/* Syscall return value and arg type */
diff --git a/sys/include/sys/sysctl.h b/sys/include/sys/sysctl.h
index 078135b..ce7510d 100644
--- a/sys/include/sys/sysctl.h
+++ b/sys/include/sys/sysctl.h
@@ -30,16 +30,35 @@
#ifndef _SYS_SYSCTL_H_
#define _SYS_SYSCTL_H_
-#include <sys/types.h>
#if defined(_KERNEL)
+#include <sys/types.h>
#include <sys/syscall.h>
+#else
+#include <stdint.h>
+#include <stddef.h>
#endif
#include <sys/param.h>
+/*
+ * List of 'kern.* ' identifiers
+ */
#define KERN_OSTYPE 0
#define KERN_OSRELEASE 1
#define KERN_VERSION 2
#define KERN_VCACHE_TYPE 3
+#define KERN_HOSTNAME 4
+
+/*
+ * List of 'hw.* ' identifiers
+ */
+#define HW_PAGESIZE 5
+#define HW_NCPU 6
+#define HW_MACHINE 7
+
+/*
+ * List of 'proc.*' identifiers
+ */
+#define PROC_COUNT 8
/*
* Option types (i.e., int, string, etc) for
@@ -61,6 +80,7 @@ struct sysctl_entry {
};
scret_t sys_sysctl(struct syscall_args *scargs);
+int sysctl_clearstr(int name);
#endif /* _KERNEL */
/*
diff --git a/sys/include/sys/syslog.h b/sys/include/sys/syslog.h
index defb341..b9d34ab 100644
--- a/sys/include/sys/syslog.h
+++ b/sys/include/sys/syslog.h
@@ -31,11 +31,13 @@
#define _SYS_SYSLOG_H_
#include <stdarg.h>
+#include <stdbool.h>
#if defined(_KERNEL)
#define OMIT_TIMESTAMP "\x01"
+void syslog_silence(bool option);
void kprintf(const char *fmt, ...);
void serial_init(void);
void serial_putc(char c);
diff --git a/sys/include/sys/systm.h b/sys/include/sys/systm.h
index 42e1723..2f69175 100644
--- a/sys/include/sys/systm.h
+++ b/sys/include/sys/systm.h
@@ -39,6 +39,7 @@
int copyin(const void *uaddr, void *kaddr, size_t len);
int copyout(const void *kaddr, void *uaddr, size_t len);
int copyinstr(const void *uaddr, char *kaddr, size_t len);
+int cpu_report_count(uint32_t count);
__always_inline static inline void
__sigraise(int signo)
diff --git a/sys/include/sys/termios.h b/sys/include/sys/termios.h
index 27339f1..a3ba794 100644
--- a/sys/include/sys/termios.h
+++ b/sys/include/sys/termios.h
@@ -33,8 +33,33 @@
/*
* c_iflag: Input flags
*/
-#define ISTRIP 0x00000000
-#define ICRNL 0x00000001
+#define ISTRIP 0x00000001 /* Strip char */
+#define ICRNL 0x00000002 /* Map CR to NL */
+#define BRKINT 0x00000004 /* Signal interrupt on break */
+#define IGNBRK 0x00000008 /* Ignore break condition */
+#define IGNCR 0x00000010 /* Ignore CR */
+#define IGNPAR 0x00000020 /* Ignore chars with parity errors */
+#define INCLR 0x00000040 /* Map NL to CR */
+#define INPCK 0x00000080 /* Enable input parity check */
+#define IXANY 0x00000100 /* Enable any char to restart output */
+#define IXOFF 0x00000200 /* Enable start/stop control */
+#define PARMRK 0x00000400 /* Mark parity errors */
+
+/*
+ * c_oflag: Output flags
+ */
+#define OPOST 0x00000001 /* Post-process output */
+#define ONLCR 0x00000002 /* Map NL to CR-NL on output */
+#define OCRNL 0x00000004 /* Map CR to NL on output */
+#define ONOCR 0x00000008 /* Map CR to output at col 0 */
+#define ONLRET 0x00000010 /* NL performs CR function */
+#define OFILL 0x00000020 /* Use fill chars for delay */
+#define NLDLY 0x00000040 /* Select newline type */
+#define CRDLY 0x00000080 /* Select carriage-return delays */
+#define TABDLY 0x00000100 /* Select horizontal-tab delays */
+#define BSDLY 0x00000200 /* Select backspace delays */
+#define VTDLY 0x00000400 /* Select veritcal tab delays */
+#define FFDLY 0x00000800 /* Select form-feed delays */
#define NCCS 20
diff --git a/sys/include/sys/time.h b/sys/include/sys/time.h
new file mode 100644
index 0000000..ce66885
--- /dev/null
+++ b/sys/include/sys/time.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _SYS_TIME_H_
+#define _SYS_TIME_H_
+
+#include <sys/types.h>
+#if defined(_KERNEL)
+#include <sys/syscall.h>
+#endif /* _KERNEL */
+
+struct timeval {
+ time_t tv_sec;
+ time_t tv_usec;
+};
+
+struct timespec {
+ time_t tv_sec;
+ long tv_nsec;
+};
+
+struct date {
+ uint16_t year;
+ uint8_t month;
+ uint8_t day;
+ uint8_t sec;
+ uint8_t min;
+ uint8_t hour;
+};
+
+#if defined(_KERNEL)
+scret_t sys_sleep(struct syscall_args *scargs);
+#endif
+#endif /* !_SYS_TIME_H_ */
diff --git a/sys/include/sys/types.h b/sys/include/sys/types.h
index 5501cc3..223f455 100644
--- a/sys/include/sys/types.h
+++ b/sys/include/sys/types.h
@@ -36,8 +36,7 @@
/* Compat */
#if defined(_KERNEL)
-#define true 1
-#define false 0
+#include <stdbool.h>
#if !defined(NULL)
#define NULL ((void *)0)
#endif /* !NULL */
@@ -83,11 +82,11 @@ typedef __uint64_t uint64_t;
#endif
#if __SIZEOF_SIZE_T__ == 8
-typedef uint64_t __size_t;
-typedef int64_t __ssize_t; /* Byte count or error */
+typedef __uint64_t __size_t;
+typedef __int64_t __ssize_t; /* Byte count or error */
#elif __SIZEOF_SIZE_T__ == 4
-typedef uint32_t __size_t;
-typedef int32_t __ssize_t; /* Byte count or error */
+typedef __uint32_t __size_t;
+typedef __int32_t __ssize_t; /* Byte count or error */
#else
#error "Unsupported size_t size"
#endif
@@ -101,23 +100,19 @@ typedef __size_t uintptr_t;
typedef __size_t off_t;
typedef int pid_t;
typedef int dev_t;
-typedef uint32_t mode_t;
-typedef uint32_t ino_t;
-typedef uint32_t nlink_t;
-typedef uint32_t uid_t;
-typedef uint32_t gid_t;
-typedef uint32_t blksize_t;
-typedef uint32_t blkcnt_t;
-typedef uint64_t time_t;
+typedef __uint32_t uid_t;
+typedef __uint32_t mode_t;
+typedef __uint32_t ino_t;
+typedef __uint32_t nlink_t;
+typedef __uint32_t uid_t;
+typedef __uint32_t gid_t;
+typedef __uint32_t blksize_t;
+typedef __uint32_t blkcnt_t;
+typedef __uint64_t time_t;
#if defined(_HAVE_PTRDIFF_T)
typedef __ptrdiff_t ptrdiff_t;
#endif /* _HAVE_PTRDIFF_T */
-/* Compat */
-#if defined(_KERNEL)
-typedef _Bool bool;
-#endif
-
#if defined(_KERNEL)
typedef uintptr_t paddr_t;
typedef uintptr_t vaddr_t;
diff --git a/sys/include/sys/ucred.h b/sys/include/sys/ucred.h
new file mode 100644
index 0000000..f8cbbe0
--- /dev/null
+++ b/sys/include/sys/ucred.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _SYS_UCRED_H_
+#define _SYS_UCRED_H_
+
+#include <sys/types.h>
+#if defined(_KERNEL)
+#include <sys/spinlock.h>
+#include <sys/syscall.h>
+#endif
+
+/*
+ * Kernel structure for user credentials
+ */
+struct ucred {
+ uid_t euid;
+ uid_t ruid;
+#if defined(_KERNEL)
+ struct spinlock lock;
+#endif /* _KERNEL */
+};
+
+int setuid(uid_t new);
+uid_t getuid(void);
+
+#if defined(_KERNEL)
+scret_t sys_setuid(struct syscall_args *scargs);
+scret_t sys_getuid(struct syscall_args *scargs);
+#endif
+#endif /* !_SYS_UCRED_H_ */
diff --git a/sys/include/sys/uio.h b/sys/include/sys/uio.h
new file mode 100644
index 0000000..4318a53
--- /dev/null
+++ b/sys/include/sys/uio.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _SYS_UIO_H_
+#define _SYS_UIO_H_
+
+#if defined(_KERNEL)
+#include <sys/types.h>
+#else
+#include <stdint.h>
+#include <stddef.h>
+#endif /* _KERNEL */
+
+/*
+ * POSIX I/O vector
+ */
+struct iovec {
+ void *iov_base;
+ size_t iov_len;
+};
+
+ssize_t readv(int filedes, const struct iovec *iov, int iovcnt);
+ssize_t writev(int filedes, const struct iovec *iov, int iovcnt);
+
+#if defined(_KERNEL)
+
+int uio_copyin(const struct iovec *u_iov, struct iovec *k_iov, int iovcnt);
+int uio_copyout(const struct iovec *k_iov, struct iovec *u_iov, int iovcnt);
+void uio_copyin_clean(struct iovec *copy, int iovcnt);
+
+#endif /* _KERNEL */
+#endif /* !_SYS_UIO_H_ */
diff --git a/sys/include/sys/vfs.h b/sys/include/sys/vfs.h
index 1ff722a..fcb7391 100644
--- a/sys/include/sys/vfs.h
+++ b/sys/include/sys/vfs.h
@@ -40,6 +40,7 @@ scret_t sys_close(struct syscall_args *args);
scret_t sys_read(struct syscall_args *scargs);
scret_t sys_write(struct syscall_args *sargs);
scret_t sys_stat(struct syscall_args *scargs);
+scret_t sys_access(struct syscall_args *scargs);
#endif /* _KERNEL */
#endif /* !_SYS_VFS_H_ */
diff --git a/sys/include/sys/vmstat.h b/sys/include/sys/vmstat.h
new file mode 100644
index 0000000..b7faeb2
--- /dev/null
+++ b/sys/include/sys/vmstat.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _SYS_VMSTAT_H_
+#define _SYS_VMSTAT_H_
+
+#include <sys/types.h>
+
+/*
+ * Virtual memory statistics
+ *
+ * @mem_avail: Available memory in MiB
+ * @mem_used: Allocated memory in MiB
+ * @mem_total: Total system memory in MiB
+ */
+struct vm_stat {
+ uint32_t mem_avail;
+ uint32_t mem_used;
+ size_t mem_total;
+};
+
+#endif /* !_VM_STAT_H_ */
diff --git a/sys/include/sys/vnode.h b/sys/include/sys/vnode.h
index 33092f9..ff6f995 100644
--- a/sys/include/sys/vnode.h
+++ b/sys/include/sys/vnode.h
@@ -32,11 +32,11 @@
#include <sys/types.h>
#include <sys/queue.h>
+#include <sys/vnode.h>
#include <sys/atomic.h>
#include <sys/sio.h>
-#include <vm/vm_obj.h>
-
#if defined(_KERNEL)
+#include <vm/vm_obj.h>
struct vops;
@@ -47,6 +47,8 @@ struct vnode {
const struct vops *vops;
struct vm_object vobj;
uint32_t refcount;
+ dev_t major;
+ dev_t dev;
TAILQ_ENTRY(vnode) vcache_link;
};
@@ -74,6 +76,7 @@ struct vcache {
#define VDIR 0x02 /* Directory */
#define VCHR 0x03 /* Character device */
#define VBLK 0x04 /* Block device */
+#define VSOCK 0x05 /* Socket */
#define VNOVAL -1
@@ -83,6 +86,23 @@ struct vop_lookup_args {
struct vnode **vpp; /* Result vnode */
};
+struct vop_create_args {
+ const char *path; /* Full path */
+ const char *ppath; /* Parent path */
+ struct vnode *dirvp; /* Directory vnode */
+ struct vnode **vpp; /* Result vnode */
+};
+
+struct vop_getattr_args {
+ struct vnode *vp; /* Target vnode */
+ struct vattr *res; /* Result vattr */
+};
+
+struct vop_readdir_args {
+ struct vnode *vp; /* Target vnode */
+ struct sio_txn *sio; /* SIO data to read into */
+};
+
/*
* A field in this structure is unavailable
* if it has a value of VNOVAL.
@@ -92,34 +112,33 @@ struct vattr {
size_t size;
};
-struct vop_getattr_args {
- struct vnode *vp;
- struct vattr *res;
-};
-
struct vops {
int(*lookup)(struct vop_lookup_args *args);
int(*getattr)(struct vop_getattr_args *args);
+ int(*readdir)(struct vop_readdir_args *args);
int(*read)(struct vnode *vp, struct sio_txn *sio);
int(*write)(struct vnode *vp, struct sio_txn *sio);
int(*reclaim)(struct vnode *vp);
+ int(*create)(struct vop_create_args *args);
};
extern struct vnode *g_root_vnode;
+/* Vnode cache operations */
int vfs_vcache_type(void);
int vfs_vcache_migrate(int newtype);
-
int vfs_vcache_enter(struct vnode *vp);
struct vnode *vfs_recycle_vnode(void);
+/* Vnode operations */
int vfs_alloc_vnode(struct vnode **res, int type);
int vfs_release_vnode(struct vnode *vp);
-int vfs_vop_lookup(struct vnode *vp, struct vop_lookup_args *args);
+/* Vnode operation wrappers */
+int vfs_vop_lookup(struct vop_lookup_args *args);
+int vfs_vop_getattr(struct vop_getattr_args *args);
int vfs_vop_read(struct vnode *vp, struct sio_txn *sio);
int vfs_vop_write(struct vnode *vp, struct sio_txn *sio);
-int vfs_vop_getattr(struct vnode *vp, struct vop_getattr_args *args);
#endif /* _KERNEL */
#endif /* !_SYS_VNODE_H_ */
diff --git a/sys/include/sys/vsr.h b/sys/include/sys/vsr.h
new file mode 100644
index 0000000..e63cce1
--- /dev/null
+++ b/sys/include/sys/vsr.h
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _SYS_VSR_H_
+#define _SYS_VSR_H_
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/param.h>
+#include <sys/ucred.h>
+#include <sys/limits.h>
+#if defined(_KERNEL)
+#include <sys/mutex.h>
+#endif /* _KERNEL */
+
+struct proc;
+
+#define VSR_FILE 0x00000000 /* Represented by file */
+
+/*
+ * Defines the access semantics of whether
+ * r/w operations should be passed down to the
+ * global state or soley affecting a per-process
+ * shallow copy.
+ */
+typedef uint32_t vsr_mode_t;
+
+/*
+ * The Virtual System Resource namespace consists of
+ * domains containing named "capsules". The domain is
+ * simply a table indexed by a type value e.g. VSR_FILE
+ * and a capsule is simply a structure containing global data
+ * as well as a shallow copy which is controlled locally by the
+ * process. The capsule also contains various access semantics
+ * that help the VSR subsystem determine whether the access should
+ * be passed down globally or virtualized locally within the process.
+ */
+typedef uint8_t vsr_domain_t;
+
+/*
+ * VSR mode bits
+ */
+#define VSR_GLOB_WRITE BIT(0) /* Writes are global */
+#define VSR_GLOB_READ BIT(1) /* Reads are global */
+#define VSR_GLOB_CRED BIT(2) /* Global for specific creds */
+
+#if defined(_KERNEL)
+
+struct vsr_capsule;
+
+/*
+ * VSR capsule operations
+ *
+ * @reclaim: Cleanup resources
+ */
+struct capsule_ops {
+ int(*reclaim)(struct vsr_capsule *cap, int flags);
+};
+
+/*
+ * Virtual system resource access
+ * semantics.
+ *
+ * @glob: Global data
+ * @shallow: Local per process copy
+ * @mode: VSR mode (see VSR_GLOB_*)
+ * @cred: Creds (used if VSR_GLOBAL_CRED set)
+ */
+struct vsr_access {
+ void *glob;
+ void *shallow;
+ vsr_mode_t mode;
+ struct ucred cred;
+};
+
+/*
+ * A virtual system resource capsule containing
+ * resource owner specific data and hashmap
+ * buckets.
+ *
+ * @name: Capsule name (e.g., "consfeat"), must be freed
+ * @data: Owner specific data
+ * @shadow: Local shadow copy (per-process)
+ * @buckets: Hashmap buckets
+ * @link: Bucket link
+ * @ops: Capsule operations
+ * @lock: Mutex lock protecting fields
+ */
+struct vsr_capsule {
+ char *name;
+ void *data;
+ void *shadow;
+ TAILQ_HEAD(, vsr_capsule) buckets;
+ TAILQ_ENTRY(vsr_capsule) link;
+ struct capsule_ops ops;
+ struct mutex lock;
+};
+
+/*
+ * Virtual system resource table containg
+ * VSRs for various types.
+ *
+ * Each VSR table belongs to a VSR domain
+ * (e.g., VSR_FILE).
+ *
+ * @ncaps: Number of capsules
+ * @is_init: Set if hashmap is set up
+ * @capsules: VSR capsule hashmap
+ */
+struct vsr_table {
+ struct vsr_capsule *capsules[VSR_MAX_CAPSULE];
+};
+
+/*
+ * Virtual system resource domain (VSR).
+ *
+ * A VSR is represented by a specific VSR type
+ * (see VSR_*). Each VSR has a table of VSR capsules
+ * looked up by a VSR capsule name.
+ *
+ * One per process.
+ *
+ * @type: VSR type
+ * @table: VSR table
+ */
+struct vsr_domain {
+ int type;
+ struct vsr_table table;
+};
+
+void vsr_init_domains(struct proc *td);
+void vsr_destroy_domains(struct proc *td);
+
+struct vsr_domain *vsr_new_domain(struct proc *td, vsr_domain_t type);
+struct vsr_capsule *vsr_new_capsule(struct proc *td, vsr_domain_t type, const char *name);
+struct vsr_capsule *vsr_lookup_capsule(struct proc *td, vsr_domain_t type, const char *name);
+
+#endif /* _KERNEL */
+#endif /* !_SYS_VSR_H_ */
diff --git a/sys/include/sys/wait.h b/sys/include/sys/wait.h
new file mode 100644
index 0000000..07a2d4e
--- /dev/null
+++ b/sys/include/sys/wait.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _SYS_WAIT_H_
+#define _SYS_WAIT_H_
+
+#include <sys/types.h>
+
+pid_t waitpid(pid_t pid, int *wstatus, int options);
+
+#endif /* !_SYS_WAIT_H_ */
diff --git a/sys/include/sys/workqueue.h b/sys/include/sys/workqueue.h
new file mode 100644
index 0000000..9925f79
--- /dev/null
+++ b/sys/include/sys/workqueue.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _SYS_WORKQUEUE_H_
+#define _SYS_WORKQUEUE_H_
+
+#if defined(_KERNEL)
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/mutex.h>
+#include <sys/proc.h>
+
+struct workqueue;
+struct work;
+
+/*
+ * A work function can either refer to a work thread
+ * entry (or actual work to be done
+ */
+typedef void(*workfunc_t)(struct workqueue *wqp, struct work *wp);
+
+/*
+ * Represents work that may be added to a
+ * workqueue.
+ *
+ * @name: Name of this work/task [i]
+ * @data: Optional data to be passed with work [p]
+ * @func: Function with work to be done [p]
+ * @cookie: Used for validating the work structure [i]
+ *
+ * Field attributes:
+ * - [i]: Used internally
+ * - [p]: Used as parameter
+ */
+struct work {
+ char *name;
+ void *data;
+ workfunc_t func;
+ TAILQ_ENTRY(work) link;
+};
+
+/*
+ * A workqueue contains tasks that are
+ * queued up to be completed in their own
+ * thread context.
+ *
+ * @name: Name of workqueue.
+ * @work: Start of the workqueue
+ * @ipl: IPL that work here must run with
+ * @max_work: Max number of jobs that can be queued
+ * @nwork: Number of tasks to be done
+ * @cookie: For validating workqueues
+ * @worktd: Thread associated with the workqueue
+ * @lock: Protects the workqueue
+ */
+struct workqueue {
+ char *name;
+ TAILQ_HEAD(, work) work;
+ uint8_t ipl;
+ size_t max_work;
+ ssize_t nwork;
+ uint16_t cookie;
+ struct proc *worktd;
+ struct mutex *lock;
+};
+
+struct workqueue *workqueue_new(const char *name, size_t max_work, int ipl);
+
+int workqueue_enq(struct workqueue *wqp, const char *name, struct work *wp);
+int workqueue_destroy(struct workqueue *wqp);
+int work_destroy(struct work *wp);
+
+#endif /* !_KERNEL */
+#endif /* !_SYS_WORKQUEUE_H_ */
diff --git a/sys/include/vm/physmem.h b/sys/include/vm/physmem.h
index ae11530..3f1da61 100644
--- a/sys/include/vm/physmem.h
+++ b/sys/include/vm/physmem.h
@@ -32,6 +32,10 @@
#include <sys/types.h>
+uint32_t vm_mem_used(void);
+uint32_t vm_mem_free(void);
+size_t vm_mem_total(void);
+
void vm_physmem_init(void);
uintptr_t vm_alloc_frame(size_t count);
void vm_free_frame(uintptr_t base, size_t count);
diff --git a/sys/include/vm/pmap.h b/sys/include/vm/pmap.h
index 9eed184..e0549d4 100644
--- a/sys/include/vm/pmap.h
+++ b/sys/include/vm/pmap.h
@@ -76,9 +76,25 @@ int pmap_map(struct vas vas, vaddr_t va, paddr_t pa, vm_prot_t prot);
int pmap_unmap(struct vas vas, vaddr_t va);
/*
+ * Returns true if the page is clean (modified), otherwise
+ * returns false.
+ */
+bool pmap_is_clean(struct vas vas, vaddr_t va);
+
+/*
+ * Marks a page as clean (unmodified)
+ */
+void pmap_mark_clean(struct vas vas, vaddr_t va);
+
+/*
* Mark a virtual address with a specific
* caching type.
*/
int pmap_set_cache(struct vas vas, vaddr_t va, int type);
+/*
+ * Machine dependent pmap init code.
+ */
+int pmap_init(void);
+
#endif /* !_VM_PMAP_H_ */
diff --git a/sys/include/vm/stat.h b/sys/include/vm/stat.h
new file mode 100644
index 0000000..7e9a4a9
--- /dev/null
+++ b/sys/include/vm/stat.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _VM_STAT_H_
+#define _VM_STAT_H_
+
+#include <sys/types.h>
+#include <sys/vmstat.h>
+
+int vm_stat_get(struct vm_stat *vmstat);
+void vm_stat_init(void);
+
+#endif /* !_VM_STAT_H_ */
diff --git a/sys/include/vm/vm_device.h b/sys/include/vm/vm_device.h
new file mode 100644
index 0000000..da476e2
--- /dev/null
+++ b/sys/include/vm/vm_device.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _VM_DEVICE_H_
+#define _VM_DEVICE_H_
+
+#include <sys/types.h>
+#include <sys/vnode.h>
+#include <sys/device.h>
+#include <vm/vm_pager.h>
+#include <vm/vm_obj.h>
+
+extern const struct vm_pagerops vm_vnops;
+
+struct vm_object *dv_attach(devmajor_t major, dev_t dev, vm_prot_t prot);
+
+#endif /* !_VM_DEVICE_H_ */
diff --git a/sys/kern/disk_engine.c b/sys/kern/disk_engine.c
new file mode 100644
index 0000000..1061165
--- /dev/null
+++ b/sys/kern/disk_engine.c
@@ -0,0 +1,208 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/syscall.h>
+#include <sys/syslog.h>
+#include <sys/systm.h>
+#include <sys/disk.h>
+#include <vm/dynalloc.h>
+
+#define pr_trace(fmt, ...) kprintf("disk: " fmt, ##__VA_ARGS__)
+#define pr_error(...) pr_trace(__VA_ARGS__)
+
+/*
+ * Clones a disk parameter structure passed
+ * by a user. The structure returned is safe
+ * to be accessed freely by the kernel.
+ *
+ * @u_param: Contains user-side pointer
+ * @res: Resulting safe data
+ *
+ * Returns zero on success, otherwise a less than
+ * zero value is returned.
+ */
+static int
+disk_param_clone(struct disk_param *u_param, struct disk_param *res)
+{
+ void *data;
+ int error;
+
+ if (u_param == NULL) {
+ pr_error("disk_param_clone: got NULL u_param\n");
+ return -EINVAL;
+ }
+
+ error = copyin(u_param, res, sizeof(*res));
+ if (error < 0) {
+ return error;
+ }
+
+ /*
+ * If these parameters do not have a valid cookie, fuck
+ * that object, something is not right with it...
+ */
+ if (res->cookie != DISK_PARAM_COOKIE) {
+ pr_error("disk_param_clone: erroneous params (bad cookie)\n");
+ return -EACCES;
+ }
+
+ data = dynalloc(res->size);
+ if (data == NULL) {
+ pr_error("disk_param_clone: out of memory\n");
+ return -ENOMEM;
+ }
+
+ error = copyin(res->buf, data, res->size);
+ if (error < 0) {
+ pr_error("failed to copy in param data\n");
+ dynfree(data);
+ return error;
+ }
+
+ res->u_buf = res->buf;
+ res->buf = data;
+ return 0;
+}
+
+/*
+ * Deallocate a kernel managed disk parameter
+ * structure created by disk_param_clone()
+ *
+ * @param: Params to free
+ *
+ * Returns zero on success, otherwise a less than
+ * zero value is returned.
+ */
+static int
+disk_param_free(struct disk_param *param)
+{
+ if (param == NULL) {
+ return -EINVAL;
+ }
+
+ if (param->cookie != DISK_PARAM_COOKIE) {
+ return -EACCES;
+ }
+
+ dynfree(param->buf);
+ return 0;
+}
+
+/*
+ * Perform an operation on a disk.
+ *
+ * @id: ID of disk to operate on
+ * @opcode: Operation to perform (see DISK_IO_*)
+ * @u_param: User side disk parameters
+ *
+ * Returns a less than zero value on error
+ */
+static ssize_t
+disk_mux_io(diskid_t id, diskop_t opcode, struct disk_param *u_param)
+{
+ struct disk_param param;
+ struct disk *dp;
+ ssize_t retval = -EIO;
+ int error;
+
+ if (u_param == NULL) {
+ return -EINVAL;
+ }
+
+ error = disk_param_clone(u_param, &param);
+ if (error < 0) {
+ return error;
+ }
+
+ /* First, attempt to acquire the disk */
+ error = disk_get_id(id, &dp);
+ if (error < 0) {
+ pr_error("disk_mux_io: no such device (id=%d)\n", id);
+ return error;
+ }
+
+ switch (opcode) {
+ case DISK_IO_READ:
+ retval = disk_read(
+ id,
+ param.blk,
+ param.buf,
+ param.size
+ );
+
+ /* Write back the data to the user program */
+ error = copyout(param.buf, param.u_buf, param.size);
+ if (error < 0) {
+ retval = error;
+ }
+ break;
+ case DISK_IO_WRITE:
+ retval = disk_write(
+ id,
+ param.blk,
+ param.buf,
+ param.size
+ );
+ break;
+ case DISK_IO_QUERY:
+ retval = disk_query(
+ id,
+ param.buf
+ );
+
+ /* Write back info to user program */
+ error = copyout(param.buf, param.u_buf, param.size);
+ if (error < 0) {
+ retval = error;
+ }
+ break;
+ }
+
+ disk_param_free(&param);
+ return retval;
+}
+
+/*
+ * Disk I/O multiplexer syscall
+ *
+ * arg0: disk id
+ * arg1: opcode
+ * arg2: disk params
+ */
+scret_t
+sys_disk(struct syscall_args *scargs)
+{
+ struct disk_param *u_param = (void *)scargs->arg2;
+ diskid_t id = scargs->arg0;
+ diskop_t opcode = scargs->arg1;
+
+ return disk_mux_io(id, opcode, u_param);
+}
diff --git a/sys/kern/driver_blacklist.c b/sys/kern/driver_blacklist.c
new file mode 100644
index 0000000..982d5c9
--- /dev/null
+++ b/sys/kern/driver_blacklist.c
@@ -0,0 +1,170 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/queue.h>
+#include <sys/driver.h>
+#include <vm/dynalloc.h>
+#include <string.h>
+
+#define BLACKLIST_SIZE 64
+
+/*
+ * A driver blacklist entry
+ *
+ * @name: Name of driver to be blacklisted
+ * @buckets: To handle collisions
+ */
+struct blacklist_entry {
+ char *name;
+ TAILQ_ENTRY(blacklist_entry) link;
+ TAILQ_HEAD(, blacklist_entry) buckets;
+};
+
+static struct blacklist_entry blacklist[BLACKLIST_SIZE];
+
+static uint32_t
+fnv1_hash(const char *s)
+{
+ uint32_t hash = 2166136261UL;
+ const uint8_t *p = (uint8_t *)s;
+
+ while (*p != '\0') {
+ hash ^= *p;
+ hash = hash * 0x01000193;
+ ++p;
+ }
+
+ return hash;
+}
+
+/*
+ * Returns a bucket in case of collision
+ */
+static struct blacklist_entry *
+blacklist_collide(struct blacklist_entry *entp, const char *name)
+{
+ struct blacklist_entry *tmp;
+
+ if (entp->name == NULL) {
+ return NULL;
+ }
+
+ TAILQ_FOREACH(tmp, &entp->buckets, link) {
+ if (strcmp(name, tmp->name) == 0) {
+ return tmp;
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * Mark a driver to be ignored during startup.
+ * Blacklisted drivers will not be ran.
+ *
+ * @name: Name of driver (e.g., 'ahci')
+ */
+int
+driver_blacklist(const char *name)
+{
+ struct blacklist_entry *ent;
+ struct blacklist_entry *bucket;
+ size_t name_len;
+ uint32_t hash;
+
+ if (name == NULL) {
+ return -EINVAL;
+ }
+
+ hash = fnv1_hash(name);
+ ent = &blacklist[hash % BLACKLIST_SIZE];
+ if (ent->name != NULL) {
+ bucket = dynalloc(sizeof(*bucket));
+ if (bucket == NULL) {
+ return -EINVAL;
+ }
+ TAILQ_INSERT_TAIL(&ent->buckets, bucket, link);
+ return 0;
+ }
+
+ name_len = strlen(name);
+ ent->name = dynalloc(name_len + 1);
+ if (ent->name == NULL) {
+ return -ENOMEM;
+ }
+ memcpy(ent->name, name, name_len + 1);
+ return 0;
+}
+
+/*
+ * Checks if a driver name is in the blacklist.
+ * Returns 0 if not, otherwise 1.
+ */
+int
+driver_blacklist_check(const char *name)
+{
+ struct blacklist_entry *ent;
+ uint32_t hash;
+
+ if (name == NULL) {
+ return -EINVAL;
+ }
+
+ hash = fnv1_hash(name);
+ ent = &blacklist[hash % BLACKLIST_SIZE];
+ if (ent->name == NULL) {
+ return 0;
+ }
+
+ if (strcmp(ent->name, name) == 0) {
+ return 1;
+ }
+
+ ent = blacklist_collide(ent, name);
+ if (ent != NULL) {
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * Initialize each entry in the driver
+ * blacklist
+ */
+void
+driver_blacklist_init(void)
+{
+ for (size_t i = 0; i < BLACKLIST_SIZE; ++i) {
+ blacklist[i].name = NULL;
+ TAILQ_INIT(&blacklist[i].buckets);
+ }
+}
diff --git a/sys/kern/driver_subr.c b/sys/kern/driver_subr.c
new file mode 100644
index 0000000..a0f9f73
--- /dev/null
+++ b/sys/kern/driver_subr.c
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/driver.h>
+#include <sys/proc.h>
+#include <sys/cdefs.h>
+#include <sys/syslog.h>
+#include <sys/panic.h>
+#include <dev/timer.h>
+#include <machine/sync.h>
+
+/*
+ * Initialize early drivers
+ *
+ * XXX: This should *NOT* be called directly,
+ * use DRIVERS_SCHED() instead.
+ */
+void
+__driver_init_td(void)
+{
+ const struct driver *dp;
+ struct driver_var *var;
+ struct proc *td;
+ uintptr_t start, end;
+
+ td = this_td();
+ start = (uintptr_t)__driversd_init_start;
+ end = (uintptr_t)__driversd_init_end;
+
+ for (dp = (void *)start; (uintptr_t)dp < end; ++dp) {
+ var = dp->data;
+
+ /*
+ * Check the blacklist to see if this driver
+ * is marked to be ignored. If so, just continue
+ * to the next.
+ */
+ if (driver_blacklist_check(dp->name)) {
+ continue;
+ }
+
+ if (var->deferred) {
+ dp->init();
+ var->deferred = 0;
+ }
+ }
+
+ exit1(td, 0);
+ __builtin_unreachable();
+}
diff --git a/sys/kern/exec_elf64.c b/sys/kern/exec_elf64.c
index 3767b0b..8dc87dc 100644
--- a/sys/kern/exec_elf64.c
+++ b/sys/kern/exec_elf64.c
@@ -49,11 +49,43 @@
#define PHDR(HDRP, IDX) \
(void *)((uintptr_t)HDRP + (HDRP)->e_phoff + (HDRP->e_phentsize * IDX))
+#define SHDR(HDRP, IDX) \
+ (void *)((uintptr_t)HDRP + (HDRP)->e_shoff + (HDRP->e_shentsize * IDX))
+
struct elf_file {
char *data;
size_t size;
};
+static int
+elf_parse_shdrs(Elf64_Ehdr *eh)
+{
+ Elf64_Shdr *shp;
+ uint32_t nshdr;
+
+ if (eh == NULL) {
+ return -EINVAL;
+ }
+
+ nshdr = eh->e_shnum;
+ for (uint32_t i = 0; i < nshdr; ++i) {
+ shp = SHDR(eh, i);
+
+ /* Drop null entries */
+ if (shp->sh_type == SHT_NULL) {
+ continue;
+ }
+
+ switch (shp->sh_type) {
+ case SHT_NOBITS:
+ memset((void *)shp->sh_addr, 0x0, shp->sh_size);
+ break;
+ }
+ }
+
+ return 0;
+}
+
/*
* Load the file and give back an "elf_file"
* structure.
@@ -80,7 +112,7 @@ elf_get_file(const char *pathname, struct elf_file *res)
getattr_args.res = &vattr;
getattr_args.vp = vp;
- status = vfs_vop_getattr(vp, &getattr_args);
+ status = vfs_vop_getattr(&getattr_args);
if (status != 0)
goto done;
@@ -192,6 +224,7 @@ elf64_load(const char *pathname, struct proc *td, struct exec_prog *prog)
if ((status = elf64_verify(hdr)) != 0)
goto done;
+ memset(loadmap, 0, sizeof(loadmap));
pcbp = &td->pcb;
start = -1;
end = 0;
@@ -242,6 +275,7 @@ elf64_load(const char *pathname, struct proc *td, struct exec_prog *prog)
}
}
+ elf_parse_shdrs(hdr);
memcpy(prog->loadmap, loadmap, sizeof(loadmap));
prog->start = start;
prog->end = end;
diff --git a/sys/kern/init_main.c b/sys/kern/init_main.c
index 667bb97..4a0f7a8 100644
--- a/sys/kern/init_main.c
+++ b/sys/kern/init_main.c
@@ -35,14 +35,26 @@
#include <sys/exec.h>
#include <sys/driver.h>
#include <sys/panic.h>
+#include <sys/sysctl.h>
+#include <sys/systm.h>
+#include <dev/acpi/uacpi.h>
#include <dev/cons/cons.h>
#include <dev/acpi/acpi.h>
#include <machine/cpu.h>
#include <machine/cdefs.h>
#include <vm/vm.h>
+#include <vm/stat.h>
#include <string.h>
-static struct proc proc0;
+#define _START_PATH "/usr/sbin/init"
+#if defined(_INSTALL_MEDIA)
+#define _START_ARG "/usr/sbin/install"
+#else
+#define _START_ARG NULL
+#endif /* _INSTALL_MEDIA */
+
+struct proc g_proc0;
+struct proc *g_init;
static void
copyright(void)
@@ -56,9 +68,10 @@ start_init(void)
{
struct proc *td = this_td();
struct execve_args execve_args;
- char *argv[] = { "/usr/bin/osh", NULL };
+ char *argv[] = { _START_PATH, _START_ARG, NULL };
char *envp[] = { NULL };
+ kprintf("starting init...\n");
execve_args.pathname = argv[0];
execve_args.argv = argv;
execve_args.envp = envp;
@@ -92,6 +105,9 @@ main(void)
/* Init the virtual file system */
vfs_init();
+ /* Init vmstats */
+ vm_stat_init();
+
/* Expose the console to devfs */
cons_expose();
@@ -99,14 +115,25 @@ main(void)
md_intoff();
sched_init();
+ memset(&g_proc0, 0, sizeof(g_proc0));
+ sysctl_clearstr(KERN_HOSTNAME);
+
/* Startup pid 1 */
- memset(&proc0, 0, sizeof(proc0.tf));
- fork1(&proc0, 0, start_init, NULL);
+ spawn(&g_proc0, start_init, NULL, 0, &g_init);
+ md_inton();
- /* Load all drivers */
+ uacpi_init();
+
+ /* Load all early drivers */
DRIVERS_INIT();
- /* Bootstrap APs and here we go! */
+ /* Only log to kmsg from here */
+ syslog_silence(true);
+
+ /*
+ * Bootstrap APs, schedule all other drivers
+ * and here we go!
+ */
mp_bootstrap_aps(&g_bsp_ci);
sched_enter();
__builtin_unreachable();
diff --git a/sys/kern/kern_accnt.c b/sys/kern/kern_accnt.c
new file mode 100644
index 0000000..51905e7
--- /dev/null
+++ b/sys/kern/kern_accnt.c
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * System Accounting
+ */
+
+#include <sys/sched.h>
+#include <sys/schedvar.h>
+#include <sys/proc.h>
+#include <fs/ctlfs.h>
+#include <machine/cpu.h>
+#include <string.h>
+
+/* Called within kern_sched.c */
+void sched_accnt_init(void);
+
+static struct ctlops sched_stat_ctl;
+volatile size_t g_nthreads;
+
+static int
+ctl_stat_read(struct ctlfs_dev *cdp, struct sio_txn *sio)
+{
+ struct sched_stat stat;
+
+ if (sio->len > sizeof(stat)) {
+ sio->len = sizeof(stat);
+ }
+
+ sched_stat(&stat);
+ memcpy(sio->buf, &stat, sio->len);
+ return sio->len;
+}
+
+static uint16_t
+cpu_nhlt(void)
+{
+ uint16_t nhlt = 0;
+ struct cpu_info *ci;
+
+ for (size_t i = 0; i < CPU_MAX; ++i) {
+ ci = cpu_get(i);
+ if (ci == NULL) {
+ continue;
+ }
+ if (!ci->online) {
+ ++nhlt;
+ }
+ }
+
+ return nhlt;
+}
+
+/*
+ * Get scheduler accounting information
+ *
+ * @statp: Info gets copied here
+ */
+void
+sched_stat(struct sched_stat *statp)
+{
+ struct sched_cpu *cpustat;
+
+ statp->nproc = atomic_load_64(&g_nthreads);
+ statp->ncpu = cpu_count();
+ statp->quantum_usec = DEFAULT_TIMESLICE_USEC;
+ statp->nhlt = cpu_nhlt();
+
+ /*
+ * Setup the per-cpu info/statistics
+ */
+ for (int i = 0; i < CPU_MAX; ++i) {
+ cpustat = cpu_get_stat(i);
+ if (cpustat == NULL) {
+ break;
+ }
+
+ statp->cpus[i] = *cpustat;
+ }
+}
+
+void
+sched_accnt_init(void)
+{
+ char devname[] = "sched";
+ struct ctlfs_dev ctl;
+
+ /*
+ * Register some accounting information in
+ * '/ctl/sched/stat'
+ */
+ ctl.mode = 0444;
+ ctlfs_create_node(devname, &ctl);
+ ctl.devname = devname;
+ ctl.ops = &sched_stat_ctl;
+ ctlfs_create_entry("stat", &ctl);
+}
+
+static struct ctlops sched_stat_ctl = {
+ .read = ctl_stat_read,
+ .write = NULL
+};
diff --git a/sys/kern/kern_cpu.c b/sys/kern/kern_cpu.c
new file mode 100644
index 0000000..69d44c4
--- /dev/null
+++ b/sys/kern/kern_cpu.c
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/systm.h>
+#include <sys/sysctl.h>
+#include <sys/types.h>
+
+/*
+ * Report the number of processors that are online
+ * in the machine.
+ *
+ * @count: Number of processors active
+ *
+ * Returns zero on success, otherwise a less
+ * than zero value is returned.
+ */
+int
+cpu_report_count(uint32_t count)
+{
+ struct sysctl_args args;
+ int error, name = HW_NCPU;
+
+ args.name = &name;
+ args.nlen = 1;
+ args.oldlenp = 0;
+ args.oldp = NULL;
+ args.newp = &count;
+ args.newlen = sizeof(count);
+
+ if ((error = sysctl(&args)) != 0) {
+ return error;
+ }
+
+ return 0;
+}
diff --git a/sys/kern/kern_cred.c b/sys/kern/kern_cred.c
new file mode 100644
index 0000000..017b22a
--- /dev/null
+++ b/sys/kern/kern_cred.c
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/ucred.h>
+#include <sys/proc.h>
+
+int
+setuid(uid_t new)
+{
+ struct proc *td;
+ struct ucred *cur_cred;
+
+ td = this_td();
+ cur_cred = &td->cred;
+
+ /*
+ * Only root can become other users. If you are not
+ * root, fuck off.
+ */
+ if (cur_cred->ruid != 0) {
+ return -EPERM;
+ }
+
+ spinlock_acquire(&cur_cred->lock);
+ cur_cred->euid = new;
+ cur_cred->ruid = new;
+ spinlock_release(&cur_cred->lock);
+ return 0;
+}
+
+uid_t
+getuid(void)
+{
+ struct proc *td;
+
+ td = this_td();
+ if (td == NULL) {
+ return -1;
+ }
+
+ return td->cred.ruid;
+}
+
+/*
+ * setuid() syscall
+ *
+ * arg0: `new'
+ */
+scret_t
+sys_setuid(struct syscall_args *scargs)
+{
+ return setuid(scargs->arg0);
+}
+
+scret_t
+sys_getuid(struct syscall_args *scargs)
+{
+ return getuid();
+}
diff --git a/sys/kern/kern_descrip.c b/sys/kern/kern_descrip.c
index d122e89..83845f6 100644
--- a/sys/kern/kern_descrip.c
+++ b/sys/kern/kern_descrip.c
@@ -41,6 +41,7 @@
/*
* Allocate a file descriptor.
*
+ * @td: Process to allocate from (null for CURRENT)
* @fd_out: Pointer to allocated file descriptor output.
*
* This routine will create a new file descriptor
@@ -49,10 +50,13 @@
* Returns 0 on success.
*/
int
-fd_alloc(struct filedesc **fd_out)
+fd_alloc(struct proc *td, struct filedesc **fd_out)
{
struct filedesc *fd;
- struct proc *td = this_td();
+
+ if (td == NULL) {
+ td = this_td();
+ }
/* Find free fd table entry */
for (size_t i = 3; i < PROC_MAX_FILEDES; ++i) {
@@ -85,12 +89,15 @@ fd_alloc(struct filedesc **fd_out)
* Fetch a file descriptor from a file descriptor
* number.
*
+ * @td: Process to get fd from (NULL for current)
* @fdno: File descriptor to fetch
*/
struct filedesc *
-fd_get(unsigned int fdno)
+fd_get(struct proc *td, unsigned int fdno)
{
- struct proc *td = this_td();
+ if (td == NULL) {
+ td = this_td();
+ }
if (fdno > PROC_MAX_FILEDES) {
return NULL;
@@ -111,7 +118,7 @@ fd_close(unsigned int fd)
struct filedesc *filedes;
struct proc *td;
- if ((filedes = fd_get(fd)) == NULL) {
+ if ((filedes = fd_get(NULL, fd)) == NULL) {
return -EBADF;
}
@@ -149,18 +156,32 @@ fd_rw(unsigned int fd, void *buf, size_t count, uint8_t write)
{
char *kbuf = NULL;
ssize_t n;
+ uint32_t seal;
struct filedesc *filedes;
struct sio_txn sio;
scret_t retval = 0;
+ if (fd > PROC_MAX_FILEDES) {
+ return -EBADF;
+ }
+
if (count > SSIZE_MAX) {
retval = -EINVAL;
goto done;
}
- filedes = fd_get(fd);
- kbuf = dynalloc(count);
+ filedes = fd_get(NULL, fd);
+ seal = filedes->flags;
+ /* Check the seal */
+ if (write && !ISSET(seal, O_ALLOW_WR)) {
+ return -EPERM;
+ }
+ if (!write && ISSET(seal, O_WRONLY)) {
+ return -EPERM;
+ }
+
+ kbuf = dynalloc(count);
if (kbuf == NULL) {
retval = -ENOMEM;
goto done;
@@ -187,6 +208,7 @@ fd_rw(unsigned int fd, void *buf, size_t count, uint8_t write)
sio.buf = kbuf;
sio.offset = filedes->offset;
+ spinlock_acquire(&filedes->lock);
if (write) {
/* Copy in user buffer */
if (copyin(buf, kbuf, count) < 0) {
@@ -205,19 +227,52 @@ fd_rw(unsigned int fd, void *buf, size_t count, uint8_t write)
goto done;
}
+ /* End of file? */
+ if (n == 0) {
+ retval = 0;
+ goto done;
+ }
+
if (copyout(kbuf, buf, count) < 0) {
retval = -EFAULT;
goto done;
}
}
- retval = count;
+
+ /* Increment the offset per read */
+ filedes->offset += n;
+ retval = n;
done:
if (kbuf != NULL) {
dynfree(kbuf);
}
+ spinlock_release(&filedes->lock);
return retval;
}
+static int
+fd_do_create(const char *path, struct nameidata *ndp)
+{
+ struct vop_create_args cargs;
+ struct vnode *dirvp = ndp->vp;
+ const struct vops *vops = dirvp->vops;
+ int error;
+
+ if (vops->create == NULL) {
+ return -EINVAL;
+ }
+
+ cargs.path = path;
+ cargs.ppath = ndp->path;
+ cargs.dirvp = dirvp;
+ cargs.vpp = &ndp->vp;
+ if ((error = vops->create(&cargs)) < 0) {
+ return error;
+ }
+
+ return 0;
+}
+
int
fd_read(unsigned int fd, void *buf, size_t count)
{
@@ -236,28 +291,35 @@ fd_write(unsigned int fd, void *buf, size_t count)
*
* @pathname: Path of file to open.
* @flags: Flags to use.
- *
- * TODO: Use of flags.
*/
int
fd_open(const char *pathname, int flags)
{
int error;
+ const struct vops *vops;
struct filedesc *filedes;
struct nameidata nd;
nd.path = pathname;
- nd.flags = 0;
+ nd.flags = ISSET(flags, O_CREAT) ? NAMEI_WANTPARENT : 0;
if ((error = namei(&nd)) < 0) {
return error;
}
- if ((error = fd_alloc(&filedes)) != 0) {
+ if ((error = fd_alloc(NULL, &filedes)) != 0) {
vfs_release_vnode(nd.vp);
return error;
}
+ vops = nd.vp->vops;
+ if (ISSET(flags, O_CREAT) && vops->create != NULL) {
+ error = fd_do_create(pathname, &nd);
+ }
+ if (error < 0) {
+ return error;
+ }
+
filedes->vp = nd.vp;
filedes->flags = flags;
return filedes->fdno;
@@ -266,18 +328,25 @@ fd_open(const char *pathname, int flags)
/*
* Duplicate a file descriptor. New file descriptor
* points to the same vnode.
+ *
+ * @td: Process of fd to dup (NULL for current)
+ * @fd: File descriptor to dup
*/
int
-fd_dup(int fd)
+fd_dup(struct proc *td, int fd)
{
int error;
struct filedesc *new_desc, *tmp;
- tmp = fd_get(fd);
+ if (td == NULL) {
+ td = this_td();
+ }
+
+ tmp = fd_get(td, fd);
if (tmp == NULL)
return -EBADF;
- if ((error = fd_alloc(&new_desc)) != 0)
+ if ((error = fd_alloc(td, &new_desc)) != 0)
return error;
/* Ref that vnode before we point to it */
@@ -285,3 +354,51 @@ fd_dup(int fd)
new_desc->vp = tmp->vp;
return new_desc->fdno;
}
+
+off_t
+fd_seek(int fildes, off_t offset, int whence)
+{
+ struct filedesc *tmp;
+ struct vattr attr;
+ struct vop_getattr_args getattr_args;
+
+ tmp = fd_get(NULL, fildes);
+ if (tmp == NULL) {
+ return -EBADF;
+ }
+
+ getattr_args.vp = tmp->vp;
+ getattr_args.res = &attr;
+ if ((vfs_vop_getattr(&getattr_args)) < 0) {
+ return -EPIPE;
+ }
+
+ switch (whence) {
+ case SEEK_SET:
+ tmp->offset = offset;
+ break;
+ case SEEK_CUR:
+ tmp->offset += offset;
+ break;
+ case SEEK_END:
+ tmp->offset = attr.size + offset;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return tmp->offset;
+}
+
+/*
+ * Update file offset
+ *
+ * arg0: `filedes'
+ * arg1: `offset'
+ * arg2: `whence'
+ */
+scret_t
+sys_lseek(struct syscall_args *scargs)
+{
+ return fd_seek(scargs->arg0, scargs->arg1, scargs->arg2);
+}
diff --git a/sys/kern/kern_disk.c b/sys/kern/kern_disk.c
new file mode 100644
index 0000000..a3fa05e
--- /dev/null
+++ b/sys/kern/kern_disk.c
@@ -0,0 +1,475 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/errno.h>
+#include <sys/syslog.h>
+#include <sys/sio.h>
+#include <sys/param.h>
+#include <sys/panic.h>
+#include <sys/spinlock.h>
+#include <sys/device.h>
+#include <sys/disk.h>
+#include <vm/dynalloc.h>
+#include <assert.h>
+#include <string.h>
+
+#define pr_trace(fmt, ...) kprintf("disk: " fmt, ##__VA_ARGS__)
+#define pr_error(...) pr_trace(__VA_ARGS__)
+
+#define DEFAULT_BSIZE 512 /* Default block size in bytes */
+#define DISKQ_COOKIE 0xD9EA /* Verification cookie */
+
+/*
+ * The maximum disks supported by the kernel
+ * is defined by the `DISK_MAX' kconf(9) option.
+ *
+ * We define a default of 16 if that option is not
+ * specified.
+ */
+#if defined(__DISK_MAX)
+#define DISK_MAX __DISK_MAX
+#else
+#define DISK_MAX 16 /* Maximum disks */
+#endif
+
+/*
+ * We set a hard limit at 64 disks to prevent misconfiguration as
+ * it is unlikely that one would ever have that many on a single
+ * instance. Though of course, anything is possible, so one may
+ * patch the hard limit defined below to a higher value if needed.
+ */
+__static_assert(DISK_MAX < 64, "DISK_MAX exceeds hard limit");
+
+/*
+ * The disk queue stores descriptors of disks that
+ * are registered with the system. This allows for
+ * easy and simplified access of the storage medium.
+ *
+ * XXX: An array would be more efficent, however disks
+ * could be detached or swapped during runtime thus
+ * making the usage of queues a more sane design.
+ *
+ * This also provides the added benefit of lazy-allocation
+ * so memory isn't wasted and only allocated when we actually
+ * have a disk descriptor that it would be used to store.
+ */
+static struct spinlock diskq_lock;
+static TAILQ_HEAD(, disk) diskq;
+static uint16_t disk_count = 0;
+static uint16_t diskq_cookie = 0;
+
+/*
+ * Verify that a disk descriptor has been properly
+ * initialized by comparing against the cookie field.
+ *
+ * Returns a value of zero if valid, otherwise a less
+ * than zero value is returned.
+ */
+__always_inline static inline int
+check_disk_cookie(struct disk *dp)
+{
+ __assert(dp != NULL);
+ return (dp->cookie == DISKQ_COOKIE) ? 0 : -1;
+}
+
+/*
+ * Verify if the disk queue is initialized and
+ * ready for descriptors to be added.
+ *
+ * Returns a value of zero if it has already been
+ * initialized, otherwise a value less than zero
+ * is returned after check_diskq() initializes
+ * the disk queue.
+ */
+static inline int
+check_diskq(void)
+{
+ if (diskq_cookie != DISKQ_COOKIE) {
+ TAILQ_INIT(&diskq);
+ diskq_cookie = DISKQ_COOKIE;
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Acquire a disk descriptor through a zero-based
+ * disk index. Returns a pointer to the disk descriptor
+ * on success, otherwise a less than zero value is returned.
+ *
+ * @id: Disk index
+ *
+ * XXX: This is the lockless internal implementation,
+ * please use disk_get_id() instead.
+ */
+static struct disk *
+__disk_get_id(diskid_t id)
+{
+ struct disk *dp;
+
+ if (id >= disk_count) {
+ return NULL;
+ }
+
+ dp = TAILQ_FIRST(&diskq);
+ if (dp == NULL) {
+ return NULL;
+ }
+
+ /*
+ * Now, we start at the first disk entry and
+ * traverse the list. If the ID of a disk matches
+ * the ID we are looking for, return it.
+ */
+ while (dp != NULL) {
+ if (dp->id == id) {
+ return dp;
+ }
+
+ dp = TAILQ_NEXT(dp, link);
+ }
+
+ /* Nothing found :( */
+ return NULL;
+}
+
+/*
+ * Attempt to perform a read/write operation on
+ * a disk.
+ *
+ * @id: ID of disk to operate on
+ * @blk: Block offset to read at
+ * @buf: Buffer to read data into
+ * @len: Number of bytes to read
+ * @write: If true, do a write
+ *
+ * XXX: The size in which blocks are read at is in
+ * virtual blocks which is defined by V_BSIZE
+ * in sys/disk.h
+ */
+static ssize_t
+disk_rw(diskid_t id, blkoff_t blk, void *buf, size_t len, bool write)
+{
+ const struct bdevsw *bdev;
+ struct sio_txn sio;
+ struct disk *dp;
+ int error;
+
+ len = ALIGN_UP(len, V_BSIZE);
+
+ /* Attempt to grab the disk object */
+ error = disk_get_id(id, &dp);
+ if (error < 0) {
+ return error;
+ }
+
+ /* Sanity check, should not happen */
+ bdev = dp->bdev;
+ if (__unlikely(bdev == NULL)) {
+ return -EIO;
+ }
+
+ /* Prepare the buffer */
+ sio.buf = buf;
+ sio.offset = blk * dp->bsize;
+ sio.len = len;
+
+ /* Handle writes */
+ if (write) {
+ if (bdev->write == NULL) {
+ return -ENOTSUP;
+ }
+
+ return bdev->write(dp->dev, &sio, 0);
+ }
+
+ /* Do we support this operation? */
+ if (bdev->read == NULL) {
+ return -ENOTSUP;
+ }
+
+ return bdev->read(dp->dev, &sio, 0);
+}
+
+/*
+ * Register a disk with the system so that it may
+ * be accessible independently of its device major
+ * and minor numbers
+ *
+ * @name: Name of the disk
+ * @dev: Device minor
+ * @bdev: Block device operations associated with device
+ *
+ * Returns zero on success, otherwise a less than zero
+ * value is returned.
+ */
+int
+disk_add(const char *name, dev_t dev, const struct bdevsw *bdev, int flags)
+{
+ struct disk *dp;
+ size_t name_len;
+
+ if (name == NULL || bdev == NULL) {
+ return -EINVAL;
+ }
+
+ /* Disk queue must be initialized */
+ check_diskq();
+
+ /* There is a limit to how many can be added */
+ if (disk_count >= DISK_MAX) {
+ pr_error("disk_add: disk limit %d/%d reached\n",
+ disk_count, DISK_MAX);
+ return -EAGAIN;
+ }
+
+ /* Is the disk name of correct length? */
+ name_len = strlen(name);
+ if (name_len >= sizeof(dp->name) - 1) {
+ pr_error("disk_add: name too big (len=%d)\n", name_len);
+ return -E2BIG;
+ }
+
+ dp = dynalloc(sizeof(*dp));
+ if (dp == NULL) {
+ pr_error("failed to allocate disk\n");
+ return -ENOMEM;
+ }
+
+ /* Initialize the descriptor */
+ memset(dp, 0, sizeof(*dp));
+ memcpy(dp->name, name, name_len);
+ dp->cookie = DISKQ_COOKIE;
+ dp->bdev = bdev;
+ dp->dev = dev;
+ dp->id = disk_count++;
+ dp->bsize = DEFAULT_BSIZE;
+
+ /*
+ * We are to panic if the virtual blocksize
+ * defined is not a multiple of any hardware
+ * block size
+ */
+ if ((V_BSIZE & (dp->bsize - 1)) != 0) {
+ panic("virtual block size not hw bsize aligned\n");
+ }
+
+ /* Now we can add it to the queue */
+ spinlock_acquire(&diskq_lock);
+ TAILQ_INSERT_TAIL(&diskq, dp, link);
+ spinlock_release(&diskq_lock);
+ return 0;
+}
+
+/*
+ * Acquire a disk descriptor by using a zero-based
+ * index.
+ *
+ * @id: Disk index (0: primary)
+ * @res: Resulting disk descriptor
+ *
+ * Returns zero on success, otherwise a less than
+ * zero value is returned.
+ */
+int
+disk_get_id(diskid_t id, struct disk **res)
+{
+ int error;
+ struct disk *dp;
+
+ if (res == NULL) {
+ return -EINVAL;
+ }
+
+ if (id >= disk_count) {
+ return -ENODEV;
+ }
+
+ /* Grab the disk */
+ spinlock_acquire(&diskq_lock);
+ dp = __disk_get_id(id);
+ spinlock_release(&diskq_lock);
+
+ /* Did it even exist? */
+ if (dp == NULL) {
+ return -ENODEV;
+ }
+
+ /* Should not fail but make sure */
+ error = check_disk_cookie(dp);
+ if (__unlikely(error < 0)) {
+ panic("disk_get_id: got bad disk object\n");
+ }
+
+ *res = dp;
+ return 0;
+}
+
+/*
+ * Allocate a memory buffer that may be used for
+ * disk I/O.
+ *
+ * @id: ID of disk buffer will be used for
+ * @len: Length to allocate
+ */
+void *
+disk_buf_alloc(diskid_t id, size_t len)
+{
+ struct disk *dp;
+ void *buf;
+
+ if (len == 0) {
+ return NULL;
+ }
+
+ /* Attempt to acquire the disk */
+ if (disk_get_id(id, &dp) < 0) {
+ return NULL;
+ }
+
+ /*
+ * Here we will align the buffer size by the
+ * virtual block size to ensure it is big enough.
+ */
+ len = ALIGN_UP(len, V_BSIZE);
+ buf = dynalloc(len);
+ return buf;
+}
+
+/*
+ * Free a memory buffer that was allocated by
+ * disk_buf_alloc()
+ */
+void
+disk_buf_free(void *p)
+{
+ if (p != NULL) {
+ dynfree(p);
+ }
+}
+
+/*
+ * Attempt to perform a read operation on
+ * a disk.
+ *
+ * @id: ID of disk to operate on
+ * @blk: Block offset to read at
+ * @buf: Buffer to read data into
+ * @len: Number of bytes to read
+ */
+ssize_t
+disk_read(diskid_t id, blkoff_t blk, void *buf, size_t len)
+{
+ ssize_t retval;
+ char *tmp;
+
+ tmp = disk_buf_alloc(id, len);
+ if (tmp == NULL) {
+ return -ENOMEM;
+ }
+
+ retval = disk_rw(id, blk, tmp, len, false);
+ if (retval < 0) {
+ disk_buf_free(tmp);
+ return retval;
+ }
+
+ memcpy(buf, tmp, len);
+ disk_buf_free(tmp);
+ return retval;
+}
+
+/*
+ * Attempt to perform a write operation on
+ * a disk.
+ *
+ * @id: ID of disk to operate on
+ * @blk: Block offset to read at
+ * @buf: Buffer containing data to write
+ * @len: Number of bytes to read
+ */
+ssize_t
+disk_write(diskid_t id, blkoff_t blk, const void *buf, size_t len)
+{
+ ssize_t retval;
+ char *tmp;
+
+ tmp = disk_buf_alloc(id, len);
+ if (tmp == NULL) {
+ return -ENOMEM;
+ }
+
+ memcpy(tmp, buf, len);
+ retval = disk_rw(id, blk, tmp, len, true);
+ disk_buf_free(tmp);
+ return retval;
+}
+
+/*
+ * Attempt to request attributes from a specific
+ * device.
+ *
+ * @id: ID of disk to query
+ * @res: Resulting information goes here
+ *
+ * This function returns zero on success, otherwise
+ * a less than zero value is returned.
+ */
+int
+disk_query(diskid_t id, struct disk_info *res)
+{
+ const struct bdevsw *bdev;
+ struct disk *dp;
+ int error;
+
+ if (res == NULL) {
+ return -EINVAL;
+ }
+
+ /* Attempt to grab the disk */
+ error = disk_get_id(id, &dp);
+ if (error < 0) {
+ pr_error("disk_query: bad disk ID %d\n", id);
+ return error;
+ }
+
+ bdev = dp->bdev;
+ if (__unlikely(bdev == NULL)) {
+ pr_error("disk_query: no bdev for disk %d\n", id);
+ return -EIO;
+ }
+
+ res->block_size = dp->bsize;
+ res->vblock_size = V_BSIZE;
+ res->n_block = bdev->bsize(dp->dev);
+ return 0;
+}
diff --git a/sys/kern/kern_exec.c b/sys/kern/kern_exec.c
index bf6a26e..2a53b8a 100644
--- a/sys/kern/kern_exec.c
+++ b/sys/kern/kern_exec.c
@@ -37,6 +37,7 @@
#include <vm/map.h>
#include <vm/physmem.h>
#include <machine/pcb.h>
+#include <machine/cdefs.h>
#include <string.h>
/*
@@ -87,6 +88,7 @@ execve(struct proc *td, const struct execve_args *args)
release_stack(td);
/* Save program state */
+ md_intoff();
memcpy(&td->exec, &prog, sizeof(td->exec));
/* Set new stack and map it to userspace */
@@ -99,7 +101,7 @@ execve(struct proc *td, const struct execve_args *args)
stack_top = td->stack_base + (PROC_STACK_SIZE - 1);
/* Setup registers, signals and stack */
- md_td_stackinit(td, (void *)(stack_top + VM_HIGHER_HALF), &prog);
+ stack_top = md_td_stackinit(td, (void *)(stack_top + VM_HIGHER_HALF), &prog);
setregs(td, &prog, stack_top);
signals_init(td);
diff --git a/sys/kern/kern_exit.c b/sys/kern/kern_exit.c
index 75ab0e9..af697d7 100644
--- a/sys/kern/kern_exit.c
+++ b/sys/kern/kern_exit.c
@@ -30,15 +30,24 @@
#include <sys/proc.h>
#include <sys/sched.h>
#include <sys/syslog.h>
+#include <sys/atomic.h>
+#include <sys/panic.h>
+#include <sys/filedesc.h>
+#include <sys/vnode.h>
+#include <dev/cons/cons.h>
#include <vm/physmem.h>
#include <vm/dynalloc.h>
#include <vm/vm.h>
#include <vm/map.h>
#include <machine/pcb.h>
+#include <machine/cpu.h>
#define pr_trace(fmt, ...) kprintf("exit: " fmt, ##__VA_ARGS__)
#define pr_error(...) pr_trace(__VA_ARGS__)
+extern volatile size_t g_nthreads;
+extern struct proc g_init;
+
static void
unload_td(struct proc *td)
{
@@ -48,6 +57,11 @@ unload_td(struct proc *td)
struct pcb *pcbp;
size_t len;
+ sched_detach(td);
+ if (ISSET(td->flags, PROC_KTD)) {
+ return;
+ }
+
execp = &td->exec;
auxvalp = &execp->auxval;
pcbp = &td->pcb;
@@ -72,56 +86,138 @@ unload_td(struct proc *td)
}
}
+void
+proc_reap(struct proc *td)
+{
+ struct pcb *pcbp;
+ struct filedesc *fdp;
+ vaddr_t stack_va;
+ paddr_t stack_pa;
+
+ cons_detach();
+
+ /* Clear out all fds */
+ for (size_t i = 4; i < PROC_MAX_FILEDES; ++i) {
+ fdp = td->fds[i];
+ if (fdp == NULL) {
+ continue;
+ }
+ if (fdp->refcnt == 1) {
+ vfs_release_vnode(fdp->vp);
+ dynfree(fdp);
+ fdp = NULL;
+ }
+ }
+
+ pcbp = &td->pcb;
+ unload_td(td);
+
+ /*
+ * User space stacks are identity mapped and
+ * kernel space stacks are not.
+ */
+ if (ISSET(td->flags, PROC_KTD)) {
+ stack_va = td->stack_base;
+ stack_pa = td->stack_base - VM_HIGHER_HALF;
+ } else {
+ stack_va = td->stack_base;
+ stack_pa = td->stack_base;
+ vm_unmap(pcbp->addrsp, stack_va, PROC_STACK_SIZE);
+ }
+
+ vm_free_frame(stack_pa, PROC_STACK_PAGES);
+ pmap_destroy_vas(pcbp->addrsp);
+}
+
/*
* Kill a thread and deallocate its resources.
*
* @td: Thread to exit
*/
int
-exit1(struct proc *td)
+exit1(struct proc *td, int flags)
{
- struct pcb *pcbp;
- struct proc *curtd;
- uintptr_t stack;
+ struct proc *curtd, *procp;
+ struct proc *parent;
+ struct cpu_info *ci;
pid_t target_pid, curpid;
+ if (td->pid == 1) {
+ panic("init died\n");
+ }
+
+ ci = this_cpu();
target_pid = td->pid;
curtd = this_td();
- pcbp = &td->pcb;
curpid = curtd->pid;
- stack = td->stack_base;
+
td->flags |= PROC_EXITING;
+ parent = td->parent;
- /*
- * If this is on the higher half, it is kernel
- * mapped and we need to convert it to a physical
- * address.
- */
- if (stack >= VM_HIGHER_HALF) {
- stack -= VM_HIGHER_HALF;
+ /* We have one less process in the system! */
+ atomic_dec_64(&g_nthreads);
+
+ /* Reassign children to init */
+ if (td->nleaves > 0) {
+ TAILQ_FOREACH(procp, &td->leafq, leaf_link) {
+ procp->parent = &g_init;
+ }
}
- unload_td(td);
- vm_unmap(pcbp->addrsp, td->stack_base, PROC_STACK_SIZE);
- vm_free_frame(stack, PROC_STACK_PAGES);
+ if (target_pid != curpid) {
+ proc_reap(td);
+ }
- pmap_destroy_vas(pcbp->addrsp);
- dynfree(td);
+ if (td->data != NULL) {
+ dynfree(td->data);
+ }
+
+ /*
+ * Only free the process structure if we aren't
+ * being waited on, otherwise let it be so the
+ * parent can examine what's left of it.
+ */
+ if (!ISSET(td->flags, PROC_WAITED)) {
+ dynfree(td);
+ } else {
+ td->flags |= PROC_ZOMB;
+ td->flags &= ~PROC_WAITED;
+ }
/*
* If we are the thread exiting, reenter the scheduler
* and do not return.
*/
- if (target_pid == curpid)
+ if (target_pid == curpid) {
+ /*
+ * If the thread is exiting on a core that is not
+ * preemptable, something is not right.
+ */
+ if (__unlikely(!sched_preemptable())) {
+ panic("exit1: cpu %d not preemptable\n", ci->id);
+ }
+
+ ci->curtd = NULL;
+ if (parent->pid == 0)
+ sched_enter();
+
+ parent->flags &= ~PROC_SLEEP;
sched_enter();
+ }
return 0;
}
+/*
+ * arg0: Exit status.
+ */
scret_t
sys_exit(struct syscall_args *scargs)
{
- exit1(this_td());
+ struct proc *td = this_td();
+
+ td->exit_status = scargs->arg0;
+ exit1(td, 0);
__builtin_unreachable();
}
diff --git a/sys/kern/kern_fork.c b/sys/kern/kern_fork.c
index abb7707..2755ea0 100644
--- a/sys/kern/kern_fork.c
+++ b/sys/kern/kern_fork.c
@@ -27,61 +27,7 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
-#include <sys/mman.h>
-#include <sys/tree.h>
#include <sys/types.h>
#include <sys/proc.h>
-#include <sys/errno.h>
-#include <sys/sched.h>
-#include <sys/signal.h>
-#include <vm/dynalloc.h>
-#include <string.h>
-static size_t nthreads = 0;
-/*
- * Fork1 - fork and direct a thread to 'ip'
- *
- * @cur: Current process.
- * @flags: Flags to set.
- * @ip: Location for new thread to start at.
- * @newprocp: Will contain new thread if not NULL.
- */
-int
-fork1(struct proc *cur, int flags, void(*ip)(void), struct proc **newprocp)
-{
- struct proc *newproc;
- struct mmap_lgdr *mlgdr;
- int status = 0;
-
- newproc = dynalloc(sizeof(*newproc));
- if (newproc == NULL)
- return -ENOMEM;
-
- mlgdr = dynalloc(sizeof(*mlgdr));
- if (mlgdr == NULL)
- return -ENOMEM;
-
- memset(newproc, 0, sizeof(*newproc));
- status = md_fork(newproc, cur, (uintptr_t)ip);
- if (status != 0)
- goto done;
-
- /* Set proc output if we can */
- if (newprocp != NULL)
- *newprocp = newproc;
-
- /* Initialize the mmap ledger */
- mlgdr->nbytes = 0;
- RBT_INIT(lgdr_entries, &mlgdr->hd);
- newproc->mlgdr = mlgdr;
-
- newproc->pid = ++nthreads;
- signals_init(newproc);
- sched_enqueue_td(newproc);
-done:
- if (status != 0)
- dynfree(newproc);
-
- return status;
-}
diff --git a/sys/kern/kern_krq.c b/sys/kern/kern_krq.c
new file mode 100644
index 0000000..c12a98c
--- /dev/null
+++ b/sys/kern/kern_krq.c
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/syscall.h>
+#include <sys/krq.h>
+#include <sys/errno.h>
+#include <sys/spinlock.h>
+#include <sys/driver.h>
+#include <sys/syslog.h>
+
+static struct spinlock krq_lock = {0};
+
+/*
+ * Load a kernel runtime quantum (KRQ)
+ *
+ * @arg0: path
+ *
+ * XXX: If the 'path' argument is NULL, all deferrable
+ * drivers are loaded.
+ *
+ * TODO: Handle non-null paths where a completly seperate
+ * module/krq can be loaded.
+ */
+scret_t
+sys_inject(struct syscall_args *scargs)
+{
+ if (scargs->arg0 != 0) {
+ return -EINVAL;
+ }
+
+ spinlock_acquire(&krq_lock);
+ DRIVERS_SCHED();
+ spinlock_release(&krq_lock);
+ return 0;
+}
diff --git a/sys/kern/kern_panic.c b/sys/kern/kern_panic.c
index 950ea8f..13b4964 100644
--- a/sys/kern/kern_panic.c
+++ b/sys/kern/kern_panic.c
@@ -31,6 +31,25 @@
#include <sys/spinlock.h>
#include <sys/syslog.h>
#include <sys/reboot.h>
+#include <dev/cons/cons.h>
+#include <machine/cdefs.h>
+#include <machine/cpu.h>
+#include <string.h>
+
+#if defined(__PANIC_SCR)
+#define PANIC_SCR __PANIC_SCR
+#else
+#define PANIC_SCR 0
+#endif
+
+static void
+panic_puts(const char *str)
+{
+ size_t len;
+
+ len = strlen(str);
+ cons_putstr(&g_root_scr, str, len);
+}
/*
* Burn and sizzle - the core logic that really ends
@@ -47,14 +66,40 @@ bas(bool do_trace, int reboot_type)
spinlock_acquire(&lock); /* Never released */
if (do_trace) {
- kprintf(OMIT_TIMESTAMP "** backtrace\n");
+ panic_puts(" ** backtrace\n");
md_backtrace();
}
+ panic_puts("\n-- ALL CORES HAVE BEEN HALTED --\n");
cpu_reboot(reboot_type);
__builtin_unreachable();
}
+static void
+panic_screen(void)
+{
+ struct cons_screen *scr = &g_root_scr;
+
+ if (scr->fb_mem != NULL) {
+ scr->bg = 0x8B0000;
+ scr->fg = 0xAABBAA;
+ cons_reset_cursor(scr);
+ cons_clear_scr(scr, 0x393B39);
+ }
+}
+
+static void
+do_panic(const char *fmt, va_list *ap)
+{
+ syslog_silence(false);
+ spinlock_release(&g_root_scr.lock);
+ panic_puts("panic: ");
+ vkprintf(fmt, ap);
+ bas(true, REBOOT_HALT);
+
+ __builtin_unreachable();
+}
+
/*
* Tells the user something terribly wrong happened then
* halting the system as soon as possible.
@@ -69,11 +114,15 @@ panic(const char *fmt, ...)
{
va_list ap;
- va_start(ap, fmt);
- kprintf(OMIT_TIMESTAMP "panic: ");
- vkprintf(fmt, &ap);
- bas(true, REBOOT_HALT);
+ /* Shut everything else up */
+ md_intoff();
+ cpu_halt_others();
+ if (PANIC_SCR) {
+ panic_screen();
+ }
+ va_start(ap, fmt);
+ do_panic(fmt, &ap);
__builtin_unreachable();
}
@@ -89,7 +138,6 @@ hcf(const char *fmt, ...)
{
va_list ap;
-
if (fmt != NULL) {
va_start(ap, fmt);
kprintf(OMIT_TIMESTAMP);
diff --git a/sys/kern/kern_proc.c b/sys/kern/kern_proc.c
new file mode 100644
index 0000000..8bc5680
--- /dev/null
+++ b/sys/kern/kern_proc.c
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/proc.h>
+#include <sys/errno.h>
+#include <sys/cdefs.h>
+#include <sys/vnode.h>
+#include <sys/tree.h>
+#include <sys/syscall.h>
+#include <sys/filedesc.h>
+#include <sys/fcntl.h>
+#include <string.h>
+#include <crc32.h>
+
+extern volatile size_t g_nthreads;
+
+pid_t
+getpid(void)
+{
+ struct proc *td;
+
+ td = this_td();
+ if (td == NULL) {
+ return -1;
+ }
+
+ return td->pid;
+}
+
+pid_t
+getppid(void)
+{
+ struct proc *td;
+
+ td = this_td();
+ if (td == NULL) {
+ return -1;
+ }
+ if (td->parent == NULL) {
+ return -1;
+ }
+
+ return td->parent->pid;
+}
+
+void
+proc_coredump(struct proc *td, uintptr_t fault_addr)
+{
+ struct coredump core;
+ struct sio_txn sio;
+ struct vnode *vp;
+ char pathname[128];
+ int fd;
+
+ snprintf(pathname, sizeof(pathname), "/tmp/core.%d", td->pid);
+ fd = fd_open(pathname, O_RDWR | O_CREAT);
+
+ /* ... Hopefully not */
+ if (__unlikely(fd < 0)) {
+ return;
+ }
+
+ core.pid = td->pid;
+ core.fault_addr = fault_addr;
+ memcpy(&core.tf, &td->tf, sizeof(td->tf));
+
+ core.checksum = crc32(&core, sizeof(core) - sizeof(core.checksum));
+ vp = fd_get(NULL, fd)->vp;
+
+ sio.buf = &core;
+ sio.len = sizeof(core);
+ sio.offset = 0;
+
+ /* Write the core file */
+ vfs_vop_write(vp, &sio);
+ fd_close(fd);
+}
+
+int
+proc_init(struct proc *td, struct proc *parent)
+{
+ struct mmap_lgdr *mlgdr;
+
+ mlgdr = dynalloc(sizeof(*mlgdr));
+ if (mlgdr == NULL) {
+ return -ENOMEM;
+ }
+
+ /* Add to parent leafq */
+ TAILQ_INSERT_TAIL(&parent->leafq, td, leaf_link);
+ atomic_inc_int(&parent->nleaves);
+ atomic_inc_64(&g_nthreads);
+ td->parent = parent;
+ td->exit_status = -1;
+ td->cred = parent->cred;
+
+ /* Initialize the mmap ledger */
+ mlgdr->nbytes = 0;
+ RBT_INIT(lgdr_entries, &mlgdr->hd);
+ td->mlgdr = mlgdr;
+ td->flags |= PROC_WAITED;
+ signals_init(td);
+ return 0;
+}
+
+scret_t
+sys_getpid(struct syscall_args *scargs)
+{
+ return getpid();
+}
+
+scret_t
+sys_getppid(struct syscall_args *scargs)
+{
+ return getppid();
+}
diff --git a/sys/kern/kern_sched.c b/sys/kern/kern_sched.c
index 4bbe5a0..9c5e215 100644
--- a/sys/kern/kern_sched.c
+++ b/sys/kern/kern_sched.c
@@ -34,6 +34,7 @@
#include <sys/param.h>
#include <sys/syslog.h>
#include <sys/atomic.h>
+#include <dev/cons/cons.h>
#include <machine/frame.h>
#include <machine/cpu.h>
#include <machine/cdefs.h>
@@ -44,7 +45,8 @@
#define pr_trace(fmt, ...) kprintf("ksched: " fmt, ##__VA_ARGS__)
-void sched_switch(struct trapframe *tf);
+void md_sched_switch(struct trapframe *tf);
+void sched_accnt_init(void);
static sched_policy_t policy = SCHED_POLICY_MLFQ;
@@ -63,7 +65,7 @@ __cacheline_aligned static struct spinlock tdq_lock = {0};
/*
* Perform timer oneshot
*/
-static inline void
+void
sched_oneshot(bool now)
{
struct timer timer;
@@ -77,39 +79,75 @@ sched_oneshot(bool now)
}
/*
- * Save thread state and enqueue it back into one
- * of the ready queues.
+ * Returns true if a processor is associated
+ * with a specific thread
+ *
+ * @ci: CPU that wants to take 'td'
+ * @td: Thread to check against
*/
-static void
-sched_save_td(struct proc *td, struct trapframe *tf)
+static bool
+cpu_is_assoc(struct cpu_info *ci, struct proc *td)
{
/*
- * Save trapframe to process structure only
- * if PROC_EXEC is not set.
+ * If we are not pinned, any processor is
+ * associated.
*/
- if (!ISSET(td->flags, PROC_EXEC)) {
- memcpy(&td->tf, tf, sizeof(td->tf));
+ if (!ISSET(td->flags, PROC_PINNED)) {
+ return true;
}
- sched_enqueue_td(td);
+ return ci->id == td->affinity;
}
-static struct proc *
+struct proc *
sched_dequeue_td(void)
{
struct sched_queue *queue;
struct proc *td = NULL;
+ struct cpu_info *ci;
+ uint32_t ncpu = 0;
spinlock_acquire(&tdq_lock);
+ ci = this_cpu();
for (size_t i = 0; i < SCHED_NQUEUE; ++i) {
queue = &qlist[i];
- if (!TAILQ_EMPTY(&queue->q)) {
- td = TAILQ_FIRST(&queue->q);
- TAILQ_REMOVE(&queue->q, td, link);
- spinlock_release(&tdq_lock);
- return td;
+ if (TAILQ_EMPTY(&queue->q)) {
+ continue;
}
+
+ td = TAILQ_FIRST(&queue->q);
+ if (td == NULL) {
+ continue;
+ }
+
+ while (ISSET(td->flags, PROC_SLEEP)) {
+ td = TAILQ_NEXT(td, link);
+ if (td == NULL) {
+ break;
+ }
+ }
+
+ /*
+ * If we are on a multicore system and this isn't
+ * our process, don't take it. Some threads might
+ * be pinned to a specific processor.
+ */
+ ncpu = cpu_count();
+ while (!cpu_is_assoc(ci, td) && ncpu > 1) {
+ td = TAILQ_NEXT(td, link);
+ if (td == NULL) {
+ break;
+ }
+ }
+
+ if (td == NULL) {
+ continue;
+ }
+
+ TAILQ_REMOVE(&queue->q, td, link);
+ spinlock_release(&tdq_lock);
+ return td;
}
/* We got nothing */
@@ -141,6 +179,9 @@ this_td(void)
struct cpu_info *ci;
ci = this_cpu();
+ if (ci == NULL) {
+ return NULL;
+ }
return ci->curtd;
}
@@ -177,62 +218,21 @@ td_pri_update(struct proc *td)
}
/*
- * Perform a context switch.
+ * MI work to be done during a context
+ * switch. Called by md_sched_switch()
*/
void
-sched_switch(struct trapframe *tf)
+mi_sched_switch(struct proc *from)
{
- struct cpu_info *ci;
- struct pcb *pcbp;
- struct proc *next_td, *td;
- bool use_current = true;
-
- ci = this_cpu();
- td = ci->curtd;
-
- if (td != NULL) {
- dispatch_signals(td);
- td_pri_update(td);
- }
-
- /*
- * Get the next thread and use it only if it isn't
- * in the middle of an exit, exec, or whatever.
- */
- do {
- if ((next_td = sched_dequeue_td()) == NULL) {
- sched_oneshot(false);
+ if (from != NULL) {
+ if (from->pid == 0)
return;
- }
- /*
- * If we are in the middle of an exec, don't use this
- * thread.
- */
- if (ISSET(next_td->flags, PROC_EXEC)) {
- use_current = false;
- }
-
- /*
- * Don't use this thread if we are currently
- * exiting.
- */
- if (ISSET(next_td->flags, PROC_EXITING)) {
- use_current = false;
- }
- } while (!use_current);
-
- /* Save the previous thread */
- if (td != NULL) {
- sched_save_td(td, tf);
+ dispatch_signals(from);
+ td_pri_update(from);
}
- memcpy(tf, &next_td->tf, sizeof(*tf));
- ci->curtd = next_td;
- pcbp = &next_td->pcb;
-
- pmap_switch_vas(pcbp->addrsp);
- sched_oneshot(false);
+ cons_detach();
}
/*
@@ -242,9 +242,8 @@ void
sched_enter(void)
{
md_inton();
- md_sync_all();
+ sched_oneshot(false);
for (;;) {
- sched_oneshot(false);
md_pause();
}
}
@@ -252,14 +251,154 @@ sched_enter(void)
void
sched_yield(void)
{
- struct proc *td = this_td();
+ struct proc *td;
+ struct cpu_info *ci = this_cpu();
- if (td != NULL) {
- td->rested = true;
+ if ((td = ci->curtd) == NULL) {
+ return;
}
+ td->rested = true;
+
+ /* FIXME: Hang yielding when waited on */
+ if (ISSET(td->flags, PROC_WAITED)) {
+ return;
+ }
+
+ ci->curtd = NULL;
+ md_inton();
sched_oneshot(false);
- while (td->rested);
+
+ md_hlt();
+ md_intoff();
+ ci->curtd = td;
+}
+
+void
+sched_detach(struct proc *td)
+{
+ struct sched_queue *queue;
+
+ spinlock_acquire(&tdq_lock);
+ queue = &qlist[td->priority];
+
+ TAILQ_REMOVE(&queue->q, td, link);
+ spinlock_release(&tdq_lock);
+}
+
+/*
+ * Pin a process to a specific processor
+ *
+ * @td: Process to pin
+ * @cpu: Logical processor ID to pin `td' to.
+ *
+ * XXX: 'cpu' is a machine independent value, representing
+ * CPU<n>
+ */
+void
+proc_pin(struct proc *td, affinity_t cpu)
+{
+ td->affinity = cpu;
+ td->flags |= PROC_PINNED;
+}
+
+/*
+ * Unpin a pinned process, allowing it to be
+ * picked up by any processor
+ *
+ * @td: Process to unpin
+ */
+void
+proc_unpin(struct proc *td)
+{
+ td->affinity = 0;
+ td->flags &= ~PROC_PINNED;
+}
+
+/*
+ * Suspend a process for a specified amount
+ * of time. This calling process will yield for
+ * the amount of time specified in 'tv'
+ *
+ * @td: Process to suspend (NULL for current)
+ * @tv: Time value to use
+ *
+ * XXX: 'tv' being NULL is equivalent to calling
+ * sched_detach()
+ */
+void
+sched_suspend(struct proc *td, const struct timeval *tv)
+{
+ struct timer tmr;
+ const time_t USEC_PER_SEC = 1000000;
+ ssize_t usec;
+ time_t usec_cur, usec_tmp;
+ bool have_timer = true;
+ tmrr_status_t tmr_status;
+
+ if (td == NULL)
+ td = this_td();
+ if (__unlikely(td == NULL))
+ return;
+
+ if (tv == NULL) {
+ sched_detach(td);
+ return;
+ }
+
+ /*
+ * Now, we need a generic timer so that we can compute
+ * how much time has elapsed since this process has
+ * requested to be suspended. However, we cannot assume
+ * that it would be present. If the lookup fails, all we
+ * can do is try to estimate how much time went by which
+ * works fine too, just not as accurate.
+ */
+ tmr_status = req_timer(TIMER_GP, &tmr);
+ if (tmr_status != TMRR_SUCCESS) {
+ have_timer = false;
+ }
+
+ /* We need microsecond precision */
+ if (tmr.get_time_sec == NULL) {
+ have_timer = false;
+ }
+
+ /*
+ * Compute the max time in microseconds that
+ * we will wait. We are using both tv->tv_sec
+ * and tv->tv_usec
+ */
+ usec = tv->tv_usec;
+ usec += tv->tv_sec * USEC_PER_SEC;
+ usec_cur = (have_timer) ? tmr.get_time_usec() : 0;
+
+ for (;;) {
+ sched_yield();
+
+ /*
+ * If we have a timer in our paws, compute how much
+ * time went by. Otherwise we estimate by subtracting
+ * the scheduler quantum.
+ *
+ * XXX: The timing here works decently as intended. However,
+ * it would be nice to smoothen out any jitter. Such can
+ * probably be done by subtracting 'usec' by the exponential
+ * moving average of 'usec_tmp' rather than the raw original
+ * value.
+ */
+ if (have_timer) {
+ usec_tmp = (tmr.get_time_usec() - usec_cur);
+ } else {
+ usec_tmp = DEFAULT_TIMESLICE_USEC;
+ }
+
+ /* We are done here! */
+ usec -= usec_tmp;
+ if (usec <= 0) {
+ break;
+ }
+ }
}
void
@@ -272,4 +411,6 @@ sched_init(void)
pr_trace("prepared %d queues (policy=0x%x)\n",
SCHED_NQUEUE, policy);
+
+ sched_accnt_init();
}
diff --git a/sys/kern/kern_sig.c b/sys/kern/kern_sig.c
index 58bd52d..044de7b 100644
--- a/sys/kern/kern_sig.c
+++ b/sys/kern/kern_sig.c
@@ -58,6 +58,12 @@ static struct sigaction sa_tab[] = {
.sa_flags = 0,
.sa_sigaction = NULL
},
+ [SIGTERM] = {
+ .sa_handler = sigterm_default,
+ .sa_mask = 0,
+ .sa_flags = 0,
+ .sa_sigaction = NULL
+ }
};
/*
diff --git a/sys/kern/kern_socket.c b/sys/kern/kern_socket.c
new file mode 100644
index 0000000..d0fbe19
--- /dev/null
+++ b/sys/kern/kern_socket.c
@@ -0,0 +1,1009 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/socket.h>
+#include <sys/sio.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/time.h>
+#include <sys/namei.h>
+#include <sys/sched.h>
+#include <sys/errno.h>
+#include <sys/syslog.h>
+#include <sys/filedesc.h>
+#include <sys/fcntl.h>
+#include <sys/vnode.h>
+#include <vm/dynalloc.h>
+#include <string.h>
+
+#define pr_trace(fmt, ...) kprintf("socket: " fmt, ##__VA_ARGS__)
+#define pr_error(...) pr_trace(__VA_ARGS__)
+
+static struct vops socket_vops;
+
+/*
+ * This table maps socket option names to
+ * lengths of their underlying structure.
+ *
+ * This is used for bounds/length checking within
+ * setsockopt()
+ */
+static size_t sockopt_lentab[_SO_MAX] = {
+ [ SO_RCVTIMEO ] = sizeof(struct timeval)
+};
+
+/*
+ * Get a kernel socket structure from a
+ * file descriptor.
+ *
+ * @sockfd: File descriptor to lookup
+ * @res: Result pointer
+ *
+ * Returns zero on success, otherwise a less
+ * than zero errno.
+ */
+static int
+get_ksock(int sockfd, struct ksocket **res)
+{
+ struct ksocket *ksock;
+ struct filedesc *fdesc;
+ struct vnode *vp;
+
+ if (res == NULL) {
+ return -EINVAL;
+ }
+
+ /* Grab the file descriptor */
+ fdesc = fd_get(NULL, sockfd);
+ if (fdesc == NULL) {
+ return -EBADF;
+ }
+
+ /* Is this even a socket? */
+ if ((vp = fdesc->vp) == NULL) {
+ return -ENOTSOCK;
+ }
+ if (vp->type != VSOCK) {
+ return -ENOTSOCK;
+ }
+
+ ksock = vp->data;
+ if (__unlikely(ksock == NULL)) {
+ return -EIO;
+ }
+
+ *res = ksock;
+ return 0;
+}
+
+/*
+ * VFS reclaim callback for the socket
+ * layer
+ *
+ * Returns zero on success, otherwise a less
+ * than zero errno.
+ */
+static int
+socket_reclaim(struct vnode *vp)
+{
+ struct ksocket *ksock;
+ struct sockopt *opt;
+
+ /* Is this even a socket? */
+ if (vp->type != VSOCK) {
+ return -ENOTSOCK;
+ }
+
+ /* Is there any data attached? */
+ if ((ksock = vp->data) == NULL) {
+ return -EIO;
+ }
+
+ /* Free up any used options */
+ for (int i = 0; i < _SO_MAX; ++i) {
+ opt = ksock->opt[i];
+ if (opt != NULL) {
+ dynfree(opt);
+ ksock->opt[i] = NULL;
+ }
+ }
+
+ fd_close(ksock->sockfd);
+ mutex_free(ksock->mtx);
+ dynfree(ksock);
+ return 0;
+}
+
+/*
+ * Create a socket file from the sockaddr
+ * structure
+ *
+ * @ksock: Socket to create a file for
+ * @sockaddr_un: domain sockaddr
+ */
+static int
+socket_mkfile(struct ksocket *ksock, struct sockaddr_un *un)
+{
+ struct filedesc *fdesc;
+ struct vnode *vp;
+ int fd;
+
+ fd = fd_open(un->sun_path, O_CREAT | O_RDONLY);
+ if (fd < 0) {
+ return fd;
+ }
+
+ /* Grab the actual handle now */
+ fdesc = fd_get(NULL, fd);
+ if (fdesc == NULL) {
+ fd_close(fd);
+ return -EIO;
+ }
+
+ /* Hijack the vnode */
+ vp = fdesc->vp;
+ vp->type = VSOCK;
+ vp->vops = &socket_vops;
+ vp->data = ksock;
+ return fd;
+}
+
+/*
+ * Connect to a domain socket - used by connect()
+ *
+ * @sockfd: Socket file descriptor
+ * @ksock: Current ksock
+ * @un: Current sockaddr_un
+ */
+static int
+connect_domain(int sockfd, struct ksocket *ksock, struct sockaddr_un *un)
+{
+ int error;
+ struct nameidata ndp;
+ struct filedesc *filedesc;
+ struct vnode *vp;
+
+ ndp.path = un->sun_path;
+ ndp.flags = 0;
+ if ((error = namei(&ndp)) < 0) {
+ return error;
+ }
+
+ vp = ndp.vp;
+ filedesc = fd_get(NULL, sockfd);
+ if (filedesc == NULL) {
+ pr_error("connect: no filedesc for current\n");
+ return -EIO;
+ }
+
+ filedesc->vp = vp;
+ return 0;
+}
+
+/*
+ * Wait until data is received for the
+ * recv() function.
+ *
+ * @sockfd: Socket we are waiting on
+ *
+ * Returns zero on success, otherwise a less
+ * than zero value is returned.
+ */
+static int
+socket_rx_wait(int sockfd)
+{
+ struct ksocket *ksock;
+ struct sockopt *opt;
+ struct timeval tv;
+ int error;
+
+ if (ksock == NULL) {
+ return -EINVAL;
+ }
+
+ error = get_ksock(sockfd, &ksock);
+ if (error < 0) {
+ return error;
+ }
+
+ /*
+ * If the socket does not have this option set,
+ * we will assume that there is no timeout value.
+ */
+ opt = ksock->opt[SO_RCVTIMEO];
+ if (opt == NULL) {
+ return 0;
+ }
+
+ memcpy(&tv, opt->data, opt->len);
+ sched_suspend(NULL, &tv);
+ return 0;
+}
+
+/*
+ * Send data to socket - POSIX send(2) core
+ *
+ * @sockfd: File descriptor that backs this socket
+ * @buf: Buffer containing data to transmit
+ * @size: Size of the buffer
+ * @flags: Optional flags
+ *
+ * Returns zero on success, otherwise a less
+ * than zero errno.
+ */
+ssize_t
+send(int sockfd, const void *buf, size_t size, int flags)
+{
+ struct ksocket *ksock;
+ struct sockbuf *sbuf;
+ struct netbuf *netbuf;
+ size_t tail;
+ int error;
+
+ /* Size cannot be zero */
+ if (size == 0) {
+ return -EINVAL;
+ }
+
+ if ((error = get_ksock(sockfd, &ksock)) < 0) {
+ return error;
+ }
+
+ sbuf = &ksock->buf;
+ netbuf = &sbuf->buf;
+ mutex_acquire(ksock->mtx, 0);
+
+ /* Make sure we dont overflow */
+ if (netbuf->len > sbuf->watermark) {
+ mutex_release(ksock->mtx);
+ return -ENOBUFS;
+ }
+
+ if (netbuf->len == 0) {
+ sbuf->head = 0;
+ sbuf->tail = 0;
+ }
+
+ /* Clamp the size if needed */
+ if ((netbuf->len + size) > sbuf->watermark) {
+ size = sbuf->watermark - netbuf->len;
+ }
+ if (size == 0) {
+ return -ENOBUFS;
+ }
+
+ /* Copy the new data */
+ tail = sbuf->tail;
+ memcpy(&netbuf->data[tail], buf, size);
+
+ sbuf->tail += size;
+ netbuf->len += size;
+ mutex_release(ksock->mtx);
+ return size;
+}
+
+/*
+ * Recv data from socket - POSIX recv(2) core
+ *
+ * @sockfd: File descriptor that backs this socket
+ * @buf: RX buffer
+ * @size: Size of the buffer
+ * @flags: Optional flags
+ *
+ * Returns length on success, otherwise a less
+ * than zero errno.
+ */
+ssize_t
+recv(int sockfd, void *buf, size_t len, int flags)
+{
+ struct ksocket *ksock;
+ struct sockbuf *sbuf;
+ struct netbuf *netbuf;
+ size_t head;
+ ssize_t retval = len;
+ int error;
+
+ /* Length cannot be zero */
+ if (len == 0) {
+ return -EINVAL;
+ }
+
+ if ((error = get_ksock(sockfd, &ksock)) < 0) {
+ return error;
+ }
+
+ sbuf = &ksock->buf;
+ netbuf = &sbuf->buf;
+ mutex_acquire(ksock->mtx, 0);
+
+ /* Is it empty? */
+ if (netbuf->len == 0) {
+ sbuf->head = 0;
+ sbuf->tail = 0;
+ retval = -EAGAIN;
+ goto done;
+ }
+
+ if (len > netbuf->len) {
+ len = netbuf->len;
+ }
+
+ head = sbuf->head;
+ memcpy(buf, &netbuf->data[head], len);
+ sbuf->head = (sbuf->head + len) % NETBUF_LEN;
+done:
+ mutex_release(ksock->mtx);
+ return retval;
+}
+
+/*
+ * POSIX socket(7) core
+ *
+ * @domain: Address family (see AF_*)
+ * @type: Socket type
+ * @protocol: Socket protocol
+ *
+ * Returns zero on success, otherwise a less
+ * than zero errno.
+ */
+int
+socket(int domain, int type, int protocol)
+{
+ struct ksocket *ksock = NULL;
+ struct sockbuf *sbuf = NULL;
+ struct proc *td = this_td();
+ int fd, error = -1;
+
+ ksock = dynalloc(sizeof(*ksock));
+ if (ksock == NULL) {
+ error = -ENOMEM;
+ goto fail;
+ }
+
+ memset(ksock, 0, sizeof(*ksock));
+ sbuf = &ksock->buf;
+ sbuf->head = 0;
+ sbuf->tail = 0;
+
+ switch (domain) {
+ case AF_UNIX:
+ {
+ struct sockaddr_un *un;
+
+ un = &ksock->un;
+ sbuf->watermark = NETBUF_LEN;
+
+ /* Set up a path and create a socket file */
+ un->sun_family = domain;
+ snprintf(un->sun_path, sizeof(un->sun_path), "/tmp/%d-sock0", td->pid);
+ fd = socket_mkfile(ksock, un);
+ }
+ return fd;
+ default:
+ error = -EINVAL;
+ break;
+ }
+
+fail:
+ if (ksock != NULL)
+ dynfree(ksock);
+
+ fd_close(fd);
+ return error;
+}
+
+/*
+ * Bind address to socket - POSIX bind(2) core
+ *
+ * @sockfd: File descriptor
+ * @addr: Address to bind
+ * @len: Sockaddr len
+ *
+ * Returns zero on success, otherwise a less
+ * than zero errno.
+ */
+int
+bind(int sockfd, const struct sockaddr *addr, socklen_t len)
+{
+ struct proc *td;
+ struct ksocket *ksock;
+ struct cmsg_list *clp;
+ int error;
+
+ if ((error = get_ksock(sockfd, &ksock)) < 0) {
+ kprintf("error=%d\n", error);
+ return error;
+ }
+
+ /* Create the new mutex lock */
+ ksock->mtx = mutex_new("ksocket");
+ if (ksock->mtx == NULL) {
+ return -ENOMEM;
+ }
+
+ /* Mark ourselves as the owner */
+ td = this_td();
+ ksock->owner = td;
+
+ /* Initialize the cmsg list queue */
+ clp = &ksock->cmsg_list;
+ TAILQ_INIT(&clp->list);
+ clp->is_init = 1;
+ return 0;
+}
+
+/*
+ * Set socket options - POSIX setsockopt(3) core
+ *
+ * @sockfd: File descriptor of socket
+ * @level: Protocol level
+ * @v: Options value
+ * @len: Length of data pointed to by 'v'
+ */
+int
+setsockopt(int sockfd, int level, int name, const void *v, socklen_t len)
+{
+ struct ksocket *ksock;
+ struct sockopt *opt;
+ size_t exp_len;
+ int error;
+
+ /* Must have a valid fd */
+ if (sockfd < 0) {
+ return -EBADF;
+ }
+
+ /* Ensure value and length are valid */
+ if (v == NULL || len == 0) {
+ return -EINVAL;
+ }
+
+ /* Verify the name */
+ if (name >= _SO_MAX) {
+ return -EINVAL;
+ }
+
+ /* Grab a new socket */
+ if ((error = get_ksock(sockfd, &ksock)) < 0) {
+ return error;
+ }
+
+ /* Clamp the input length as needed */
+ exp_len = sockopt_lentab[name];
+ if (len > exp_len) {
+ len = exp_len;
+ }
+
+ /*
+ * Here we will grab the socket options. If it is
+ * NULL, we'll need to allocate one.
+ */
+ if ((opt = ksock->opt[name]) == NULL) {
+ opt = dynalloc(sizeof(*opt) + len);
+
+ if (opt == NULL) {
+ return -ENOMEM;
+ }
+
+ opt->len = len;
+ ksock->opt[name] = opt;
+ }
+
+ memcpy(opt->data, v, len);
+ opt->len = len;
+ return 0;
+}
+
+/*
+ * Connect to a socket
+ *
+ * @sockfd: File descriptor to connect
+ * @addr: Address to connect to
+ * @len: Length of address
+ */
+int
+connect(int sockfd, const struct sockaddr *addr, socklen_t len)
+{
+ struct ksocket *ksock;
+ int error = -1;
+
+ if ((error = get_ksock(sockfd, &ksock)) < 0) {
+ return error;
+ }
+
+ switch (addr->sa_family) {
+ case AF_UNIX:
+ {
+ struct sockaddr_un *un;
+
+ un = (struct sockaddr_un *)addr;
+ if (un->sun_path[0] == '\0') {
+ pr_error("connect: bad socket path\n");
+ return -1;
+ }
+
+ /* Wait for the connection to be established */
+ do {
+ error = connect_domain(sockfd, ksock, un);
+ if (error != 0) {
+ sched_yield();
+ }
+ } while (error != 0);
+
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+/*
+ * Send socket control message - POSIX.1-2008
+ *
+ * @socket: Socket to transmit on
+ * @msg: Further arguments
+ * @flags: Optional flags
+ *
+ * Returns zero on success, otherwise a less
+ * than zero errno.
+ */
+ssize_t
+sendmsg(int socket, const struct msghdr *msg, int flags)
+{
+ struct ksocket *ksock;
+ struct cmsg *cmsg;
+ struct sockaddr_un *un;
+ struct cmsg_list *clp;
+ size_t control_len = 0;
+ int error;
+
+ if ((error = get_ksock(socket, &ksock)) < 0) {
+ return error;
+ }
+
+ /* We cannot do sendmsg() non domain sockets */
+ un = &ksock->un;
+ if (un->sun_family != AF_UNIX) {
+ return -EBADF;
+ }
+
+ control_len = MALIGN(msg->msg_controllen);
+
+ /* Allocate a new cmsg */
+ cmsg = dynalloc(control_len + sizeof(struct cmsg));
+ if (cmsg == NULL) {
+ return -EINVAL;
+ }
+
+ memcpy(cmsg->buf, msg->msg_control, control_len);
+ clp = &ksock->cmsg_list;
+ cmsg->control_len = control_len;
+ TAILQ_INSERT_TAIL(&clp->list, cmsg, link);
+ return 0;
+}
+
+/*
+ * Receive socket control message - POSIX.1‐2017
+ *
+ * @socket: Socket to receive on
+ * @msg: Further arguments
+ * @flags: Optional flags
+ *
+ * Returns zero on success, otherwise a less
+ * than zero errno.
+ */
+ssize_t
+recvmsg(int socket, struct msghdr *msg, int flags)
+{
+ struct ksocket *ksock;
+ struct sockaddr_un *un;
+ struct cmsg *cmsg, *tmp;
+ struct cmsghdr *cmsghdr;
+ struct cmsg_list *clp;
+ uint8_t *fds;
+ int error;
+
+ if (socket < 0) {
+ return -EINVAL;
+ }
+
+ /* Grab the socket descriptor */
+ if ((error = get_ksock(socket, &ksock)) < 0) {
+ return error;
+ }
+
+ /* Must be a unix domain socket */
+ un = &ksock->un;
+ if (un->sun_family != AF_UNIX) {
+ return -EBADF;
+ }
+
+ /* Grab the control message list */
+ clp = &ksock->cmsg_list;
+ cmsg = TAILQ_FIRST(&clp->list);
+
+ /* Empty? */
+ while (cmsg == NULL) {
+ sched_yield();
+ cmsg = TAILQ_FIRST(&clp->list);
+ }
+
+ while (cmsg != NULL) {
+ cmsghdr = &cmsg->hdr;
+
+ /* Check the control message type */
+ switch (cmsghdr->cmsg_type) {
+ case SCM_RIGHTS:
+ {
+ fds = (uint8_t *)CMSG_DATA(cmsghdr);
+ pr_trace("SCM_RIGHTS -> fd %d (from pid %d)\n", fds[0],
+ ksock->owner->pid);
+
+ break;
+ }
+ }
+
+ tmp = cmsg;
+ cmsg = TAILQ_NEXT(cmsg, link);
+
+ TAILQ_REMOVE(&clp->list, tmp, link);
+ dynfree(tmp);
+ }
+
+ return 0;
+}
+
+/*
+ * socket(7) syscall
+ *
+ * arg0: domain
+ * arg1: type
+ * arg2: protocol
+ */
+scret_t
+sys_socket(struct syscall_args *scargs)
+{
+ int domain = scargs->arg0;
+ int type = scargs->arg1;
+ int protocol = scargs->arg2;
+
+ return socket(domain, type, protocol);
+}
+
+/*
+ * bind(2) syscall
+ *
+ * arg0: sockfd
+ * arg1: addr
+ * arg2: len
+ */
+scret_t
+sys_bind(struct syscall_args *scargs)
+{
+ const struct sockaddr *u_addr = (void *)scargs->arg1;
+ struct sockaddr addr_copy;
+ int sockfd = scargs->arg0;
+ int len = scargs->arg2;
+ int error;
+
+ error = copyin(u_addr, &addr_copy, sizeof(addr_copy));
+ if (error < 0) {
+ return error;
+ }
+
+ return bind(sockfd, &addr_copy, len);
+}
+
+/*
+ * recv(2) syscall
+ *
+ * arg0: sockfd
+ * arg1: buf
+ * arg2: size
+ * arg3: flags
+ */
+scret_t
+sys_recv(struct syscall_args *scargs)
+{
+ char buf[NETBUF_LEN];
+ void *u_buf = (void *)scargs->arg1;
+ int sockfd = scargs->arg0;
+ size_t len = scargs->arg2;
+ int error, flags = scargs->arg3;
+
+ if (len > sizeof(buf)) {
+ return -ENOBUFS;
+ }
+
+ for (;;) {
+ error = recv(sockfd, buf, len, flags);
+ if (error <= 0 && error != -EAGAIN) {
+ break;
+ }
+
+ /*
+ * Wait for data to be ready on the socket.
+ * If a less than zero value is returned, don't
+ * handle timeouts.
+ */
+ error = socket_rx_wait(sockfd);
+ if (error < 0) {
+ continue;
+ }
+
+ /* Try one more time, obey timeout */
+ error = recv(sockfd, buf, len, flags);
+ if (error == -EAGAIN) {
+ return error;
+ }
+ break;
+ }
+
+ if (error < 0) {
+ pr_error("sys_recv: recv() fail (fd=%d)\n", sockfd);
+ return error;
+ }
+
+ error = copyout(buf, u_buf, len);
+ return (error == 0) ? len : error;
+}
+
+/*
+ * send(2) syscall
+ *
+ * arg0: sockfd
+ * arg1: buf
+ * arg2: size
+ * arg3: flags
+ */
+scret_t
+sys_send(struct syscall_args *scargs)
+{
+ char buf[NETBUF_LEN];
+ const void *u_buf = (void *)scargs->arg1;
+ int sockfd = scargs->arg0;
+ size_t len = scargs->arg2;
+ int error, flags = scargs->arg3;
+
+ if (len > sizeof(buf)) {
+ return -ENOBUFS;
+ }
+
+ error = copyin(u_buf, buf, len);
+ if (error < 0) {
+ pr_error("sys_send: copyin() failure (fd=%d)\n", sockfd);
+ return error;
+ }
+
+ return send(sockfd, buf, len, flags);
+}
+
+/*
+ * recvmsg(3) syscall
+ *
+ * arg0: socket
+ * arg1: msg
+ * arg2: flags
+ */
+scret_t
+sys_recvmsg(struct syscall_args *scargs)
+{
+ struct msghdr *u_msg = (void *)scargs->arg1;
+ void *u_control, *control = NULL;
+ size_t controllen;
+ struct iovec msg_iov;
+ struct msghdr msg;
+ ssize_t retval;
+ int socket = scargs->arg0;
+ int flags = scargs->arg2;
+ int error;
+
+ /* Read the message header */
+ error = copyin(u_msg, &msg, sizeof(msg));
+ if (error < 0) {
+ pr_error("sys_recvmsg: bad msg\n");
+ return error;
+ }
+
+ /* Grab the message I/O vector */
+ error = uio_copyin(msg.msg_iov, &msg_iov, msg.msg_iovlen);
+ if (error < 0) {
+ return error;
+ }
+
+ /* Save control fields */
+ u_control = msg.msg_control;
+ controllen = msg.msg_controllen;
+
+ /* Allocate a new control field to copy in */
+ control = dynalloc(controllen);
+ msg.msg_control = control;
+ if (msg.msg_control == NULL) {
+ uio_copyin_clean(&msg_iov, msg.msg_iovlen);
+ return -ENOMEM;
+ }
+
+ memset(msg.msg_control, 0, controllen);
+ error = copyin(u_control, msg.msg_control, controllen);
+ if (error < 0) {
+ retval = error;
+ goto done;
+ }
+
+ /*
+ * Now wait until we get a control
+ * message
+ */
+ msg.msg_iov = &msg_iov;
+ for (;;) {
+ retval = recvmsg(socket, &msg, flags);
+ if (retval == 0) {
+ break;
+ }
+
+ sched_yield();
+ }
+done:
+ uio_copyin_clean(&msg_iov, msg.msg_iovlen);
+ dynfree(control);
+ return retval;
+}
+
+/*
+ * sendmsg(3) syscall
+ *
+ * arg0: socket
+ * arg1: msg
+ * arg2: flags
+ */
+scret_t
+sys_sendmsg(struct syscall_args *scargs)
+{
+ struct iovec msg_iov;
+ struct msghdr *u_msg = (void *)scargs->arg1;
+ struct msghdr msg;
+ ssize_t retval;
+ int socket = scargs->arg0;
+ int flags = scargs->arg2;
+ int error;
+
+ /* Read the message header */
+ error = copyin(u_msg, &msg, sizeof(msg));
+ if (error < 0) {
+ pr_error("sys_sendmsg: bad msg\n");
+ return error;
+ }
+
+ /* Grab the message I/O vector */
+ error = uio_copyin(msg.msg_iov, &msg_iov, msg.msg_iovlen);
+ if (error < 0) {
+ return error;
+ }
+
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &msg_iov;
+
+ for (;;) {
+ retval = sendmsg(socket, &msg, flags);
+ if (retval == 0) {
+ break;
+ }
+ sched_yield();
+ }
+ uio_copyin_clean(&msg_iov, msg.msg_iovlen);
+ return retval;
+}
+
+/*
+ * connect(3) syscall
+ *
+ * arg0: sockfd
+ * arg1: address
+ * arg2: len
+ */
+scret_t
+sys_connect(struct syscall_args *scargs)
+{
+ char buf[256];
+ struct sockaddr *u_addr = (void *)scargs->arg1;
+ struct sockaddr *sockaddr;
+ int error;
+ int sockfd = scargs->arg0;
+ socklen_t len = scargs->arg2;
+
+ if (len >= sizeof(buf)) {
+ pr_error("sys_connect: address too big\n");
+ return -E2BIG;
+ }
+
+ error = copyin(u_addr, buf, len);
+ if (error < 0) {
+ pr_error("sys_connect: bad 'address'\n");
+ return error;
+ }
+
+ sockaddr = (struct sockaddr *)buf;
+ return connect(sockfd, sockaddr, len);
+}
+
+/*
+ * POSIX setsockopt(3) syscall
+ *
+ * arg0: sockfd
+ * arg1: level
+ * arg2: name
+ * arg3: data
+ * arg4: len
+ */
+scret_t
+sys_setsockopt(struct syscall_args *scargs)
+{
+ int sockfd = scargs->arg0;
+ int level = scargs->arg1;
+ int name = scargs->arg2;
+ void *u_data = (void *)scargs->arg3;
+ socklen_t len = scargs->arg4;
+ void *data;
+ size_t exp_len;
+ int retval;
+
+ /* Verify that the name is correct */
+ if (name >= _SO_MAX) {
+ return -EINVAL;
+ }
+
+ /* Clamp length as needed */
+ exp_len = sockopt_lentab[name];
+ if (len > exp_len) {
+ len = exp_len;
+ }
+
+ data = dynalloc(len);
+ if (data == NULL) {
+ return -ENOMEM;
+ }
+
+ /* Grab data from userland */
+ retval = copyin(u_data, data, len);
+ if (retval < 0) {
+ dynfree(data);
+ return retval;
+ }
+
+ retval = setsockopt(sockfd, level, name, data, len);
+ dynfree(data);
+ return retval;
+}
+
+static struct vops socket_vops = {
+ .read = NULL,
+ .write = NULL,
+ .reclaim = socket_reclaim,
+};
diff --git a/sys/kern/kern_spawn.c b/sys/kern/kern_spawn.c
new file mode 100644
index 0000000..7962ced
--- /dev/null
+++ b/sys/kern/kern_spawn.c
@@ -0,0 +1,295 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/spawn.h>
+#include <sys/wait.h>
+#include <sys/proc.h>
+#include <sys/exec.h>
+#include <sys/mman.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/syslog.h>
+#include <sys/syscall.h>
+#include <sys/signal.h>
+#include <sys/limits.h>
+#include <sys/sched.h>
+#include <vm/dynalloc.h>
+#include <string.h>
+
+#define pr_trace(fmt, ...) kprintf("spawn: " fmt, ##__VA_ARGS__)
+#define pr_error(...) pr_trace(__VA_ARGS__)
+
+#define ARGVP_MAX (ARG_MAX / sizeof(void *))
+
+static size_t next_pid = 1;
+
+/*
+ * TODO: envp
+ */
+struct spawn_args {
+ char path[PATH_MAX];
+ char argv_blk[ARG_MAX];
+ char *argv[ARGVP_MAX];
+};
+
+static inline void
+try_free_data(void *p)
+{
+ if (p != NULL) {
+ dynfree(p);
+ }
+}
+
+static void
+spawn_thunk(void)
+{
+ const char *path;
+ char pathbuf[PATH_MAX];
+ struct proc *cur;
+ struct execve_args execve_args;
+ struct spawn_args *args;
+ char *envp[] = { NULL };
+
+ cur = this_td();
+ args = cur->data;
+ path = args->path;
+ memset(pathbuf, 0, sizeof(pathbuf));
+ memcpy(pathbuf, path, strlen(path));
+
+ execve_args.pathname = pathbuf;
+ execve_args.argv = (char **)&args->argv[0];
+ execve_args.envp = envp;
+ path = NULL;
+
+ if (execve(cur, &execve_args) != 0) {
+ pr_error("execve failed, aborting\n");
+ exit1(this_td(), 0);
+ }
+ __builtin_unreachable();
+}
+
+pid_t
+waitpid(pid_t pid, int *wstatus, int options)
+{
+ struct proc *child, *td;
+ pid_t ret;
+
+ td = this_td();
+ child = get_child(td, pid);
+
+ if (child == NULL) {
+ return -1;
+ }
+
+ /* Wait for it to be done */
+ while (!ISSET(child->flags, PROC_ZOMB)) {
+ sched_yield();
+ }
+
+
+ /* Give back the status */
+ if (wstatus != NULL) {
+ copyout(&child->exit_status, wstatus, sizeof(*wstatus));
+ }
+
+ ret = child->pid;
+ proc_reap(child);
+ return ret;
+}
+
+/*
+ * Spawn a new process
+ *
+ * @cur: Parent (current) process.
+ * @func: Address of start code.
+ * @p: Data to pass to new process (used for user procs)
+ * @flags: Spawn flags.
+ * @newprocp: If not NULL, will contain the new process.
+ *
+ * Returns the PID of the child on success, otherwise an
+ * errno value that is less than zero.
+ *
+ * XXX: `p` is only used by sys_spawn and should be set
+ * to NULL if called in the kernel.
+ */
+pid_t
+spawn(struct proc *cur, void(*func)(void), void *p, int flags, struct proc **newprocp)
+{
+ struct proc *newproc;
+ int error;
+ pid_t pid;
+
+ newproc = dynalloc(sizeof(*newproc));
+ if (newproc == NULL) {
+ pr_error("could not alloc proc (-ENOMEM)\n");
+ try_free_data(p);
+ return -ENOMEM;
+ }
+
+ memset(newproc, 0, sizeof(*newproc));
+ error = md_spawn(newproc, cur, (uintptr_t)func);
+ if (error < 0) {
+ dynfree(newproc);
+ try_free_data(p);
+ pr_error("error initializing proc\n");
+ return error;
+ }
+
+ /* Set proc output if we can */
+ if (newprocp != NULL) {
+ *newprocp = newproc;
+ }
+
+ if (!ISSET(cur->flags, PROC_LEAFQ)) {
+ TAILQ_INIT(&cur->leafq);
+ cur->flags |= PROC_LEAFQ;
+ }
+
+ error = proc_init(newproc, cur);
+ if (error < 0) {
+ dynfree(newproc);
+ try_free_data(p);
+ pr_error("error initializing proc\n");
+ return error;
+ }
+
+ newproc->data = p;
+ newproc->pid = next_pid++;
+ sched_enqueue_td(newproc);
+ pid = newproc->pid;
+ return pid;
+}
+
+/*
+ * Get the child of a process by PID.
+ *
+ * @cur: Parent process.
+ * @pid: Child PID.
+ *
+ * Returns NULL if no child was found.
+ */
+struct proc *
+get_child(struct proc *cur, pid_t pid)
+{
+ struct proc *procp;
+
+ TAILQ_FOREACH(procp, &cur->leafq, leaf_link) {
+ if (procp == NULL) {
+ continue;
+ }
+ if (procp->pid == pid) {
+ return procp;
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * arg0: PID
+ * arg1: wstatus
+ * arg2: options
+ *
+ * Returns PID of terminated child, returns
+ * -1 on failure.
+ */
+scret_t
+sys_waitpid(struct syscall_args *scargs)
+{
+ pid_t pid;
+ int *u_wstatus;
+ int options;
+
+ pid = scargs->arg0;
+ u_wstatus = (void *)scargs->arg1;
+ options = scargs->arg2;
+ return waitpid(pid, u_wstatus, options);
+}
+
+/*
+ * arg0: The file /path/to/executable
+ * arg1: Argv
+ * arg2: Envp (TODO)
+ * arg3: Optional flags (`flags')
+ */
+scret_t
+sys_spawn(struct syscall_args *scargs)
+{
+ struct spawn_args *args;
+ char *path;
+ const char *u_path, **u_argv;
+ const char *u_p = NULL;
+ struct proc *td;
+ int flags, error;
+ size_t len, bytes_copied = 0;
+ size_t argv_i = 0;
+
+ td = this_td();
+ u_path = (const char *)scargs->arg0;
+ u_argv = (const char **)scargs->arg1;
+ flags = scargs->arg3;
+
+ args = dynalloc(sizeof(*args));
+ if (args == NULL) {
+ return -ENOMEM;
+ }
+
+ error = copyinstr(u_path, args->path, sizeof(args->path));
+ if (error < 0) {
+ dynfree(args);
+ return error;
+ }
+
+ memset(args->argv, 0, ARG_MAX);
+ for (size_t i = 0; i < ARG_MAX - 1; ++i) {
+ error = copyin(&u_argv[argv_i], &u_p, sizeof(u_p));
+ if (error < 0) {
+ dynfree(args);
+ return error;
+ }
+ if (u_p == NULL) {
+ args->argv[argv_i++] = NULL;
+ break;
+ }
+
+ path = &args->argv_blk[i];
+ error = copyinstr(u_p, path, ARG_MAX - bytes_copied);
+ if (error < 0) {
+ dynfree(args);
+ return error;
+ }
+
+ args->argv[argv_i++] = &args->argv_blk[i];
+ len = strlen(path);
+ bytes_copied += (len + 1);
+ i += len;
+ }
+
+ return spawn(td, spawn_thunk, args, flags, NULL);
+}
diff --git a/sys/kern/kern_stub.c b/sys/kern/kern_stub.c
index 8603fd5..a9a56ac 100644
--- a/sys/kern/kern_stub.c
+++ b/sys/kern/kern_stub.c
@@ -40,8 +40,10 @@ sigfpe_default(int signo)
static struct proc *td;
td = this_td();
- kprintf("Floating point exception (pid=%d)\n", td->pid);
- exit1(td);
+ syslog_silence(false);
+ kprintf(OMIT_TIMESTAMP "Floating point exception (pid=%d)\n", td->pid);
+ syslog_silence(true);
+ exit1(td, 0);
}
void
@@ -50,8 +52,10 @@ sigkill_default(int signo)
static struct proc *td;
td = this_td();
- kprintf("Terminated (pid=%d)\n", td->pid);
- exit1(td);
+ syslog_silence(false);
+ kprintf(OMIT_TIMESTAMP "Terminated (pid=%d)\n", td->pid);
+ syslog_silence(true);
+ exit1(td, 0);
}
void
@@ -60,8 +64,22 @@ sigsegv_default(int signo)
static struct proc *td;
td = this_td();
- kprintf("Segmentation fault (pid=%d)\n", td->pid);
- exit1(td);
+ syslog_silence(false);
+ kprintf(OMIT_TIMESTAMP "Segmentation fault (pid=%d)\n", td->pid);
+ syslog_silence(true);
+ exit1(td, 0);
+}
+
+void
+sigterm_default(int signo)
+{
+ static struct proc *td;
+
+ td = this_td();
+ syslog_silence(false);
+ kprintf(OMIT_TIMESTAMP "Terminated (pid=%d)\n", td->pid);
+ syslog_silence(true);
+ exit1(td, 0);
}
int
@@ -75,3 +93,9 @@ dev_nowrite(void)
{
return -ENOTSUP;
}
+
+int
+dev_nobsize(void)
+{
+ return -ENOTSUP;
+}
diff --git a/sys/kern/kern_subr.c b/sys/kern/kern_subr.c
index f437ec7..8a08f33 100644
--- a/sys/kern/kern_subr.c
+++ b/sys/kern/kern_subr.c
@@ -29,9 +29,12 @@
#include <sys/proc.h>
#include <sys/types.h>
+#include <sys/param.h>
#include <sys/errno.h>
+#include <sys/mman.h>
#include <sys/exec.h>
#include <sys/systm.h>
+#include <vm/vm.h>
#include <string.h>
/*
@@ -45,6 +48,8 @@ static bool
check_uaddr(const void *uaddr)
{
vaddr_t stack_start, stack_end;
+ struct mmap_lgdr *lp;
+ struct mmap_entry find, *res;
struct exec_prog exec;
struct proc *td;
uintptr_t addr;
@@ -61,6 +66,22 @@ check_uaddr(const void *uaddr)
if (addr >= stack_start && addr <= stack_end)
return true;
+ /* Try to grab the mmap ledger */
+ if ((lp = td->mlgdr) == NULL) {
+ return false;
+ }
+
+ /*
+ * Now give an attempt at looking through the
+ * mmap ledger. Perhaps this memory was allocated
+ * in the user heap?
+ */
+ find.va_start = ALIGN_DOWN(addr, DEFAULT_PAGESIZE);
+ res = RBT_FIND(lgdr_entries, &lp->hd, &find);
+ if (res != NULL) {
+ return true;
+ }
+
return false;
}
diff --git a/sys/kern/kern_synch.c b/sys/kern/kern_synch.c
index 57b27d0..7660f1f 100644
--- a/sys/kern/kern_synch.c
+++ b/sys/kern/kern_synch.c
@@ -28,19 +28,20 @@
*/
#include <sys/types.h>
+#include <sys/mutex.h>
#include <sys/systm.h>
#include <sys/errno.h>
+#include <sys/sched.h>
#include <sys/atomic.h>
#include <sys/syslog.h>
#include <sys/spinlock.h>
+#include <machine/cdefs.h>
#include <dev/timer.h>
+#include <string.h>
#define pr_trace(fmt, ...) kprintf("synch: " fmt, ##__VA_ARGS__)
#define pr_error(...) pr_trace(__VA_ARGS__)
-/* XXX: Be very careful with this */
-static struct spinlock __syslock;
-
/*
* Returns 0 on success, returns non-zero value
* on timeout/failure.
@@ -80,7 +81,10 @@ spinlock_usleep(struct spinlock *lock, size_t usec_max)
void
spinlock_acquire(struct spinlock *lock)
{
- while (__atomic_test_and_set(&lock->lock, __ATOMIC_ACQUIRE));
+ sched_preempt_set(false);
+ while (__atomic_test_and_set(&lock->lock, __ATOMIC_ACQUIRE)) {
+ md_pause();
+ }
}
/*
@@ -104,35 +108,66 @@ spinlock_try_acquire(struct spinlock *lock)
return 1;
}
- while (__atomic_test_and_set(&lock->lock, __ATOMIC_ACQUIRE));
- return 0;
+ return __atomic_test_and_set(&lock->lock, __ATOMIC_ACQUIRE);
}
void
spinlock_release(struct spinlock *lock)
{
__atomic_clear(&lock->lock, __ATOMIC_RELEASE);
+ sched_preempt_set(true);
}
/*
- * Attempt to hold the system-wide lock, returns 1
- * if already held.
- *
- * XXX: Only use for CRITICAL code sections.
+ * Create a new mutex lock object
*/
-int
-syslock(void)
+struct mutex *
+mutex_new(const char *name)
{
- return spinlock_try_acquire(&__syslock);
+ struct mutex *mtx;
+ size_t namelen;
+
+ mtx = dynalloc(sizeof(*mtx));
+ if (mtx == NULL) {
+ return NULL;
+ }
+
+ mtx->lock = 0;
+ namelen = strlen(name);
+
+ /* Don't overflow the name buffer */
+ if (namelen >= MUTEX_NAME_LEN) {
+ namelen = MUTEX_NAME_LEN - 1;
+ }
+
+ memcpy(mtx->name, name, namelen);
+ return mtx;
}
/*
- * Release the system-wide lock
+ * Acquire a mutex
*
- * XXX: Only use for CRITICAL code sections.
+ * @mtx: Mutex to acquire
+ * @flags: Optional flags
*/
+int
+mutex_acquire(struct mutex *mtx, int flags)
+{
+ while (__atomic_test_and_set(&mtx->lock, __ATOMIC_ACQUIRE)) {
+ sched_yield();
+ }
+
+ return 0;
+}
+
+void
+mutex_release(struct mutex *mtx)
+{
+ __atomic_clear(&mtx->lock, __ATOMIC_RELEASE);
+}
+
void
-sysrel(void)
+mutex_free(struct mutex *mtx)
{
- spinlock_release(&__syslock);
+ dynfree(mtx);
}
diff --git a/sys/kern/kern_syscall.c b/sys/kern/kern_syscall.c
index 986d82a..c352b9c 100644
--- a/sys/kern/kern_syscall.c
+++ b/sys/kern/kern_syscall.c
@@ -29,9 +29,16 @@
#include <sys/syscall.h>
#include <sys/sysctl.h>
+#include <sys/socket.h>
+#include <sys/reboot.h>
#include <sys/types.h>
+#include <sys/ucred.h>
+#include <sys/disk.h>
+#include <sys/time.h>
+#include <sys/mman.h>
#include <sys/proc.h>
#include <sys/vfs.h>
+#include <sys/krq.h>
scret_t(*g_sctab[])(struct syscall_args *) = {
NULL, /* SYS_none */
@@ -42,6 +49,28 @@ scret_t(*g_sctab[])(struct syscall_args *) = {
sys_stat, /* SYS_stat */
sys_sysctl, /* SYS_sysctl */
sys_write, /* SYS_write */
+ sys_spawn, /* SYS_spawn */
+ sys_reboot, /* SYS_reboot */
+ sys_mmap, /* SYS_mmap */
+ sys_munmap, /* SYS_munap */
+ sys_access, /* SYS_access */
+ sys_lseek, /* SYS_lseek */
+ sys_sleep, /* SYS_sleep */
+ sys_inject, /* SYS_inject */
+ sys_getpid, /* SYS_getpid */
+ sys_getppid, /* SYS_getppid */
+ sys_setuid, /* SYS_setuid */
+ sys_getuid, /* SYS_getuid */
+ sys_waitpid, /* SYS_waitpid */
+ sys_socket, /* SYS_socket */
+ sys_bind, /* SYS_bind */
+ sys_recv, /* SYS_recv */
+ sys_send, /* SYS_send */
+ sys_sendmsg, /* SYS_sendmsg */
+ sys_recvmsg, /* SYS_recvmsg */
+ sys_connect, /* SYS_connect */
+ sys_setsockopt, /* SYS_setsockopt */
+ sys_disk, /* SYS_disk */
};
const size_t MAX_SYSCALLS = NELEM(g_sctab);
diff --git a/sys/kern/kern_sysctl.c b/sys/kern/kern_sysctl.c
index 7679aa1..1f5e578 100644
--- a/sys/kern/kern_sysctl.c
+++ b/sys/kern/kern_sysctl.c
@@ -33,12 +33,16 @@
#include <sys/errno.h>
#include <sys/systm.h>
#include <vm/dynalloc.h>
+#include <vm/vm.h>
#include <string.h>
#define HYRA_RELEASE "Hyra/" HYRA_ARCH " " \
HYRA_VERSION " " \
HYRA_BUILDDATE
+extern size_t g_nthreads;
+static uint32_t pagesize = DEFAULT_PAGESIZE;
+static char machine[] = HYRA_ARCH;
static char hyra[] = "Hyra";
static char hyra_version[] = HYRA_VERSION;
static char osrelease[] = HYRA_RELEASE;
@@ -49,10 +53,20 @@ static char osrelease[] = HYRA_RELEASE;
* allocated through dynalloc(9).
*/
static struct sysctl_entry common_optab[] = {
+ /* 'kern.*' */
[KERN_OSTYPE] = { KERN_OSTYPE, SYSCTL_OPTYPE_STR_RO, hyra },
[KERN_OSRELEASE] = { KERN_OSRELEASE, SYSCTL_OPTYPE_STR_RO, &osrelease },
[KERN_VERSION] = { KERN_VERSION, SYSCTL_OPTYPE_STR_RO, &hyra_version },
- [KERN_VCACHE_TYPE] = { KERN_VCACHE_TYPE, SYSCTL_OPTYPE_STR, NULL }
+ [KERN_VCACHE_TYPE] = { KERN_VCACHE_TYPE, SYSCTL_OPTYPE_STR, NULL },
+ [KERN_HOSTNAME] = { KERN_HOSTNAME, SYSCTL_OPTYPE_STR, NULL },
+
+ /* 'hw.*' */
+ [HW_PAGESIZE] = { HW_PAGESIZE, SYSCTL_OPTYPE_INT_RO, &pagesize },
+ [HW_NCPU] = { HW_NCPU, SYSCTL_OPTYPE_INT, NULL },
+ [HW_MACHINE] = {HW_MACHINE, SYSCTL_OPTYPE_STR_RO, &machine },
+
+ /* 'proc.*' */
+ [PROC_COUNT] = { PROC_COUNT, SYSCTL_OPTYPE_INT_RO, &g_nthreads }
};
static int
@@ -91,19 +105,18 @@ static int
do_sysctl(struct sysctl_args *args)
{
struct sysctl_args new_args;
- size_t name_len, oldlenp;
+ size_t name_len = 1, oldlenp = 0;
int *name = NULL;
void *oldp = NULL, *newp = NULL;
- int retval = 0;
-
- if (args->oldlenp == NULL) {
- return -EINVAL;
- }
-
- name_len = args->nlen;
- retval = copyin(args->oldlenp, &oldlenp, sizeof(oldlenp));
- if (retval != 0) {
- goto done;
+ int retval = 0, have_oldlen = 0;
+
+ if (args->oldlenp != NULL) {
+ have_oldlen = 1;
+ name_len = args->nlen;
+ retval = copyin(args->oldlenp, &oldlenp, sizeof(oldlenp));
+ if (retval != 0) {
+ goto done;
+ }
}
/* Copy in newp if it is set */
@@ -124,25 +137,30 @@ do_sysctl(struct sysctl_args *args)
return retval;
}
- oldp = dynalloc(oldlenp);
- retval = copyin(args->oldp, oldp, oldlenp);
- if (retval != 0) {
- return retval;
+ if (oldlenp != 0) {
+ oldp = dynalloc(oldlenp);
+ retval = copyin(args->oldp, oldp, oldlenp);
+ if (retval != 0) {
+ return retval;
+ }
}
/* Prepare the arguments for the sysctl call */
new_args.name = name;
new_args.nlen = name_len;
new_args.oldp = oldp;
- new_args.oldlenp = &oldlenp;
+ new_args.oldlenp = (have_oldlen) ? &oldlenp : NULL;
new_args.newp = newp;
+ new_args.newlen = args->newlen;
retval = sysctl(&new_args);
if (retval != 0) {
goto done;
}
- copyout(oldp, args->oldp, oldlenp);
+ if (oldlenp != 0) {
+ copyout(oldp, args->oldp, oldlenp);
+ }
done:
if (name != NULL)
dynfree(name);
@@ -154,6 +172,33 @@ done:
return retval;
}
+/*
+ * Clear a writable sysctl string variable to the
+ * value of "(undef)"
+ *
+ * @name: Name to clear
+ */
+int
+sysctl_clearstr(int name)
+{
+ struct sysctl_args args;
+ char val[] = "(undef)";
+ int error;
+
+ args.name = &name;
+ args.nlen = 1;
+ args.oldlenp = 0;
+ args.oldp = NULL;
+ args.newp = val;
+ args.newlen = sizeof(val);
+
+ if ((error = sysctl(&args)) != 0) {
+ return error;
+ }
+
+ return 0;
+}
+
int
sysctl(struct sysctl_args *args)
{
diff --git a/sys/kern/kern_syslog.c b/sys/kern/kern_syslog.c
index 10bf348..c7f51f7 100644
--- a/sys/kern/kern_syslog.c
+++ b/sys/kern/kern_syslog.c
@@ -28,9 +28,14 @@
*/
#include <sys/syslog.h>
+#include <sys/cdefs.h>
+#include <sys/sio.h>
#include <sys/spinlock.h>
+#include <sys/device.h>
+#include <sys/errno.h>
#include <dev/cons/cons.h>
#include <dev/timer.h>
+#include <fs/devfs.h>
#include <stdarg.h>
#include <string.h>
@@ -40,21 +45,105 @@
#define SERIAL_DEBUG 0
#endif
+#if defined(__USER_KMSG)
+#define USER_KMSG __USER_KMSG
+#else
+#define USER_KMSG 0
+#endif
+
+#define KBUF_SIZE (1 << 16)
+
+/* Sanity check */
+__static_assert(KBUF_SIZE <= (1 << 16), "KBUF_SIZE too high!");
+
/* Global logger lock */
-static struct spinlock lock = {0};
+static struct spinlock kmsg_lock = {0};
+static bool no_cons_log = false;
+
+/* Kernel message buffer */
+static char kmsg[KBUF_SIZE];
+static size_t kmsg_i = 0;
+static struct cdevsw kmsg_cdevw;
+
+static void
+kmsg_append(const char *s, size_t len)
+{
+ spinlock_acquire(&kmsg_lock);
+ if ((kmsg_i + len) >= KBUF_SIZE) {
+ kmsg_i = 0;
+ }
+
+ for (size_t i = 0; i < len; ++i) {
+ kmsg[kmsg_i + i] = s[i];
+ }
+ kmsg_i += len;
+ spinlock_release(&kmsg_lock);
+}
+
+/*
+ * Character device function.
+ */
+static int
+kmsg_read(dev_t dev, struct sio_txn *sio, int flags)
+{
+ size_t len, offset, j;
+ size_t bytes_read = 0;
+ char *p = sio->buf;
+
+ spinlock_acquire(&kmsg_lock);
+ len = sio->len;
+ offset = sio->offset;
+
+ if (len == 0) {
+ spinlock_release(&kmsg_lock);
+ return -EINVAL;
+ }
+ if (offset >= kmsg_i) {
+ spinlock_release(&kmsg_lock);
+ return 0;
+ }
+
+ for (size_t i = 0; i < len; ++i) {
+ j = offset + i;
+ if (j > kmsg_i) {
+ break;
+ }
+
+ p[i] = kmsg[j];
+ ++bytes_read;
+ }
+
+ spinlock_release(&kmsg_lock);
+ return bytes_read;
+}
static void
syslog_write(const char *s, size_t len)
{
- const char *p = s;
+ const char *p;
+ size_t l;
- while (len--) {
- cons_putch(&g_root_scr, *p);
- if (SERIAL_DEBUG) {
+ if (SERIAL_DEBUG) {
+ p = s;
+ l = len;
+ while (l--) {
serial_putc(*p);
+ ++p;
}
- ++p;
}
+
+ kmsg_append(s, len);
+
+ /*
+ * If the USER_KMSG option is disabled in kconf,
+ * do not log to the console if everything else
+ * has already started.
+ */
+ if (!USER_KMSG && no_cons_log) {
+ return;
+ }
+
+ cons_putstr(&g_root_scr, s, len);
}
/*
@@ -105,10 +194,42 @@ kprintf(const char *fmt, ...)
syslog_write(timestamp, strlen(timestamp));
}
- spinlock_acquire(&lock);
va_start(ap, fmt);
vkprintf(fmt_p, &ap);
va_end(ap);
- spinlock_release(&lock);
}
+
+/*
+ * Silence kernel messages in if the system
+ * is already operating in a user context.
+ *
+ * XXX: This is ignored if the kconf USER_KMSG
+ * option is set to "no". A kmsg device file
+ * is also created on the first call.
+ */
+void
+syslog_silence(bool option)
+{
+ static bool once = false;
+ static char devname[] = "kmsg";
+ devmajor_t major;
+ dev_t dev;
+
+ if (!once) {
+ once = true;
+ major = dev_alloc_major();
+ dev = dev_alloc(major);
+
+ dev_register(major, dev, &kmsg_cdevw);
+ devfs_create_entry(devname, major, dev, 0444);
+
+ }
+
+ no_cons_log = option;
+}
+
+static struct cdevsw kmsg_cdevw = {
+ .read = kmsg_read,
+ .write = nowrite
+};
diff --git a/sys/kern/kern_time.c b/sys/kern/kern_time.c
new file mode 100644
index 0000000..e741157
--- /dev/null
+++ b/sys/kern/kern_time.c
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/syscall.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/cdefs.h>
+#include <dev/timer.h>
+#include <machine/cdefs.h>
+
+/*
+ * arg0: Timespec
+ * arg1: Remaining timeval
+ */
+scret_t
+sys_sleep(struct syscall_args *scargs)
+{
+ struct timespec ts;
+ struct timer tmr;
+ size_t timeout_msec;
+ tmrr_status_t status;
+ int error;
+
+ error = copyin((void *)scargs->arg0, &ts, sizeof(ts));
+ if (error < 0) {
+ return error;
+ }
+
+ if (ts.tv_nsec >= 1000000000) {
+ return -EINVAL;
+ }
+
+ status = req_timer(TIMER_GP, &tmr);
+ if (__unlikely(status != TMRR_SUCCESS)) {
+ return -ENOTSUP;
+ }
+ if (__unlikely(tmr.msleep == NULL)) {
+ return -ENOTSUP;
+ }
+
+ timeout_msec = ts.tv_nsec / 1000000;
+ timeout_msec += ts.tv_sec * 1000;
+ tmr.msleep(timeout_msec);
+ return 0;
+}
diff --git a/sys/kern/kern_uio.c b/sys/kern/kern_uio.c
new file mode 100644
index 0000000..2ec1532
--- /dev/null
+++ b/sys/kern/kern_uio.c
@@ -0,0 +1,272 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/limits.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/filedesc.h>
+
+/*
+ * Clean up after a UIO copyin() operation
+ *
+ * @iov: iovec copy to clean up
+ * @iovcnt: Number of iovec entries
+ */
+void
+uio_copyin_clean(struct iovec *iov, int iovcnt)
+{
+ for (int i = 0; i < iovcnt; ++i) {
+ if (iov[i].iov_base == NULL) {
+ continue;
+ }
+
+ dynfree(iov[i].iov_base);
+ iov[i].iov_base = NULL;
+ }
+}
+
+/*
+ * Read data into POSIX.1‐2017 iovec
+ *
+ * @filedes: File descriptor number
+ * @iov: I/O vector to read file into
+ * @iovnt: Number of I/O vectors
+ */
+ssize_t
+readv(int filedes, const struct iovec *iov, int iovcnt)
+{
+ void *base;
+ size_t len;
+ ssize_t tmp, bytes_read = 0;
+
+ if (filedes < 0) {
+ return -EINVAL;
+ }
+
+ /*
+ * Make sure that this conforms to our max
+ * iovec limit.
+ */
+ if (iovcnt > IOVEC_MAX) {
+ return -EINVAL;
+ }
+
+ /*
+ * Go through each I/O vector and read a chunk
+ * of data into one.
+ */
+ for (int i = 0; i < iovcnt; ++i) {
+ base = iov[i].iov_base;
+ len = iov[i].iov_len;
+
+ /*
+ * If we encounter a base that is NULL,
+ * or if the length to read is an invalid
+ * value of zero. We can just assume this
+ * is some sort of weird list termination?
+ */
+ if (base == NULL || len == 0) {
+ break;
+ }
+
+ /* Read the file into this base */
+ tmp = fd_read(filedes, base, len);
+
+ /* Did anything go wrong? */
+ if (tmp < 0) {
+ return tmp;
+ }
+
+ /* No more data */
+ if (tmp == 0) {
+ break;
+ }
+
+ /* Read more bytes */
+ bytes_read += tmp;
+ }
+
+ return bytes_read;
+}
+
+/*
+ * Write data from POSIX.1‐2017 iovec
+ *
+ * @filedes: File descriptor number
+ * @iov: I/O vector to write to file
+ * @iovnt: Number of I/O vectors
+ */
+ssize_t
+writev(int filedes, const struct iovec *iov, int iovcnt)
+{
+ void *base;
+ size_t len;
+ ssize_t bytes_written = 0;
+ ssize_t tmp;
+
+ if (filedes < 0) {
+ return -EINVAL;
+ }
+
+ /*
+ * Are we within the limits? Return an
+ * error if not.
+ */
+ if (iovcnt > IOVEC_MAX) {
+ return -EINVAL;
+ }
+
+ for (int i = 0; i < iovcnt; ++i) {
+ base = iov[i].iov_base;
+ len = iov[i].iov_len;
+
+ /*
+ * These are invalid, whatever these are,
+ * terminate our walk through.
+ */
+ if (base == NULL || len == 0) {
+ break;
+ }
+
+ /* Write the data from the iovec */
+ tmp = fd_write(filedes, base, len);
+
+ /* Was there an error? */
+ if (tmp < 0) {
+ return tmp;
+ }
+
+ /* No more data to read? */
+ if (tmp == 0) {
+ break;
+ }
+
+ bytes_written += tmp;
+ }
+
+ return bytes_written;
+}
+
+/*
+ * Validate iovecs coming in from userland
+ * and copy it to a kernel buffer.
+ *
+ * XXX: A new buffer is allocated in k_iov[i]->iov_base
+ * and must be freed with dynfree() after use.
+ *
+ * @u_iov: Userspace source iovecs
+ * @k_iov: Kernel destination iovec
+ * @iovcnt: Number of iovecs to copy
+ */
+int
+uio_copyin(const struct iovec *u_iov, struct iovec *k_iov, int iovcnt)
+{
+ struct iovec *iov_dest;
+ const struct iovec *iov_src;
+ size_t len;
+ void *old_base;
+ int error;
+
+ if (u_iov == NULL || k_iov == NULL) {
+ return -EINVAL;
+ }
+
+ for (int i = 0; i < iovcnt; ++i) {
+ iov_dest = &k_iov[i];
+ iov_src = &u_iov[i];
+ error = copyin(iov_src, iov_dest, sizeof(*iov_dest));
+
+ if (error < 0) {
+ uio_copyin_clean(iov_dest, i + 1);
+ return error;
+ }
+
+ /*
+ * Save the old base so that we may copy the data to
+ * the new kernel buffer. First we'd need to allocate
+ * one of course.
+ */
+ old_base = iov_dest->iov_base;
+ len = iov_dest->iov_len;
+ iov_dest->iov_base = dynalloc(len);
+
+ /* Did it fail? */
+ if (iov_dest->iov_base == NULL) {
+ uio_copyin_clean(iov_dest, i + 1);
+ return -ENOMEM;
+ }
+
+ /* Copy actual data in */
+ error = copyin(old_base, iov_dest->iov_base, len);
+ if (error < 0) {
+ uio_copyin_clean(iov_dest, i + 1);
+ return error;
+ }
+ }
+
+ return 0;
+}
+
+
+/*
+ * Validate iovecs going out from kernel space (us)
+ * before actually sending it out.
+ *
+ * @k_iov: Kernel iovec to copyout
+ * @u_iov: Userspace destination
+ * @iovcnt: Number of iovecs
+ */
+int
+uio_copyout(const struct iovec *k_iov, struct iovec *u_iov, int iovcnt)
+{
+ struct iovec iov_shadow, *iov_dest;
+ const struct iovec *iov_src;
+ int error;
+
+ for (int i = 0; i < iovcnt; ++i) {
+ iov_dest = &u_iov[i];
+ iov_src = &k_iov[i];
+
+ /* Grab a shadow copy */
+ error = copyin(iov_src, &iov_shadow, sizeof(iov_shadow));
+ if (error < 0) {
+ return error;
+ }
+
+ /* Copy out actual data */
+ error = copyout(iov_src->iov_base, iov_dest->iov_base, iov_dest->iov_len);
+ if (error < 0) {
+ return error;
+ }
+ }
+
+ return 0;
+}
diff --git a/sys/kern/kern_vsr.c b/sys/kern/kern_vsr.c
new file mode 100644
index 0000000..c59be1e
--- /dev/null
+++ b/sys/kern/kern_vsr.c
@@ -0,0 +1,344 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/vsr.h>
+#include <sys/proc.h>
+#include <sys/param.h>
+#include <sys/limits.h>
+#include <sys/syslog.h>
+#include <vm/dynalloc.h>
+#include <string.h>
+
+#define pr_trace(fmt, ...) kprintf("vsr: " fmt, ##__VA_ARGS__)
+#define pr_error(...) pr_trace(__VA_ARGS__)
+
+static uint32_t
+fnv1_hash(const char *s)
+{
+ uint32_t hash = 2166136261UL;
+ const uint8_t *p = (uint8_t *)s;
+
+ while (*p != '\0') {
+ hash ^= *p;
+ hash = hash * 0x01000193;
+ ++p;
+ }
+
+ return hash;
+}
+
+/*
+ * Add a VSR capsule to a domain.
+ */
+static void
+vsr_domain_add(struct vsr_domain *vsp, struct vsr_capsule *cap)
+{
+ struct vsr_table *tab;
+ struct vsr_capsule **slot;
+ uint32_t hash;
+
+ if (vsp == NULL || cap == NULL) {
+ return;
+ }
+
+ if (cap->name == NULL) {
+ pr_error("vsr_domain_add: cap->name == NULL\n");
+ return;
+ }
+
+ tab = &vsp->table;
+ hash = fnv1_hash(cap->name);
+ slot = &tab->capsules[hash % VSR_MAX_CAPSULE];
+
+ /* If this slot is free, set it */
+ if (*slot == NULL) {
+ *slot = cap;
+ return;
+ }
+
+ /* Handle collision */
+ TAILQ_INSERT_TAIL(&(*slot)->buckets, cap, link);
+}
+
+/*
+ * Handle VSR domain hashmap collisions.
+ *
+ * @slot: Slot that we have collided with
+ * @name: Name to lookup
+ *
+ * Returns the pointer to the actual capsule if the
+ * collision has been resolved, otherwise, NULL if the
+ * entry to look up was not found.
+ */
+static struct vsr_capsule *
+vsr_domain_clash(struct vsr_capsule *slot, const char *name)
+{
+ struct vsr_capsule *cap_ent;
+
+ TAILQ_FOREACH(cap_ent, &slot->buckets, link) {
+ if (cap_ent == NULL) {
+ continue;
+ }
+
+ if (strcmp(cap_ent->name, name) == 0) {
+ return cap_ent;
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * Lookup a capsule within a VSR domain
+ * by name.
+ *
+ * @vsp: Domain to lookup within
+ * @name: Name to use as lookup key
+ *
+ * Returns NULL if no entry was found.
+ */
+static struct vsr_capsule *
+vfs_domain_lookup(struct vsr_domain *vsp, const char *name)
+{
+ uint32_t hash;
+ struct vsr_table *tab;
+ struct vsr_capsule **slot;
+
+ if (vsp == NULL || name == NULL) {
+ return NULL;
+ }
+
+ tab = &vsp->table;
+ hash = fnv1_hash(name);
+ slot = &tab->capsules[hash % VSR_MAX_CAPSULE];
+
+ if (*slot == NULL) {
+ return NULL;
+ }
+
+ if (strcmp((*slot)->name, name) != 0) {
+ return vsr_domain_clash(*slot, name);
+ }
+
+ return *slot;
+}
+
+/*
+ * Destroy a VSR capsule
+ *
+ * @capule: Capsule to destroy
+ */
+static void
+vsr_destroy_capsule(struct vsr_capsule *capsule)
+{
+ struct vsr_capsule *bucket;
+ struct capsule_ops *ops;
+
+ if (capsule->name != NULL) {
+ dynfree(capsule->name);
+ capsule->name = NULL;
+ }
+
+ ops = &capsule->ops;
+ if (ops->reclaim != NULL) {
+ ops->reclaim(capsule, 0);
+ }
+
+ TAILQ_FOREACH(bucket, &capsule->buckets, link) {
+ if (bucket == NULL) {
+ continue;
+ }
+ vsr_destroy_capsule(bucket);
+ }
+
+ /* Release any held locks */
+ mutex_release(&capsule->lock);
+}
+
+/*
+ * Destroy a VSR table
+ *
+ * @tab: Table to destroy.
+ */
+static void
+vsr_destroy_table(struct vsr_table *tab)
+{
+ struct vsr_capsule *capsule;
+
+ if (tab == NULL) {
+ pr_error("vsr_destroy_table: tab is NULL\n");
+ return;
+ }
+
+ for (int i = 0; i < VSR_MAX_CAPSULE; ++i) {
+ if ((capsule = tab->capsules[i]) == NULL) {
+ continue;
+ }
+
+ vsr_destroy_capsule(capsule);
+ }
+}
+
+/*
+ * Allocate a new VSR capsule and add it to
+ * VSR domain.
+ *
+ * @type: Domain type (e.g., VSR_FILE)
+ * @name: Capsule name (e.g., "mod0.data")
+ * @sz: Length of capsulized data
+ */
+struct vsr_capsule *
+vsr_new_capsule(struct proc *td, vsr_domain_t type, const char *name)
+{
+ struct vsr_capsule *capsule;
+ struct vsr_domain *domain;
+
+ /* Valid args? */
+ if (type >= VSR_MAX_DOMAIN || td == NULL) {
+ return NULL;
+ }
+
+ /*
+ * The VSR domain must be registered for
+ * us to add any capsules to it.
+ */
+ if ((domain = td->vsr_tab[type]) == NULL) {
+ pr_error("VSR domain %d not registered\n", type);
+ return NULL;
+ }
+
+ /* Allocate a new capsule */
+ capsule = dynalloc(sizeof(*capsule));
+ if (capsule == NULL) {
+ return NULL;
+ }
+
+ memset(capsule, 0, sizeof(*capsule));
+ capsule->name = strdup(name);
+
+ TAILQ_INIT(&capsule->buckets);
+ vsr_domain_add(domain, capsule);
+ return capsule;
+}
+
+/*
+ * Allocate a new VSR domain and add it to
+ * a specific process.
+ *
+ * @type: VSR type (e.g., VSR_FILE)
+ */
+struct vsr_domain *
+vsr_new_domain(struct proc *td, vsr_domain_t type)
+{
+ struct vsr_domain *domain;
+
+ /* Valid args? */
+ if (type >= VSR_MAX_DOMAIN || td == NULL) {
+ return NULL;
+ }
+
+ /*
+ * Do not overwrite the entry if it is
+ * already allocated and log this anomalous
+ * activity.
+ */
+ if (td->vsr_tab[type] != NULL) {
+ pr_error("[security]: type %d already allocated\n", type);
+ return NULL;
+ }
+
+ domain = dynalloc(sizeof(*domain));
+ if (domain == NULL) {
+ return NULL;
+ }
+
+ /* Initialize the domain */
+ memset(domain, 0, sizeof(*domain));
+ domain->type = type;
+
+ td->vsr_tab[type] = domain;
+ return domain;
+}
+
+/*
+ * Lookup a capsule by name for the current
+ * process.
+ */
+struct vsr_capsule *
+vsr_lookup_capsule(struct proc *td, vsr_domain_t type, const char *name)
+{
+ struct vsr_domain *domain;
+
+ if (td == NULL) {
+ return NULL;
+ }
+
+ /*
+ * The VSR domain must be registered for
+ * us to lookup any capsules from it.
+ */
+ if ((domain = td->vsr_tab[type]) == NULL) {
+ pr_error("VSR domain %d not registered\n", type);
+ return NULL;
+ }
+
+ return vfs_domain_lookup(domain, name);
+}
+
+/*
+ * Initialize per-process domains
+ */
+void
+vsr_init_domains(struct proc *td)
+{
+ if (vsr_new_domain(td, VSR_FILE) == NULL) {
+ pr_error("failed to initialize VSR file domain\n");
+ }
+}
+
+/*
+ * Destroy per-process domains
+ */
+void
+vsr_destroy_domains(struct proc *td)
+{
+ struct vsr_domain *domain;
+
+ if (td == NULL) {
+ return;
+ }
+
+ for (int i = 0; i < VSR_MAX_DOMAIN; ++i) {
+ if ((domain = td->vsr_tab[i]) == NULL) {
+ continue;
+ }
+
+ vsr_destroy_table(&domain->table);
+ }
+}
diff --git a/sys/kern/kern_work.c b/sys/kern/kern_work.c
new file mode 100644
index 0000000..918af89
--- /dev/null
+++ b/sys/kern/kern_work.c
@@ -0,0 +1,274 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/panic.h>
+#include <sys/proc.h>
+#include <sys/sched.h>
+#include <sys/syslog.h>
+#include <sys/workqueue.h>
+#include <vm/dynalloc.h>
+#include <string.h>
+
+#define pr_trace(fmt, ...) kprintf("workq: " fmt, ##__VA_ARGS__)
+#define pr_error(...) pr_trace(__VA_ARGS__)
+
+extern struct proc g_proc0;
+
+/*
+ * The workqueue cookie value that is used for
+ * verifying if a workqueue object is properly
+ * set up or not.
+ */
+#define WQ_COOKIE 0xFC0B
+
+/*
+ * A worker services work in the queue
+ * and there is one per workqueue.
+ */
+static void
+workqueue_worker(void)
+{
+ struct proc *td;
+ struct workqueue *wqp;
+ struct work *wp;
+
+ td = this_td();
+ if ((wqp = td->data) == NULL) {
+ panic("no workqueue in thread\n");
+ }
+
+ /*
+ * Weird things can happen, just be careful
+ * here...
+ */
+ if (wqp->cookie != WQ_COOKIE) {
+ panic("bad WQ_COOKIE in worker\n");
+ }
+
+ for (;;) {
+ mutex_acquire(wqp->lock, 0);
+ wp = TAILQ_FIRST(&wqp->work);
+
+ /* Try again later if empty */
+ if (wp == NULL) {
+ mutex_release(wqp->lock);
+ sched_yield();
+ continue;
+ }
+
+ wp->func(wqp, wp);
+ TAILQ_REMOVE(&wqp->work, wp, link);
+
+ /*
+ * Decrement the amount of work that is
+ * left to get done. Check for underflows
+ * which should not happen unless something
+ * clobbers the fields.
+ */
+ if ((--wqp->nwork) < 0) {
+ panic("wqp nwork underflow\n");
+ }
+
+ mutex_release(wqp->lock);
+ sched_yield();
+ }
+}
+
+/*
+ * Allocates a new work queue that may be used
+ * to hold queued up tasks.
+ *
+ * @name: Name to give the workqueue
+ * @max_work: Maximum number of jobs to be added
+ * @ipl: IPL that the work must operate in
+ *
+ * Returns a pointer to the new workqueue on success,
+ * otherwise a value of NULL is returned.
+ */
+struct workqueue *
+workqueue_new(const char *name, size_t max_work, int ipl)
+{
+ struct workqueue *wqp;
+ struct proc *td;
+
+ td = this_td();
+ if (__unlikely(td == NULL)) {
+ pr_error("no thread in workqueue_new()\n");
+ return NULL;
+ }
+
+ wqp = dynalloc(sizeof(*wqp));
+ if (wqp == NULL) {
+ return NULL;
+ }
+
+ wqp->name = strdup(name);
+ TAILQ_INIT(&wqp->work);
+ wqp->ipl = ipl;
+ wqp->max_work = max_work;
+ wqp->nwork = 0;
+ wqp->cookie = WQ_COOKIE;
+ wqp->lock = mutex_new(wqp->name);
+
+ /*
+ * We need to spawn the work thread which
+ * is behind the management of this specific
+ * workqueue. It typically does something like
+ * dequeuing at the head of the workqueue, performing
+ * the work, cleaning up as needed and dequeuing the
+ * next and waiting if there are none yet.
+ */
+ spawn(
+ &g_proc0, workqueue_worker,
+ wqp, 0,
+ &wqp->worktd
+ );
+
+ return wqp;
+}
+
+/*
+ * Enqueue a work item onto a specific
+ * workqueue.
+ *
+ * @wqp: Pointer to specific workqueue
+ * @name: Name to set for work unit
+ * @wp: Pointer to work that should be enqueued
+ *
+ * Returns zero on success, otherwise a less than
+ * zero value is returned.
+ */
+int
+workqueue_enq(struct workqueue *wqp, const char *name, struct work *wp)
+{
+ if (wqp == NULL || wp == NULL) {
+ return -EINVAL;
+ }
+
+ if (name == NULL) {
+ return -EINVAL;
+ }
+
+ /* Verify that we have a valid workqueue */
+ if (__unlikely(wqp->cookie != WQ_COOKIE)) {
+ panic("workq: bad cookie on work enqueue\n");
+ }
+
+ wp->name = strdup(name);
+ mutex_acquire(wqp->lock, 0);
+
+ /*
+ * If we have reached the max amount of jobs
+ * that we can enqueue here, just log it and
+ * bail.
+ */
+ if (wqp->nwork >= wqp->max_work) {
+ pr_error("max jobs reached for '%s'\n", wqp->name);
+ mutex_release(wqp->lock);
+ return -EAGAIN;
+ }
+
+ TAILQ_INSERT_TAIL(&wqp->work, wp, link);
+ ++wqp->nwork;
+ mutex_release(wqp->lock);
+ return 0;
+}
+
+/*
+ * Destroy a workqueue and free resources
+ * associated with it.
+ *
+ * @wqp: Pointer to workqueue to destroy
+ *
+ * Returns zero on success, otherwise a less
+ * than zero value is returned.
+ */
+int
+workqueue_destroy(struct workqueue *wqp)
+{
+ if (wqp == NULL) {
+ return -EINVAL;
+ }
+
+ /* Should not happen but just make sure */
+ if (__unlikely(wqp->cookie != WQ_COOKIE)) {
+ panic("workq: bad cookie on destroy\n");
+ }
+
+ /* Free the name if we have it */
+ if (wqp->name != NULL) {
+ dynfree(wqp->name);
+ }
+
+ if (wqp->lock != NULL) {
+ mutex_free(wqp->lock);
+ }
+
+ /* Brutally murder any workthreads */
+ if (wqp->worktd != NULL) {
+ exit1(wqp->worktd, 0);
+ wqp->worktd = NULL;
+ }
+
+ /*
+ * Zero before we free for security reasons, we
+ * don't really know what will be queued up but
+ * for certain things, it is best if we make it
+ * as if it never existed in the first place.
+ *
+ * XXX: There is no need to free the workqueue here as
+ * we had to pass it to spawn() to run the worker.
+ *
+ * During an exit, spawn() will free the thread data
+ * meaning this is already cleaned up.
+ */
+ memset(wqp, 0, sizeof(*wqp));
+ return 0;
+}
+
+/*
+ * Cleanup after work
+ *
+ * @wp: Work to clean up
+ */
+int
+work_destroy(struct work *wp)
+{
+ if (wp == NULL) {
+ return -EINVAL;
+ }
+
+ if (wp->name != NULL) {
+ dynfree(wp->name);
+ }
+
+ return 0;
+}
diff --git a/sys/kern/vfs_init.c b/sys/kern/vfs_init.c
index caa0766..bc7f8b0 100644
--- a/sys/kern/vfs_init.c
+++ b/sys/kern/vfs_init.c
@@ -36,7 +36,9 @@
struct vnode *g_root_vnode = NULL;
static struct fs_info fs_list[] = {
{MOUNT_RAMFS, &g_initramfs_vfsops, 0, 0},
- {MOUNT_DEVFS, &g_devfs_vfsops, 0, 0}
+ {MOUNT_DEVFS, &g_devfs_vfsops, 0, 0},
+ {MOUNT_CTLFS, &g_ctlfs_vfsops, 0, 0},
+ {MOUNT_TMPFS, &g_tmpfs_vfsops, 0, 0}
};
void
diff --git a/sys/kern/vfs_lookup.c b/sys/kern/vfs_lookup.c
index 7419d1d..7320102 100644
--- a/sys/kern/vfs_lookup.c
+++ b/sys/kern/vfs_lookup.c
@@ -29,6 +29,7 @@
#include <sys/namei.h>
#include <sys/vnode.h>
+#include <sys/param.h>
#include <sys/mount.h>
#include <sys/errno.h>
#include <vm/dynalloc.h>
@@ -118,20 +119,60 @@ vfs_get_fname_at(const char *path, size_t idx)
}
/*
+ * Count the number of components that exist within
+ * a path minus the delimiter as well as any redundant
+ * delimiters.
+ *
+ * @path: Path to count
+ */
+static uint8_t
+namei_num_cnp(const char *path)
+{
+ const char *p = path;
+ uint8_t count = 0;
+
+ while (*p != '\0') {
+ /* Skip redundant delimiters */
+ if (p[0] == '/' && p[1] == '/') {
+ ++p;
+ continue;
+ }
+
+ if (*p == '/') {
+ ++count;
+ }
+ ++p;
+ }
+
+ /* Don't count leading slash */
+ if (*(p - 1) == '/') {
+ --count;
+ }
+
+ return count;
+}
+
+/*
* Search for a path within a mountpoint.
*
* @mp: Mountpoint to search in.
* @path: Path to search for.
+ * @ndp: Namei data pointer
*/
static struct vnode *
-namei_mp_search(struct mount *mp, const char *path)
+namei_mp_search(struct mount *mp, const char *path, struct nameidata *ndp)
{
struct vop_lookup_args lookup_args;
struct vnode *vp = mp->vp;
+ uint8_t n_cnp = 0;
char *name;
int status;
- for (size_t i = 1;; ++i) {
+ n_cnp = namei_num_cnp(path);
+ if (ISSET(ndp->flags, NAMEI_WANTPARENT)) {
+ --n_cnp;
+ }
+ for (size_t i = 1; i < n_cnp; ++i) {
name = vfs_get_fname_at(path, i);
if (name == NULL)
break;
@@ -140,14 +181,15 @@ namei_mp_search(struct mount *mp, const char *path)
lookup_args.dirvp = vp;
lookup_args.vpp = &vp;
- status = vfs_vop_lookup(vp, &lookup_args);
+ status = vfs_vop_lookup(&lookup_args);
dynfree(name);
- if (status == 0)
- return vp;
+ if (status != 0) {
+ return NULL;
+ }
}
- return NULL;
+ return vp;
}
/*
@@ -192,7 +234,7 @@ namei(struct nameidata *ndp)
lookup_args.name = path;
lookup_args.dirvp = g_root_vnode;
lookup_args.vpp = &vp;
- status = vfs_vop_lookup(lookup_args.dirvp, &lookup_args);
+ status = vfs_vop_lookup(&lookup_args);
/* Did we find it in the root */
if (status == 0) {
@@ -211,7 +253,7 @@ namei(struct nameidata *ndp)
/* If the name matches, search within */
if (strcmp(mp->name, name) == 0)
- vp = namei_mp_search(mp, path);
+ vp = namei_mp_search(mp, path, ndp);
/* Did we find it at this mountpoint? */
if (vp != NULL) {
diff --git a/sys/kern/vfs_subr.c b/sys/kern/vfs_subr.c
index da0a4f9..69417d0 100644
--- a/sys/kern/vfs_subr.c
+++ b/sys/kern/vfs_subr.c
@@ -141,8 +141,9 @@ vfs_release_vnode(struct vnode *vp)
}
int
-vfs_vop_lookup(struct vnode *vp, struct vop_lookup_args *args)
+vfs_vop_lookup(struct vop_lookup_args *args)
{
+ const struct vnode *vp = args->dirvp;
const struct vops *vops = vp->vops;
if (vops == NULL)
@@ -180,8 +181,9 @@ vfs_vop_write(struct vnode *vp, struct sio_txn *sio)
}
int
-vfs_vop_getattr(struct vnode *vp, struct vop_getattr_args *args)
+vfs_vop_getattr(struct vop_getattr_args *args)
{
+ const struct vnode *vp = args->vp;
const struct vops *vops = vp->vops;
if (vops == NULL)
diff --git a/sys/kern/vfs_syscalls.c b/sys/kern/vfs_syscalls.c
index 6f2d683..d15ecf1 100644
--- a/sys/kern/vfs_syscalls.c
+++ b/sys/kern/vfs_syscalls.c
@@ -43,7 +43,7 @@ static int
vfs_dostat(const char *path, struct stat *sbuf)
{
char pathbuf[PATH_MAX];
- struct vattr *attr;
+ struct vattr attr;
struct stat st;
struct vnode *vp;
struct vop_getattr_args gattr;
@@ -54,11 +54,11 @@ vfs_dostat(const char *path, struct stat *sbuf)
return -EINVAL;
}
- if ((copyinstr(path, pathbuf, sizeof(path))) < 0) {
+ if ((copyinstr(path, pathbuf, sizeof(pathbuf))) < 0) {
return -EFAULT;
}
- nd.path = path;
+ nd.path = pathbuf;
nd.flags = 0;
if ((error = namei(&nd)) != 0) {
@@ -67,19 +67,42 @@ vfs_dostat(const char *path, struct stat *sbuf)
vp = nd.vp;
gattr.vp = vp;
- error = vfs_vop_getattr(vp, &gattr);
+ gattr.res = &attr;
+ error = vfs_vop_getattr(&gattr);
if (error != 0) {
return error;
}
- attr = gattr.res;
memset(&st, VNOVAL, sizeof(st));
/* Copy stat data to userspace statbuf */
- st.st_mode = attr->mode;
- st.st_size = attr->size;
+ st.st_mode = attr.mode;
+ st.st_size = attr.size;
copyout(&st, sbuf, sizeof(*sbuf));
+ vfs_release_vnode(vp);
+ return 0;
+}
+
+static int
+vfs_doaccess(const char *path)
+{
+ struct nameidata nd;
+ char pathbuf[PATH_MAX];
+ int error;
+
+ if ((copyinstr(path, pathbuf, sizeof(pathbuf))) < 0) {
+ return -EFAULT;
+ }
+
+ nd.path = pathbuf;
+ nd.flags = 0;
+
+ if ((error = namei(&nd)) != 0) {
+ return error;
+ }
+
+ vfs_release_vnode(nd.vp);
return 0;
}
@@ -149,3 +172,14 @@ sys_stat(struct syscall_args *scargs)
{
return vfs_dostat((const char *)scargs->arg0, (void *)scargs->arg1);
}
+
+/*
+ * Check if a file can be accessed.
+ *
+ * @arg0: path
+ */
+scret_t
+sys_access(struct syscall_args *scargs)
+{
+ return vfs_doaccess((const char *)scargs->arg0);
+}
diff --git a/sys/kern/vfs_vcache.c b/sys/kern/vfs_vcache.c
index 25e244c..6c08caf 100644
--- a/sys/kern/vfs_vcache.c
+++ b/sys/kern/vfs_vcache.c
@@ -161,7 +161,7 @@ vfs_vcache_migrate(int newtype)
args.oldp = NULL;
args.oldlenp = NULL;
args.newp = sysctl_val;
- args.newlen = strlen(sysctl_val);
+ args.newlen = strlen(sysctl_val) + 1;
if ((retval = sysctl(&args)) != 0) {
return retval;
diff --git a/sys/lib/crc32.c b/sys/lib/crc32.c
new file mode 100644
index 0000000..dda4428
--- /dev/null
+++ b/sys/lib/crc32.c
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <crc32.h>
+
+static const uint32_t crc32_tab[] = {
+ 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F,
+ 0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
+ 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2,
+ 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
+ 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9,
+ 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
+ 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C,
+ 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
+ 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423,
+ 0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
+ 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x01DB7106,
+ 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
+ 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D,
+ 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
+ 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950,
+ 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
+ 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7,
+ 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
+ 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA,
+ 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
+ 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81,
+ 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
+ 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84,
+ 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
+ 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB,
+ 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
+ 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E,
+ 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
+ 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55,
+ 0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
+ 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28,
+ 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
+ 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F,
+ 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
+ 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242,
+ 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
+ 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69,
+ 0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
+ 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC,
+ 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
+ 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693,
+ 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
+ 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
+};
+
+uint32_t
+crc32(const void *data, size_t len)
+{
+ const uint8_t *p = data;
+ uint32_t val = 0xFFFFFFFF;
+
+ for (size_t i = 0; i < len; ++i) {
+ val = (val >> 8) ^ crc32_tab[(val ^ p[i]) & 0xFF];
+ }
+
+ return val ^ 0xFFFFFFFF;
+}
diff --git a/sys/lib/string/memmove.c b/sys/lib/string/memmove.c
new file mode 100644
index 0000000..f1271ee
--- /dev/null
+++ b/sys/lib/string/memmove.c
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <string.h>
+
+void *
+memmove(void *s1, const void *s2, size_t n)
+{
+ const char *f = s2;
+ char *t = s1;
+
+ if (f < t) {
+ f += n;
+ t += n;
+ while (n-- > 0) {
+ *--t = *--f;
+ }
+ } else {
+ while (n-- > 0) {
+ *t++ = *f++;
+ }
+ }
+ return s1;
+}
diff --git a/sys/lib/string/strdup.c b/sys/lib/string/strdup.c
new file mode 100644
index 0000000..9c101bc
--- /dev/null
+++ b/sys/lib/string/strdup.c
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <string.h>
+#include <vm/dynalloc.h>
+
+char *
+strdup(const char *s)
+{
+ size_t s_len;
+ char *p;
+
+ /* Make sure size is not zero */
+ if ((s_len = strlen(s)) == 0) {
+ return NULL;
+ }
+
+ /* Allocate new memory for this string */
+ p = dynalloc(s_len + 1);
+ if (p == NULL) {
+ return NULL;
+ }
+
+ memcpy(p, s, s_len);
+ return p;
+}
diff --git a/sys/lib/string/vsnprintf.c b/sys/lib/string/vsnprintf.c
index e9e391f..489514f 100644
--- a/sys/lib/string/vsnprintf.c
+++ b/sys/lib/string/vsnprintf.c
@@ -96,6 +96,9 @@ vsnprintf(char *s, size_t size, const char *fmt, va_list ap)
c1 = (char )va_arg(ap, int);
printc(s, size, &off, c1);
break;
+ case '%':
+ printc(s, size, &off, c);
+ break;
case 'd':
num = va_arg(ap, int);
itoa(num, num_buf, 10);
@@ -104,6 +107,7 @@ vsnprintf(char *s, size_t size, const char *fmt, va_list ap)
num_len = strlen(num_buf);
for (size_t i = num_len; i < pad_width; ++i)
printc(s, size, &off, '0');
+ pad_width = 0;
}
printstr(s, size, &off, num_buf);
break;
diff --git a/sys/net/if.c b/sys/net/if.c
new file mode 100644
index 0000000..5c9bc01
--- /dev/null
+++ b/sys/net/if.c
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/spinlock.h>
+#include <sys/errno.h>
+#include <net/if_var.h>
+#include <string.h>
+
+static TAILQ_HEAD(, netif) netif_list;
+static bool netif_init = false;
+
+/*
+ * Expose a network interface to the rest of the
+ * system.
+ */
+void
+netif_add(struct netif *nifp)
+{
+ if (!netif_init) {
+ TAILQ_INIT(&netif_list);
+ netif_init = true;
+ }
+
+ TAILQ_INSERT_TAIL(&netif_list, nifp, link);
+}
+
+/*
+ * Lookup a network interface by name or type.
+ *
+ * @name: Name to lookup (use `type' if NULL)
+ * @type: Type to lookup (use if `name' is NULL)
+ */
+int
+netif_lookup(const char *name, uint8_t type, struct netif **res)
+{
+ struct netif *netif;
+
+ if (!netif_init) {
+ return -EAGAIN;
+ }
+
+ TAILQ_FOREACH(netif, &netif_list, link) {
+ if (name != NULL) {
+ if (strcmp(netif->name, name) == 0) {
+ *res = netif;
+ return 0;
+ }
+ }
+
+ if (name == NULL && netif->type == type) {
+ *res = netif;
+ return 0;
+ }
+ }
+
+ return -ENODEV;
+}
diff --git a/sys/netinet/if_ether.c b/sys/netinet/if_ether.c
new file mode 100644
index 0000000..db1d6d4
--- /dev/null
+++ b/sys/netinet/if_ether.c
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/endian.h>
+#include <sys/errno.h>
+#include <vm/dynalloc.h>
+#include <net/ethertypes.h>
+#include <netinet/if_ether.h>
+#include <string.h>
+
+struct arp_pkt {
+ struct ether_frame ehfr;
+ struct ether_arp payload;
+};
+
+static struct arp_pkt *
+arp_create(struct netif *nifp, uint32_t *sproto, uint32_t *tproto, uint16_t op)
+{
+ struct arp_pkt *packet;
+ struct arp_hdr *hdrp;
+ struct ether_frame *frp;
+ struct ether_arp *payload;
+
+ packet = dynalloc(sizeof(*packet));
+ if (packet == NULL) {
+ return NULL;
+ }
+
+ frp = &packet->ehfr;
+ payload = &packet->payload;
+ hdrp = &payload->hdr;
+
+ /* Ethernet frame, from source to all */
+ memcpy(frp->ether_saddr, &nifp->addr, ETHER_ADDR_LEN);
+ memset(frp->ether_daddr, 0xFF, ETHER_ADDR_LEN);
+ frp->ether_type = swap16(ETHERTYPE_ARP);
+
+ /* Now for the ARP header */
+ hdrp->hw_type = swap16(ARP_HWTYPE_ETHER);
+ hdrp->proto_type = swap16(ETHERTYPE_IPV4);
+ hdrp->hw_len = ETHER_ADDR_LEN;
+ hdrp->proto_len = 4;
+ hdrp->op_type = swap16(op);
+
+ memcpy(payload->sha, frp->ether_saddr, ETHER_ADDR_LEN);
+ memset(payload->tha, 0xFF, ETHER_ADDR_LEN);
+
+ /* Protocol source address */
+ *((uint32_t *)payload->spa) = *sproto;
+ *((uint32_t *)payload->tpa) = *tproto;
+ return packet;
+}
+
+static int
+arp_send(struct netif *nifp, uint8_t *sproto, uint8_t *tproto, uint16_t op)
+{
+ struct arp_pkt *packet;
+ struct netbuf nb;
+ uint32_t *src_tmp, *targ_tmp;
+
+ if (nifp->tx_enq == NULL) {
+ return -ENOTSUP;
+ }
+ if (nifp->tx_start == NULL) {
+ return -ENOTSUP;
+ }
+
+ src_tmp = (uint32_t *)sproto;
+ targ_tmp = (uint32_t *)tproto;
+
+ packet = arp_create(nifp, src_tmp, targ_tmp, op);
+ if (packet == NULL) {
+ return -ENOMEM;
+ }
+
+ nb.len = sizeof(*packet);
+ memcpy(nb.data, packet, nb.len);
+
+ nifp->tx_enq(nifp, &nb, NULL);
+ nifp->tx_start(nifp);
+ dynfree(packet);
+ return 0;
+}
+
+int
+arp_request(struct netif *nifp, uint8_t *sproto, uint8_t *tproto)
+{
+ return arp_send(nifp, sproto, tproto, ARP_REQUEST);
+}
+
+int
+arp_reply(struct netif *nifp, uint8_t *sproto, uint8_t *tproto)
+{
+ return arp_send(nifp, sproto, tproto, ARP_REPLY);
+}
diff --git a/sys/vm/vm_device.c b/sys/vm/vm_device.c
new file mode 100644
index 0000000..e990b47
--- /dev/null
+++ b/sys/vm/vm_device.c
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/device.h>
+#include <sys/syslog.h>
+#include <vm/vm_device.h>
+
+#define pr_trace(fmt, ...) kprintf("vm_device: " fmt, ##__VA_ARGS__)
+#define pr_error(...) pr_trace(__VA_ARGS__)
+
+const struct vm_pagerops dv_vnops;
+
+/*
+ * Attach a cdev to a vm_object
+ *
+ * @major: Char device major
+ * @minor: Char device minor.
+ */
+struct vm_object *
+dv_attach(devmajor_t major, dev_t dev, vm_prot_t prot)
+{
+ int error;
+ struct cdevsw *cdevp;
+ struct vm_object *vmobj;
+
+ if ((cdevp = dev_get(major, dev)) == NULL) {
+ pr_error("bad attach (major=%d, dev=%d)\n", major, dev);
+ return NULL;
+ }
+
+ if (cdevp->mmap == NULL) {
+ pr_error("cdev lacks mmap() (major=%d, dev=%d)\n", major, dev);
+ return NULL;
+ }
+
+ error = vm_obj_init(&cdevp->vmobj, &dv_vnops, 1);
+ if (error != 0) {
+ return NULL;
+ }
+
+ vmobj = &cdevp->vmobj;
+ vmobj->prot = prot;
+ vmobj->data = cdevp;
+ vmobj->pgops = &dv_vnops;
+ return vmobj;
+}
+
+/* TODO */
+const struct vm_pagerops dv_vnops = {
+ .get = NULL,
+};
diff --git a/sys/vm/vm_init.c b/sys/vm/vm_init.c
index 2846a69..7518838 100644
--- a/sys/vm/vm_init.c
+++ b/sys/vm/vm_init.c
@@ -56,6 +56,7 @@ vm_init(void)
void *pool;
vm_physmem_init();
+ pmap_init();
g_kvas = pmap_read_vas();
vm_ctx.dynalloc_pool_sz = DYNALLOC_POOL_SZ;
diff --git a/sys/vm/vm_map.c b/sys/vm/vm_map.c
index b56e896..bb9df83 100644
--- a/sys/vm/vm_map.c
+++ b/sys/vm/vm_map.c
@@ -35,8 +35,10 @@
#include <sys/syscall.h>
#include <sys/syslog.h>
#include <sys/mman.h>
+#include <sys/filedesc.h>
#include <vm/dynalloc.h>
#include <vm/vm_pager.h>
+#include <vm/vm_device.h>
#include <vm/pmap.h>
#include <vm/map.h>
#include <vm/vm.h>
@@ -157,51 +159,113 @@ vm_map_modify(struct vas vas, vaddr_t va, paddr_t pa, vm_prot_t prot, bool unmap
* crashes.
*/
void *
-mmap_at(void *addr, size_t len, int prot, int flags, int fildes, off_t off)
+mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t off)
{
- struct vm_object *map_obj;
+ struct vm_object *map_obj = NULL;
+ struct cdevsw *cdevp;
struct vm_page *pg;
struct mmap_entry *ep;
+ struct vnode *vp;
+ struct filedesc *fdp;
struct proc *td;
struct vas vas;
int error, npgs;
paddr_t pa;
vaddr_t va;
size_t misalign;
+ off_t page_off;
misalign = len & (DEFAULT_PAGESIZE - 1);
len = ALIGN_UP(len + misalign, DEFAULT_PAGESIZE);
npgs = len / DEFAULT_PAGESIZE;
-
- if (addr == NULL) {
- pr_error("mmap: NULL addr not supported\n");
- return NULL;
- }
+ vas = pmap_read_vas();
/* Validate flags */
- if (ISSET(flags, MAP_FIXED | MAP_SHARED)) {
- pr_error("mmap: fixed/shared mappings not yet supported\n");
+ if (ISSET(flags, MAP_FIXED)) {
+ pr_error("mmap: fixed mappings not yet supported\n");
mmap_dbg(addr, len, prot, flags, fildes, off);
return NULL;
}
- map_obj = dynalloc(sizeof(*map_obj));
- if (map_obj == NULL) {
- kprintf("mmap: failed to allocate map object\n");
- return NULL;
+
+ /*
+ * Attempt to open the file if mapping
+ * is shared.
+ */
+ if (ISSET(flags, MAP_SHARED)) {
+ fdp = fd_get(NULL, fildes);
+ if (fdp == NULL) {
+ pr_error("mmap: no such fd (fd=%d)\n", fildes);
+ return NULL;
+ }
+
+ vp = fdp->vp;
+ if (vp->type != VCHR) {
+ /* TODO */
+ pr_error("mmap: only device files supported\n");
+ return NULL;
+ }
+
+ map_obj = dv_attach(vp->major, vp->dev, prot);
+ if (map_obj == NULL) {
+ kprintf("mmap: dv_attach() failure\n");
+ return NULL;
+ }
+
+ cdevp = map_obj->data;
+ if ((pa = cdevp->mmap(vp->dev, len, off, 0)) == 0) {
+ kprintf("mmap: dev mmap() gave 0\n");
+ return NULL;
+ }
+
+ /*
+ * If the address passed is NULL, just identity
+ * map everything.
+ *
+ * XXX: This is why the bounds check done in the
+ * cdev mmap() *must* be correct.
+ *
+ * TODO: Use copy-on-write for this instead. Since mapping
+ * certain devices may required a lot of memory to
+ * be referenced anyways, we could use a buffered
+ * copy-on-write technique where only a window of
+ * pages can be mapped on-demand and other pages
+ * freed when that window is exceeded.
+ */
+ if (addr == NULL) {
+ addr = (void *)pa;
+ }
+
+ va = ALIGN_DOWN((vaddr_t)addr, DEFAULT_PAGESIZE);
+ error = vm_map(vas, va, pa, prot, len);
+ if (error != 0) {
+ kprintf("mmap: map failed (error=%d)\n", error);
+ return NULL;
+ }
+
+ goto done;
}
- error = vm_obj_init(map_obj, &vm_anonops, 1);
- if (error < 0) {
- kprintf("mmap: vm_obj_init() returned %d\n", error);
- kprintf("mmap: failed to init object\n");
- return NULL;
+
+ /* Only allocate new obj if needed */
+ if (map_obj == NULL) {
+ map_obj = dynalloc(sizeof(*map_obj));
+ if (map_obj == NULL) {
+ kprintf("mmap: failed to allocate map object\n");
+ return NULL;
+ }
+ error = vm_obj_init(map_obj, &vm_anonops, 1);
+ if (error < 0) {
+ kprintf("mmap: vm_obj_init() returned %d\n", error);
+ kprintf("mmap: failed to init object\n");
+ return NULL;
+ }
}
/* XXX: Assuming private */
- vas = pmap_read_vas();
va = ALIGN_DOWN((vaddr_t)addr, DEFAULT_PAGESIZE);
for (int i = 0; i < npgs; ++i) {
pg = vm_pagealloc(map_obj, PALLOC_ZERO);
+ page_off = i * DEFAULT_PAGESIZE;
if (pg == NULL) {
/* TODO */
@@ -209,15 +273,21 @@ mmap_at(void *addr, size_t len, int prot, int flags, int fildes, off_t off)
return NULL;
}
+ /* TODO: copy-on-write */
+ if (addr == NULL) {
+ va = pg->phys_addr;
+ addr = (void *)va;
+ }
+
pa = pg->phys_addr;
- error = vm_map(vas, va, pa, prot, len);
- pr_trace("va=%p, len=%d\n", va, len);
+ error = vm_map(vas, va + page_off, pa, prot, len);
if (error < 0) {
pr_error("mmap: failed to map page (retval=%x)\n", error);
return NULL;
}
}
+done:
/* Add entry to ledger */
td = this_td();
ep = dynalloc(sizeof(*ep));
@@ -243,7 +313,7 @@ mmap_at(void *addr, size_t len, int prot, int flags, int fildes, off_t off)
* multiple of the machine page size.
*/
int
-munmap_at(void *addr, size_t len)
+munmap(void *addr, size_t len)
{
int pgno;
vaddr_t va;
@@ -299,7 +369,7 @@ munmap_at(void *addr, size_t len)
* arg5 -> off
*/
scret_t
-mmap(struct syscall_args *scargs)
+sys_mmap(struct syscall_args *scargs)
{
void *addr;
size_t len;
@@ -308,11 +378,11 @@ mmap(struct syscall_args *scargs)
addr = (void *)scargs->arg0;
len = scargs->arg1;
- prot = scargs->arg2;
+ prot = scargs->arg2 | PROT_USER;
flags = scargs->arg3;
fildes = scargs->arg4;
off = scargs->arg5;
- return (scret_t)mmap_at(addr, len, prot, flags, fildes, off);
+ return (scret_t)mmap(addr, len, prot, flags, fildes, off);
}
/*
@@ -322,14 +392,14 @@ mmap(struct syscall_args *scargs)
* arg1 -> len
*/
scret_t
-munmap(struct syscall_args *scargs)
+sys_munmap(struct syscall_args *scargs)
{
void *addr;
size_t len;
addr = (void *)scargs->arg0;
len = scargs->arg1;
- return (scret_t)munmap_at(addr, len);
+ return (scret_t)munmap(addr, len);
}
/*
diff --git a/sys/vm/vm_physmem.c b/sys/vm/vm_physmem.c
index c7fcedb..0bd2d54 100644
--- a/sys/vm/vm_physmem.c
+++ b/sys/vm/vm_physmem.c
@@ -32,15 +32,22 @@
#include <sys/limine.h>
#include <sys/syslog.h>
#include <sys/spinlock.h>
+#include <sys/panic.h>
#include <vm/physmem.h>
#include <vm/vm.h>
#include <string.h>
-size_t highest_frame_idx = 0;
-size_t bitmap_size = 0;
-size_t bitmap_free_start = 0;
+#define BYTES_PER_MIB 8388608
-uint8_t *bitmap;
+static size_t pages_free = 0;
+static size_t pages_used = 0;
+static size_t pages_total = 0;
+static size_t highest_frame_idx = 0;
+static size_t bitmap_size = 0;
+static size_t bitmap_free_start = 0;
+static ssize_t last_idx = 0;
+
+static uint8_t *bitmap;
static struct limine_memmap_response *resp = NULL;
static struct spinlock lock = {0};
@@ -59,9 +66,11 @@ physmem_populate_bitmap(void)
for (size_t i = 0; i < resp->entry_count; ++i) {
ent = resp->entries[i];
+ pages_total += ent->length / DEFAULT_PAGESIZE;
if (ent->type != LIMINE_MEMMAP_USABLE) {
/* This memory is not usable */
+ pages_used += ent->length / DEFAULT_PAGESIZE;
continue;
}
@@ -72,6 +81,8 @@ physmem_populate_bitmap(void)
for (size_t j = 0; j < ent->length; j += DEFAULT_PAGESIZE) {
clrbit(bitmap, (ent->base + j) / DEFAULT_PAGESIZE);
}
+
+ pages_free += ent->length / DEFAULT_PAGESIZE;
}
}
@@ -137,29 +148,59 @@ physmem_init_bitmap(void)
*
* @count: Number of frames to allocate.
*/
-uintptr_t
-vm_alloc_frame(size_t count)
+static uintptr_t
+__vm_alloc_frame(size_t count)
{
size_t frames = 0;
+ ssize_t idx = -1;
uintptr_t ret = 0;
- spinlock_acquire(&lock);
- for (size_t i = 0; i < highest_frame_idx; ++i) {
+ for (size_t i = last_idx; i < highest_frame_idx; ++i) {
if (!testbit(bitmap, i)) {
- /* We have a free page */
- if (++frames != count) {
- continue;
- }
+ if (idx < 0)
+ idx = i;
+ if (++frames >= count)
+ break;
- for (size_t j = i; j < i + count; ++j) {
- setbit(bitmap, j);
- }
-
- ret = i * DEFAULT_PAGESIZE;
- break;
+ continue;
}
+
+ idx = -1;
+ frames = 0;
+ }
+
+ if (idx < 0 || frames != count) {
+ ret = 0;
+ goto done;
}
+ for (size_t i = idx; i < idx + count; ++i) {
+ setbit(bitmap, i);
+ }
+ ret = idx * DEFAULT_PAGESIZE;
+ last_idx = idx;
+ memset(PHYS_TO_VIRT(ret), 0, count * DEFAULT_PAGESIZE);
+done:
+ return ret;
+}
+
+uintptr_t
+vm_alloc_frame(size_t count)
+{
+ uintptr_t ret;
+
+ spinlock_acquire(&lock);
+ if ((ret = __vm_alloc_frame(count)) == 0) {
+ last_idx = 0;
+ ret = __vm_alloc_frame(count);
+ }
+
+ if (ret == 0) {
+ panic("out of memory\n");
+ }
+
+ pages_used += count;
+ pages_free -= count;
spinlock_release(&lock);
return ret;
}
@@ -169,13 +210,47 @@ vm_free_frame(uintptr_t base, size_t count)
{
size_t stop_at = base + (count * DEFAULT_PAGESIZE);
+ base = ALIGN_UP(base, DEFAULT_PAGESIZE);
+
spinlock_acquire(&lock);
for (uintptr_t p = base; p < stop_at; p += DEFAULT_PAGESIZE) {
clrbit(bitmap, p / DEFAULT_PAGESIZE);
}
+ pages_used -= count;
+ pages_free += count;
spinlock_release(&lock);
}
+/*
+ * Return the amount of memory in MiB that is
+ * currently allocated.
+ */
+uint32_t
+vm_mem_used(void)
+{
+ return (pages_used * DEFAULT_PAGESIZE) / BYTES_PER_MIB;
+}
+
+/*
+ * Return the amount of memory in MiB that is
+ * currently free.
+ */
+uint32_t
+vm_mem_free(void)
+{
+ return (pages_free * DEFAULT_PAGESIZE) / BYTES_PER_MIB;
+}
+
+/*
+ * Return the total amount of memory supported
+ * by the machine.
+ */
+size_t
+vm_mem_total(void)
+{
+ return (pages_total * DEFAULT_PAGESIZE) / BYTES_PER_MIB;
+}
+
void
vm_physmem_init(void)
{
diff --git a/sys/vm/vm_stat.c b/sys/vm/vm_stat.c
new file mode 100644
index 0000000..3e39047
--- /dev/null
+++ b/sys/vm/vm_stat.c
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <fs/ctlfs.h>
+#include <vm/physmem.h>
+#include <vm/vm.h>
+#include <vm/stat.h>
+#include <string.h>
+
+#include <sys/syslog.h>
+
+static struct ctlops vm_stat_ctl;
+
+/*
+ * ctlfs hook to read the virtual memory
+ * statistics.
+ */
+static int
+vm_stat_read(struct ctlfs_dev *cdp, struct sio_txn *sio)
+{
+ struct vm_stat stat;
+ int error;
+
+ if (sio->len > sizeof(stat)) {
+ sio->len = sizeof(stat);
+ }
+
+ error = vm_stat_get(&stat);
+ if (error < 0) {
+ return error;
+ }
+
+ memcpy(sio->buf, &stat, sio->len);
+ return sio->len;
+}
+
+int
+vm_stat_get(struct vm_stat *vmstat)
+{
+ if (vmstat == NULL) {
+ return -EINVAL;
+ }
+
+ vmstat->mem_avail = vm_mem_free();
+ vmstat->mem_used = vm_mem_used();
+ vmstat->mem_total = vm_mem_total();
+ return 0;
+}
+
+void
+vm_stat_init(void)
+{
+ char devname[] = "vm";
+ struct ctlfs_dev ctl;
+
+ /* Register a stat control file */
+ ctl.mode = 0444;
+ ctlfs_create_node(devname, &ctl);
+ ctl.devname = devname;
+ ctl.ops = &vm_stat_ctl;
+ ctlfs_create_entry("stat", &ctl);
+}
+
+static struct ctlops vm_stat_ctl = {
+ .read = vm_stat_read,
+ .write = NULL
+};
diff --git a/sys/vm/vm_vnode.c b/sys/vm/vm_vnode.c
index 2457c97..777b382 100644
--- a/sys/vm/vm_vnode.c
+++ b/sys/vm/vm_vnode.c
@@ -73,7 +73,7 @@ vn_io(struct vnode *vp, struct vm_page **pgs, unsigned int npages, int rw)
args.res = &vattr;
c = MAX(vattr.size / DEFAULT_PAGESIZE, 1);
- if ((err = vfs_vop_getattr(vp, &args)) != 0) {
+ if ((err = vfs_vop_getattr(&args)) != 0) {
return err;
}
@@ -162,7 +162,6 @@ vn_attach(struct vnode *vp, vm_prot_t prot)
if (vp->type != VREG) {
pr_error("vn_attach: vp=%p, prot=%x\n", vp, prot);
- pr_error("vn_attach: Special files not supported yet!\n");
return NULL;
}
diff --git a/tools/mktap b/tools/mktap
new file mode 100755
index 0000000..393a692
--- /dev/null
+++ b/tools/mktap
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+set -e
+
+ip tuntap add dev tap0 mode tap
+ip link set tap0 up
+echo "mktap: tap0 is UP"
+
+ip link add name br0 type bridge
+ip addr add 192.168.100.1/24 dev br0
+ip link set br0 up
+ip link set tap0 master br0
+echo "mktap: tap0 master br0"
diff --git a/tools/omar/Makefile b/tools/omar/Makefile
new file mode 100644
index 0000000..ee30260
--- /dev/null
+++ b/tools/omar/Makefile
@@ -0,0 +1,12 @@
+CFILES = $(shell find . -name "*.c")
+CFLAGS = -pedantic
+CC = gcc
+
+.PHONY: all
+all:
+ mkdir -p bin/
+ $(CC) $(CFLAGS) $(CFILES) -o bin/omar
+
+.PHONY: clean
+clean:
+ rm -rf bin/
diff --git a/tools/omar/README b/tools/omar/README
new file mode 100644
index 0000000..3c74d78
--- /dev/null
+++ b/tools/omar/README
@@ -0,0 +1,9 @@
+------------------------------
+OMAR - OSMORA Archive Format
+------------------------------
+
+OMAR is a minimal, bullshit free archive format aimed to replace
+the old and outdated CPIO (copy in/out) format.
+
+OMAR is designed for readonly in-memory filesystems (such as initramfs),
+with simplicity, clarity and "getting it done" in mind.
diff --git a/tools/omar/omar.c b/tools/omar/omar.c
new file mode 100644
index 0000000..a4c7ad6
--- /dev/null
+++ b/tools/omar/omar.c
@@ -0,0 +1,470 @@
+/*
+ * Copyright (c) 2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/stat.h>
+#include <sys/errno.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <assert.h>
+#include <dirent.h>
+#include <string.h>
+#include <libgen.h>
+
+/* OMAR magic constants */
+#define OMAR_MAGIC "OMAR"
+#define OMAR_EOF "RAMO"
+
+/* OMAR type constants */
+#define OMAR_REG 0
+#define OMAR_DIR 1
+
+/* OMAR modes */
+#define OMAR_ARCHIVE 0
+#define OMAR_EXTRACT 1
+
+/* Revision */
+#define OMAR_REV 2
+
+#define ALIGN_UP(value, align) (((value) + (align)-1) & ~((align)-1))
+#define BLOCK_SIZE 512
+
+static int mode = OMAR_ARCHIVE;
+static int outfd;
+static const char *inpath = NULL;
+static const char *outpath = NULL;
+
+/*
+ * The OMAR file header, describes the basics
+ * of a file.
+ *
+ * @magic: Header magic ("OMAR")
+ * @len: Length of the file
+ * @namelen: Length of the filename
+ * @rev: OMAR revision
+ * @mode: File permissions
+ */
+struct omar_hdr {
+ char magic[4];
+ uint8_t type;
+ uint8_t namelen;
+ uint32_t len;
+ uint8_t rev;
+ uint32_t mode;
+} __attribute__((packed));
+
+static inline void
+help(void)
+{
+ printf("--------------------------------------\n");
+ printf("The OSMORA archive format\n");
+ printf("Usage: omar -i [input_dir] -o [output]\n");
+ printf("-h Show this help screen\n");
+ printf("-x Extract an OMAR archive\n");
+ printf("--------------------------------------\n");
+}
+
+/*
+ * Strip out root dir
+ *
+ * XXX: This is added code to work with Hyra
+ * initramfs.
+ */
+static const char *
+strip_root(const char *path)
+{
+ const char *p;
+
+ for (p = path; *p != '\0'; ++p) {
+ if (*p == '/') {
+ ++p;
+ return p;
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * Recursive mkdir
+ */
+static void
+mkpath(struct omar_hdr *hdr, const char *path)
+{
+ size_t len;
+ char buf[256];
+ char cwd[256];
+ char *p = NULL;
+
+ len = snprintf(buf, sizeof(buf), "%s", path);
+ if (buf[len - 1] == '/') {
+ buf[len - 1] = '\0';
+ }
+ for (p = (char *)buf + 1; *p != '\0'; ++p) {
+ if (*p == '/') {
+ *p = '\0';
+ mkdir(buf, hdr->mode);
+ *p = '/';
+ }
+ }
+
+ mkdir(buf, hdr->mode);
+}
+
+/*
+ * Push a file into the archive output
+ *
+ * @pathname: Full path name of file (NULL if EOF)
+ * @name: Name of file (for EOF, set to "EOF")
+ */
+static int
+file_push(const char *pathname, const char *name)
+{
+ struct omar_hdr hdr;
+ struct stat sb;
+ int infd, rem, error;
+ int pad_len;
+ size_t len;
+ char *buf;
+
+ hdr.type = OMAR_REG;
+
+ /* Attempt to open the input file if not EOF */
+ if (pathname != NULL) {
+ if ((infd = open(pathname, O_RDONLY)) < 0) {
+ perror("open");
+ return infd;
+ }
+
+ if ((error = fstat(infd, &sb)) < 0) {
+ return error;
+ }
+
+ if (S_ISDIR(sb.st_mode)) {
+ hdr.type = OMAR_DIR;
+ }
+ hdr.mode = sb.st_mode;
+ }
+
+ hdr.len = (pathname == NULL) ? 0 : sb.st_size;
+ hdr.rev = OMAR_REV;
+ hdr.namelen = strlen(name);
+
+ /*
+ * If we are at the end of the file, use the OMAR_EOF
+ * magic constant instant of the usual OMAR_MAGIC.
+ */
+ if (pathname == NULL) {
+ memcpy(hdr.magic, OMAR_EOF, sizeof(hdr.magic));
+ } else {
+ memcpy(hdr.magic, OMAR_MAGIC, sizeof(hdr.magic));
+ }
+
+ write(outfd, &hdr, sizeof(hdr));
+ write(outfd, name, hdr.namelen);
+
+ /* If we are at the end of file, we are done */
+ if (pathname == NULL) {
+ close(infd);
+ return 0;
+ }
+
+ /* Pad directories to zero */
+ if (hdr.type == OMAR_DIR) {
+ len = sizeof(hdr) + hdr.namelen;
+ rem = len & (BLOCK_SIZE - 1);
+ pad_len = BLOCK_SIZE - rem;
+
+ buf = malloc(pad_len);
+ memset(buf, 0, pad_len);
+ write(outfd, buf, pad_len);
+ free(buf);
+ return 0;
+ }
+
+ /* We need the file data now */
+ buf = malloc(hdr.len);
+ if (buf == NULL) {
+ printf("out of memory\n");
+ close(infd);
+ return -ENOMEM;
+ }
+ if (read(infd, buf, hdr.len) <= 0) {
+ perror("read");
+ close(infd);
+ return -EIO;
+ }
+
+ /*
+ * Write the actual file contents, if the file length is not
+ * a multiple of the block size, we'll need to pad out the rest
+ * to zero.
+ */
+ write(outfd, buf, hdr.len);
+ len = sizeof(hdr) + (hdr.namelen + hdr.len);
+ rem = len & (BLOCK_SIZE - 1);
+ if (rem != 0) {
+ /* Compute the padding length */
+ pad_len = BLOCK_SIZE - rem;
+
+ buf = realloc(buf, pad_len);
+ memset(buf, 0, pad_len);
+ write(outfd, buf, pad_len);
+ }
+ close(infd);
+ free(buf);
+ return 0;
+}
+
+/*
+ * Start creating an archive from the
+ * basepath of a directory.
+ */
+static int
+archive_create(const char *base, const char *dirname)
+{
+ DIR *dp;
+ struct dirent *ent;
+ struct omar_hdr hdr;
+ const char *p = NULL, *p1;
+ char pathbuf[256];
+ char namebuf[256];
+
+ dp = opendir(base);
+ if (dp == NULL) {
+ perror("opendir");
+ return -ENOENT;
+ }
+
+ while ((ent = readdir(dp)) != NULL) {
+ if (ent->d_name[0] == '.') {
+ continue;
+ }
+
+ snprintf(pathbuf, sizeof(pathbuf), "%s/%s", base, ent->d_name);
+ snprintf(namebuf, sizeof(namebuf), "%s/%s", dirname, ent->d_name);
+ p1 = strip_root(namebuf);
+
+ if (ent->d_type == DT_DIR) {
+ printf("%s [d]\n", p1);
+ file_push(pathbuf, p1);
+ archive_create(pathbuf, namebuf);
+ } else if (ent->d_type == DT_REG) {
+ printf("%s [f]\n", p1);
+ file_push(pathbuf, p1);
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Extract a single file
+ *
+ * @hp: File header
+ * @data: Data to extract
+ * @len: Length of data
+ * @path: Path to output file
+ */
+static int
+extract_single(struct omar_hdr *hp, char *data, size_t len, const char *path)
+{
+ int fd;
+
+ if ((fd = open(path, O_WRONLY | O_CREAT, hp->mode)) < 0) {
+ return fd;
+ }
+
+ return write(fd, data, len) > 0 ? 0 : -1;
+}
+
+/*
+ * Extract an OMAR archive.
+ *
+ * XXX: The input file [-i] will be the OMAR archive to
+ * be extracted, the output directory [-o] will be
+ * where the files get extracted.
+ */
+static int
+archive_extract(void)
+{
+ char *buf, *name, *p;
+ struct stat sb;
+ struct omar_hdr *hdr;
+ int fd, error;
+ size_t len;
+ off_t off;
+ char namebuf[256];
+ char pathbuf[256];
+
+ if ((fd = open(inpath, O_RDONLY)) < 0) {
+ perror("open");
+ return fd;
+ }
+
+ if ((error = fstat(fd, &sb)) != 0) {
+ perror("fstat");
+ close(fd);
+ return error;
+ }
+
+ buf = malloc(sb.st_size);
+ if (buf == NULL) {
+ fprintf(stderr, "out of memory\n");
+ close(fd);
+ return -ENOMEM;
+ }
+
+ if (read(fd, buf, sb.st_size) <= 0) {
+ fprintf(stderr, "omar: no data read\n");
+ close(fd);
+ return -EIO;
+ }
+
+ hdr = (struct omar_hdr *)buf;
+ for (;;) {
+ if (memcmp(hdr->magic, OMAR_EOF, sizeof(OMAR_EOF)) == 0) {
+ printf("EOF!\n");
+ return 0;
+ }
+
+ /* Ensure the header is valid */
+ if (memcmp(hdr->magic, "OMAR", 4) != 0) {
+ fprintf(stderr, "bad magic\n");
+ break;
+ }
+ if (hdr->rev != OMAR_REV) {
+ fprintf(stderr, "cannot extract rev %d archive\n", hdr->rev);
+ fprintf(stderr, "current OMAR revision: %d\n", OMAR_REV);
+ }
+
+ name = (char *)hdr + sizeof(struct omar_hdr);
+ memcpy(namebuf, name, hdr->namelen);
+ namebuf[hdr->namelen] = '\0';
+
+ /* Get the full path */
+ len = snprintf(pathbuf, sizeof(pathbuf), "%s/%s", outpath, namebuf);
+ if (len < 0) {
+ free(buf);
+ return len;
+ }
+ printf("unpacking %s\n", pathbuf);
+
+ if (hdr->type == OMAR_DIR) {
+ off = 512;
+ mkpath(hdr, pathbuf);
+ } else {
+ off = ALIGN_UP(sizeof(*hdr) + hdr->namelen + hdr->len, BLOCK_SIZE);
+ p = (char *)hdr + sizeof(struct omar_hdr);
+ p += hdr->namelen;
+ extract_single(hdr, p, hdr->len, pathbuf);
+ }
+
+ hdr = (struct omar_hdr *)((char *)hdr + off);
+ memset(namebuf, 0, sizeof(namebuf));
+ }
+
+ free(buf);
+}
+
+int
+main(int argc, char **argv)
+{
+ int optc, retval;
+ int error, flags;
+
+ if (argc < 2) {
+ help();
+ return -1;
+ }
+
+ while ((optc = getopt(argc, argv, "xhi:o:")) != -1) {
+ switch (optc) {
+ case 'x':
+ mode = OMAR_EXTRACT;
+ break;
+ case 'i':
+ inpath = optarg;
+ break;
+ case 'o':
+ outpath = optarg;
+ break;
+ case 'h':
+ help();
+ return 0;
+ default:
+ help();
+ return -1;
+ }
+ }
+
+ if (inpath == NULL) {
+ fprintf(stderr, "omar: no input path\n");
+ help();
+ return -1;
+ }
+ if (outpath == NULL) {
+ fprintf(stderr, "omar: no output path\n");
+ help();
+ return -1;
+ }
+
+ /*
+ * Do our specific job based on the mode
+ * OMAR is set to be in.
+ */
+ switch (mode) {
+ case OMAR_ARCHIVE:
+ /* Begin archiving the file */
+ outfd = open(outpath, O_WRONLY | O_CREAT, 0700);
+ if (outfd < 0) {
+ printf("omar: failed to open output file\n");
+ return outfd;
+ }
+
+ retval = archive_create(inpath, basename((char *)inpath));
+ file_push(NULL, "EOF");
+ break;
+ case OMAR_EXTRACT:
+ /* Begin extracting the file */
+ if ((error = mkdir(outpath, 0700) != 0)) {
+ perror("mkdir");
+ return error;
+ }
+
+ retval = archive_extract();
+ break;
+ }
+ close(outfd);
+ return retval;
+}
diff --git a/usr.bin/Makefile b/usr.bin/Makefile
new file mode 100644
index 0000000..d754404
--- /dev/null
+++ b/usr.bin/Makefile
@@ -0,0 +1,32 @@
+LDSCRIPT =
+USRDIR =
+ROOT =
+ARGS = -I$(ROOT)/builddeps LDSCRIPT=$(LDSCRIPT) USRDIR=$(USRDIR) ROOT=$(ROOT)
+
+.PHONY: all
+all:
+ make -C osh/ $(ARGS)
+ make -C kmsg/ $(ARGS)
+ make -C fetch/ $(ARGS)
+ make -C kfgwm/ $(ARGS)
+ make -C date/ $(ARGS)
+ make -C mex/ $(ARGS)
+ make -C beep/ $(ARGS)
+ make -C mrow/ $(ARGS)
+ make -C elfdump/ $(ARGS)
+ make -C cat/ $(ARGS)
+ make -C getconf/ $(ARGS)
+ make -C echo/ $(ARGS)
+ make -C readcore/ $(ARGS)
+ make -C login/ $(ARGS)
+ make -C sleep/ $(ARGS)
+ make -C kstat/ $(ARGS)
+ make -C nerve/ $(ARGS)
+ make -C whoami/ $(ARGS)
+ make -C oasm/ $(ARGS)
+ make -C oemu/ $(ARGS)
+ make -C dmidump/ $(ARGS)
+ make -C sysctl/ $(ARGS)
+ make -C reboot/ $(ARGS)
+ make -C screensave/ $(ARGS)
+ make -C notes/ $(ARGS)
diff --git a/usr.bin/beep/Makefile b/usr.bin/beep/Makefile
new file mode 100644
index 0000000..409f87c
--- /dev/null
+++ b/usr.bin/beep/Makefile
@@ -0,0 +1,6 @@
+include user.mk
+
+CFILES = $(shell find . -name "*.c")
+
+$(ROOT)/base/usr/bin/beep:
+ gcc $(CFILES) -o $@ $(INTERNAL_CFLAGS)
diff --git a/usr.bin/beep/main.c b/usr.bin/beep/main.c
new file mode 100644
index 0000000..d64a9b5
--- /dev/null
+++ b/usr.bin/beep/main.c
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+int
+main(int argc, char **argv)
+{
+ uint32_t payload;
+ uint16_t duration;
+ uint16_t freq;
+ int beep_fd;
+
+ if (argc < 3) {
+ printf("usage: beep <freq> <duration>\n");
+ return -1;
+ }
+
+ duration = atoi(argv[2]);
+ freq = atoi(argv[1]);
+
+ if (duration == 0) {
+ printf("bad duration\n");
+ return -1;
+ }
+ if (freq == 0) {
+ printf("bad frequency\n");
+ return -1;
+ }
+
+ beep_fd = open("/dev/beep", O_WRONLY);
+ if (beep_fd < 0) {
+ printf("failed to open beep fd\n");
+ return -1;
+ }
+
+ payload = freq;
+ payload |= (duration << 16);
+ write(beep_fd, &payload, sizeof(payload));
+ return 0;
+}
diff --git a/usr.bin/cat/Makefile b/usr.bin/cat/Makefile
new file mode 100644
index 0000000..4ecfea7
--- /dev/null
+++ b/usr.bin/cat/Makefile
@@ -0,0 +1,6 @@
+include user.mk
+
+CFILES = $(shell find . -name "*.c")
+
+$(ROOT)/base/usr/bin/cat:
+ gcc $(CFILES) -o $@ $(INTERNAL_CFLAGS)
diff --git a/usr.bin/cat/cat.c b/usr.bin/cat/cat.c
new file mode 100644
index 0000000..4fb74d1
--- /dev/null
+++ b/usr.bin/cat/cat.c
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#define NUM_MODE_NONE 0
+#define NUM_MODE_ALL 1
+#define NUM_MODE_NONBLANK 2
+
+static void
+help(void)
+{
+ printf(
+ "usage: cat <flags> <file>\n"
+ "[-b] do not number blank lines\n"
+ "[-n] number all lines\n"
+ );
+}
+
+static void
+cat(const char *pathname, int num_mode)
+{
+ FILE *file;
+ char buf[64];
+ int fd;
+ size_t lineno = 1;
+
+ file = fopen(pathname, "r");
+ if (file == NULL) {
+ return;
+ }
+
+ while (fgets(buf, sizeof(buf), file) != NULL) {
+ switch (num_mode) {
+ case NUM_MODE_NONE:
+ break;
+ case NUM_MODE_ALL:
+ printf("%d ", lineno);
+ break;
+ case NUM_MODE_NONBLANK:
+ if (buf[0] == '\n') {
+ break;
+ }
+ printf("%d ", lineno);
+ break;
+ }
+ printf("%s", buf);
+ ++lineno;
+ }
+
+ fclose(file);
+}
+
+int
+main(int argc, char **argv)
+{
+ int num_mode = NUM_MODE_NONE;
+ int c;
+
+ if (argc < 2) {
+ help();
+ return -1;
+ }
+
+ while ((c = getopt(argc, argv, "nb")) != -1) {
+ switch (c) {
+ case 'n':
+ num_mode = NUM_MODE_ALL;
+ break;
+ case 'b':
+ num_mode = NUM_MODE_NONBLANK;
+ break;
+ }
+ }
+
+ for (size_t i = optind; i < argc; ++i) {
+ cat(argv[i], num_mode);
+ }
+
+ return 0;
+}
diff --git a/usr.bin/date/Makefile b/usr.bin/date/Makefile
new file mode 100644
index 0000000..09ff299
--- /dev/null
+++ b/usr.bin/date/Makefile
@@ -0,0 +1,6 @@
+include user.mk
+
+CFILES = $(shell find . -name "*.c")
+
+$(ROOT)/base/usr/bin/date:
+ gcc $(CFILES) -Iinclude/ -o $@ $(INTERNAL_CFLAGS)
diff --git a/usr.bin/date/date.c b/usr.bin/date/date.c
new file mode 100644
index 0000000..8c4a9d1
--- /dev/null
+++ b/usr.bin/date/date.c
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/time.h>
+#include <sys/cdefs.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+
+#define MONTHS_PER_YEAR 12
+#define DAYS_PER_WEEK 7
+
+/* Months of the year */
+static const char *montab[] = {
+ "Jan", "Feb", "Mar",
+ "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep",
+ "Oct", "Nov", "Dec"
+};
+
+/* Days of the week */
+static const char *daytab[] = {
+ "Sat", "Sun", "Mon",
+ "Tue", "Wed", "Thu",
+ "Fri"
+};
+
+static int
+set_time(int clock_fd, struct date *dp, char *timestr)
+{
+ uint32_t hour, min, sec;
+ char *p;
+
+ /* Hour */
+ p = strtok(timestr, ":");
+ if (p == NULL)
+ return -1;
+ hour = atoi(p);
+
+ /* Minute */
+ p = strtok(NULL, ":");
+ if (p == NULL)
+ return -1;
+ min = atoi(p);
+
+ /* Second */
+ p = strtok(NULL, ":");
+ if (p == NULL)
+ return -1;
+ sec = atoi(p);
+
+ /* Set the time */
+ dp->hour = hour;
+ dp->min = min;
+ dp->sec = sec;
+ write(clock_fd, dp, sizeof(*dp));
+ return 0;
+}
+
+int
+main(int argc, char **argv)
+{
+ const char *day, *month;
+ char date_str[32];
+ struct date d;
+ int rtc_fd, error = 0;
+
+ if ((rtc_fd = open("/dev/rtc", O_RDWR)) < 0) {
+ return rtc_fd;
+ }
+
+
+ read(rtc_fd, &d, sizeof(d));
+
+ /*
+ * If a time was specified to be set in the
+ * 'hh:mm:ss' format, attempt to write it.
+ */
+ if (argc > 1) {
+ error = set_time(rtc_fd, &d, argv[1]);
+ if (error < 0)
+ printf("bad time specified, not set\n");
+ read(rtc_fd, &d, sizeof(d));
+ }
+
+ close(rtc_fd);
+
+ /* This should not happen */
+ if (__unlikely(d.month > MONTHS_PER_YEAR)) {
+ printf("got bad month %d from RTC\n", d.month);
+ return -1;
+ }
+ if (__unlikely(d.month == 0 || d.day == 0)) {
+ printf("got zero month/day from RTC\n");
+ return -1;
+ }
+
+ day = daytab[d.day % DAYS_PER_WEEK];
+ month = montab[d.month - 1];
+
+ snprintf(date_str, sizeof(date_str), "%s %s %d %02d:%02d:%02d\n",
+ day, month, d.day, d.hour, d.min, d.sec);
+ fputs(date_str, stdout);
+ return 0;
+}
diff --git a/usr.bin/dmidump/Makefile b/usr.bin/dmidump/Makefile
new file mode 100644
index 0000000..e9cd625
--- /dev/null
+++ b/usr.bin/dmidump/Makefile
@@ -0,0 +1,6 @@
+include user.mk
+
+CFILES = $(shell find . -name "*.c")
+
+$(ROOT)/base/usr/bin/dmidump:
+ gcc $(CFILES) -o $@ $(INTERNAL_CFLAGS)
diff --git a/usr.bin/dmidump/dmidump.c b/usr.bin/dmidump/dmidump.c
new file mode 100644
index 0000000..96d97bc
--- /dev/null
+++ b/usr.bin/dmidump/dmidump.c
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/dmi.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+
+/*
+ * The kernel fills DMI structures to zero,
+ * if any of the fields are unset then p[0]
+ * will have a null terminator which tells
+ * us we should ignore it.
+ */
+static void
+dmi_printfield(const char *name, const char *p)
+{
+ if (p[0] == '\0') {
+ return;
+ }
+
+ printf("%s: %s\n", name, p);
+}
+
+static void
+dmi_dump_board(void)
+{
+ struct dmi_board board;
+ int fd;
+
+ fd = open("/ctl/dmi/board", O_RDONLY);
+ if (fd < 0) {
+ printf("failed to open board control\n");
+ return;
+ }
+
+ read(fd, &board, sizeof(board));
+ printf("** BOARD INFO **\n");
+ dmi_printfield("CPU version", board.cpu_version);
+ dmi_printfield("CPU OEM", board.cpu_manuf);
+ dmi_printfield("product", board.product);
+ dmi_printfield("vendor", board.vendor);
+ dmi_printfield("version", board.version);
+ close(fd);
+}
+
+int
+main(int argc, char **argv)
+{
+ dmi_dump_board();
+ return 0;
+}
diff --git a/usr.bin/echo/Makefile b/usr.bin/echo/Makefile
new file mode 100644
index 0000000..296461b
--- /dev/null
+++ b/usr.bin/echo/Makefile
@@ -0,0 +1,6 @@
+include user.mk
+
+CFILES = $(shell find . -name "*.c")
+
+$(ROOT)/base/usr/bin/echo:
+ gcc $(CFILES) -o $@ $(INTERNAL_CFLAGS)
diff --git a/usr.bin/echo/echo.c b/usr.bin/echo/echo.c
new file mode 100644
index 0000000..760c788
--- /dev/null
+++ b/usr.bin/echo/echo.c
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+
+int
+main(int argc, char **argv)
+{
+ for (int i = 1; i < argc; ++i) {
+ printf("%s ", argv[i]);
+ }
+
+ if (argc > 1) {
+ printf("\n");
+ }
+
+ return 0;
+}
diff --git a/usr.bin/elfdump/Makefile b/usr.bin/elfdump/Makefile
new file mode 100644
index 0000000..b450635
--- /dev/null
+++ b/usr.bin/elfdump/Makefile
@@ -0,0 +1,6 @@
+include user.mk
+
+CFILES = $(shell find . -name "*.c")
+
+$(ROOT)/base/usr/bin/elfdump:
+ gcc $(CFILES) -o $@ $(INTERNAL_CFLAGS)
diff --git a/usr.bin/elfdump/elfdump.c b/usr.bin/elfdump/elfdump.c
new file mode 100644
index 0000000..335cdec
--- /dev/null
+++ b/usr.bin/elfdump/elfdump.c
@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/elf.h>
+#include <sys/errno.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+
+/* Ehdr.e_type table */
+static const char *elftype[] = {
+ [ET_NONE] = "Untyped",
+ [ET_REL] = "Relocatable",
+ [ET_EXEC] = "Executable",
+ [ET_DYN] = "Shared object",
+ [ET_CORE] = "Core dump"
+};
+
+/* Phdr.p_type table */
+static const char *phdrtype[] = {
+ [PT_NULL] = "Null",
+ [PT_LOAD] = "Loadable",
+ [PT_DYNAMIC] = "Dynamic",
+ [PT_NOTE] = "Note (linker garbage)",
+};
+
+/*
+ * Verify the validity of the ELF header from its
+ * various fields such as magic bytes, ABI, endianness,
+ * etc.
+ *
+ * Returns 0 on success.
+ */
+static int
+elf64_verify(const Elf64_Ehdr *hdr)
+{
+ const char *mag = &hdr->e_ident[EI_MAG0];
+
+ if (memcmp(mag, ELFMAG, SELFMAG) != 0) {
+ printf("Bad ELF magic\n");
+ return -ENOEXEC;
+ }
+ if (hdr->e_ident[EI_OSABI] != ELFOSABI_SYSV) {
+ printf("Bad ELF ABI\n");
+ return -ENOEXEC;
+ }
+ if (hdr->e_ident[EI_DATA] != ELFDATA2LSB) {
+ printf("Bad endianness\n");
+ return -ENOEXEC;
+ }
+ if (hdr->e_ident[EI_CLASS] != ELFCLASS64) {
+ printf("ELF not 64 bits\n");
+ return -ENOEXEC;
+ }
+
+ return 0;
+}
+
+static void
+parse_phdrs(const Elf64_Ehdr *eh, int fd)
+{
+ Elf64_Phdr phdr;
+ const char *type = "Unknown";
+
+ lseek(fd, eh->e_phoff, SEEK_SET);
+ printf("-- PHDRS BEGIN --\n");
+ for (size_t i = 0; i < eh->e_phnum; ++i) {
+ if (read(fd, &phdr, eh->e_phentsize) <= 0) {
+ printf("failed to read phdr %d\n", i);
+ break;
+ }
+ if (phdr.p_type < NELEM(phdrtype)) {
+ type = phdrtype[phdr.p_type];
+ }
+
+ printf("* [P.%d] Type: %s\n", i, type);
+ printf("* [P.%d] Offset: %d\n", i, phdr.p_offset);
+ printf("* [P.%d] Vaddr: %p\n", i, phdr.p_vaddr);
+ printf("* [P.%d] Paddr: %p\n", i, phdr.p_paddr);
+ printf("* [P.%d] Memory size: %d\n", i, phdr.p_memsz);
+ printf("* [P.%d] Flags: %p\n", i, phdr.p_flags);
+ printf("* [P.%d] Alignment: %p\n", i, phdr.p_align);
+
+ /* Seperator */
+ if (i < (eh->e_phnum - 1)) {
+ printf("-----------------------------\n");
+ }
+ }
+ printf("-- PHDRS END --\n");
+}
+
+static int
+parse_ehdr(const Elf64_Ehdr *eh, int fd)
+{
+ const char *elf_type = "Bad";
+
+ if (eh->e_type < NELEM(elftype)) {
+ elf_type = elftype[eh->e_type];
+ }
+
+ printf("* Entrypoint: %p\n", eh->e_entry);
+ printf("* Program headers start offset: %p\n", eh->e_phoff);
+ printf("* Section headers start offset: %p\n", eh->e_shoff);
+ printf("* Number of program headers: %d\n", eh->e_phnum);
+ printf("* Endianess: Little\n");
+ parse_phdrs(eh, fd);
+ return 0;
+}
+
+static int
+elfdump_run(const char *filename)
+{
+ Elf64_Ehdr eh;
+ int fd, error;
+
+ fd = open(filename, O_RDONLY);
+ if (fd < 0) {
+ return fd;
+ }
+
+ printf("-- Dumping %s --\n", filename);
+ read(fd, &eh, sizeof(eh));
+
+ if ((error = elf64_verify(&eh)) < 0) {
+ return error;
+ }
+ if ((error = parse_ehdr(&eh, fd)) < 0) {
+ return error;
+ }
+
+ close(fd);
+ return 0;
+}
+
+int
+main(int argc, char **argv)
+{
+ if (argc < 2) {
+ printf("elfdump: usage: elfdump <elf path>\n");
+ return -1;
+ }
+
+ return elfdump_run(argv[1]);
+}
diff --git a/usr.bin/fetch/Makefile b/usr.bin/fetch/Makefile
new file mode 100644
index 0000000..4b08e84
--- /dev/null
+++ b/usr.bin/fetch/Makefile
@@ -0,0 +1,6 @@
+include user.mk
+
+CFILES = $(shell find . -name "*.c")
+
+$(ROOT)/base/usr/bin/fetch:
+ gcc $(CFILES) -o $@ $(INTERNAL_CFLAGS)
diff --git a/usr.bin/fetch/fetch.c b/usr.bin/fetch/fetch.c
new file mode 100644
index 0000000..1e8ef92
--- /dev/null
+++ b/usr.bin/fetch/fetch.c
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#define CPUID(level, a, b, c, d) \
+ __ASMV("cpuid\n\t" \
+ : "=a" (a), "=b" (b), "=c" (c), "=d" (d) \
+ : "0" (level))
+
+#define ASCII_ART \
+ " ____ \n" \
+ " | \\__\\ user: %s\n" \
+ " | /\\ \\ OS: Hyra/amd64 v"_OSVER"\n" \
+ " |/ \\ \\ arch: "_OSARCH"\n" \
+ " \\ R. \\ \\ cpu: %s\n" \
+ " \\ I. \\ \\\n"
+
+
+/*
+ * Get the processor brand string
+ *
+ * @buffer: Buffer to copy branch string
+ *
+ * Returns a pointer to newly allocated memory
+ * containing the vendor string. One must ensure
+ * to call free() after use.
+ */
+static char *
+get_brand(void)
+{
+ uint32_t eax, ebx, ecx, edx;
+ uint32_t regs[12];
+ char buf[sizeof(regs) + 1];
+ char *p = buf;
+
+ /* Can we even get the brand? */
+ CPUID(0x80000000, eax, ebx, ecx, edx);
+ if (eax < 0x80000004) {
+ return NULL;
+ }
+
+ CPUID(0x80000002, regs[0], regs[1], regs[2], regs[3]);
+ CPUID(0x80000003, regs[4], regs[5], regs[6], regs[7]);
+ CPUID(0x80000004, regs[8], regs[9], regs[10], regs[11]);
+
+ /* Log it */
+ memcpy(p, regs, sizeof(regs));
+ buf[sizeof(regs)] = '\0';
+
+ /* Strip away leading whitespaces */
+ for (int i = 0; i < sizeof(buf); ++i) {
+ if (buf[i] == ' ') {
+ ++p;
+ } else {
+ break;
+ }
+ }
+
+ return strdup(p);
+}
+
+int
+main(void)
+{
+ char *brand = get_brand();
+
+ if (brand == NULL) {
+ brand = strdup("unknown");
+ }
+
+ printf(ASCII_ART, getlogin(), brand);
+ free(brand);
+ return 0;
+}
diff --git a/usr.bin/getconf/Makefile b/usr.bin/getconf/Makefile
new file mode 100644
index 0000000..48c05a8
--- /dev/null
+++ b/usr.bin/getconf/Makefile
@@ -0,0 +1,6 @@
+include user.mk
+
+CFILES = $(shell find . -name "*.c")
+
+$(ROOT)/base/usr/bin/getconf:
+ gcc $(CFILES) -Iinclude/ -o $@ $(INTERNAL_CFLAGS)
diff --git a/usr.bin/getconf/getconf.c b/usr.bin/getconf/getconf.c
new file mode 100644
index 0000000..f028e76
--- /dev/null
+++ b/usr.bin/getconf/getconf.c
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/limits.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+
+struct sysvar {
+ const char *var;
+ uint8_t auxv : 1;
+ uint32_t val;
+};
+
+static struct sysvar vartab[] = {
+ { "PAGESIZE", 1, AT_PAGESIZE },
+ { "CHAR_BIT", 0, CHAR_BIT },
+ { "NAME_MAX", 0, NAME_MAX },
+ { "PATH_MAX", 0, PATH_MAX },
+ { "SSIZE_MAX", 0, SSIZE_MAX },
+ { NULL, 0, 0 }
+};
+
+static int
+getvar_val(struct sysvar *vp)
+{
+ if (vp->auxv) {
+ return sysconf(vp->val);
+ }
+
+ return vp->val;
+}
+
+static int
+getvar(const char *sysvar)
+{
+ for (int i = 0; vartab[i].var != NULL; ++i) {
+ if (strcmp(vartab[i].var, sysvar) == 0) {
+ return getvar_val(&vartab[i]);
+ }
+ }
+
+ return -1;
+}
+
+int
+main(int argc, char **argv)
+{
+ char *var;
+ int retval;
+
+ if (argc < 2) {
+ printf("usage: getconf <SYSTEM VAR>\n");
+ return -1;
+ }
+
+ var = argv[1];
+ if ((retval = getvar(var)) < 0) {
+ printf("bad system var \"%s\"\n", var);
+ return retval;
+ }
+
+ printf("%d\n", retval);
+ return 0;
+}
diff --git a/usr.bin/kfgwm/Makefile b/usr.bin/kfgwm/Makefile
new file mode 100644
index 0000000..a0fb49a
--- /dev/null
+++ b/usr.bin/kfgwm/Makefile
@@ -0,0 +1,6 @@
+include user.mk
+
+CFILES = $(shell find . -name "*.c")
+
+$(ROOT)/base/usr/bin/kfgwm:
+ gcc $(CFILES) -Iinclude/ -o $@ $(INTERNAL_CFLAGS)
diff --git a/usr.bin/kfgwm/font.c b/usr.bin/kfgwm/font.c
new file mode 100644
index 0000000..9873b02
--- /dev/null
+++ b/usr.bin/kfgwm/font.c
@@ -0,0 +1,379 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/errno.h>
+#include <kfg/font.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+/* TODO: Open a .psf font and get rid of this */
+const uint8_t g_KFG_FONT[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x42, 0x81, 0x81, 0xa5, 0xa5, 0x81,
+ 0x81, 0xa5, 0x99, 0x81, 0x42, 0x3c, 0x00, 0x00, 0x00, 0x3c, 0x7e, 0xff,
+ 0xff, 0xdb, 0xdb, 0xff, 0xff, 0xdb, 0xe7, 0xff, 0x7e, 0x3c, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x6c, 0xfe, 0xfe, 0xfe, 0x7c, 0x7c, 0x38, 0x38, 0x10,
+ 0x10, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x38, 0x38, 0x7c, 0x7c, 0xfe,
+ 0x7c, 0x7c, 0x38, 0x38, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18,
+ 0x3c, 0x3c, 0xdb, 0xff, 0xff, 0xdb, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0xff, 0x66, 0x18, 0x18,
+ 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x78,
+ 0x78, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0xcc, 0x84, 0x84, 0xcc, 0x78, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3, 0x99, 0xbd,
+ 0xbd, 0x99, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x1e,
+ 0x0e, 0x1e, 0x32, 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0xfc, 0x30, 0x30,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x18, 0x1c, 0x1e, 0x16, 0x12,
+ 0x10, 0x10, 0x70, 0xf0, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x30, 0x38, 0x2c,
+ 0x26, 0x32, 0x3a, 0x2e, 0x26, 0x22, 0x62, 0xe2, 0xc6, 0x0e, 0x0c, 0x00,
+ 0x00, 0x00, 0x00, 0x18, 0x18, 0xdb, 0x3c, 0xe7, 0x3c, 0xdb, 0x18, 0x18,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc0, 0xe0, 0xf8, 0xfe,
+ 0xf8, 0xe0, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0x06, 0x0e, 0x3e, 0xfe, 0x3e, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x30, 0x78, 0xfc, 0x30, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x78,
+ 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
+ 0xcc, 0xcc, 0x00, 0xcc, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xdb,
+ 0xdb, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7c, 0xc6, 0x60, 0x38, 0x6c, 0xc6, 0xc6, 0x6c, 0x38, 0x0c,
+ 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xfe, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x78,
+ 0xfc, 0x30, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x78, 0x30, 0xfc, 0x00, 0x00,
+ 0x00, 0x00, 0x30, 0x78, 0xfc, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0xfc, 0x78, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x18, 0x0c, 0xfe, 0xfe, 0x0c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x60, 0xfe, 0xfe, 0x60, 0x30, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0,
+ 0xc0, 0xc0, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x24, 0x66, 0xff, 0xff, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x38, 0x38, 0x7c, 0x7c, 0xfe, 0xfe,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x7c, 0x7c,
+ 0x38, 0x38, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x30, 0x78, 0x78, 0x78, 0x78, 0x30, 0x30, 0x30, 0x00, 0x30,
+ 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x6c,
+ 0x6c, 0xfe, 0x6c, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00,
+ 0x00, 0x18, 0x18, 0x7c, 0xc6, 0xc0, 0xc0, 0x7c, 0x06, 0x06, 0xc6, 0x7c,
+ 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0x0c, 0x0c, 0x18, 0x38,
+ 0x30, 0x60, 0x60, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c,
+ 0x6c, 0x38, 0x30, 0x76, 0xde, 0xcc, 0xcc, 0xde, 0x76, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x30, 0x60, 0x60, 0x60, 0x60,
+ 0x60, 0x60, 0x60, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x30, 0x60, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x38, 0xfe, 0x38, 0x6c, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e,
+ 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06,
+ 0x0c, 0x0c, 0x18, 0x38, 0x30, 0x60, 0x60, 0xc0, 0xc0, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xd6, 0xd6, 0xd6, 0xc6, 0xc6, 0xc6,
+ 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x38, 0x78, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6,
+ 0x06, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0xc0, 0xfe, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7c, 0xc6, 0x06, 0x06, 0x3c, 0x06, 0x06, 0x06, 0x06, 0xc6,
+ 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x0c, 0x1c, 0x3c, 0x6c, 0xcc,
+ 0xfe, 0x0c, 0x0c, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc0,
+ 0xc0, 0xc0, 0xfc, 0x06, 0x06, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3c, 0x60, 0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc6, 0x06, 0x06, 0x0c, 0x18,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6,
+ 0xc6, 0xc6, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x06, 0x06, 0x0c,
+ 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18,
+ 0x18, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, 0x00,
+ 0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x60, 0x30, 0x18, 0x0c,
+ 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00,
+ 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x60,
+ 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x06, 0x0c, 0x18, 0x30, 0x30, 0x00, 0x30,
+ 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xde, 0xde,
+ 0xde, 0xde, 0xc0, 0xc0, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xfc, 0xc6, 0xc6, 0xc6, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc0, 0xc0,
+ 0xc0, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xcc,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xcc, 0xf8, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xfc, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
+ 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xfc, 0xc0,
+ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6,
+ 0xc0, 0xc0, 0xc0, 0xde, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x30, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc6, 0xc6, 0xcc, 0xd8, 0xf0, 0xe0, 0xf0, 0xd8, 0xcc, 0xc6,
+ 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
+ 0xc0, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6,
+ 0xee, 0xfe, 0xd6, 0xd6, 0xd6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc6, 0xc6, 0xe6, 0xe6, 0xf6, 0xde, 0xce, 0xce, 0xc6, 0xc6,
+ 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xfc, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xf6, 0xda,
+ 0x6c, 0x06, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0xfc,
+ 0xd8, 0xcc, 0xcc, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6,
+ 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0xd6, 0xd6, 0xd6, 0xfe, 0x6c,
+ 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x38,
+ 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc,
+ 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xfe, 0x06, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0xc0, 0xc0,
+ 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x60, 0x60, 0x60, 0x60, 0x60,
+ 0x60, 0x60, 0x60, 0x60, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0,
+ 0x60, 0x60, 0x30, 0x38, 0x18, 0x0c, 0x0c, 0x06, 0x06, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00,
+ 0x00, 0x00, 0x18, 0x18, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x06, 0x06,
+ 0x7e, 0xc6, 0xc6, 0xc6, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0,
+ 0xc0, 0xdc, 0xe6, 0xc6, 0xc6, 0xc6, 0xc6, 0xe6, 0xdc, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc0, 0xc0, 0xc6,
+ 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x06, 0x76, 0xce, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xce, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x7c, 0xc6, 0xc6, 0xfe, 0xc0, 0xc0, 0xc0, 0x7e, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x1c, 0x36, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xce, 0xc6,
+ 0xc6, 0xc6, 0xce, 0x76, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0xc0, 0xc0,
+ 0xc0, 0xdc, 0xe6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x1e, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0xc0, 0xc0,
+ 0xc0, 0xc6, 0xcc, 0xd8, 0xf0, 0xf0, 0xd8, 0xcc, 0xc6, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0xfe, 0xd6,
+ 0xd6, 0xd6, 0xd6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xdc, 0xe6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0xe6, 0xc6,
+ 0xc6, 0xc6, 0xe6, 0xdc, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x76, 0xce, 0xc6, 0xc6, 0xc6, 0xce, 0x76, 0x06, 0x06, 0x06, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0xe6, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
+ 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0,
+ 0x70, 0x1c, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30,
+ 0x30, 0xfe, 0x30, 0x30, 0x30, 0x30, 0x30, 0x36, 0x1c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0x6c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xc6, 0xc6, 0xd6, 0xd6, 0xd6, 0xd6, 0xfe, 0x6c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0x6c, 0x38, 0x38, 0x6c, 0xc6,
+ 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xce, 0x76, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xfe, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0xfe, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x1c, 0x30, 0x30, 0x30, 0x30, 0xe0, 0x30, 0x30, 0x30, 0x30,
+ 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x30,
+ 0x30, 0x30, 0x30, 0x1c, 0x30, 0x30, 0x30, 0x30, 0xe0, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x38, 0x38, 0x6c,
+ 0x6c, 0xc6, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66,
+ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x66, 0x3c, 0x18, 0xcc, 0x78, 0x00,
+ 0x00, 0x00, 0x6c, 0x6c, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0x7c, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x00, 0x7c, 0xc6, 0xc6,
+ 0xfe, 0xc0, 0xc0, 0xc0, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c,
+ 0x00, 0x7c, 0x06, 0x06, 0x7e, 0xc6, 0xc6, 0xc6, 0x7e, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x6c, 0x6c, 0x00, 0x7c, 0x06, 0x06, 0x7e, 0xc6, 0xc6, 0xc6,
+ 0x7e, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0x06, 0x06,
+ 0x7e, 0xc6, 0xc6, 0xc6, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x38,
+ 0x00, 0x7c, 0x06, 0x06, 0x7e, 0xc6, 0xc6, 0xc6, 0x7e, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc0, 0xc0, 0xc6,
+ 0x7c, 0x18, 0x0c, 0x38, 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xc6,
+ 0xfe, 0xc0, 0xc0, 0xc0, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x6c,
+ 0x00, 0x7c, 0xc6, 0xc6, 0xfe, 0xc0, 0xc0, 0xc0, 0x7e, 0x00, 0x00, 0x00,
+ 0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xc6, 0xfe, 0xc0, 0xc0, 0xc0,
+ 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x6c, 0x00, 0x38, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c,
+ 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00,
+ 0x00, 0x60, 0x30, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x3c, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xc6,
+ 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x38, 0x00,
+ 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00,
+ 0x18, 0x30, 0x60, 0x00, 0xfe, 0xc0, 0xc0, 0xfc, 0xc0, 0xc0, 0xc0, 0xc0,
+ 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0x36, 0x36,
+ 0x76, 0xde, 0xd8, 0xd8, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x3c,
+ 0x6c, 0xcc, 0xcc, 0xfe, 0xcc, 0xcc, 0xcc, 0xcc, 0xce, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x6c, 0x00, 0x7c, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18,
+ 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x38, 0x6c, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0x7c, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x00, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x6c,
+ 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xce, 0x76, 0x06, 0xc6, 0x7c, 0x00,
+ 0x6c, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0x7c, 0x00, 0x00, 0x00, 0x6c, 0x6c, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30,
+ 0x30, 0x78, 0xcc, 0xc0, 0xc0, 0xcc, 0x78, 0x30, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x6c, 0x60, 0x60, 0x60, 0xf8, 0x60, 0x60, 0x60, 0xe6,
+ 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0xfc,
+ 0x30, 0xfc, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xcc,
+ 0xcc, 0xf8, 0xc4, 0xcc, 0xde, 0xcc, 0xcc, 0xcc, 0xc6, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x0e, 0x1b, 0x18, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0xd8, 0x70, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x00, 0x7c, 0x06, 0x06,
+ 0x7e, 0xc6, 0xc6, 0xc6, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30,
+ 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00,
+ 0x00, 0x0c, 0x18, 0x30, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0x7c, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x00, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00,
+ 0x00, 0xdc, 0xe6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00,
+ 0x76, 0xdc, 0x00, 0xc6, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6,
+ 0xc6, 0x00, 0x00, 0x00, 0x00, 0x78, 0xd8, 0xd8, 0x6c, 0x00, 0xfc, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x6c,
+ 0x38, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x30, 0x60, 0xc0, 0xc6, 0xc6,
+ 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xfe, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc0, 0xc2, 0xc6, 0xcc, 0xd8, 0x30, 0x60, 0xdc, 0x86, 0x0c,
+ 0x18, 0x3e, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc2, 0xc6, 0xcc, 0xd8, 0x30,
+ 0x66, 0xce, 0x9e, 0x3e, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30,
+ 0x00, 0x30, 0x30, 0x30, 0x78, 0x78, 0x78, 0x78, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x6c, 0xd8, 0x6c, 0x36, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x6c, 0x36,
+ 0x6c, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x88, 0x22, 0x88,
+ 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88,
+ 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa,
+ 0x55, 0xaa, 0x55, 0xaa, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77,
+ 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0xf8, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0xf8, 0x18,
+ 0x18, 0xf8, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0xf6, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0x18,
+ 0x18, 0xf8, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0xf6, 0xf6, 0x06, 0x06, 0xf6, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x06,
+ 0x06, 0xf6, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0xf6, 0xf6, 0x06, 0x06, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xfe, 0xfe, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0xf8, 0x18,
+ 0x18, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xf8, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x1f, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff,
+ 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xff, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x1f, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
+ 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0xff, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x1f, 0x18, 0x18, 0x1f, 0x1f, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37,
+ 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x37, 0x37, 0x30, 0x30, 0x3f, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x3f, 0x30, 0x30, 0x37, 0x37, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0xf7, 0x00,
+ 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xff, 0xff, 0x00, 0x00, 0xf7, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x37, 0x30, 0x30, 0x37, 0x37, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00,
+ 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0xf7, 0xf7, 0x00, 0x00, 0xf7, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff,
+ 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x3f,
+ 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x1f, 0x1f, 0x18, 0x18, 0x1f, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x1f, 0x18, 0x18, 0x1f, 0x1f, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f,
+ 0x3f, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0xff, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0xff, 0x18, 0x18, 0xff, 0xff, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8,
+ 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x1f, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xf0, 0xf0, 0xf0,
+ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x76, 0xd6, 0xdc, 0xc8, 0xc8, 0xdc, 0xd6, 0x76, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0xd8, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
+ 0xd8, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0xfe, 0xc6, 0xc6, 0xc0, 0xc0, 0xc0,
+ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x7e, 0xfe, 0x24, 0x24, 0x24, 0x24, 0x66, 0xc6, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xfe, 0xfe, 0xc2, 0x60, 0x30, 0x18, 0x30, 0x60, 0xc2, 0xfe,
+ 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xc8, 0xcc,
+ 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x76, 0x6c, 0x60, 0xc0, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0xfc, 0x98, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x30, 0x30, 0x78, 0xcc, 0xcc,
+ 0xcc, 0x78, 0x30, 0x30, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c,
+ 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x6c, 0x6c, 0x6c,
+ 0xee, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0xcc, 0x60, 0x30, 0x78, 0xcc,
+ 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x76, 0xbb, 0x99, 0x99, 0xdd, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x02, 0x06, 0x3c, 0x6c, 0xce, 0xd6, 0xd6, 0xe6, 0x6c, 0x78,
+ 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x30, 0x60, 0xc0, 0xc0, 0xfe,
+ 0xc0, 0xc0, 0x60, 0x30, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0xfc,
+ 0x30, 0x30, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x60, 0x30, 0x18, 0x0c, 0x18, 0x30, 0x60, 0x00, 0xfc, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x18, 0x30, 0x60, 0xc0, 0x60, 0x30, 0x18, 0x00,
+ 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x36, 0x36, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, 0xfc, 0x00, 0x30, 0x30, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00,
+ 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0xcc, 0xcc,
+ 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xcc, 0x6c, 0x3c, 0x1c, 0x0c, 0x00, 0x00,
+ 0x00, 0xd8, 0xec, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x0c, 0x18, 0x30, 0x60, 0x7c,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00
+};
diff --git a/usr.bin/kfgwm/include/kfg/font.h b/usr.bin/kfgwm/include/kfg/font.h
new file mode 100644
index 0000000..7752952
--- /dev/null
+++ b/usr.bin/kfgwm/include/kfg/font.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _KFGWM_FONT_H_
+#define _KFGWM_FONT_H_
+
+#include <sys/types.h>
+
+#define FONT_WIDTH 8
+#define FONT_HEIGHT 16
+
+extern const uint8_t g_KFG_FONT[];
+
+#endif /* !_KFGWM_FONT_H_ */
diff --git a/usr.bin/kfgwm/include/kfg/types.h b/usr.bin/kfgwm/include/kfg/types.h
new file mode 100644
index 0000000..2d17ae1
--- /dev/null
+++ b/usr.bin/kfgwm/include/kfg/types.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef KFG_TYPES_H_
+#define KFG_TYPES_H_
+
+#include <sys/types.h>
+#include <stddef.h>
+
+typedef uint32_t kfgpos_t;
+typedef uint32_t kfgdim_t;
+typedef uint32_t kfgpixel_t;
+
+#endif /* !KFG_TYPES_H_ */
diff --git a/usr.bin/kfgwm/include/kfg/window.h b/usr.bin/kfgwm/include/kfg/window.h
new file mode 100644
index 0000000..a597969
--- /dev/null
+++ b/usr.bin/kfgwm/include/kfg/window.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef KFG_WINDOW_H_
+#define KFG_WINDOW_H_
+
+#include <kfg/types.h>
+
+#define KFG_RED 0x6E0C24
+#define KFG_YELLOW 0xF0A401
+#define KFG_WHITE 0xF2E5BC
+#define KFG_DARK 0x1D2021
+#define KFG_BLUE 0x076678
+#define KFG_AQUA 0x427B58
+
+/* Default dimensions */
+#define KFG_BORDER_WIDTH 1
+#define KFG_BORDER_HEIGHT 1
+#define KFG_TITLE_HEIGHT 10
+
+struct kfg_window {
+ kfgpos_t x;
+ kfgpos_t y;
+ kfgdim_t width;
+ kfgdim_t height;
+ kfgdim_t fb_pitch;
+ kfgpixel_t bg;
+ kfgpixel_t border_bg;
+ kfgpixel_t *framebuf;
+};
+
+struct kfg_text {
+ const char *text;
+ kfgpos_t x;
+ kfgpos_t y;
+};
+
+struct kfg_window *kfg_win_new(struct kfg_window *parent, kfgpos_t x, kfgpos_t y);
+int kfg_win_draw(struct kfg_window *parent, struct kfg_window *wp);
+int kfg_win_putstr(struct kfg_window *wp, struct kfg_text *tp);
+
+#endif /* !KFG_WINDOW_H_ */
diff --git a/usr.bin/kfgwm/kfgwm.c b/usr.bin/kfgwm/kfgwm.c
new file mode 100644
index 0000000..5a9e7b8
--- /dev/null
+++ b/usr.bin/kfgwm/kfgwm.c
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/fbdev.h>
+#include <kfg/window.h>
+#include <fcntl.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+static struct fbattr fbattr;
+static uint32_t *framep;
+
+static void
+test_win(struct kfg_window *root, kfgpos_t x, kfgpos_t y, const char *str)
+{
+ struct kfg_text text;
+ struct kfg_window *test_win;
+
+ test_win = kfg_win_new(root, x, y);
+ text.text = str;
+ text.x = 0;
+ text.y = 0;
+
+ kfg_win_draw(root, test_win);
+ kfg_win_putstr(test_win, &text);
+}
+
+int
+main(void)
+{
+ int fb_fd, fbattr_fd, prot;
+ size_t fb_size;
+ struct kfg_window *root_win;
+
+ fb_fd = open("/dev/fb0", O_RDWR);
+ if (fb_fd < 0) {
+ return fb_fd;
+ }
+
+ fbattr_fd = open("/ctl/fb0/attr", O_RDONLY);
+ if (fbattr_fd < 0) {
+ close(fb_fd);
+ return fbattr_fd;
+ }
+
+ read(fbattr_fd, &fbattr, sizeof(fbattr));
+ close(fbattr_fd);
+
+ fb_size = fbattr.height * fbattr.pitch;
+ prot = PROT_READ | PROT_WRITE;
+ framep = mmap(NULL, fb_size, prot, MAP_SHARED, fb_fd, 0);
+
+ root_win = malloc(sizeof(*root_win));
+ root_win->x = 0;
+ root_win->y = 0;
+ root_win->width = fbattr.width;
+ root_win->height = fbattr.height;
+ root_win->fb_pitch = fbattr.pitch;
+ root_win->framebuf = framep;
+ root_win->bg = KFG_RED;
+ root_win->border_bg = KFG_RED;
+ test_win(root_win, 40, 85, "Hello, World!");
+ test_win(root_win, 150, 20, "Mrow!");
+
+ for (;;);
+}
diff --git a/usr.bin/kfgwm/window.c b/usr.bin/kfgwm/window.c
new file mode 100644
index 0000000..3908302
--- /dev/null
+++ b/usr.bin/kfgwm/window.c
@@ -0,0 +1,221 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/errno.h>
+#include <sys/cdefs.h>
+#include <sys/param.h>
+#include <kfg/window.h>
+#include <kfg/font.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+
+__always_inline static inline size_t
+pixel_index(struct kfg_window *wp, kfgpos_t x, kfgpos_t y)
+{
+ return x + y * (wp->fb_pitch / 4);
+}
+
+static int
+kfg_win_putc(struct kfg_window *wp, uint32_t x, uint32_t y, char ch)
+{
+ size_t idx;
+ const uint8_t *glyph;
+ uint32_t fg, bg;
+
+ glyph = &g_KFG_FONT[(int)ch*16];
+ fg = KFG_WHITE;
+ bg = wp->bg;
+
+ for (uint32_t cy = 0; cy < FONT_HEIGHT; ++cy) {
+ idx = pixel_index(wp, x + (FONT_WIDTH - 1), y + cy);
+ for (uint32_t cx = 0; cx < FONT_WIDTH; ++cx) {
+ wp->framebuf[idx--] = ISSET(glyph[cy], BIT(cx)) ? fg : bg;
+ }
+ }
+}
+
+static void
+draw_win(struct kfg_window *parent, struct kfg_window *wp)
+{
+ kfgpixel_t *framep;
+ kfgpos_t x_i, y_i; /* Start */
+ kfgpos_t x_end, y_end; /* End */
+ kfgpixel_t brush = wp->bg;
+ kfgpos_t rx, ry; /* Starts at 0 */
+ kfgpos_t rx_end, ry_end; /* Starts at 0 */
+ size_t i;
+
+ framep = parent->framebuf;
+ x_i = wp->x;
+ y_i = wp->y;
+ x_end = x_i + wp->width;
+ y_end = y_i + wp->height;
+
+ if (x_end > parent->width)
+ x_end = parent->width;
+ if (y_end > parent->height)
+ y_end = parent->height;
+
+ for (kfgpos_t x = x_i; x < x_end; ++x) {
+ for (kfgpos_t y = y_i; y < y_i+KFG_TITLE_HEIGHT; ++y) {
+ rx = (x - x_i);
+ ry = (y - y_i);
+
+ if (rx <= KFG_BORDER_WIDTH && (rx % 2) == 0)
+ brush = KFG_WHITE;
+ else
+ brush = KFG_AQUA;
+
+ i = pixel_index(parent, x, y);
+ framep[i] = brush;
+ }
+ }
+
+ y_i = wp->y + KFG_TITLE_HEIGHT;
+ for (kfgpos_t x = x_i; x < x_end; ++x) {
+ for (kfgpos_t y = y_i; y < y_end; ++y) {
+ rx = (x - x_i);
+ ry = (y - y_i);
+
+ if (rx <= KFG_BORDER_WIDTH)
+ brush = wp->border_bg;
+ else if (ry <= KFG_BORDER_HEIGHT)
+ brush = wp->border_bg;
+ else if (rx >= (wp->width - KFG_BORDER_WIDTH))
+ brush = wp->border_bg;
+ else if (ry >= (wp->height - KFG_BORDER_HEIGHT))
+ brush = wp->border_bg;
+ else
+ brush = wp->bg;
+
+ i = pixel_index(parent, x, y);
+ framep[i] = brush;
+ }
+ }
+}
+
+/*
+ * Draw a window on the screen
+ *
+ * @parent: Parent window
+ * @wp: New window to draw
+ *
+ * TODO: Double buffering and multiple windows.
+ */
+int
+kfg_win_draw(struct kfg_window *parent, struct kfg_window *wp)
+{
+ kfgpos_t start_x, start_y;
+ kfgpos_t end_x, end_y;
+ kfgpos_t max_x, max_y;
+ kfgdim_t width, height;
+
+ if (parent == NULL) {
+ return -EINVAL;
+ }
+ if (parent->framebuf == NULL) {
+ return -EINVAL;
+ }
+
+ max_x = wp->x + parent->width;
+ max_y = wp->y + parent->height;
+
+ /* Don't overflow the framebuffer! */
+ if ((wp->x + wp->width) > max_x) {
+ wp->x = max_x;
+ }
+ if ((wp->y + wp->height) > max_y) {
+ wp->y = max_y;
+ }
+
+ draw_win(parent, wp);
+ return 0;
+}
+
+/*
+ * Create a new default window
+ *
+ * @x: X position for this window
+ * @y: Y position for this window
+ * @w: Window width
+ * @h: Window height
+ */
+struct kfg_window *
+kfg_win_new(struct kfg_window *parent, kfgpos_t x, kfgpos_t y)
+{
+ struct kfg_window *wp;
+
+ if ((wp = malloc(sizeof(*wp))) == NULL) {
+ return NULL;
+ }
+
+ wp->x = x;
+ wp->y = y;
+ wp->width = 250;
+ wp->height = 150;
+ wp->fb_pitch = parent->fb_pitch;
+ wp->framebuf = parent->framebuf;
+ wp->bg = KFG_DARK;
+ wp->border_bg = KFG_RED;
+ return wp;
+}
+
+int
+kfg_win_putstr(struct kfg_window *wp, struct kfg_text *tp)
+{
+ size_t slen;
+ const char *p;
+ kfgpos_t x, y;
+
+ if (tp == NULL)
+ return -EINVAL;
+ if (tp->text == NULL)
+ return -EINVAL;
+
+ slen = strlen(tp->text);
+ x = (wp->x + tp->x) + (KFG_BORDER_WIDTH + 1);
+ y = (KFG_TITLE_HEIGHT + wp->y) + tp->y;
+ p = tp->text;
+
+ while (slen--) {
+ if (y >= wp->height) {
+ break;
+ }
+
+ kfg_win_putc(wp, x, y, *(p++));
+ x += FONT_WIDTH;
+ if (x >= wp->width) {
+ y += FONT_HEIGHT;
+ x = wp->x + (KFG_BORDER_WIDTH + 1);
+ }
+ }
+
+ return 0;
+}
diff --git a/usr.bin/kmsg/Makefile b/usr.bin/kmsg/Makefile
new file mode 100644
index 0000000..9b76cc2
--- /dev/null
+++ b/usr.bin/kmsg/Makefile
@@ -0,0 +1,6 @@
+include user.mk
+
+CFILES = $(shell find . -name "*.c")
+
+$(ROOT)/base/usr/bin/kmsg:
+ gcc $(CFILES) -o $@ $(INTERNAL_CFLAGS)
diff --git a/usr.bin/kmsg/kmsg.c b/usr.bin/kmsg/kmsg.c
new file mode 100644
index 0000000..2deae39
--- /dev/null
+++ b/usr.bin/kmsg/kmsg.c
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdio.h>
+
+int
+main(void)
+{
+ int mfd;
+ ssize_t retval;
+ char linebuf[256];
+
+ if ((mfd = open("/dev/kmsg", O_RDONLY)) < 0) {
+ return mfd;
+ }
+
+ for (;;) {
+ retval = read(mfd, linebuf, sizeof(linebuf) - 1);
+ if (retval <= 0) {
+ break;
+ }
+ linebuf[retval] = '\0';
+ fputs(linebuf, stdout);
+ }
+
+ close(mfd);
+ return 0;
+}
diff --git a/usr.bin/kstat/Makefile b/usr.bin/kstat/Makefile
new file mode 100644
index 0000000..ccceb3c
--- /dev/null
+++ b/usr.bin/kstat/Makefile
@@ -0,0 +1,6 @@
+include user.mk
+
+CFILES = $(shell find . -name "*.c")
+
+$(ROOT)/base/usr/bin/kstat:
+ gcc $(CFILES) -o $@ $(INTERNAL_CFLAGS)
diff --git a/usr.bin/kstat/kstat.c b/usr.bin/kstat/kstat.c
new file mode 100644
index 0000000..cbbe602
--- /dev/null
+++ b/usr.bin/kstat/kstat.c
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/sched.h>
+#include <sys/vmstat.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#define MIB_PER_GIB 1024
+
+static void
+print_size_mib(const char *name, size_t mib)
+{
+ if (name == NULL) {
+ return;
+ }
+
+ if (mib >= MIB_PER_GIB) {
+ printf("%s: %d GiB\n", name, mib / MIB_PER_GIB);
+ } else {
+ printf("%s: %d MiB\n", name, mib);
+ }
+}
+
+static void
+get_vm_stat(void)
+{
+ struct vm_stat vmstat;
+ int retval, fd;
+
+ fd = open("/ctl/vm/stat", O_RDONLY);
+ if (fd < 0) {
+ printf("failed to open '/ctl/vm/stat'\n");
+ return;
+ }
+
+ retval = read(fd, &vmstat, sizeof(vmstat));
+ if (retval <= 0) {
+ printf("failed to read vmstat\n");
+ return;
+ }
+
+ close(fd);
+ print_size_mib("memory available", vmstat.mem_avail);
+ print_size_mib("memory used", vmstat.mem_used);
+ print_size_mib("memory total", vmstat.mem_total);
+}
+
+static void
+get_sched_stat(void)
+{
+ struct sched_stat stat;
+ struct sched_cpu *cpu;
+ double nonline, noffline;
+ uint16_t online_percent;
+ int fd;
+
+ fd = open("/ctl/sched/stat", O_RDONLY);
+ if (fd < 0) {
+ printf("failed to get sched stat\n");
+ return;
+ }
+ if (read(fd, &stat, sizeof(stat)) < 0) {
+ printf("failed to read sched stat\n");
+ return;
+ }
+
+ close(fd);
+ noffline = stat.nhlt;
+ nonline = (stat.ncpu - noffline);
+ online_percent = (uint16_t)(((double)nonline / (nonline + noffline)) * 100);
+
+ printf("number of tasks: %d\n", stat.nproc);
+ printf("number of cores online: %d\n", stat.ncpu);
+ printf("scheduler quantum: %d usec\n", stat.quantum_usec);
+ printf("CPU is %d%% online\n", online_percent);
+
+ /*
+ * Log out some per-cpu information
+ */
+ for (int i = 0; i < stat.ncpu; ++i) {
+ cpu = &stat.cpus[i];
+ printf("[cpu %d]: %d switches\n", i, cpu->nswitch);
+ }
+}
+
+int
+main(void)
+{
+ printf("-- scheduler statistics --\n");
+ get_sched_stat();
+ printf("-- memory statistics --\n");
+ get_vm_stat();
+ return 0;
+}
diff --git a/usr.bin/link.ld b/usr.bin/link.ld
index 9fad881..5e99291 100644
--- a/usr.bin/link.ld
+++ b/usr.bin/link.ld
@@ -23,4 +23,9 @@ SECTIONS
*(.bss.*)
__bss_end = .;
}
+
+ /DISCARD/ : {
+ *(.eh_frame)
+ *(.note .note.*)
+ }
}
diff --git a/usr.bin/login/Makefile b/usr.bin/login/Makefile
new file mode 100644
index 0000000..8b37d4c
--- /dev/null
+++ b/usr.bin/login/Makefile
@@ -0,0 +1,6 @@
+include user.mk
+
+CFILES = $(shell find . -name "*.c")
+
+$(ROOT)/base/usr/bin/login:
+ gcc -Iinclude/ $(CFILES) -o $@ $(INTERNAL_CFLAGS)
diff --git a/usr.bin/login/login.c b/usr.bin/login/login.c
new file mode 100644
index 0000000..5b21303
--- /dev/null
+++ b/usr.bin/login/login.c
@@ -0,0 +1,295 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/spawn.h>
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <crypto/sha256.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* Row indices for /etc/passwd */
+#define ROW_USERNAME 0
+#define ROW_HASH 1
+#define ROW_USERID 2
+#define ROW_GRPID 3
+#define ROW_GECOS 4
+#define ROW_HOME 5
+#define ROW_SHELL 6
+
+#define is_ascii(C) ((C) >= 0 && (C) <= 128)
+#define is_digit(C) ((C >= '0' && C <= '9'))
+
+#define DEFAULT_SHELL "/usr/bin/osh"
+
+static char buf[64];
+static uint8_t buf_i;
+static short echo_chars = 1;
+
+/*
+ * Verify a UID is valid
+ *
+ * Returns 0 on success
+ */
+static int
+check_uid(const char *uid)
+{
+ size_t len;
+
+ len = strlen(uid);
+
+ /* Must not be greater than 4 chars */
+ if (len > 4) {
+ return -1;
+ }
+
+ for (int i = 0; i < len; ++i) {
+ if (!is_digit(uid[i])) {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Check an /etc/passwd entry against an alias
+ * (username)
+ *
+ * @alias: Alias to lookup
+ * @hash: Password hash
+ * @entry: /etc/passwd entry
+ *
+ * Returns -1 on failure
+ * Returns 0 if the entry matches
+ */
+static int
+check_user(char *alias, char *hash, char *entry)
+{
+ const char *p;
+ char shell_path[256];
+ char *shell_argv[] = { DEFAULT_SHELL, NULL };
+ char *envp[] = { NULL };
+ size_t len, row = 0;
+ size_t line = 1;
+ short have_user = 0;
+ short have_pw = 0;
+ short have_uid = 0;
+ short have_shell = 0;
+ uid_t uid = -1;
+ pid_t shell_pid;
+
+ if (alias == NULL || entry == NULL) {
+ return -EINVAL;
+ }
+
+ /* Grab the username */
+ p = strtok(entry, ":");
+ if (p == NULL) {
+ printf("bad /etc/passwd entry @ line 1\n");
+ return -1;
+ }
+
+ /* Iterate through each field */
+ while (p != NULL) {
+ switch (row) {
+ case ROW_USERNAME:
+ if (strcmp(p, alias) == 0) {
+ have_user = 1;
+ }
+ break; /* UNREACHABLE */
+ case ROW_HASH:
+ if (strcmp(p, hash) == 0) {
+ have_pw = 1;
+ }
+ break;
+ case ROW_USERID:
+ if (check_uid(p) != 0) {
+ printf("bad uid @ line %d\n", line);
+ return -1;
+ }
+
+ uid = atoi(p);
+ have_uid = 1;
+ break;
+ case ROW_SHELL:
+ len = strlen(p) - 1;
+ if (len >= sizeof(shell_path) - 1) {
+ printf("bad shell path @ line %d\n", line);
+ return -1;
+ }
+
+ memcpy(shell_path, p, len);
+ shell_path[len] = '\0';
+ have_shell = 1;
+ break;
+ }
+
+ p = strtok(NULL, ":");
+ ++row;
+ ++line;
+ }
+
+ /*
+ * We need to have found the password hash,
+ * the username, AND the UID. If we have not,
+ * then this has failed.
+ */
+ if (!have_pw || !have_user || !have_uid) {
+ return -1;
+ }
+
+ /* Do we have the shell path? */
+ if (!have_shell) {
+ return -1;
+ }
+
+ setuid(uid);
+ shell_argv[0] = shell_path;
+ shell_pid = spawn(shell_argv[0], shell_argv, envp, 0);
+ return 0;
+}
+
+static char *
+getstr(void)
+{
+ char c, printc;
+ int input;
+
+ buf_i = 0;
+
+ for (;;) {
+ if ((input = getchar()) < 0) {
+ continue;
+ }
+
+ c = (char)input;
+ if (c == '\t') {
+ continue;
+ }
+
+ /*
+ * If we want to echo characters, 'printc' becomes
+ * exactly the character we got. Otherwise, just
+ * print little stars to redact it.
+ */
+ printc = echo_chars ? c : '*';
+
+ /* return on newline */
+ if (c == '\n') {
+ buf[buf_i] = '\0';
+ putchar('\n');
+ return buf;
+ }
+
+ /* handle backspaces and DEL */
+ if (c == '\b' || c == 127) {
+ if (buf_i > 0) {
+ fputs("\b \b", stdout);
+ buf[--buf_i] = '\0';
+ }
+ } else if (is_ascii(c) && buf_i < sizeof(buf) - 1) {
+ /* write to fd and add to buffer */
+ buf[buf_i++] = c;
+ putchar(printc);
+ }
+ }
+}
+
+static int
+getuser(FILE *fp)
+{
+ char *pwtmp, *alias, *p;
+ char entry[256];
+ char pwhash[SHA256_HEX_SIZE];
+ int retval;
+
+ printf("username: ");
+ p = getstr();
+ alias = strdup(p);
+
+ /* Grab the password now */
+ echo_chars = 0;
+ printf("password: ");
+ p = getstr();
+ pwtmp = strdup(p);
+ sha256_hex(pwtmp, strlen(pwtmp), pwhash);
+
+ /* Paranoia */
+ memset(pwtmp, 0, strlen(pwtmp));
+ buf_i = 0;
+ memset(buf, 0, sizeof(buf));
+
+ /* Clean up */
+ free(pwtmp);
+ pwtmp = NULL;
+
+ /* See if anything matches */
+ while (fgets(entry, sizeof(entry), fp) != NULL) {
+ retval = check_user(alias, pwhash, entry);
+ if (retval == 0) {
+ free(alias);
+ return 0;
+ }
+ }
+
+ /* If we reach this point, bad creds */
+ free(alias);
+ alias = NULL;
+
+ printf("bad username or password\n");
+ fseek(fp, 0, SEEK_SET);
+ memset(buf, 0, sizeof(buf));
+ buf_i = 0;
+ echo_chars = 1;
+ return -1;
+}
+
+int
+main(void)
+{
+ FILE *fp;
+
+ fp = fopen("/etc/passwd", "r");
+ if (fp == NULL) {
+ printf("failed to open /etc/passwd\n");
+ return -1;
+ }
+
+ printf("- Please authenticate yourself -\n");
+ for (;;) {
+ if (getuser(fp) == 0) {
+ break;
+ }
+ }
+
+ fclose(fp);
+ return 0;
+}
diff --git a/usr.bin/mex/Makefile b/usr.bin/mex/Makefile
new file mode 100644
index 0000000..6c0db59
--- /dev/null
+++ b/usr.bin/mex/Makefile
@@ -0,0 +1,6 @@
+include user.mk
+
+CFILES = $(shell find . -name "*.c")
+
+$(ROOT)/base/usr/bin/mex:
+ gcc $(CFILES) -o $@ $(INTERNAL_CFLAGS)
diff --git a/usr.bin/mex/mex.c b/usr.bin/mex/mex.c
new file mode 100644
index 0000000..7e6f8aa
--- /dev/null
+++ b/usr.bin/mex/mex.c
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <unistd.h>
+#include <stddef.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdio.h>
+
+#define LINE_LEN 16
+
+static void
+dump_line(const char *line, size_t len)
+{
+ /* The amount of bytes we write */
+ const uint8_t BYTE_COUNT = 2;
+
+ for (size_t i = 0; i < LINE_LEN; ++i) {
+ if (i < len) {
+ printf("%02x", line[i] & 0xFF);
+ } else {
+ printf(" ");
+ }
+
+ /* Put spacing between bytes */
+ if (((i + 1) % BYTE_COUNT) == 0) {
+ printf(" ");
+ }
+ }
+
+ printf(" ");
+ for (size_t i = 0; i < len; ++i) {
+ if (line[i] > 31 && line[i] < 127) {
+ printf("%c", line[i]);
+ } else {
+ printf(".");
+ }
+ }
+
+ printf("\n");
+}
+
+static void
+dump_file(int fd)
+{
+ char buf[LINE_LEN];
+ ssize_t count;
+ size_t offset = 0;
+
+ for (;;) {
+ count = read(fd, buf, sizeof(char) * LINE_LEN);
+ if (count <= 0) {
+ break;
+ }
+
+ printf("%08x: ", offset);
+ offset += LINE_LEN;
+ dump_line(buf, count);
+ }
+}
+
+int
+main(int argc, char **argv)
+{
+ int fd;
+
+ if (argc < 2) {
+ printf("mex: usage: mex <filename>\n");
+ return -1;
+ }
+
+ if ((fd = open(argv[1], O_RDONLY)) < 0) {
+ printf("mex: failed to open input\n");
+ return fd;
+ }
+
+ dump_file(fd);
+ return 0;
+}
diff --git a/usr.bin/mrow/Makefile b/usr.bin/mrow/Makefile
new file mode 100644
index 0000000..d7c7ef4
--- /dev/null
+++ b/usr.bin/mrow/Makefile
@@ -0,0 +1,6 @@
+include user.mk
+
+CFILES = $(shell find . -name "*.c")
+
+$(ROOT)/base/usr/bin/mrow:
+ gcc $(CFILES) -o $@ $(INTERNAL_CFLAGS) -lgfx
diff --git a/usr.bin/mrow/mrow.c b/usr.bin/mrow/mrow.c
new file mode 100644
index 0000000..1179f7e
--- /dev/null
+++ b/usr.bin/mrow/mrow.c
@@ -0,0 +1,287 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <fcntl.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <time.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <libgfx/gfx.h>
+#include <libgfx/draw.h>
+
+#define IS_ASCII(C) ((C) > 0 && (C) < 127)
+
+#define PLAYER_BG 0x808080
+#define MOUSE_BG 0x404040
+#define GAME_BG 0x000000
+
+#define SPRITE_WIDTH 20
+#define SPRITE_HEIGHT 20
+#define MAX_MOUSE_SPEED 2
+#define MIN_MOUSE_SPEED 1
+#define PLAYER_SPEED 30
+
+#define SCR_WIDTH (gfx_ctx.fbdev.width)
+#define SCR_HEIGHT (gfx_ctx.fbdev.height)
+#define MAX_X (SCR_WIDTH - SPRITE_WIDTH)
+#define MAX_Y (SCR_HEIGHT - SPRITE_HEIGHT)
+
+/* Hit beep stuff */
+#define HIT_BEEP_MSEC 50
+#define HIT_BEEP_FREQ 600
+
+static struct gfx_ctx gfx_ctx;
+static uint32_t *framep;
+static int beep_fd;
+static size_t hit_count = 0;
+
+struct player {
+ int32_t x;
+ int32_t y;
+};
+
+struct mouse {
+ int32_t x;
+ int32_t y;
+ uint8_t x_inc : 1;
+ uint8_t y_inc : 1;
+ uint8_t speed;
+};
+
+static void
+draw_sprite(uint32_t x, uint32_t y, uint32_t color)
+{
+ struct gfx_shape sprite_shape = GFX_SHAPE_DEFAULT;
+
+ sprite_shape.x = x;
+ sprite_shape.y = y;
+ sprite_shape.width = SPRITE_WIDTH;
+ sprite_shape.height = SPRITE_HEIGHT;
+ sprite_shape.color = color;
+ gfx_draw_shape(&gfx_ctx, &sprite_shape);
+}
+
+static void
+update_mouse(struct mouse *mouse)
+{
+ draw_sprite(mouse->x, mouse->y, GAME_BG);
+
+ /* Move the mouse in the x direction */
+ if (mouse->x_inc) {
+ mouse->x += mouse->speed;
+ } else {
+ mouse->x -= mouse->speed;
+ }
+
+ /* Move the mouse in the y direction */
+ if (mouse->y_inc) {
+ mouse->y += mouse->speed;
+ } else {
+ mouse->y -= mouse->speed;
+ }
+
+ if (mouse->x >= MAX_X) {
+ mouse->x = MAX_X;
+ mouse->x_inc = 0;
+ } else if (mouse->x <= 0) {
+ mouse->x = 0;
+ mouse->x_inc = 1;
+ }
+
+ if (mouse->y >= MAX_Y) {
+ mouse->y = MAX_Y;
+ mouse->y_inc = 0;
+ } else if (mouse->y <= 0) {
+ mouse->y = 0;
+ mouse->y_inc = 1;
+ }
+
+ draw_sprite(mouse->x, mouse->y, MOUSE_BG);
+}
+
+static void
+beep(uint16_t msec, uint16_t freq)
+{
+ uint32_t payload;
+
+ /* Can't beep :( */
+ if (beep_fd < 0) {
+ return;
+ }
+
+ payload = freq;
+ payload |= (msec << 16);
+ write(beep_fd, &payload, sizeof(payload));
+}
+
+static void
+score_increment(struct player *p, struct mouse *m)
+{
+ printf("\033[31;40mSCORE: %d\033[0m\n", ++hit_count);
+
+ if (m->speed < MAX_MOUSE_SPEED) {
+ m->speed += 1;
+ } else {
+ m->speed = MIN_MOUSE_SPEED;
+ }
+}
+
+static bool
+mouse_collide(struct player *p, struct mouse *m)
+{
+ bool detected = false;
+ bool x_overlap, y_overlap;
+
+ x_overlap = p->x < (m->x + SPRITE_WIDTH) &&
+ (p->x + SPRITE_WIDTH) > m->x;
+ y_overlap = p->y < (m->y + SPRITE_HEIGHT) &&
+ (p->y + SPRITE_HEIGHT) > m->y;
+ detected = x_overlap && y_overlap;
+
+ /*
+ * Play a little ACK sound and reset the game
+ * if we collide
+ */
+ if (detected) {
+ beep(HIT_BEEP_MSEC, HIT_BEEP_FREQ);
+
+ /* Clear the sprites */
+ draw_sprite(m->x, m->y, GAME_BG);
+ draw_sprite(p->x, p->y, GAME_BG);
+
+ m->x = 0;
+ m->y = rand() % MAX_Y;
+ m->x_inc ^= 1;
+ m->y_inc ^= 1;
+ score_increment(p, m);
+ }
+
+ return detected;
+
+}
+
+static void
+game_loop(void)
+{
+ struct timespec ts;
+ struct mouse mouse;
+ struct player p;
+ char c;
+ bool running = true;
+
+ ts.tv_sec = 0;
+ ts.tv_nsec = 7000000;
+
+ /* Setup the player */
+ p.x = 0;
+ p.y = 0;
+
+ /* Setup the mouse */
+ mouse.x = MAX_X;
+ mouse.y = MAX_Y;
+ mouse.x_inc = 0;
+ mouse.y_inc = 0;
+ mouse.speed = MIN_MOUSE_SPEED;
+
+ /* Draw player and mouse */
+ draw_sprite(p.x, p.y, PLAYER_BG);
+ draw_sprite(mouse.x, mouse.y, MOUSE_BG);
+
+ while (running) {
+ if (mouse_collide(&p, &mouse)) {
+ continue;
+ }
+
+ c = getchar();
+ sleep(&ts, &ts);
+ update_mouse(&mouse);
+
+ if (IS_ASCII(c)) {
+ draw_sprite(p.x, p.y, GAME_BG);
+ }
+
+ switch (c) {
+ case 'w':
+ p.y -= PLAYER_SPEED;
+ if (p.y <= 0) {
+ p.y = 0;
+ }
+ break;
+ case 'a':
+ p.x -= PLAYER_SPEED;
+ if (p.x <= 0) {
+ p.x = 0;
+ }
+ break;
+ case 's':
+ p.y += PLAYER_SPEED;
+ if (p.y > MAX_Y){
+ p.y = MAX_Y;
+ }
+ break;
+ case 'd':
+ p.x += PLAYER_SPEED;
+ if (p.x > MAX_X) {
+ p.x = MAX_X;
+ }
+ break;
+ case 'q':
+ running = false;
+ default:
+ continue;
+ }
+
+ draw_sprite(p.x, p.y, PLAYER_BG);
+ }
+}
+
+int
+main(void)
+{
+ int error;
+ char c;
+
+ error = gfx_init(&gfx_ctx);
+ if (error < 0) {
+ printf("failed to init libgfx\n");
+ return error;
+ }
+
+ beep_fd = open("/dev/beep", O_WRONLY);
+ game_loop();
+ printf("\033[35;40mYOUR FINAL SCORE: %d\033[0m\n", hit_count);
+
+ /* Cleanup */
+ close(beep_fd);
+ gfx_cleanup(&gfx_ctx);
+ return 0;
+}
diff --git a/usr.bin/nerve/Makefile b/usr.bin/nerve/Makefile
new file mode 100644
index 0000000..cc0fd91
--- /dev/null
+++ b/usr.bin/nerve/Makefile
@@ -0,0 +1,6 @@
+include user.mk
+
+CFILES = $(shell find . -name "*.c")
+
+$(ROOT)/base/usr/bin/nerve:
+ gcc $(CFILES) -o $@ $(INTERNAL_CFLAGS)
diff --git a/usr.bin/nerve/nerve.c b/usr.bin/nerve/nerve.c
new file mode 100644
index 0000000..75a19be
--- /dev/null
+++ b/usr.bin/nerve/nerve.c
@@ -0,0 +1,377 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/errno.h>
+#include <sys/console.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdint.h>
+
+/* Verb numeric defs (see string defs) */
+#define VERB_UNKNOWN -1
+#define VERB_POKE 0x0000
+#define VERB_PEEK 0x0001
+
+/* Verb string defs (see numeric defs) */
+#define SVERB_POKE "poke"
+#define SVERB_PEEK "peek"
+
+/* Nerve numeric defs (see string defs) */
+#define NERVE_UNKNOWN -1
+#define NERVE_CONSATTR 0x0000
+#define NERVE_CONSFEAT 0x0001
+
+/* Nerve string defs (see numeric defs) */
+#define SNERVE_CONSATTR "consattr"
+#define SNERVE_CONSFEAT "consfeat"
+
+/* Misc defines */
+#define NERVE_PACKET_LEN 16
+
+struct verb_handler;
+
+static int poke_nerve(const char *nerve, struct verb_handler *h);
+static int peek_nerve(const char *nerve, struct verb_handler *h);
+static int nerve_to_def(const char *nerve);
+
+/*
+ * Contains verb handlers called when a verb
+ * (e.g., 'poke') is matched.
+ */
+struct verb_handler {
+ int(*run)(const char *nerve, struct verb_handler *h);
+ char **argv;
+ size_t argc;
+};
+
+/*
+ * Holds information that may be sent down
+ * my nerves.
+ *
+ * Example: nerve poke <x> 1 0 1
+ * * * *
+ * +--------+ / / /
+ * | meow | <------+ / /
+ * |--------| / /
+ * | foo | <------+ /
+ * |--------| /
+ * | foobar | <------+
+ * +--------+
+ * packet
+ */
+struct nerve_payload {
+ uint32_t packet[NERVE_PACKET_LEN];
+ uint16_t len;
+};
+
+/*
+ * Verb handler table, when a verb is matched,
+ * its respective handler is called.
+ */
+static struct verb_handler verbtab[] = {
+ { poke_nerve },
+ { peek_nerve }
+};
+
+/*
+ * Print list of available options as well as
+ * information about the program.
+ */
+static void
+help(void)
+{
+ printf(
+ "nerve: usage: nerve <verb> [ .. data ..]\n"
+ "verb 'poke': Poke a control (/ctl) nerve\n"
+ "???????????????? NERVES ????????????????\n"
+ "consattr: Console attributes\n"
+ "consfeat: Console features\n"
+ );
+}
+
+/*
+ * The user gets to send data down my nerves through
+ * a nerve payload. This function acquires the nerve
+ * payload. Please don't hurt me.
+ *
+ * @argc: Number of arguments within argv
+ * @argv: Argument vector
+ * @res: Where the payload goes
+ */
+static int
+get_nerve_payload(int argc, char *argv[], struct nerve_payload *res)
+{
+ char *payload_str;
+ uint32_t datum;
+
+ /* Do we have a nerve payload? */
+ if (argc < 4) {
+ printf("[!] missing nerve payload\n");
+ return -1;
+ }
+
+ /* Reset fields */
+ res->len = 0;
+ memset(res->packet, 0, sizeof(res->packet));
+
+ /* Start grabbing bytes */
+ for (int i = 3; i < argc; ++i) {
+ if (res->len >= NERVE_PACKET_LEN) {
+ printf("[*] truncated packet\n");
+ break;
+ }
+ payload_str = argv[i];
+ datum = atoi(payload_str);
+ res->packet[res->len++] = datum;
+ }
+
+ return 0;
+}
+
+/*
+ * Peek at a control nerve located in /ctl/
+ *
+ * @nerve: Name of nerve to peek at
+ * @h: Verb handler, instance of self
+ *
+ * Returns less than zero if the nerve does
+ * not match.
+ */
+static int
+peek_nerve(const char *nerve, struct verb_handler *h)
+{
+ int error, nerve_idx = -1;
+
+ if (nerve == NULL || h == NULL) {
+ return -EINVAL;
+ }
+
+ /* Grab the nerve table index */
+ nerve_idx = nerve_to_def(nerve);
+ if (nerve_idx == NERVE_UNKNOWN) {
+ printf("[&^]: This is not my nerve.\n");
+ return -1;
+ }
+
+ switch (nerve_idx) {
+ case NERVE_CONSATTR:
+ {
+ struct console_attr c;
+ int fd;
+
+ fd = open("/ctl/console/attr", O_RDONLY);
+ read(fd, &c, sizeof(c));
+ printf("(cursx=%d, cursy=%d)\n", c.cursor_x, c.cursor_y);
+ close(fd);
+ break;
+ }
+ case NERVE_CONSFEAT:
+ {
+ struct console_feat f;
+ int fd;
+
+ fd = open("/ctl/console/feat", O_RDONLY);
+ read(fd, &f, sizeof(f));
+ printf("ansi_esc=%d\n", f.ansi_esc);
+ printf("show_curs=%d\n", f.show_curs);
+ close(fd);
+ break;
+ }
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ * Poke a control nerve located in /ctl/
+ *
+ * @nerve: Name of the nerve (e.g., consattr)
+ * @h: Verb handler, instance of self
+ *
+ * Returns less than zero if the nerve does not
+ * match.
+ */
+static int
+poke_nerve(const char *nerve, struct verb_handler *h)
+{
+ struct nerve_payload payload;
+ int error, nerve_idx = -1;
+
+ if (nerve == NULL || h == NULL) {
+ return -EINVAL;
+ }
+
+ /* Grab the nerve table index */
+ nerve_idx = nerve_to_def(nerve);
+ if (nerve_idx == NERVE_UNKNOWN) {
+ printf("[&^]: This is not my nerve.\n");
+ return -1;
+ }
+
+ /* Grab the payload passed by the user */
+ error = get_nerve_payload(h->argc, h->argv, &payload);
+ if (error < 0) {
+ printf("[!] nerve error\n");
+ return -1;
+ }
+
+ switch (nerve_idx) {
+ case NERVE_CONSATTR:
+ {
+ struct console_attr c;
+ int fd;
+
+ c.cursor_x = payload.packet[0];
+ c.cursor_y = payload.packet[1];
+
+ fd = open("/ctl/console/attr", O_WRONLY);
+ write(fd, &c, sizeof(c));
+ close(fd);
+ break;
+ }
+ case NERVE_CONSFEAT:
+ {
+ struct console_feat f;
+ int fd;
+
+ f.ansi_esc = payload.packet[0] & 0xFF;
+ f.show_curs = payload.packet[1] & 0xFF;
+
+ fd = open("/ctl/console/feat", O_WRONLY);
+ write(fd, &f, sizeof(f));
+ close(fd);
+ break;
+ }
+ default:
+ break;
+ }
+
+ return -1;
+}
+
+/*
+ * Convert a nerve name into a numeric nerve
+ * definition
+ *
+ * @nerve: Nerve name to convert
+ */
+static int
+nerve_to_def(const char *nerve)
+{
+ /*
+ * Now we need to parse the nerve string
+ * and see if it matches with anything
+ * that we know.
+ */
+ switch (*nerve) {
+ case 'c':
+ if (strcmp(nerve, SNERVE_CONSATTR) == 0) {
+ return NERVE_CONSATTR;
+ } else if (strcmp(nerve, SNERVE_CONSFEAT) == 0) {
+ return NERVE_CONSFEAT;
+ }
+ }
+
+ return NERVE_UNKNOWN;
+}
+
+/*
+ * Convert a string verb, passed in through the command
+ * line, into a numeric definition
+ *
+ * @verb: String verb
+ */
+static int
+verb_to_def(const char *verb)
+{
+ if (verb == NULL) {
+ return -EINVAL;
+ }
+
+ /*
+ * Parse the verb and try to match it against
+ * a constant.
+ *
+ * XXX: Here we are first matching the first character
+ * before we match the entire verb as that is more
+ * efficient than scanning each entire string until
+ * one matches.
+ */
+ switch (*verb) {
+ case 'p':
+ if (strcmp(verb, SVERB_POKE) == 0) {
+ return VERB_POKE;
+ }
+ if (strcmp(verb, SVERB_PEEK) == 0) {
+ return VERB_PEEK;
+ }
+ default:
+ printf("[!] bad verb \"%s\"\n", verb);
+ return VERB_UNKNOWN;
+ }
+
+ return VERB_UNKNOWN;
+}
+
+int
+main(int argc, char **argv)
+{
+ struct verb_handler *verbd;
+ int verb;
+
+ if (argc < 2) {
+ help();
+ return -1;
+ }
+
+ verb = verb_to_def(argv[1]);
+ if (verb < 0) {
+ return -1;
+ }
+
+ /* Make sure the arguments match */
+ switch (verb) {
+ case VERB_POKE:
+ if (argc < 3) {
+ printf("[!] missing nerve name\n");
+ help();
+ return -1;
+ }
+ break;
+ }
+
+ verbd = &verbtab[verb];
+ verbd->argv = argv;
+ verbd->argc = argc;
+ return verbd->run(argv[2], verbd);
+}
diff --git a/usr.bin/notes/Makefile b/usr.bin/notes/Makefile
new file mode 100644
index 0000000..c8717a9
--- /dev/null
+++ b/usr.bin/notes/Makefile
@@ -0,0 +1,6 @@
+include user.mk
+
+CFILES = $(shell find . -name "*.c")
+
+$(ROOT)/base/usr/bin/notes:
+ gcc $(CFILES) -o $@ $(INTERNAL_CFLAGS)
diff --git a/usr.bin/notes/notes.c b/usr.bin/notes/notes.c
new file mode 100644
index 0000000..9db8b60
--- /dev/null
+++ b/usr.bin/notes/notes.c
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+
+#define BEEP_MSEC 100
+#define key_step(KEY) ('9' - ((KEY)))
+
+static uint8_t freq_addend = 0;
+
+static uint16_t freqtab[] = {
+ [ key_step('0') ] = 950,
+ [ key_step('9') ] = 900,
+ [ key_step('8') ] = 850,
+ [ key_step('7') ] = 800,
+ [ key_step('6') ] = 750,
+ [ key_step('5') ] = 700,
+ [ key_step('4') ] = 650,
+ [ key_step('3') ] = 600,
+ [ key_step('2') ] = 550,
+ [ key_step('1') ] = 500
+};
+
+static int beep_fd = 0;
+static bool running = false;
+
+static void
+beep(uint16_t freq)
+{
+ uint32_t payload;
+
+ if (beep_fd < 0) {
+ return;
+ }
+
+ payload = freq;
+ payload |= (BEEP_MSEC << 16);
+ write(beep_fd, &payload, sizeof(payload));
+}
+
+static inline void
+play_notekey(char key)
+{
+ uint8_t step = key_step(key);
+ uint16_t freq;
+
+ /* Should not happen */
+ if (step >= NELEM(freqtab)) {
+ step = key_step('0');
+ }
+
+ freq = freqtab[step] + freq_addend;
+ beep(freq);
+}
+
+static void
+play_loop(void)
+{
+ uint16_t freq = 0;
+ char c;
+
+ running = true;
+ while (running) {
+ c = getchar();
+ switch (c) {
+ case 'q':
+ running = false;
+ break;
+ case 'i':
+ /* NOTE: Overflow purposefully allowed here */
+ ++freq_addend;
+ printf("%d ", freq_addend);
+ break;
+ case 'd':
+ /* NOTE: Underflow purposefully allowed here */
+ --freq_addend;
+ printf("%d ", freq_addend);
+ break;
+ default:
+ if (!isdigit(c)) {
+ break;
+ }
+
+ play_notekey(c);
+ }
+ }
+
+ printf("\ncya!\n");
+}
+
+int
+main(int argc, char **argv)
+{
+ beep_fd = open("/dev/beep", O_WRONLY);
+ if (beep_fd < 0) {
+ return -1;
+ }
+
+ printf("bleep bloop time! - [i]nc/[d]ec\n");
+ play_loop();
+ close(beep_fd);
+}
diff --git a/usr.bin/oasm/Makefile b/usr.bin/oasm/Makefile
new file mode 100644
index 0000000..a83aaab
--- /dev/null
+++ b/usr.bin/oasm/Makefile
@@ -0,0 +1,7 @@
+include user.mk
+
+CFILES = $(shell find . -name "*.c")
+CFLAGS = -Iinclude/
+
+$(ROOT)/base/usr/bin/oasm:
+ gcc $(CFILES) -o $@ $(INTERNAL_CFLAGS) $(CFLAGS)
diff --git a/usr.bin/oasm/emit.c b/usr.bin/oasm/emit.c
new file mode 100644
index 0000000..099f0b2
--- /dev/null
+++ b/usr.bin/oasm/emit.c
@@ -0,0 +1,564 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/errno.h>
+#include <oasm/emit.h>
+#include <oasm/log.h>
+#include <stdlib.h>
+#include <string.h>
+
+static inline void
+emit_bytes(struct emit_state *state, void *p, size_t len)
+{
+ write(state->out_fd, p, len);
+}
+
+/*
+ * Convert an IR register to an OSMX64
+ * valid register value that can be encoded
+ * into the instruction.
+ */
+static inline reg_t
+ir_to_reg(tt_t ir)
+{
+ switch (ir) {
+ case TT_X0: return OSMX64_R_X0;
+ case TT_X1: return OSMX64_R_X1;
+ case TT_X2: return OSMX64_R_X2;
+ case TT_X3: return OSMX64_R_X3;
+ case TT_X4: return OSMX64_R_X4;
+ case TT_X5: return OSMX64_R_X5;
+ case TT_X6: return OSMX64_R_X6;
+ case TT_X7: return OSMX64_R_X7;
+ case TT_X8: return OSMX64_R_X8;
+ case TT_X9: return OSMX64_R_X9;
+ case TT_X10: return OSMX64_R_X10;
+ case TT_X11: return OSMX64_R_X11;
+ case TT_X12: return OSMX64_R_X12;
+ case TT_X13: return OSMX64_R_X13;
+ case TT_X14: return OSMX64_R_X14;
+ case TT_X15: return OSMX64_R_X15;
+ }
+
+ return OSMX64_R_BAD;
+}
+
+/*
+ * Encode a MOV instruction
+ *
+ * mov [r], [r/imm]
+ *
+ * Returns the next token on success,
+ * otherwise NULL.
+ */
+static struct oasm_token *
+emit_encode_mov(struct emit_state *state, struct oasm_token *tok)
+{
+ inst_t curinst;
+ reg_t rd;
+
+ if (state == NULL || tok == NULL) {
+ return NULL;
+ }
+
+ /* Next token should be a register */
+ tok = TAILQ_NEXT(tok, link);
+ if (tok == NULL) {
+ return NULL;
+ }
+ if (!tok_is_xreg(tok->type)) {
+ oasm_err("[emit error]: bad 'mov' order\n");
+ return NULL;
+ }
+
+ rd = ir_to_reg(tok->type);
+ if (rd == OSMX64_R_BAD) {
+ oasm_err("[emit error]: got bad reg in 'mov'\n");
+ return NULL;
+ }
+
+ /* Next token should be an IMM */
+ tok = TAILQ_NEXT(tok, link);
+ if (tok == NULL) {
+ oasm_err("[emit error]: bad 'mov' order\n");
+ return NULL;
+ }
+ if (tok->type != TT_IMM) {
+ oasm_err("[emit error]: expected <imm>\n");
+ return NULL;
+ }
+
+ curinst.opcode = OSMX64_MOV_IMM;
+ curinst.rd = rd;
+ curinst.imm = tok->imm;
+ emit_bytes(state, &curinst, sizeof(curinst));
+ return TAILQ_NEXT(tok, link);
+}
+
+/*
+ * Encode a INC/DEC instruction
+ *
+ * inc/dec [r]
+ *
+ * Returns the next token on success,
+ * otherwise NULL.
+ */
+static struct oasm_token *
+emit_encode_incdec(struct emit_state *state, struct oasm_token *tok)
+{
+ inst_t curinst;
+ reg_t rd;
+ uint8_t opcode = OSMX64_INC;
+ char *inst_str = "inc";
+
+ if (state == NULL || tok == NULL) {
+ return NULL;
+ }
+
+ if (tok->type == TT_DEC) {
+ inst_str = "dec";
+ opcode = OSMX64_DEC;
+ }
+
+ /* Next token should be a register */
+ tok = TAILQ_NEXT(tok, link);
+ if (tok == NULL) {
+ return NULL;
+ }
+ if (!tok_is_xreg(tok->type)) {
+ oasm_err("[emit error]: bad '%s' order\n", inst_str);
+ return NULL;
+ }
+
+ rd = ir_to_reg(tok->type);
+ if (rd == OSMX64_R_BAD) {
+ oasm_err("[emit error]: got bad reg in '%s'\n", inst_str);
+ return NULL;
+ }
+
+ curinst.opcode = opcode;
+ curinst.rd = rd;
+ curinst.unused = 0;
+ emit_bytes(state, &curinst, sizeof(curinst));
+ return TAILQ_NEXT(tok, link);
+}
+
+/*
+ * Encode an arithmetic instruction
+ *
+ * add [r], <imm>
+ *
+ * Returns the next token on success,
+ * otherwise NULL.
+ */
+static struct oasm_token *
+emit_encode_arith(struct emit_state *state, struct oasm_token *tok)
+{
+ inst_t curinst;
+ reg_t rd;
+ uint8_t opcode = OSMX64_ADD;
+ char *inst_str = "add";
+
+ switch (tok->type) {
+ case TT_SUB:
+ inst_str = "sub";
+ opcode = OSMX64_SUB;
+ break;
+ case TT_MUL:
+ inst_str = "mul";
+ opcode = OSMX64_MUL;
+ break;
+ case TT_DIV:
+ inst_str = "div";
+ opcode = OSMX64_DIV;
+ break;
+ }
+
+ /*
+ * The next operand must be an X<n>
+ * register.
+ */
+ tok = TAILQ_NEXT(tok, link);
+ if (tok == NULL) {
+ return NULL;
+ }
+ if (!tok_is_xreg(tok->type)) {
+ oasm_err("[emit error]: bad '%s' order\n", inst_str);
+ return NULL;
+ }
+
+ /* Get the register and validate it */
+ rd = ir_to_reg(tok->type);
+ if (rd == OSMX64_R_BAD) {
+ oasm_err("[emit error]: got bad reg in '%s'\n", inst_str);
+ return NULL;
+ }
+
+ /* The next token should be an <imm> */
+ tok = TAILQ_NEXT(tok, link);
+ if (tok == NULL) {
+ return NULL;
+ }
+ if (tok->type != TT_IMM) {
+ oasm_err("[emit error]: expected <imm> in '%s'\n", inst_str);
+ return NULL;
+ }
+
+ curinst.opcode = opcode;
+ curinst.rd = rd;
+ curinst.imm = tok->imm;
+ emit_bytes(state, &curinst, sizeof(curinst));
+ return TAILQ_NEXT(tok, link);
+}
+
+/*
+ * Encode a HLT instruction
+ *
+ * 'hlt' - no operands
+ *
+ * Returns the next token on success,
+ * otherwise NULL.
+ */
+static struct oasm_token *
+emit_encode_hlt(struct emit_state *state, struct oasm_token *tok)
+{
+ inst_t curinst;
+
+ curinst.opcode = OSMX64_HLT;
+ curinst.rd = 0;
+ curinst.unused = 0;
+ emit_bytes(state, &curinst, sizeof(curinst));
+ return TAILQ_NEXT(tok, link);
+}
+
+/*
+ * Encode a BR instruction
+ *
+ * br [r]
+ *
+ * Returns the next token on success,
+ * otherwise NULL.
+ */
+static struct oasm_token *
+emit_encode_br(struct emit_state *state, struct oasm_token *tok)
+{
+ inst_t curinst;
+ reg_t rd;
+ uint8_t opcode = OSMX64_BR;
+ char *inst_str = "br";
+
+ /* Grab the register */
+ tok = TAILQ_NEXT(tok, link);
+ if (tok == NULL) {
+ return NULL;
+ }
+ if (!tok_is_xreg(tok->type)) {
+ oasm_err("[emit error]: expected register in '%s'\n", inst_str);
+ return NULL;
+ }
+
+ rd = ir_to_reg(tok->type);
+ if (rd == OSMX64_R_BAD) {
+ oasm_err("[emit error]: got bad in register in '%s'\n", inst_str);
+ return NULL;
+ }
+
+ curinst.opcode = opcode;
+ curinst.rd = rd;
+ curinst.unused = 0;
+ emit_bytes(state, &curinst, sizeof(curinst));
+ return TAILQ_NEXT(tok, link);
+}
+
+/*
+ * Encode the MRO type instructions
+ *
+ * mrob x1[7:0]
+ * mrow x1[15:0] ! Mrowwww :3333
+ * mrod x1[31:0]
+ * mroq x[63:0]
+ *
+ * Returns the next token on success,
+ * otherwise NULL.
+ */
+static struct oasm_token *
+emit_encode_mro(struct emit_state *state, struct oasm_token *tok)
+{
+ inst_t curinst;
+ reg_t rd;
+ uint8_t opcode = OSMX64_MROB;
+ char *inst_str = "mrob";
+
+ switch (tok->type) {
+ case TT_MROW:
+ opcode = OSMX64_MROW;
+ inst_str = "mrow";
+ break;
+ case TT_MROD:
+ opcode = OSMX64_MROD;
+ inst_str = "mrod";
+ break;
+ case TT_MROQ:
+ opcode = OSMX64_MROQ;
+ inst_str = "mroq";
+ break;
+ }
+
+ /* Next token should be a register */
+ tok = TAILQ_NEXT(tok, link);
+ if (!tok_is_xreg(tok->type)) {
+ oasm_err("[emit error]: expected register in '%s'\n", inst_str);
+ return NULL;
+ }
+
+ rd = ir_to_reg(tok->type);
+ if (rd == OSMX64_R_BAD) {
+ oasm_err("[emit error]: got bad register in '%s'\n", inst_str);
+ return NULL;
+ }
+
+ /* Next token should be an IMM */
+ tok = TAILQ_NEXT(tok, link);
+ if (tok->type != TT_IMM) {
+ oasm_err("[emit error]: expected <imm> after reg in '%s'\n", inst_str);
+ return NULL;
+ }
+
+ curinst.opcode = opcode;
+ curinst.rd = rd;
+ curinst.imm = tok->imm;
+ emit_bytes(state, &curinst, sizeof(curinst));
+ return TAILQ_NEXT(tok, link);
+}
+
+/*
+ * Encode a NOP instruction
+ *
+ * 'nop' - no operands
+ *
+ * Returns the next token on success,
+ * otherwise NULL.
+ */
+static struct oasm_token *
+emit_encode_nop(struct emit_state *state, struct oasm_token *tok)
+{
+ inst_t curinst;
+
+ curinst.opcode = OSMX64_NOP;
+ curinst.rd = 0;
+ curinst.unused = 0;
+ emit_bytes(state, &curinst, sizeof(curinst));
+ return TAILQ_NEXT(tok, link);
+}
+
+/*
+ * Encode a bitwise instruction:
+ *
+ * and r, r/imm
+ * or r, r/imm
+ * xor r, r/imm
+ */
+static struct oasm_token *
+emit_encode_bitw(struct emit_state *state, struct oasm_token *tok)
+{
+ inst_t curinst;
+ imm_t imm;
+ reg_t rd;
+ uint8_t opcode = OSMX64_AND;
+ char *inst_str = "and";
+
+ switch (tok->type) {
+ case TT_OR:
+ opcode = OSMX64_OR;
+ inst_str = "or";
+ break;
+ case TT_XOR:
+ opcode = OSMX64_XOR;
+ inst_str = "xor";
+ break;
+ case TT_LSR:
+ opcode = OSMX64_LSR;
+ inst_str = "lsr";
+ break;
+ case TT_LSL:
+ opcode = OSMX64_LSL;
+ inst_str = "lsl";
+ break;
+ }
+
+ /* Next token should be a register */
+ tok = TAILQ_NEXT(tok, link);
+ if (tok == NULL) {
+ oasm_err("[emit error]: expected register for '%s'\n", inst_str);
+ return NULL;
+ }
+ if (!tok_is_xreg(tok->type)) {
+ oasm_err("[emit error]: bad register for '%s'\n", inst_str);
+ return NULL;
+ }
+
+ rd = ir_to_reg(tok->type);
+ tok = TAILQ_NEXT(tok, link);
+ if (tok == NULL) {
+ oasm_err("[emit error]: missing operand in '%s'\n", inst_str);
+ return NULL;
+ }
+
+ /*
+ * Check that the next token is an immediate
+ * value.
+ *
+ * TODO: Allow a register operand to be passed
+ * to these instructions.
+ */
+ if (tok->type != TT_IMM) {
+ oasm_err("[emit error]: expected <imm> for '%s'\n", inst_str);
+ return NULL;
+ }
+
+ imm = tok->imm;
+ curinst.opcode = opcode;
+ curinst.rd = rd;
+ curinst.imm = imm;
+ emit_bytes(state, &curinst, sizeof(curinst));
+ return TAILQ_NEXT(tok, link);
+}
+
+int
+emit_osmx64(struct emit_state *state, struct oasm_token *tp)
+{
+ struct oasm_token *toknew;
+
+ if (state == NULL || tp == NULL) {
+ return -EINVAL;
+ }
+
+ /*
+ * We need to create a copy of the object as the
+ * caller will likely end up destroying it.
+ */
+ toknew = malloc(sizeof(*toknew));
+ if (toknew == NULL) {
+ return -ENOMEM;
+ }
+
+ memcpy(toknew, tp, sizeof(*toknew));
+ TAILQ_INSERT_TAIL(&state->ir, toknew, link);
+ return 0;
+}
+
+int
+emit_init(struct emit_state *state)
+{
+ state->last_token = TT_UNKNOWN;
+ state->is_init = 1;
+ TAILQ_INIT(&state->ir);
+ return 0;
+}
+
+int
+emit_destroy(struct emit_state *state)
+{
+ struct oasm_token *curtok, *last = NULL;
+
+ TAILQ_FOREACH(curtok, &state->ir, link) {
+ if (last != NULL) {
+ free(last);
+ last = NULL;
+ }
+ if (curtok->raw != NULL) {
+ free(curtok->raw);
+ }
+
+ last = curtok;
+ }
+
+ /* Clean up any last objects */
+ if (last != NULL) {
+ free(last);
+ }
+
+ return 0;
+}
+
+int
+emit_process(struct oasm_state *oasm, struct emit_state *emit)
+{
+ struct oasm_token *curtok;
+ tt_t last_tok;
+
+ if (!emit->is_init) {
+ return -1;
+ }
+
+ emit->out_fd = oasm->out_fd;
+ curtok = TAILQ_FIRST(&emit->ir);
+ while (curtok != NULL) {
+ switch (curtok->type) {
+ case TT_NOP:
+ curtok = emit_encode_nop(emit, curtok);
+ break;
+ case TT_MOV:
+ curtok = emit_encode_mov(emit, curtok);
+ break;
+ case TT_INC:
+ case TT_DEC:
+ curtok = emit_encode_incdec(emit, curtok);
+ break;
+ case TT_ADD:
+ case TT_SUB:
+ case TT_MUL:
+ case TT_DIV:
+ curtok = emit_encode_arith(emit, curtok);
+ break;
+ case TT_AND:
+ case TT_OR:
+ case TT_XOR:
+ case TT_LSR:
+ case TT_LSL:
+ curtok = emit_encode_bitw(emit, curtok);
+ break;
+ case TT_BR:
+ curtok = emit_encode_br(emit, curtok);
+ break;
+ case TT_HLT:
+ curtok = emit_encode_hlt(emit, curtok);
+ break;
+ default:
+ if (tok_is_mro(curtok->type)) {
+ curtok = emit_encode_mro(emit, curtok);
+ break;
+ }
+ curtok = TAILQ_NEXT(curtok, link);
+ break;
+ }
+ }
+
+ return 0;
+}
diff --git a/usr.bin/oasm/include/oasm/emit.h b/usr.bin/oasm/include/oasm/emit.h
new file mode 100644
index 0000000..57683a8
--- /dev/null
+++ b/usr.bin/oasm/include/oasm/emit.h
@@ -0,0 +1,120 @@
+/* Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _EMIT_H_
+#define _EMIT_H_
+
+#include <sys/queue.h>
+#include <stdint.h>
+#include <stddef.h>
+#include <oasm/lex.h>
+#include <oasm/state.h>
+
+/*
+ * The OSMX64 architecture has 32-bit instructions
+ * that are encoded in the following manner:
+ *
+ * - [0:7]: Opcode
+ * - [11:8]: Register
+ * - [31:12]: Reserved
+ *
+ * The values below define various operation
+ * codes.
+ */
+#define OSMX64_NOP 0x00 /* No-operation */
+#define OSMX64_ADD 0x01 /* Add operation */
+#define OSMX64_SUB 0x02 /* Sub operation */
+#define OSMX64_MUL 0x03 /* Multiply operation */
+#define OSMX64_DIV 0x04 /* Divide operation */
+#define OSMX64_INC 0x05 /* Increment operation */
+#define OSMX64_DEC 0x06 /* Decrement operation */
+#define OSMX64_OR 0x07 /* Bitwise OR operation */
+#define OSMX64_XOR 0x08 /* Bitwise XOR operation */
+#define OSMX64_AND 0x09 /* Bitwise AND operation */
+#define OSMX64_NOT 0x0A /* Bitwise NOT operation */
+#define OSMX64_SLL 0x0B /* Shift left logical operation */
+#define OSMX64_SRL 0x0C /* Shift right logical operation */
+#define OSMX64_MOV_IMM 0x0D /* Data move operation from IMM */
+#define OSMX64_HLT 0x0E /* Halt the processor */
+#define OSMX64_BR 0x0F /* Branch */
+#define OSMX64_MROB 0x10 /* Mask register over byte */
+#define OSMX64_MROW 0x11 /* Mask register over word */
+#define OSMX64_MROD 0x12 /* Mask register over dword */
+#define OSMX64_MROQ 0x13 /* Mask register over qword */
+#define OSMX64_LSR 0x14 /* Logical shift right */
+#define OSMX64_LSL 0x15 /* Logical shift left */
+
+/*
+ * OSMX64 register definitions
+ */
+#define OSMX64_R_X0 0x00
+#define OSMX64_R_X1 0x01
+#define OSMX64_R_X2 0x02
+#define OSMX64_R_X3 0x03
+#define OSMX64_R_X4 0x04
+#define OSMX64_R_X5 0x05
+#define OSMX64_R_X6 0x06
+#define OSMX64_R_X7 0x07
+#define OSMX64_R_X8 0x08
+#define OSMX64_R_X9 0x09
+#define OSMX64_R_X10 0x0A
+#define OSMX64_R_X11 0x0B
+#define OSMX64_R_X12 0x0C
+#define OSMX64_R_X13 0x0D
+#define OSMX64_R_X14 0x0E
+#define OSMX64_R_X15 0x0F
+#define OSMX64_R_BAD 0xFF
+
+typedef uint8_t reg_t;
+typedef uint16_t imm_t;
+
+/*
+ * OSMX64 instruction
+ */
+typedef struct {
+ uint8_t opcode;
+ uint8_t rd;
+ union {
+ uint16_t imm;
+ uint16_t unused;
+ };
+} inst_t;
+
+struct emit_state {
+ tt_t last_token;
+ uint8_t is_init : 1;
+ int out_fd;
+ TAILQ_HEAD(, oasm_token) ir;
+};
+
+int emit_init(struct emit_state *state);
+int emit_destroy(struct emit_state *state);
+int emit_process(struct oasm_state *oasm, struct emit_state *emit);
+int emit_osmx64(struct emit_state *state, struct oasm_token *tp);
+
+#endif /* !_EMIT_H_ */
diff --git a/usr.bin/oasm/include/oasm/label.h b/usr.bin/oasm/include/oasm/label.h
new file mode 100644
index 0000000..8acb369
--- /dev/null
+++ b/usr.bin/oasm/include/oasm/label.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _OASM_LABEL_H_
+#define _OASM_LABEL_H_
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#define MAX_LABELS 128
+
+/*
+ * Represents a label
+ *
+ * @name: Label name (e.g., _start)
+ * @ip: Address at which label code starts
+ */
+struct oasm_label {
+ char *name;
+ uintptr_t ip;
+ TAILQ_ENTRY(oasm_label) link;
+ TAILQ_HEAD(, oasm_label) buckets;
+};
+
+void labels_destroy(void);
+int label_enter(const char *name, uintptr_t ip);
+struct oasm_label *label_lookup(const char *name);
+
+#endif /* !_OASM_LABEL_H_ */
diff --git a/usr.bin/oasm/include/oasm/lex.h b/usr.bin/oasm/include/oasm/lex.h
new file mode 100644
index 0000000..93422a6
--- /dev/null
+++ b/usr.bin/oasm/include/oasm/lex.h
@@ -0,0 +1,188 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _OASM_LEX_H_
+#define _OASM_LEX_H_
+
+#include <sys/queue.h>
+#include <sys/cdefs.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+struct oasm_state;
+
+#define __XN_REGS \
+ TT_X0, \
+ TT_X1, \
+ TT_X2, \
+ TT_X3, \
+ TT_X4, \
+ TT_X5, \
+ TT_X6, \
+ TT_X7, \
+ TT_X8, \
+ TT_X9, \
+ TT_X10, \
+ TT_X11, \
+ TT_X12, \
+ TT_X13, \
+ TT_X14, \
+ TT_X15
+
+#define __FN_REGS \
+ TT_F0, \
+ TT_F1, \
+ TT_F2, \
+ TT_F3, \
+ TT_F4, \
+ TT_F5, \
+ TT_F6, \
+ TT_F7
+
+#define __DN_REGS \
+ TT_D0, \
+ TT_D1, \
+ TT_D2, \
+ TT_D3, \
+ TT_D4, \
+ TT_D5, \
+ TT_D6, \
+ TT_D7
+
+#define __VN_REGS \
+ TT_V0, \
+ TT_V1, \
+ TT_V2, \
+ TT_V3, \
+ TT_V4, \
+ TT_V5, \
+ TT_V6, \
+ TT_V7
+
+/*
+ * Token type definitions
+ */
+typedef enum {
+ TT_UNKNOWN, /* Unknown token */
+ TT_NOP, /* No operation */
+
+ /* Arithmetic instructions */
+ TT_ADD, /* 'add' */
+ TT_SUB, /* 'sub' */
+ TT_MUL, /* 'mul' */
+ TT_DIV, /* 'div' */
+ TT_HLT, /* 'hlt' */
+ TT_BR, /* 'br' */
+ TT_MROB, /* 'mrob' */
+ TT_MROW, /* 'mrow' */
+ TT_MROD, /* 'mrod' */
+ TT_MROQ, /* 'mroq' */
+ TT_AND, /* 'and' */
+ TT_OR, /* 'or' */
+ TT_XOR, /* 'xor' */
+ TT_LSR, /* 'lsr' */
+ TT_LSL, /* 'lsl' */
+
+ /* Register ops */
+ TT_MOV, /* 'mov' */
+ TT_INC, /* 'inc' */
+ TT_DEC, /* 'dec' */
+ TT_IMM, /* #<n> */
+ TT_LABEL, /* 'label: ...' */
+
+ /* Register sets */
+ __XN_REGS, /* x0-x15 */
+ __FN_REGS, /* f0-f7 */
+ __DN_REGS, /* d0-d7 */
+ __VN_REGS, /* v0-v7 */
+
+ /* Symbols */
+ TT_COMMA, /* ',' */
+} tt_t;
+
+struct oasm_token {
+ tt_t type;
+ uint8_t is_reg : 1;
+ uint16_t imm;
+ char *raw;
+ TAILQ_ENTRY(oasm_token) link;
+};
+
+int lex_tok(struct oasm_state *state, struct oasm_token *ttp);
+
+
+/*
+ * Check if a token is an X<n> register.
+ * Returns true on match.
+ */
+__always_inline static inline bool
+tok_is_xreg(tt_t tok)
+{
+ switch (tok) {
+ case TT_X0:
+ case TT_X1:
+ case TT_X2:
+ case TT_X3:
+ case TT_X4:
+ case TT_X5:
+ case TT_X6:
+ case TT_X7:
+ case TT_X8:
+ case TT_X9:
+ case TT_X10:
+ case TT_X11:
+ case TT_X12:
+ case TT_X13:
+ case TT_X14:
+ case TT_X15:
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * Check if a token is of an MRO type
+ * instruction. Returns true on match.
+ */
+__always_inline static inline bool
+tok_is_mro(tt_t tok)
+{
+ switch (tok) {
+ case TT_MROB:
+ case TT_MROW:
+ case TT_MROD:
+ case TT_MROQ:
+ return true;
+ }
+
+ return false;
+}
+
+#endif /* !_OASM_LEX_H_ */
diff --git a/usr.bin/oasm/include/oasm/log.h b/usr.bin/oasm/include/oasm/log.h
new file mode 100644
index 0000000..330c273
--- /dev/null
+++ b/usr.bin/oasm/include/oasm/log.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _OASM_LOG_H_
+#define _OASM_LOG_H_
+
+#include <sys/types.h>
+#include <sys/cdefs.h>
+#include <stdio.h>
+
+#define ERROR_COLOR "\033[31;40m"
+#define WARN_COLOR "\033[35;40m"
+
+void __oasm_debug(const char *fmt, ...);
+void __oasm_err(const char *fmt, ...);
+void __oasm_warn(const char *fmt, ...);
+
+#define oasm_debug(...) __oasm_debug(__VA_ARGS__)
+#define oasm_err(...) __oasm_err(__VA_ARGS__)
+#define oasm_warn(...) __oasm_warn(__VA_ARGS__)
+
+#endif /* !_OASM_LOG_H_ */
diff --git a/usr.bin/oasm/include/oasm/parse.h b/usr.bin/oasm/include/oasm/parse.h
new file mode 100644
index 0000000..04962e7
--- /dev/null
+++ b/usr.bin/oasm/include/oasm/parse.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _OASM_PARSE_H_
+#define _OASM_PARSE_H_
+
+#include <oasm/state.h>
+
+void parse_enter(struct oasm_state *state);
+
+#endif /* !_OASM_PARSE_H_ */
diff --git a/usr.bin/oasm/include/oasm/state.h b/usr.bin/oasm/include/oasm/state.h
new file mode 100644
index 0000000..6dd2435
--- /dev/null
+++ b/usr.bin/oasm/include/oasm/state.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _OASM_STATE_H_
+#define _OASM_STATE_H_
+
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <oasm/lex.h>
+
+/*
+ * OASM state:
+ *
+ * @filename: Filname of unit we are parsing
+ * @pip: Pseudo instruction pointer
+ * @label_ip: IP at current label start
+ * @in_fd: Input file descriptor
+ * @out_fd: Resulting binary output file descriptor
+ * @line: Current line number
+ * @last: Last token
+ */
+struct oasm_state {
+ char *filename;
+ off_t pip;
+ off_t label_ip;
+ int in_fd;
+ int out_fd;
+ off_t line;
+ tt_t last;
+};
+
+#endif /* !_OASM_STATE_H_ */
diff --git a/usr.bin/oasm/label.c b/usr.bin/oasm/label.c
new file mode 100644
index 0000000..2647bb9
--- /dev/null
+++ b/usr.bin/oasm/label.c
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/queue.h>
+#include <oasm/label.h>
+#include <oasm/log.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stddef.h>
+#include <stdbool.h>
+
+static struct oasm_label *labels[MAX_LABELS];
+static size_t label_count = 0;
+
+static uint32_t
+fnv1_hash(const char *s)
+{
+ uint32_t hash = 2166136261UL;
+ const uint8_t *p = (uint8_t *)s;
+
+ while (*p != '\0') {
+ hash ^= *p;
+ hash = hash * 0x01000193;
+ ++p;
+ }
+
+ return hash;
+}
+
+/*
+ * The label table is a big hashmap containing
+ * label entries. This function creates and add
+ * a new label into the table.
+ *
+ * @name: Name of the label (e.g., _start)
+ * @ip: Instruction pointer
+ */
+int
+label_enter(const char *name, uintptr_t ip)
+{
+ uint32_t hash = fnv1_hash(name);
+ uint32_t idx = hash % MAX_LABELS;
+ struct oasm_label *lp, *lp_new;
+
+ if (label_count >= MAX_LABELS) {
+ oasm_err("[internal error]: too many labels\n");
+ return -EIO;
+ }
+
+ lp_new = malloc(sizeof(*lp_new));
+ if (lp_new == NULL) {
+ oasm_err("[internal error]: out of memory\n");
+ return -ENOMEM;
+ }
+
+ /* Initialize the label */
+ lp_new->name = strdup(name);
+ lp_new->ip = ip;
+ TAILQ_INIT(&lp_new->buckets);
+
+ /*
+ * If there is no existing entry here, we
+ * can take this slot.
+ */
+ lp = labels[idx];
+ if (lp == NULL) {
+ labels[idx] = lp_new;
+ ++label_count;
+ return 0;
+ }
+
+ /*
+ * To prevent collisions in our table here,
+ * we must check if the name matches at all.
+ * If it does not, there is a collision and
+ * we'll have to add this to a bucket.
+ */
+ if (strcmp(name, lp->name) != 0) {
+ TAILQ_INSERT_TAIL(&lp->buckets, lp_new, link);
+ ++label_count;
+ return 0;
+ }
+
+ /* Can't put the same entry in twice */
+ oasm_err("[internal error]: duplicate labels\n");
+ return -EEXIST;
+}
+
+/*
+ * Find a label entry in the label table.
+ *
+ * @name: Name of the label to lookup (e.g., _start)
+ */
+struct oasm_label *
+label_lookup(const char *name)
+{
+ uint32_t hash = fnv1_hash(name);
+ uint32_t idx = hash % MAX_LABELS;
+ struct oasm_label *lp, *lp_tmp;
+
+ lp = labels[idx];
+ if (lp == NULL) {
+ return NULL;
+ }
+
+ /* Is this the label we are looking up? */
+ if (strcmp(name, lp->name) == 0) {
+ return lp;
+ }
+
+ /* Maybe there was a collision? */
+ TAILQ_FOREACH(lp_tmp, &lp->buckets, link) {
+ if (strcmp(name, lp_tmp->name) == 0) {
+ return lp_tmp;
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * Clean up all allocated labels by
+ * calling free() on each entry of
+ * the queue.
+ */
+void
+labels_destroy(void)
+{
+ struct oasm_label *lp;
+
+ for (size_t i = 0; i < MAX_LABELS; ++i) {
+ lp = labels[i];
+ if (lp != NULL) {
+ free(lp->name);
+ free(lp);
+ }
+ }
+}
diff --git a/usr.bin/oasm/lex.c b/usr.bin/oasm/lex.c
new file mode 100644
index 0000000..1f58d07
--- /dev/null
+++ b/usr.bin/oasm/lex.c
@@ -0,0 +1,445 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <oasm/state.h>
+#include <oasm/lex.h>
+#include <oasm/log.h>
+
+#define COMMENT '!'
+#define is_num(c) ((c) >= '0' && (c) <= '9')
+
+static char putback = '\0';
+
+/* Instruction mnemonic strings */
+#define S_IMN_NOP "nop"
+#define S_IMN_MOV "mov"
+#define S_IMN_ADD "add"
+#define S_IMN_SUB "sub"
+#define S_IMN_MUL "mul"
+#define S_IMN_DIV "div"
+#define S_IMN_INC "inc"
+#define S_IMN_DEC "dec"
+#define S_IMN_HLT "hlt"
+#define S_IMN_BR "br"
+#define S_IMN_MROB "mrob"
+#define S_IMN_MROW "mrow"
+#define S_IMN_MROD "mrod"
+#define S_IMN_MROQ "mroq"
+#define S_IMN_AND "and"
+#define S_IMN_OR "or"
+#define S_IMN_XOR "xor"
+#define S_IMN_LSL "lsl"
+#define S_IMN_LSR "lsr"
+
+/* Instruction length */
+#define OSMX64_INST_LEN 4
+
+/*
+ * Update the state when the caller encounters
+ * a newline.
+ */
+static inline void
+lex_newline(struct oasm_state *state)
+{
+ ++state->line;
+ state->pip += OSMX64_INST_LEN;
+}
+
+/*
+ * Returns 0 if a char is counted as a
+ * skippable token. Otherwise, -1
+ */
+static inline int
+lex_skippable(struct oasm_state *state, char c)
+{
+ switch (c) {
+ case ' ': return 0;
+ case '\f': return 0;
+ case '\t': return 0;
+ case '\r': return 0;
+ case '\n':
+ lex_newline(state);
+ return 0;
+ }
+
+ return -1;
+}
+
+/*
+ * For cleaning up allocated sources
+ * during error conditions
+ *
+ * @p: Memory to free
+ */
+static inline void
+lex_try_free(void *p)
+{
+ if (p != NULL) {
+ free(p);
+ }
+}
+
+/*
+ * Put back a token to grab later
+ *
+ * @c: Character to put back
+ */
+static inline char
+lex_putback(char c)
+{
+ putback = c;
+ return c;
+}
+
+/*
+ * Grab a character from the input file
+ * descriptor.
+ */
+static char
+lex_cin(struct oasm_state *state)
+{
+ char retval;
+
+ if (putback != '\0') {
+ retval = putback;
+ putback = '\0';
+ return retval;
+ }
+
+ if (read(state->in_fd, &retval, 1) <= 0) {
+ return '\0';
+ }
+ return retval;
+}
+
+/*
+ * Nom an operation, directive or any kind
+ * of raw string (unquoted/builtin) and return
+ * memory allocated by strdup() pointing to the
+ * string.
+ *
+ * @state: OASM state pointer
+ * @res: Resulting string
+ *
+ * Returns 0 on success. Greater than zero
+ * value of the last character if a comma or
+ * space was not buffered.
+ */
+static int
+lex_nomstr(struct oasm_state *state, char **res)
+{
+ char buf[256];
+ int retval = 0, n = 0;
+ int tmp;
+
+ memset(buf, 0, sizeof(buf));
+
+ /*
+ * We are filling the buffer containing
+ * the operation or directive.
+ *
+ * Keep going until we hit a space or comman (^)
+ * Examples of such strings (everything in '[]'):
+ *
+ * [mov] [x0], [#1]
+ * ^ ^
+ */
+ while ((tmp = lex_cin(state)) != 0) {
+ if (tmp == ' ' || tmp == ',') {
+ retval = tmp;
+ break;
+ }
+ if (tmp == ':') {
+ retval = tmp;
+ break;
+ }
+ if (tmp == '\n') {
+ ++state->line;
+ retval = tmp;
+ break;
+
+ }
+
+ buf[n++] = tmp;
+ }
+
+ *res = strdup(buf);
+ return retval;
+}
+
+static tt_t
+token_arith(char *p)
+{
+ if (strcmp(p, S_IMN_MOV) == 0) {
+ return TT_MOV;
+ } else if (strcmp(p, S_IMN_INC) == 0) {
+ return TT_INC;
+ } else if (strcmp(p, S_IMN_DEC) == 0) {
+ return TT_DEC;
+ } else if (strcmp(p, S_IMN_ADD) == 0) {
+ return TT_ADD;
+ } else if (strcmp(p, S_IMN_SUB) == 0) {
+ return TT_SUB;
+ } else if (strcmp(p, S_IMN_DIV) == 0) {
+ return TT_DIV;
+ } else if (strcmp(p, S_IMN_HLT) == 0) {
+ return TT_HLT;
+ } else if (strcmp(p, S_IMN_MUL) == 0) {
+ return TT_MUL;
+ } else if (strcmp(p, S_IMN_XOR) == 0) {
+ return TT_XOR;
+ }
+
+ return TT_UNKNOWN;
+}
+
+/*
+ * Control flow instructions
+ */
+static tt_t
+token_cfi(char *p)
+{
+ if (strcmp(p, S_IMN_BR) == 0) {
+ return TT_BR;
+ }
+
+ return TT_UNKNOWN;
+}
+
+/*
+ * Bitwise MRO instructions
+ */
+static tt_t
+token_bitw_mro(char *p)
+{
+ if (strcmp(p, S_IMN_MROB) == 0) {
+ return TT_MROB;
+ } else if (strcmp(p, S_IMN_MROW) == 0) {
+ return TT_MROW;
+ } else if (strcmp(p, S_IMN_MROD) == 0) {
+ return TT_MROD;
+ } else if (strcmp(p, S_IMN_MROQ) == 0) {
+ return TT_MROQ;
+ } else if (strcmp(p, S_IMN_AND) == 0) {
+ return TT_AND;
+ } else if (strcmp(p, S_IMN_OR) == 0) {
+ return TT_OR;
+ }
+
+ return TT_UNKNOWN;
+}
+
+/*
+ * Bitwise instructions
+ */
+static tt_t
+token_bitw(char *p)
+{
+ tt_t token;
+
+ token = token_bitw_mro(p);
+ if (token != TT_UNKNOWN) {
+ return token;
+ }
+
+ if (strcmp(p, S_IMN_LSL) == 0) {
+ return TT_LSL;
+ } else if (strcmp(p, S_IMN_LSR) == 0) {
+ return TT_LSR;
+ }
+
+ return TT_UNKNOWN;
+}
+
+static tt_t
+token_xreg(char *p)
+{
+ int num;
+
+ if (p[0] != 'x') {
+ return TT_UNKNOWN;
+ }
+
+ if (!is_num(p[1])) {
+ return TT_UNKNOWN;
+ }
+
+ num = atoi(&p[1]);
+ switch (num) {
+ case 0: return TT_X0;
+ case 1: return TT_X1;
+ case 2: return TT_X2;
+ case 3: return TT_X3;
+ case 4: return TT_X4;
+ case 5: return TT_X5;
+ case 6: return TT_X6;
+ case 7: return TT_X7;
+ case 8: return TT_X8;
+ case 9: return TT_X9;
+ case 10: return TT_X10;
+ case 11: return TT_X11;
+ case 12: return TT_X12;
+ case 13: return TT_X13;
+ case 14: return TT_X14;
+ case 15: return TT_X15;
+ }
+
+ return TT_UNKNOWN;
+}
+
+static tt_t
+token_operand(char *p)
+{
+ /* Is this a numeric constant? */
+ if (p[0] == '#') {
+ return TT_IMM;
+ }
+
+ return TT_UNKNOWN;
+}
+
+static tt_t
+token_reg(char *p)
+{
+ tt_t tok;
+
+ if ((tok = token_xreg(p)) != TT_UNKNOWN) {
+ return tok;
+ }
+
+ return TT_UNKNOWN;
+}
+
+int
+lex_tok(struct oasm_state *state, struct oasm_token *ttp)
+{
+ char *p = NULL;
+ char c = ' ';
+ short in_comment = 0;
+ int tmp;
+ tt_t tok;
+
+ if (state == NULL || ttp == NULL) {
+ return -EINVAL;
+ }
+
+ /*
+ * Grab characters. If they are skippable or
+ * comments, don't use them.
+ */
+ while (lex_skippable(state, c) == 0 || in_comment) {
+ if ((c = lex_cin(state)) == 0) {
+ return -1;
+ }
+
+ if (c == COMMENT) {
+ in_comment = 1;
+ } else if (c == '\n') {
+ in_comment = 0;
+ }
+ }
+
+ switch (c) {
+ case '\n':
+ lex_newline(state);
+ return 0;
+ case '\0':
+ return -1;
+ case ',':
+ return TT_COMMA;
+ default:
+ ttp->type = TT_UNKNOWN;
+ ttp->raw = NULL;
+
+ lex_putback(c);
+ c = lex_nomstr(state, &p);
+
+ while (c == ':') {
+ ttp->type = TT_LABEL;
+ ttp->raw = p;
+ state->label_ip = state->pip;
+ return 0;
+ }
+
+ /* No operation? */
+ if (strcmp(p, S_IMN_NOP) == 0) {
+ ttp->type = TT_NOP;
+ ttp->raw = p;
+ return 0;
+ }
+
+ /* Arithmetic operation? */
+ if ((tok = token_arith(p)) != TT_UNKNOWN) {
+ ttp->type = tok;
+ ttp->raw = p;
+ return 0;
+ }
+
+ /* Control flow instruction? */
+ if ((tok = token_cfi(p)) != TT_UNKNOWN) {
+ ttp->type = tok;
+ ttp->raw = p;
+ return 0;
+ }
+
+ /* Register? */
+ if ((tok = token_reg(p)) != TT_UNKNOWN) {
+ ttp->is_reg = 1;
+ ttp->type = tok;
+ ttp->raw = p;
+ return 0;
+ }
+
+ if ((tok = token_bitw(p)) != TT_UNKNOWN) {
+ ttp->type = tok;
+ ttp->raw = p;
+ return 0;
+ }
+
+ /* Immediate operand? */
+ if ((tok = token_operand(p)) != TT_UNKNOWN) {
+ if (tok == TT_IMM) {
+ ttp->imm = atoi(&p[1]);
+ }
+
+ ttp->type = tok;
+ ttp->raw = p;
+ return 0;
+ }
+
+ oasm_err("bad token \"%s\"\n", p);
+ lex_try_free(p);
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/usr.bin/oasm/log.c b/usr.bin/oasm/log.c
new file mode 100644
index 0000000..c408865
--- /dev/null
+++ b/usr.bin/oasm/log.c
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <oasm/log.h>
+#include <oasm/state.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+/* TODO FIXME: Use stdarg.h */
+#define __va_start(ap, fmt) __builtin_va_start(ap, fmt)
+#define __va_end(ap) __builtin_va_end(ap)
+
+extern struct oasm_state g_state;
+
+void
+oasm_debug(const char *fmt, ...)
+{
+ char buf[512];
+ va_list ap;
+ int ret;
+
+ __va_start(ap, fmt);
+ ret = vsnprintf(buf, sizeof(buf), fmt, ap);
+ printf("[debug]: %s\033[0m", buf);
+ printf("\033[0m");
+ __va_end(ap);
+}
+
+void
+oasm_err(const char *fmt, ...)
+{
+ char buf[512];
+ va_list ap;
+ int ret;
+
+ __va_start(ap, fmt);
+ ret = vsnprintf(buf, sizeof(buf), fmt, ap);
+ printf(ERROR_COLOR "error: %s\033[0m", buf);
+ printf("%s: line %d\n", g_state.filename, g_state.line);
+ __va_end(ap);
+}
+
+void
+oasm_warn(const char *fmt, ...)
+{
+ char buf[512];
+ va_list ap;
+ int ret;
+
+ __va_start(ap, fmt);
+ ret = vsnprintf(buf, sizeof(buf), fmt, ap);
+ printf(WARN_COLOR "warning: %s\033[0m", buf);
+ printf("line %d\n", g_state.filename, g_state.line);
+ __va_end(ap);
+}
diff --git a/usr.bin/oasm/oasm.c b/usr.bin/oasm/oasm.c
new file mode 100644
index 0000000..6c37778
--- /dev/null
+++ b/usr.bin/oasm/oasm.c
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <oasm/state.h>
+#include <oasm/parse.h>
+#define OASM_DBG
+#include <oasm/log.h>
+
+struct oasm_state g_state;
+
+static void
+oasm_start(struct oasm_state *state)
+{
+ state->line = 1;
+ parse_enter(state);
+}
+
+int
+main(int argc, char **argv)
+{
+ if (argc < 3) {
+ printf("oasm: usage: oasm <file> <output>\n");
+ return -1;
+ }
+
+ g_state.in_fd = open(argv[1], O_RDONLY);
+ if (g_state.in_fd < 0) {
+ printf("could not open \"%s\"\n", argv[1]);
+ return -1;
+ }
+
+ g_state.out_fd = open(argv[2], O_CREAT | O_WRONLY);
+ if (g_state.out_fd < 0) {
+ printf("could not open output \"%s\"\n", argv[2]);
+ close(g_state.in_fd);
+ return -1;
+ }
+
+ g_state.filename = argv[1];
+ oasm_start(&g_state);
+ close(g_state.in_fd);
+ close(g_state.out_fd);
+ return 0;
+}
diff --git a/usr.bin/oasm/parse.c b/usr.bin/oasm/parse.c
new file mode 100644
index 0000000..042cce8
--- /dev/null
+++ b/usr.bin/oasm/parse.c
@@ -0,0 +1,285 @@
+/* Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdint.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <oasm/emit.h>
+#include <oasm/state.h>
+#include <oasm/lex.h>
+#include <oasm/parse.h>
+#include <oasm/log.h>
+#include <oasm/label.h>
+
+static struct emit_state emit_state;
+static const char *tokstr[] = {
+ [ TT_UNKNOWN] = "bad",
+ [ TT_NOP ] = "nop",
+ [ TT_ADD ] = "add",
+ [ TT_SUB ] = "sub",
+ [ TT_MUL ] = "mul",
+ [ TT_DIV ] = "div",
+ [ TT_HLT ] = "hlt",
+ [ TT_BR ] = "br",
+ [ TT_COMMA ] = ",",
+ [ TT_INC ] = "inc",
+ [ TT_DEC ] = "dec",
+ [ TT_MOV ] = "mov",
+ [ TT_IMM ] = "<imm>",
+ [ TT_LABEL ] = "<label>",
+ [ TT_LSR ] = "lsr",
+ [ TT_LSL ] = "lsl",
+
+ /* Bitwise */
+ [ TT_MROB ] = "mrob",
+ [ TT_MROW ] = "mrow",
+ [ TT_MROD ] = "mrod",
+ [ TT_MROQ ] = "mroq",
+ [ TT_AND ] = "and",
+ [ TT_OR ] = "or",
+ [ TT_XOR ] = "xor",
+
+ /* X<n> registers */
+ [ TT_X0 ] = "x0",
+ [ TT_X1 ] = "x1",
+ [ TT_X2 ] = "x2",
+ [ TT_X3 ] = "x3",
+ [ TT_X4 ] = "x4",
+ [ TT_X5 ] = "x5",
+ [ TT_X6 ] = "x6",
+ [ TT_X7 ] = "x7",
+ [ TT_X8 ] = "x8",
+ [ TT_X9 ] = "x9",
+ [ TT_X10 ] = "x10",
+ [ TT_X11 ] = "x11",
+ [ TT_X12 ] = "x12",
+ [ TT_X13 ] = "x13",
+ [ TT_X14 ] = "x14",
+ [ TT_X15 ] = "x15",
+
+ /* V<n> registers */
+ [ TT_F0 ] = "v0",
+ [ TT_F1 ] = "v1",
+ [ TT_F2 ] = "v2",
+ [ TT_F3 ] = "v3",
+ [ TT_F4 ] = "v4",
+ [ TT_F5 ] = "v5",
+ [ TT_F6 ] = "v6",
+ [ TT_F7 ] = "v7",
+
+ /* D<n> registers */
+ [ TT_D0 ] = "d0",
+ [ TT_D1 ] = "d1",
+ [ TT_D2 ] = "d2",
+ [ TT_D3 ] = "d3",
+ [ TT_D4 ] = "d4",
+ [ TT_D5 ] = "d5",
+ [ TT_D6 ] = "d6",
+ [ TT_D7 ] = "d7",
+
+ /* V<n> registers */
+ [ TT_V0 ] = "v0",
+ [ TT_V1 ] = "v1",
+ [ TT_V2 ] = "v2",
+ [ TT_V3 ] = "v3",
+ [ TT_V4 ] = "v4",
+ [ TT_V5 ] = "v5",
+ [ TT_V6 ] = "v6",
+ [ TT_V7 ] = "v7",
+};
+
+static int
+parse_reg(struct oasm_state *state, struct oasm_token *tok)
+{
+ const char *p;
+
+ /* Valid instructions that go with regs */
+ switch (state->last) {
+ case TT_MOV:
+ case TT_DEC:
+ case TT_INC:
+ case TT_ADD:
+ case TT_SUB:
+ case TT_MUL:
+ case TT_DIV:
+ case TT_BR:
+ case TT_AND:
+ case TT_OR:
+ case TT_XOR:
+ case TT_LSR:
+ case TT_LSL:
+ state->last = tok->type;
+ break;
+ default:
+ if (tok_is_mro(state->last)) {
+ break;
+ }
+
+ p = tokstr[state->last];
+ oasm_err("bad token '%s' for regop\n", p);
+ return -1;
+ }
+
+ if (!tok_is_xreg(tok->type)) {
+ p = tokstr[tok->type];
+ oasm_err("bad register \"%s\"\n", p);
+ return -1;
+ }
+
+ state->last = tok->type;
+ emit_osmx64(&emit_state, tok);
+ return 0;
+}
+
+static int
+parse_tok(struct oasm_state *state, struct oasm_token *tok)
+{
+ const char *p;
+ int error;
+
+ switch (tok->type) {
+ case TT_NOP:
+ state->last = tok->type;
+ emit_osmx64(&emit_state, tok);
+ break;
+ case TT_BR:
+ state->last = tok->type;
+ emit_osmx64(&emit_state, tok);
+ break;
+ case TT_LABEL:
+ state->last = tok->type;
+ label_enter(tok->raw, state->pip);
+ break;
+ case TT_AND:
+ state->last = tok->type;
+ emit_osmx64(&emit_state, tok);
+ break;
+ case TT_OR:
+ state->last = tok->type;
+ emit_osmx64(&emit_state, tok);
+ break;
+ case TT_XOR:
+ state->last = tok->type;
+ emit_osmx64(&emit_state, tok);
+ break;
+ case TT_HLT:
+ state->last = tok->type;
+ emit_osmx64(&emit_state, tok);
+ break;
+ case TT_MUL:
+ state->last = tok->type;
+ emit_osmx64(&emit_state, tok);
+ break;
+ case TT_DIV:
+ state->last = tok->type;
+ emit_osmx64(&emit_state, tok);
+ break;
+ case TT_LSR:
+ state->last = tok->type;
+ emit_osmx64(&emit_state, tok);
+ break;
+ case TT_LSL:
+ state->last = tok->type;
+ emit_osmx64(&emit_state, tok);
+ break;
+ case TT_MOV:
+ state->last = tok->type;
+ emit_osmx64(&emit_state, tok);
+ break;
+ case TT_ADD:
+ state->last = tok->type;
+ emit_osmx64(&emit_state, tok);
+ break;
+ case TT_SUB:
+ state->last = tok->type;
+ emit_osmx64(&emit_state, tok);
+ break;
+ case TT_DEC:
+ case TT_INC:
+ state->last = tok->type;
+ emit_osmx64(&emit_state, tok);
+ break;
+ case TT_IMM:
+ p = tokstr[state->last];
+ if (!tok_is_xreg(state->last)) {
+ oasm_err("expected X<n> but got %s\n", p);
+ return -1;
+ }
+ emit_osmx64(&emit_state, tok);
+ break;
+ default:
+ if (tok_is_mro(tok->type)) {
+ state->last = tok->type;
+ emit_osmx64(&emit_state, tok);
+ return 0;
+ }
+
+ if (!tok->is_reg) {
+ oasm_err("syntax error\n");
+ return -1;
+ }
+
+ error = parse_reg(state, tok);
+ if (error < 0) {
+ return error;
+ }
+ break;
+ }
+
+ return 0;
+}
+
+void
+parse_enter(struct oasm_state *state)
+{
+ struct oasm_token tok;
+ const char *type, *raw;
+ int error = 0;
+
+ emit_init(&emit_state);
+
+ for (;;) {
+ error = lex_tok(state, &tok);
+ if (error < 0) {
+ break;
+ }
+
+ if (parse_tok(state, &tok) < 0) {
+ break;
+ }
+
+ type = tokstr[tok.type];
+ raw = tok.raw;
+ oasm_debug("got token type %s (%s)\n", type, raw);
+ }
+
+ /* Process then destroy the emit state */
+ emit_process(state, &emit_state);
+ emit_destroy(&emit_state);
+ labels_destroy();
+}
diff --git a/usr.bin/oemu/Makefile b/usr.bin/oemu/Makefile
new file mode 100644
index 0000000..366208c
--- /dev/null
+++ b/usr.bin/oemu/Makefile
@@ -0,0 +1,7 @@
+include user.mk
+
+CFILES = $(shell find . -name "*.c")
+CFLAGS = -Iinclude/
+
+$(ROOT)/base/usr/bin/oemu:
+ gcc $(CFILES) -o $@ $(INTERNAL_CFLAGS) $(CFLAGS)
diff --git a/usr.bin/oemu/cpu.c b/usr.bin/oemu/cpu.c
new file mode 100644
index 0000000..de8b465
--- /dev/null
+++ b/usr.bin/oemu/cpu.c
@@ -0,0 +1,523 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <oemu/cpu.h>
+#include <oemu/types.h>
+#include <oemu/osmx64.h>
+
+/*
+ * Return true if the instruction is an
+ * MRO type instruction.
+ */
+static bool
+cpu_is_mro(inst_t *inst)
+{
+ switch (inst->opcode) {
+ case INST_MROB:
+ case INST_MROW:
+ case INST_MROD:
+ case INST_MROQ:
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * Decode the INST_MOV_IMM instruction
+ *
+ * @cpu: CPU that is executing
+ * @inst: Instruction dword
+ */
+static void
+cpu_mov_imm(struct oemu_cpu *cpu, inst_t *inst)
+{
+ struct cpu_regs *regs = &cpu->regs;
+
+ if (inst->rd > NELEM(regs->xreg)) {
+ printf("bad register operand for 'mov'\n");
+ return;
+ }
+
+ regs->xreg[inst->rd] = inst->imm;
+ printf("#%d -> x%d\n", inst->imm, inst->rd);
+}
+
+/*
+ * Decode the INST_INC instruction
+ *
+ * @cpu: CPU that is executing
+ * @inst: Instruction dword
+ */
+static void
+cpu_inc(struct oemu_cpu *cpu, inst_t *inst)
+{
+ struct cpu_regs *regs = &cpu->regs;
+ imm_t imm;
+
+ if (inst->rd > NELEM(regs->xreg)) {
+ printf("bad register operand for 'mov'\n");
+ return;
+ }
+
+ imm = regs->xreg[inst->rd]++;
+ printf("INC X%d [%x], new=%x\n", inst->rd,
+ imm, regs->xreg[inst->rd]);
+}
+
+/*
+ * Decode the INST_DEC instruction
+ *
+ * @cpu: CPU that is executing
+ * @inst: Instruction dword
+ */
+static void
+cpu_dec(struct oemu_cpu *cpu, inst_t *inst)
+{
+ struct cpu_regs *regs = &cpu->regs;
+ imm_t imm;
+
+ if (inst->rd > NELEM(regs->xreg)) {
+ printf("bad register operand for 'mov'\n");
+ return;
+ }
+
+ imm = regs->xreg[inst->rd]--;
+ printf("DEC X%d [%x], new=%x\n", inst->rd,
+ imm, regs->xreg[inst->rd]);
+}
+
+/*
+ * Decode the INST_ADD instruction
+ *
+ * @cpu: CPU that is executing
+ * @inst: Instruction dword
+ */
+static void
+cpu_add(struct oemu_cpu *cpu, inst_t *inst)
+{
+ struct cpu_regs *regs = &cpu->regs;
+ imm_t imm;
+
+ if (inst->rd > NELEM(regs->xreg)) {
+ printf("bad register operand for 'add'\n");
+ return;
+ }
+
+ imm = regs->xreg[inst->rd];
+ regs->xreg[inst->rd] += inst->imm;
+ printf("%d + %d -> X%d, new=%d\n",
+ imm, inst->imm, inst->rd, regs->xreg[inst->rd]);
+}
+
+/*
+ * Decode the INST_SUB instruction
+ *
+ * @cpu: CPU that is executing
+ * @inst: Instruction dword
+ */
+static void
+cpu_sub(struct oemu_cpu *cpu, inst_t *inst)
+{
+ struct cpu_regs *regs = &cpu->regs;
+ imm_t imm;
+
+ if (inst->rd > NELEM(regs->xreg)) {
+ printf("bad register operand for 'sub'\n");
+ return;
+ }
+
+ imm = regs->xreg[inst->rd];
+ regs->xreg[inst->rd] -= inst->imm;
+ printf("%d - %d -> X%d, new=%d\n",
+ imm, inst->imm, inst->rd, regs->xreg[inst->rd]);
+}
+
+/*
+ * Decode the INST_MUL instruction
+ *
+ * @cpu: CPU that is executing
+ * @inst: Instruction dword
+ */
+static void
+cpu_mul(struct oemu_cpu *cpu, inst_t *inst)
+{
+ struct cpu_regs *regs = &cpu->regs;
+ imm_t imm;
+
+ if (inst->rd > NELEM(regs->xreg)) {
+ printf("bad register operand for 'mul'\n");
+ return;
+ }
+
+ imm = regs->xreg[inst->rd];
+ regs->xreg[inst->rd] *= inst->imm;
+ printf("%d * %d -> X%d, new=%d\n",
+ imm, inst->imm, inst->rd, regs->xreg[inst->rd]);
+}
+static void
+cpu_and(struct oemu_cpu *cpu, inst_t *inst)
+{
+ struct cpu_regs *regs = &cpu->regs;
+ imm_t imm;
+
+ if (inst->rd > NELEM(regs->xreg)) {
+ printf("bad register operand for 'and'\n");
+ return;
+ }
+
+ imm = inst->imm;
+ regs->xreg[inst->rd] &= inst->imm;
+ printf("X%d & %x -> X%d, new=%d\n",
+ inst->rd, inst->imm, inst->rd, regs->xreg[inst->rd]);
+}
+
+static void
+cpu_or(struct oemu_cpu *cpu, inst_t *inst)
+{
+ struct cpu_regs *regs = &cpu->regs;
+ imm_t imm;
+
+ if (inst->rd > NELEM(regs->xreg)) {
+ printf("bad register operand for 'or'\n");
+ return;
+ }
+
+ imm = inst->imm;
+ regs->xreg[inst->rd] |= imm;
+ printf("X%d | %x -> X%d, new=%d\n",
+ inst->rd, inst->imm, inst->rd, regs->xreg[inst->rd]);
+}
+
+static void
+cpu_xor(struct oemu_cpu *cpu, inst_t *inst)
+{
+ struct cpu_regs *regs = &cpu->regs;
+ imm_t imm;
+
+ if (inst->rd > NELEM(regs->xreg)) {
+ printf("bad register operand for 'xor'\n");
+ return;
+ }
+
+ imm = inst->imm;
+ regs->xreg[inst->rd] ^= imm;
+ printf("X%d ^ %x -> X%d, new=%d\n",
+ inst->rd, inst->imm, inst->rd, regs->xreg[inst->rd]);
+}
+
+/*
+ * Decode the INST_DIV instruction
+ *
+ * @cpu: CPU that is executing
+ * @inst: Instruction dword
+ */
+static void
+cpu_div(struct oemu_cpu *cpu, inst_t *inst)
+{
+ struct cpu_regs *regs = &cpu->regs;
+ imm_t imm;
+
+ if (inst->rd > NELEM(regs->xreg)) {
+ printf("bad register operand for 'div'\n");
+ return;
+ }
+
+ imm = regs->xreg[inst->rd];
+ if (imm == 0) {
+ /* TODO: Some sort of interrupt */
+ printf("** DIVIDE BY ZERO **\n");
+ return;
+ }
+
+ regs->xreg[inst->rd] /= inst->imm;
+ printf("%d / %d -> X%d, new=%d\n",
+ imm, inst->imm, inst->rd, regs->xreg[inst->rd]);
+}
+
+/*
+ * Decode the INST_DIV instruction
+ */
+static void
+cpu_br(struct oemu_cpu *cpu, inst_t *inst)
+{
+ struct cpu_regs *regs = &cpu->regs;
+ imm_t imm;
+ addr_t br_to;
+
+ if (inst->rd > NELEM(regs->xreg)) {
+ printf("bad register operand for 'br'\n");
+ return;
+ }
+
+ /*
+ * If we are branching to the reset vector, might
+ * as well reset all state.
+ */
+ br_to = regs->xreg[inst->rd];
+ if (br_to == 0) {
+ cpu_reset(cpu);
+ }
+
+ regs->ip = br_to;
+}
+
+/*
+ * Decode a logical shift instruction:
+ *
+ * LSR r, r/imm
+ * LSL r, r/imm
+ */
+static void
+cpu_lshift(struct oemu_cpu *cpu, inst_t *inst)
+{
+ struct cpu_regs *regs = &cpu->regs;
+ reg_t reg = inst->rd;
+ imm_t shift = inst->imm;
+
+ switch (inst->opcode) {
+ case INST_LSR:
+ regs->xreg[reg] >>= shift;
+ printf("X%d >> %d -> %d\n", reg, shift, regs->xreg[reg]);
+ break;
+ case INST_LSL:
+ regs->xreg[reg] <<= shift;
+ printf("X%d << %d -> %d\n", reg, shift, regs->xreg[reg]);
+ break;
+ }
+}
+
+/*
+ * Decode MRO type instructions
+ */
+static void
+cpu_mro(struct oemu_cpu *cpu, inst_t *inst)
+{
+ inst_t *next_inst;
+ struct cpu_regs *regs = &cpu->regs;
+ char *inst_str = "bad";
+ uint64_t mask = 0;
+ bool set_mask = false;
+ imm_t imm;
+
+ switch (inst->imm) {
+ case 0: break;
+ case 1:
+ set_mask = true;
+ break;
+ default:
+ imm = inst->imm & 1;
+ if (inst->imm == 1) {
+ set_mask = true;
+ }
+ break;
+ }
+
+ switch (inst->opcode) {
+ case INST_MROB:
+ inst_str = "mrob";
+ if (!set_mask) {
+ break;
+ }
+ mask |= MASK(8);
+ break;
+ case INST_MROW:
+ inst_str = "mrow";
+ if (!set_mask) {
+ break;
+ }
+ mask |= MASK(16);
+ break;
+ case INST_MROD:
+ inst_str = "mrod";
+ if (!set_mask) {
+ break;
+ }
+ mask |= MASK(32);
+ break;
+ case INST_MROQ:
+ inst_str = "mroq";
+ if (!set_mask) {
+ break;
+ }
+ mask |= __UINT64_MAX;
+ break;
+ }
+
+ if (inst->rd > NELEM(regs->xreg)) {
+ printf("bad register operand for '%s'\n", inst_str);
+ return;
+ }
+
+ if (set_mask) {
+ imm = regs->xreg[inst->rd] |= mask;
+ printf("set %x->x%d, new=%x\n", mask, inst->rd, imm);
+ } else {
+ imm = regs->xreg[inst->rd] &= ~mask;
+ printf("cleared %x->x%d, new=%x\n", mask, inst->rd, imm);
+ }
+}
+
+/*
+ * Reset a CPU to a default state
+ */
+void
+cpu_reset(struct oemu_cpu *cpu)
+{
+ struct cpu_regs *regs;
+
+ /*
+ * When an OSMX64 processor first starts up, it will
+ * initially be executing in supervisor mode with all
+ * of its registeres initialized to zeros.
+ */
+ regs = &cpu->regs;
+ regs->ip = 0;
+ regs->sr_state = CPU_SRS_SV;
+ regs->blr = 0x0;
+ regs->ilr = 0x0;
+ memset(regs->xreg, 0x0, sizeof(regs->xreg));
+}
+void
+cpu_regdump(struct oemu_cpu *cpu)
+{
+ struct cpu_regs *regs;
+
+ regs = &cpu->regs;
+ printf(
+ "X0=%p, X1=%p, X2=%p\n"
+ "X3=%p, X4=%p, X5=%p\n"
+ "X6=%p, X7=%p, X8=%p\n"
+ "X9=%p, X10=%p, X11=%p\n"
+ "X12=%p, X13=%p, X14=%p\n"
+ "X15=%p, IP=%p, SRS=%p\n"
+ "BLR=%p, ILR=%p\n",
+ regs->xreg[0], regs->xreg[1],
+ regs->xreg[2], regs->xreg[3],
+ regs->xreg[4], regs->xreg[5],
+ regs->xreg[6], regs->xreg[7],
+ regs->xreg[8], regs->xreg[9],
+ regs->xreg[10], regs->xreg[11],
+ regs->xreg[12], regs->xreg[13],
+ regs->xreg[14], regs->xreg[15],
+ regs->ip, regs->sr_state,
+ regs->blr, regs->ilr
+ );
+}
+
+/*
+ * Main instruction execution loop.
+ */
+void
+cpu_kick(struct oemu_cpu *cpu, struct sysmem *mem)
+{
+ struct cpu_regs *regs = &cpu->regs;
+ inst_t *inst;
+ uint8_t *memp = mem->mem;
+
+ for (;;) {
+ inst = (inst_t *)&memp[regs->ip];
+
+ switch (inst->opcode) {
+ case INST_NOP:
+ /* NOP */
+ regs->ip += sizeof(*inst);
+ continue;
+ case INST_MOV_IMM:
+ cpu_mov_imm(cpu, inst);
+ break;
+ case INST_INC:
+ cpu_inc(cpu, inst);
+ break;
+ case INST_DEC:
+ cpu_dec(cpu, inst);
+ break;
+ case INST_ADD:
+ cpu_add(cpu, inst);
+ break;
+ case INST_SUB:
+ cpu_sub(cpu, inst);
+ break;
+ case INST_MUL:
+ cpu_mul(cpu, inst);
+ break;
+ case INST_DIV:
+ cpu_div(cpu, inst);
+ break;
+ case INST_AND:
+ cpu_and(cpu, inst);
+ break;
+ case INST_OR:
+ cpu_or(cpu, inst);
+ break;
+ case INST_XOR:
+ cpu_xor(cpu, inst);
+ break;
+ case INST_BR:
+ cpu_br(cpu, inst);
+ break;
+ case INST_LSL:
+ case INST_LSR:
+ cpu_lshift(cpu, inst);
+ break;
+ default:
+ if (cpu_is_mro(inst)) {
+ cpu_mro(cpu, inst);
+ }
+ break;
+ }
+
+ /*
+ * X0 is readonly and should always be zero, undo
+ * any writes or side effects from any operations
+ * upon this register.
+ */
+ regs->xreg[0] = 0;
+
+ /* Is this a halt instruction? */
+ if (inst->opcode == INST_HLT) {
+ printf("HALTED\n");
+ break;
+ }
+
+ if (regs->ip >= MEMORY_SIZE) {
+ break;
+ }
+
+ regs->ip += sizeof(*inst);
+ }
+
+ cpu_regdump(cpu);
+}
diff --git a/usr.bin/oemu/emu.c b/usr.bin/oemu/emu.c
new file mode 100644
index 0000000..1b4280b
--- /dev/null
+++ b/usr.bin/oemu/emu.c
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/errno.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <oemu/cpu.h>
+
+static struct oemu_cpu core_0;
+struct sysmem g_mem;
+
+static void
+help(void)
+{
+ printf(
+ "OSMORA OSMX64 Emulator\n"
+ "usage: oemu <binary file>\n"
+ );
+}
+
+/*
+ * Allocate and initialize platform
+ * memory.
+ */
+static int
+mem_init(void)
+{
+ printf("allocating 0x%x bytes of memory\n", MEMORY_SIZE);
+ g_mem.mem_size = MEMORY_SIZE;
+ g_mem.mem = malloc(MEMORY_SIZE);
+ if (g_mem.mem == NULL) {
+ printf("failed to allocate memory\n");
+ return -1;
+ }
+}
+
+/*
+ * Load a program specified by a path into
+ * memory for execution.
+ */
+static int
+program_load(const char *path, paddr_t loadoff)
+{
+ void *mem = g_mem.mem;
+ size_t size;
+ int fd;
+
+ fd = open(path, O_RDONLY);
+ if (fd < 0) {
+ printf("failed to open \"%s\"\n", path);
+ return -ENOENT;
+ }
+
+ /* Grab the size of the file */
+ size = lseek(fd, 0, SEEK_END);
+ lseek(fd, loadoff, SEEK_SET);
+ printf("loading size %d\n", size);
+
+ /* Is it too big? */
+ if (size >= g_mem.mem_size) {
+ printf("program too big !! (memsize=%x)\n", g_mem.mem_size);
+ close(fd);
+ return -1;
+ }
+
+ printf("read data into %p\n", mem);
+ printf("read %d bytes\n", read(fd, mem, size));
+ close(fd);
+ return 0;
+}
+
+int
+main(int argc, char **argv)
+{
+ if (argc < 2) {
+ help();
+ return -1;
+ }
+
+ /* Initialize memory */
+ if (mem_init() < 0) {
+ return -1;
+ }
+
+ /* Put the CPU in a known state */
+ cpu_reset(&core_0);
+
+ /*
+ * Load the program and send the little guy off
+ * to start nomming those 32-bit instructions
+ */
+ if (program_load(argv[1], 0x00000000) < 0) {
+ return -1;
+ }
+ cpu_kick(&core_0, &g_mem);
+ free(g_mem.mem);
+ return 0;
+}
diff --git a/usr.bin/oemu/include/oemu/cpu.h b/usr.bin/oemu/include/oemu/cpu.h
new file mode 100644
index 0000000..882fe93
--- /dev/null
+++ b/usr.bin/oemu/include/oemu/cpu.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _OEMU_CPU_H_
+#define _OEMU_CPU_H_
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <stdint.h>
+#include <stddef.h>
+#include <oemu/types.h>
+
+#define MEMORY_SIZE 512
+
+/*
+ * Processor state register
+ */
+#define CPU_SRS_SV BIT(1) /* Supervisor flag */
+#define CPU_SRS_CARRY BIT(2) /* Carry flag */
+
+/*
+ * System memory
+ *
+ * @mem: Data
+ * @mem_size: Memory size max
+ */
+struct sysmem {
+ void *mem;
+ size_t mem_size;
+};
+
+/*
+ * CPU register state
+ *
+ * @xreg: X<n>
+ * @ip: Instruction pointer
+ * @sr_state: Processor state register
+ * @blr: Branch link register
+ * @ilr: Interrupt link register
+ */
+struct cpu_regs {
+ reg_t xreg[16];
+ reg_t ip;
+ reg_t sr_state;
+ reg_t blr;
+ reg_t ilr;
+};
+
+struct oemu_cpu {
+ struct cpu_regs regs;
+};
+
+void cpu_regdump(struct oemu_cpu *cpu);
+void cpu_reset(struct oemu_cpu *cpu);
+void cpu_kick(struct oemu_cpu *cpu, struct sysmem *mem);
+
+#endif /* !_OEMU_CPU_H_ */
diff --git a/usr.bin/oemu/include/oemu/osmx64.h b/usr.bin/oemu/include/oemu/osmx64.h
new file mode 100644
index 0000000..1e094d0
--- /dev/null
+++ b/usr.bin/oemu/include/oemu/osmx64.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _OEMU_OSMX64_H_
+#define _OEMU_OSMX64_H_
+
+#include <stdint.h>
+
+/* Opcodes */
+#define INST_NOP 0x00 /* No-operation */
+#define INST_ADD 0x01 /* Add operation */
+#define INST_SUB 0x02 /* Sub operation */
+#define INST_MUL 0x03 /* Multiply operation */
+#define INST_DIV 0x04 /* Divide operation */
+#define INST_INC 0x05 /* Increment operation */
+#define INST_DEC 0x06 /* Decrement operation */
+#define INST_OR 0x07 /* Bitwise OR operation */
+#define INST_XOR 0x08 /* Bitwise XOR operation */
+#define INST_AND 0x09 /* Bitwise AND operation */
+#define INST_NOT 0x0A /* Bitwise NOT operation */
+#define INST_SLL 0x0B /* Shift left logical operation */
+#define INST_SRL 0x0C /* Shift right logical operation */
+#define INST_MOV_IMM 0x0D /* Data move operation from IMM */
+#define INST_HLT 0x0E /* Halt */
+#define INST_BR 0x0F /* Branch */
+#define INST_MROB 0x10 /* Mask register over byte */
+#define INST_MROW 0x11 /* Mask register over word */
+#define INST_MROD 0x12 /* Mask register over dword */
+#define INST_MROQ 0x13 /* Mask register over qword */
+#define INST_LSR 0x14 /* Logical shift right */
+#define INST_LSL 0x15 /* Logical shift left */
+
+/* Registers */
+#define REG_X0 0x00
+#define REG_X1 0x01
+#define REG_X2 0x02
+#define REG_X3 0x03
+#define REG_X4 0x04
+#define REG_X5 0x05
+#define REG_X6 0x06
+#define REG_X7 0x07
+#define REG_X8 0x08
+#define REG_X9 0x09
+#define REG_X10 0x0A
+#define REG_X11 0x0B
+#define REG_X12 0x0C
+#define REG_X13 0x0D
+#define REG_X14 0x0E
+#define REG_X15 0x0F
+#define REG_BAD 0xFF
+
+/*
+ * OSMX64 instruction format
+ */
+typedef struct {
+ uint8_t opcode;
+ uint8_t rd;
+ union {
+ uint16_t imm;
+ uint16_t unused;
+ };
+} inst_t;
+
+#endif /* !_OEMU_OSMX64_H_ */
diff --git a/usr.bin/oemu/include/oemu/types.h b/usr.bin/oemu/include/oemu/types.h
new file mode 100644
index 0000000..caf6e9b
--- /dev/null
+++ b/usr.bin/oemu/include/oemu/types.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _OEMU_TYPES_H_
+#define _OEMU_TYPES_H_
+
+#include <sys/types.h>
+
+typedef uint64_t reg_t;
+typedef uintptr_t addr_t;
+typedef uint16_t imm_t;
+typedef addr_t paddr_t;
+
+#endif /* !_OEMU_TYPES_H_ */
diff --git a/usr.bin/osh/Makefile b/usr.bin/osh/Makefile
index 28981fe..1505412 100644
--- a/usr.bin/osh/Makefile
+++ b/usr.bin/osh/Makefile
@@ -2,5 +2,5 @@ include user.mk
CFILES = $(shell find . -name "*.c")
-osh:
- $(CC) $(CFILES) -o $@ $(INTERNAL_CFLAGS)
+$(ROOT)/base/usr/bin/osh:
+ gcc $(CFILES) -o $@ $(INTERNAL_CFLAGS)
diff --git a/usr.bin/osh/osh.c b/usr.bin/osh/osh.c
index dea4b3f..71ca6de 100644
--- a/usr.bin/osh/osh.c
+++ b/usr.bin/osh/osh.c
@@ -29,90 +29,455 @@
#include <sys/types.h>
#include <sys/cdefs.h>
+#include <sys/errno.h>
+#include <sys/spawn.h>
+#include <sys/wait.h>
#include <fcntl.h>
#include <stddef.h>
+#include <stdbool.h>
#include <unistd.h>
#include <string.h>
+#include <stdio.h>
-#define prcons(FD, STR) write((FD), (STR), strlen((STR)))
+#define is_printable(C) ((C) >= 32 && (C) <= 126)
#define is_ascii(C) ((C) >= 0 && (C) <= 128)
+
+#define INPUT_SIZE 64
+
+#define REPEAT "!!"
+#define COMMENT '@'
#define WELCOME \
":::::::::::::::::::::::::::::::::::::::\n" \
":: OSMORA GATEWAY ~ Every key echos ::\n" \
":: ..... Proceed with purpose ..... ::\n" \
- ":::::::::::::::::::::::::::::::::::::::\n"
+ ":::::::::::::::::::::::::::::::::::::::"
+
+#define HELP \
+ "Default commands:\n" \
+ "help - Display this help message\n" \
+ "echo - Print the arguments to the console\n" \
+ "reboot - Reboot the machine\n" \
+ "kmsg - Print kernel message buffer\n" \
+ "fetch - System information\n" \
+ "kfg - Start up kfgwm\n" \
+ "bell - Toggle backspace bell\n" \
+ "date - Get the current date\n" \
+ "clear - Clear the screen\n" \
+ "exit - Exit the shell"
+
+#define PROMPT "[%s::%s]~ "
+
+static char last_command[INPUT_SIZE];
+static char buf[INPUT_SIZE];
+static int running;
+static int bell_fd;
+static bool bs_bell = true; /* Beep on backspace */
-#define CMD_ECHO "echo"
+static void cmd_help(int argc, char *argv[]);
+static void cmd_echo(int argc, char *argv[]);
+static void cmd_exit(int argc, char *argv[]);
+static void cmd_bell(int argc, char *argv[]);
+static void cmd_clear(int argc, char *argv[]);
+
+struct builtin_cmd {
+ const char *name;
+ void (*func)(int argc, char *argv[]);
+};
+
+/*
+ * Results after parsing a command
+ *
+ * @bg: Run command in background
+ */
+struct parse_state {
+ uint8_t bg : 1;
+};
+
+static struct builtin_cmd cmds[] = {
+ {"help",cmd_help},
+ {"exit",cmd_exit},
+ {"bell", cmd_bell},
+ {"clear", cmd_clear},
+ {NULL, NULL}
+};
+
+static void
+cmd_help(int argc, char *argv[])
+{
+ puts(HELP);
+}
+
+static void
+cmd_exit(int argc, char *argv[])
+{
+ running = 0;
+}
-static char buf[64];
-static uint8_t i;
+static void
+cmd_clear(int argc, char *argv[])
+{
+ fputs("\033[2J", stdout);
+}
static void
-cmd_run(int fd)
+cmd_bell(int argc, char *argv[])
+{
+ const char *usage_str = "usage: bell [on/off]";
+ const char *arg;
+
+ if (argc < 2) {
+ puts(usage_str);
+ return;
+ }
+
+ arg = argv[1];
+ if (strcmp(arg, "on") == 0) {
+ bs_bell = true;
+ } else if (strcmp(arg, "off") == 0) {
+ bs_bell = false;
+ } else {
+ puts(usage_str);
+ }
+}
+
+static int
+parse_args(char *input, char *argv[], int max_args, struct parse_state *p)
{
- int cmp;
- char *p = buf;
- size_t len;
- int valid_cmd = 1;
-
- switch (buf[0]) {
- case 'e':
- len = strlen(CMD_ECHO);
- if (memcmp(buf, CMD_ECHO, len) == 0) {
- p += len + 1;
- prcons(fd, "\n");
- prcons(fd, p);
+ int argc = 0;
+
+ /* ignore comments */
+ if (*input == '@') {
+ return 0;
+ }
+
+ /* setup default state */
+ p->bg = 0;
+
+ /* parse loop */
+ while (*input != '\0') {
+ /* skip leading spaces */
+ while (*input == ' ') {
+ input++;
+ }
+
+ /* check if empty */
+ if (*input == '\0') {
+ break;
+ }
+
+ /* comment? */
+ if (*input == COMMENT) {
break;
}
- valid_cmd = 0;
- break;
- default:
- valid_cmd = 0;
- break;
+ /* run in background? */
+ if (*input == '&') {
+ p->bg = 1;
+ }
+
+ if (argc < max_args) {
+ argv[argc++] = input; /* mark start of the argument */
+ }
+ /* move forward until next space or end */
+ while (*input != '\0' && *input != ' ') {
+ /* ignore comments */
+ if (*input == COMMENT) {
+ return 0;
+ }
+
+ input++;
+ }
+
+ /* end */
+ if (*input != '\0') {
+ *input = '\0';
+ input++;
+ }
+ }
+
+ return argc;
+}
+
+/*
+ * Grab a string from stdin and return
+ * the resulting offset within the input
+ * buffer we are at.
+ */
+static uint8_t
+getstr(void)
+{
+ char c;
+ int input;
+ uint32_t beep_payload;
+ uint8_t buf_i = 0;
+
+ /*
+ * Prepare the beep payload @ 500 Hz
+ * for 20ms
+ */
+ beep_payload = 500;
+ beep_payload |= (30 << 16);
+
+ for (;;) {
+ if ((input = getchar()) < 0) {
+ continue;
+ }
+
+ c = (char)input;
+ if (c == '\t') {
+ continue;
+ }
+
+ /* return on newline */
+ if (c == '\n') {
+ buf[buf_i] = '\0';
+ putchar('\n');
+ return buf_i;
+ }
+
+ /* handle backspaces and DEL */
+ if (c == '\b' || c == 127) {
+ if (buf_i > 0) {
+ buf_i--;
+ fputs("\b \b", stdout);
+ } else if (bell_fd > 0 && bs_bell) {
+ write(bell_fd, &beep_payload, sizeof(beep_payload));
+ }
+ } else if (is_printable(c) && buf_i < sizeof(buf) - 1) {
+ /* write to fd and add to buffer */
+ buf[buf_i++] = c;
+ putchar(c);
+ }
}
+}
- if (!valid_cmd) {
- prcons(fd, "\nunrecognized command\n");
+static void
+builtin_run(struct builtin_cmd *cmd, int argc, char *argv[])
+{
+ if (cmd->func != NULL) {
+ cmd->func(argc, argv);
+ return;
}
}
-int
-main(int argc, char **argv)
+static int
+cmd_run(const char *input, int argc, char *argv[])
+{
+ char bin_path[512];
+ char *envp[1] = { NULL };
+ pid_t child;
+
+ /*
+ * If we can access the raw input as a file, try to
+ * spawn it as a program. This case would run if for
+ * example, the user entered /usr/sbin/foo, or some
+ * path directly into the console.
+ */
+ if (access(input, F_OK) == 0) {
+ child = spawn(input, argv, envp, 0);
+ if (child < 0) {
+ return child;
+ }
+ return child;
+ }
+
+ snprintf(bin_path, sizeof(bin_path), "/usr/bin/%s", input);
+
+ /* See if we can access it */
+ if (access(bin_path, F_OK) != 0) {
+ return -1;
+ }
+
+ if ((child = spawn(bin_path, argv, envp, 0)) < 0) {
+ return child;
+ }
+
+ return child;
+}
+
+/*
+ * Match a command with a builtin or binary
+ *
+ * @input: Command input
+ * @argc: Argument count
+ * @argv: Argument vector
+ */
+static int
+command_match(const char *input, int argc, char *argv[])
+{
+ int found = 0;
+ int i;
+ pid_t child = -1;
+
+ for (i = 0; cmds[i].name != NULL; i++) {
+ if (strcmp(input, cmds[i].name) == 0) {
+ builtin_run(&cmds[i], argc, argv);
+ found = 1;
+ }
+ }
+
+ if (found == 0) {
+ if ((child = cmd_run(input, argc, argv)) < 0) {
+ puts("Unrecognized command");
+ return -1;
+ }
+ }
+
+ return child;
+}
+
+static void
+script_skip_comment(int fd)
{
- int fd;
- uint16_t input;
char c;
- if ((fd = open("/dev/console", O_RDWR)) < 0) {
+ while (c != '\n') {
+ if (read(fd, &c, 1) <= 0)
+ break;
+ }
+}
+
+/*
+ * Parse a single line typed in from the
+ * user.
+ *
+ * @input: Input line
+ */
+static int
+parse_line(char *input)
+{
+ int argc;
+ char *argv[16];
+ struct parse_state state = {0};
+ pid_t child;
+
+ /*
+ * If we are using the REPEAT shorthand,
+ * repeat the last command. We return -EAGAIN
+ * to indicate we did not parse a normal command
+ * so the repeat command isn't pushed into the last
+ * command buffer and we enter a recursive hell.
+ */
+ if (strcmp(input, REPEAT) == 0) {
+ parse_line(last_command);
+ return -EAGAIN;
+ }
+
+ /* Ensure the aux vector is zeored */
+ memset(argv, 0, sizeof(argv));
+
+ /*
+ * Grab args from the user, there should be
+ * at least one.
+ */
+ argc = parse_args(input, argv, sizeof(argv), &state);
+ if (argc == 0) {
+ return -EAGAIN;
+ }
+
+ child = command_match(input, argc, argv);
+ if (child > 0 && !state.bg) {
+ waitpid(child, NULL, 0);
+ }
+
+ return 0;
+}
+
+static int
+open_script(const char *pathname)
+{
+ int fd, argc, buf_i = 0;
+ char c, *input;
+ char buf[256];
+
+ fd = open(pathname, O_RDONLY);
+ if (fd < 0) {
+ printf("osh: failed to open %s\n", pathname);
return fd;
}
- i = 0;
- prcons(fd, WELCOME);
- prcons(fd, "[root::osmora]~ ");
- for (;;) {
- if (read(fd, &input, 2) <= 0) {
+ while (read(fd, &c, 1) > 0) {
+ /* Skip comments */
+ if (c == COMMENT) {
+ script_skip_comment(fd);
continue;
}
- c = input & 0xFF;
- if (!is_ascii(c)) {
+ /* Skip blank newlines */
+ if (c == '\n' && buf_i == 0) {
continue;
}
- if (i < sizeof(buf)) {
- buf[i++] = c;
- buf[i] = 0;
+ if (buf_i >= sizeof(buf) - 1) {
+ buf_i = 0;
}
+
if (c == '\n') {
- cmd_run(fd);
- i = 0;
- buf[i] = 0;
- prcons(fd, "[root::osmora]~ ");
- } else {
- write(fd, &c, 1);
+ buf[buf_i] = '\0';
+ parse_line(buf);
+ buf_i = 0;
+ continue;
+ }
+ buf[buf_i++] = c;
+ }
+
+ return 0;
+}
+
+static void
+dump_file(const char *pathname)
+{
+ FILE *file;
+ char buf[64];
+ int fd;
+
+ file = fopen(pathname, "r");
+ if (file == NULL) {
+ return;
+ }
+
+ while (fgets(buf, sizeof(buf), file) != NULL) {
+ printf("%s", buf);
+ }
+
+ fclose(file);
+}
+
+int
+main(int argc, char **argv)
+{
+ int found, prog_argc;
+ int stdout_fd;
+ char hostname[128] = "osmora";
+ uint8_t buf_i;
+ char *p;
+ char c;
+ pid_t child;
+
+ if (argc > 1) {
+ return open_script(argv[1]);
+ }
+
+ running = 1;
+ bell_fd = open("/dev/beep", O_WRONLY);
+ dump_file("/etc/motd");
+ gethostname(hostname, sizeof(hostname));
+
+ while (running) {
+ printf(PROMPT, getlogin(), hostname);
+
+ buf_i = getstr();
+ if (buf[0] == '\0') {
+ continue;
}
+
+ buf[buf_i] = '\0';
+ if (parse_line(buf) < 0) {
+ continue;
+ }
+
+ memcpy(last_command, buf, buf_i + 1);
+ buf[0] = '\0';
}
return 0;
}
diff --git a/usr.bin/readcore/Makefile b/usr.bin/readcore/Makefile
new file mode 100644
index 0000000..0c18475
--- /dev/null
+++ b/usr.bin/readcore/Makefile
@@ -0,0 +1,6 @@
+include user.mk
+
+CFILES = $(shell find . -name "*.c")
+
+$(ROOT)/base/usr/bin/readcore:
+ gcc -Iinclude/ $(CFILES) -o $@ $(INTERNAL_CFLAGS)
diff --git a/usr.bin/readcore/core.c b/usr.bin/readcore/core.c
new file mode 100644
index 0000000..6871d06
--- /dev/null
+++ b/usr.bin/readcore/core.c
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include "frame.h"
+#include "core.h"
+
+void
+core_dumpframe(const struct core *core)
+{
+#if defined(__x86_64__)
+ printf(
+ "RDI=%p, RSI=%p\n"
+ "RCX=%p, RDX=%p\n"
+ "RAX=%p, RIP=%p\n",
+ core->frame.rdi, core->frame.rsi,
+ core->frame.rcx, core->frame.rdx,
+ core->frame.rax, core->frame.rip
+ );
+#else
+ printf("core_dumpframe: unsupported arch\n");
+#endif
+}
diff --git a/usr.bin/readcore/crc32.c b/usr.bin/readcore/crc32.c
new file mode 100644
index 0000000..2df49ac
--- /dev/null
+++ b/usr.bin/readcore/crc32.c
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include "crc32.h"
+
+static const uint32_t crc32_tab[] = {
+ 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F,
+ 0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
+ 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2,
+ 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
+ 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9,
+ 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
+ 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C,
+ 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
+ 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423,
+ 0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
+ 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x01DB7106,
+ 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
+ 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D,
+ 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
+ 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950,
+ 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
+ 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7,
+ 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
+ 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA,
+ 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
+ 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81,
+ 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
+ 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84,
+ 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
+ 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB,
+ 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
+ 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E,
+ 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
+ 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55,
+ 0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
+ 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28,
+ 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
+ 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F,
+ 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
+ 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242,
+ 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
+ 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69,
+ 0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
+ 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC,
+ 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
+ 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693,
+ 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
+ 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
+};
+
+uint32_t
+crc32(const void *data, size_t len)
+{
+ const uint8_t *p = data;
+ uint32_t val = 0xFFFFFFFF;
+
+ for (size_t i = 0; i < len; ++i) {
+ val = (val >> 8) ^ crc32_tab[(val ^ p[i]) & 0xFF];
+ }
+
+ return val ^ 0xFFFFFFFF;
+}
diff --git a/usr.bin/readcore/include/core.h b/usr.bin/readcore/include/core.h
new file mode 100644
index 0000000..51105f4
--- /dev/null
+++ b/usr.bin/readcore/include/core.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef CORE_H_
+#define CORE_H_
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include "frame.h"
+
+struct __packed core {
+ pid_t pid;
+ uintptr_t fault_addr;
+ struct core_frame frame;
+ uint32_t checksum;
+};
+
+#endif /* !CORE_H_ */
diff --git a/usr.bin/readcore/include/crc32.h b/usr.bin/readcore/include/crc32.h
new file mode 100644
index 0000000..a175a30
--- /dev/null
+++ b/usr.bin/readcore/include/crc32.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef CRC32_H_
+#define CRC32_H_
+
+#include <stdint.h>
+
+uint32_t crc32(const void *data, size_t len);
+
+#endif
diff --git a/usr.bin/readcore/include/frame.h b/usr.bin/readcore/include/frame.h
new file mode 100644
index 0000000..50740fb
--- /dev/null
+++ b/usr.bin/readcore/include/frame.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef FRAME_H_
+#define FRAME_H_
+
+#include <sys/types.h>
+
+struct core;
+
+#if defined(__x86_64__)
+struct core_frame {
+ uint64_t trapno;
+ uint64_t rax;
+ uint64_t rcx;
+ uint64_t rdx;
+ uint64_t rbx;
+ uint64_t rsi;
+ uint64_t rdi;
+ uint64_t rbp;
+ uint64_t r8;
+ uint64_t r9;
+ uint64_t r10;
+ uint64_t r11;
+ uint64_t r12;
+ uint64_t r13;
+ uint64_t r14;
+ uint64_t r15;
+ uint64_t error_code;
+ uint64_t rip;
+ uint64_t cs;
+ uint64_t rflags;
+ uint64_t rsp;
+ uint64_t ss;
+};
+#elif defined(__aarch64__)
+struct core_frame {
+ lreg_t x30;
+ lreg_t x29;
+ lreg_t x28;
+ lreg_t x27;
+ lreg_t x26;
+ lreg_t x25;
+ lreg_t x24;
+ lreg_t x23;
+ lreg_t x22;
+ lreg_t x21;
+ lreg_t x20;
+ lreg_t x19;
+ lreg_t x18;
+ lreg_t x17;
+ lreg_t x16;
+ lreg_t x15;
+ lreg_t x14;
+ lreg_t x13;
+ lreg_t x12;
+ lreg_t x11;
+ lreg_t x10;
+ lreg_t x9;
+ lreg_t x8;
+ lreg_t x7;
+ lreg_t x6;
+ lreg_t x5;
+ lreg_t x4;
+ lreg_t x3;
+ lreg_t x2;
+ lreg_t x1;
+ lreg_t x0;
+ lreg_t elr;
+ lreg_t esr;
+ frament_t trapno;
+};
+#else
+struct core_frame {
+ uint64_t data[30];
+};
+#endif
+
+void core_dumpframe(const struct core *core);
+
+#endif /* !FRAME_H_ */
diff --git a/usr.bin/readcore/readcore.c b/usr.bin/readcore/readcore.c
new file mode 100644
index 0000000..9d8b17b
--- /dev/null
+++ b/usr.bin/readcore/readcore.c
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdint.h>
+#include "crc32.h"
+#include "frame.h"
+#include "core.h"
+
+static void
+parse_core(const struct core *dump)
+{
+ uint32_t checksum;
+
+ printf("-- CORE DUMP OF PID %d CRASH -- \n", dump->pid);
+ printf("Faulting address: %p\n", dump->fault_addr);
+ core_dumpframe(dump);
+
+ checksum = crc32(dump, sizeof(*dump) - sizeof(checksum));
+ if (checksum != dump->checksum) {
+ printf("!! WARNING: coredump might be corrupt !!\n");
+ }
+}
+
+int
+main(int argc, char **argv)
+{
+ int fd;
+ struct core core;
+
+ if (argc < 2) {
+ printf("usage: readcore <coredump>\n");
+ return -1;
+ }
+
+ fd = open(argv[1], O_RDONLY);
+ if (fd < 2) {
+ printf("readcore: Could not open \"%s\"\n", argv[1]);
+ return fd;
+ }
+
+ if (read(fd, &core, sizeof(core)) < sizeof(core)) {
+ printf("readcore: bad read()\n");
+ return -1;
+ }
+
+ parse_core(&core);
+ close(fd);
+ return 0;
+}
diff --git a/usr.bin/reboot/Makefile b/usr.bin/reboot/Makefile
new file mode 100644
index 0000000..3700676
--- /dev/null
+++ b/usr.bin/reboot/Makefile
@@ -0,0 +1,6 @@
+include user.mk
+
+CFILES = $(shell find . -name "*.c")
+
+$(ROOT)/base/usr/bin/reboot:
+ gcc $(CFILES) -o $@ $(INTERNAL_CFLAGS)
diff --git a/usr.bin/reboot/reboot.c b/usr.bin/reboot/reboot.c
new file mode 100644
index 0000000..ac9785a
--- /dev/null
+++ b/usr.bin/reboot/reboot.c
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/reboot.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#define REBOOT_FLAGS "rhp"
+#define REBOOT_FLAG_RB 'r' /* Reboot */
+#define REBOOT_FLAG_HLT 'h' /* Halt */
+#define REBOOT_FLAG_PWR 'p' /* Power off */
+
+static void
+help(void)
+{
+ printf(
+ "reboot: usage: reboot [flags]\n"
+ "flags:\n"
+ " [-r] Reboot\n"
+ " [-h] Halt\n"
+ " [-p] Power off\n"
+ );
+}
+
+int
+main(int argc, char **argv)
+{
+ int c;
+
+ if (argc < 2) {
+ help();
+ return -1;
+ }
+
+ /*
+ * Now we parse the args. Every case is a fall through
+ * as if one fails (indicated by us returning), another
+ * method is attempted.
+ */
+ while ((c = getopt(argc, argv, REBOOT_FLAGS)) != -1) {
+ switch (c) {
+ case REBOOT_FLAG_RB:
+ cpu_reboot(REBOOT_RESET);
+ printf("REBOOT failed\n");
+ /* Fall through */
+ case REBOOT_FLAG_HLT:
+ cpu_reboot(REBOOT_HALT);
+ printf("HALT failed\n");
+ /* Fall through */
+ case REBOOT_FLAG_PWR:
+ cpu_reboot(REBOOT_POWEROFF);
+ printf("POWEROFF failed\n");
+ /* Fall through */
+ default:
+ printf("got bad flag '%c'\n", c);
+ break;
+ }
+ }
+
+ return 0;
+}
diff --git a/usr.bin/screensave/Makefile b/usr.bin/screensave/Makefile
new file mode 100644
index 0000000..a005346
--- /dev/null
+++ b/usr.bin/screensave/Makefile
@@ -0,0 +1,6 @@
+include user.mk
+
+CFILES = $(shell find . -name "*.c")
+
+$(ROOT)/base/usr/bin/screensave:
+ gcc $(CFILES) -lgfx -o $@ $(INTERNAL_CFLAGS)
diff --git a/usr.bin/screensave/screensave.c b/usr.bin/screensave/screensave.c
new file mode 100644
index 0000000..bb67cde
--- /dev/null
+++ b/usr.bin/screensave/screensave.c
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <time.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <libgfx/draw.h>
+#include <libgfx/gfx.h>
+
+static struct gfx_ctx ctx;
+
+static ssize_t
+rand_bytes(char *buf, size_t len)
+{
+ ssize_t retval;
+ int fd;
+
+ fd = open("/dev/random", O_RDONLY);
+ if (fd < 0) {
+ return fd;
+ }
+
+ if ((retval = read(fd, buf, len)) < 0) {
+ close(fd);
+ return retval;
+ }
+
+ close(fd);
+ return retval;
+}
+
+static void
+screensave(void)
+{
+ size_t n_iter = 0; /* Monotonic */
+ struct timespec ts;
+ char randbuf[2];
+ color_t curpix, nextpix;
+ uint8_t step = 0;
+
+ ts.tv_sec = 0;
+ ts.tv_nsec = 3000000;
+
+ /* Begin the radiation ::) */
+ for (;;) {
+ rand_bytes(randbuf, sizeof(randbuf));
+ for (size_t i = 0; i < (ctx.fb_size / 4) - 1; i += step + 1) {
+ curpix = ctx.io[i];
+ nextpix = ctx.io[i + 1];
+
+ /* If a multiple of 16, AND, otherwise XOR */
+ if ((n_iter & 15) != 0) {
+ curpix ^= randbuf[0] & 3;
+ nextpix ^= (curpix | (nextpix << 1));
+ nextpix ^= step;
+ } else {
+ curpix &= randbuf[0] & 3;
+ nextpix &= (curpix | (nextpix << 1));
+ nextpix &= step;
+ }
+
+ ctx.io[i] = curpix;
+ ctx.io[i + 1] = nextpix;
+ }
+
+ sleep(&ts, &ts);
+ if ((++step) > 50) {
+ step = 0;
+ }
+ ++n_iter;
+ }
+}
+
+int
+main(void)
+{
+ int error;
+
+ error = gfx_init(&ctx);
+ if (error < 0) {
+ printf("could not init libgfx\n");
+ return error;
+ }
+
+ screensave();
+ gfx_cleanup(&ctx);
+ return 0;
+}
diff --git a/usr.bin/sleep/Makefile b/usr.bin/sleep/Makefile
new file mode 100644
index 0000000..5bfd04f
--- /dev/null
+++ b/usr.bin/sleep/Makefile
@@ -0,0 +1,6 @@
+include user.mk
+
+CFILES = $(shell find . -name "*.c")
+
+$(ROOT)/base/usr/bin/sleep:
+ gcc $(CFILES) -o $@ $(INTERNAL_CFLAGS)
diff --git a/usr.bin/sleep/sleep.c b/usr.bin/sleep/sleep.c
new file mode 100644
index 0000000..f7d07f8
--- /dev/null
+++ b/usr.bin/sleep/sleep.c
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <time.h>
+#include <string.h>
+#include <stdio.h>
+
+int
+main(int argc, char **argv)
+{
+ struct timespec ts;
+ uint16_t nsec = 0;
+
+ if (argc < 2) {
+ printf("sleep: usage: sleep <seconds>\n");
+ return -1;
+ }
+
+ nsec = atoi(argv[1]);
+ if (nsec == 0) {
+ printf("sleep: bad argument\n");
+ return -1;
+ }
+
+ ts.tv_nsec = 0;
+ ts.tv_sec = nsec;
+ sleep(&ts, &ts);
+ return 0;
+}
diff --git a/usr.bin/sysctl/Makefile b/usr.bin/sysctl/Makefile
new file mode 100644
index 0000000..e32dbc4
--- /dev/null
+++ b/usr.bin/sysctl/Makefile
@@ -0,0 +1,6 @@
+include user.mk
+
+CFILES = $(shell find . -name "*.c")
+
+$(ROOT)/base/usr/bin/sysctl:
+ gcc $(CFILES) -o $@ $(INTERNAL_CFLAGS)
diff --git a/usr.bin/sysctl/sysctl.c b/usr.bin/sysctl/sysctl.c
new file mode 100644
index 0000000..4a84484
--- /dev/null
+++ b/usr.bin/sysctl/sysctl.c
@@ -0,0 +1,314 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/sysctl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdbool.h>
+
+#define BUF_SIZE 128
+
+/* Kern var string constants */
+#define NAME_OSTYPE "ostype"
+#define NAME_OSRELEASE "osrelease"
+#define NAME_VERSION "version"
+#define NAME_VCACHE_TYPE "vcache_type"
+
+/* Hw var string constants */
+#define NAME_PAGESIZE "pagesize"
+#define NAME_NCPU "ncpu"
+#define NAME_MACHINE "machine"
+
+/* Proc var string constants */
+#define NAME_COUNT "count"
+
+/* Name start string constants */
+#define NAME_KERN "kern"
+#define NAME_HW "hw"
+#define NAME_PROC "proc"
+
+/* Name start int constants */
+#define NAME_DEF_KERN 0
+#define NAME_DEF_HW 1
+#define NAME_DEF_PROC 2
+
+/*
+ * Print the contents read from a sysctl
+ * variable depending on its type.
+ *
+ * @data: Data to print
+ * @is_str: True if a string
+ */
+static inline void
+varbuf_print(char data[BUF_SIZE], bool is_str)
+{
+ uint32_t *val;
+
+ if (is_str) {
+ printf("%s\n", data);
+ } else {
+ val = (uint32_t *)data;
+ printf("%d\n", *val);
+ }
+}
+
+/*
+ * Convert string name to a internal name
+ * definition.
+ *
+ * @name: Name to convert
+ *
+ * Convert to int def
+ * /
+ * kern.ostype
+ * ^^
+ *
+ * --
+ * Returns a less than zero value on failure
+ * (e.g., entry not found).
+ */
+static int
+name_to_def(const char *name)
+{
+ switch (*name) {
+ case 'k':
+ if (strcmp(name, NAME_KERN) == 0) {
+ return NAME_DEF_KERN;
+ }
+
+ return -1;
+ case 'h':
+ if (strcmp(name, NAME_HW) == 0) {
+ return NAME_DEF_HW;
+ }
+
+ return -1;
+ case 'p':
+ if (strcmp(name, NAME_PROC) == 0) {
+ return NAME_DEF_PROC;
+ }
+
+ return -1;
+ }
+
+ return -1;
+}
+
+/*
+ * Handle parsing of 'kern.*' node names
+ *
+ * @node: Node name to parse
+ * @is_str: Set to true if string
+ */
+static int
+kern_node(const char *node, bool *is_str)
+{
+ switch (*node) {
+ case 'v':
+ if (strcmp(node, NAME_VERSION) == 0) {
+ return KERN_VERSION;
+ }
+
+ if (strcmp(node, NAME_VCACHE_TYPE) == 0) {
+ return KERN_VCACHE_TYPE;
+ }
+ return -1;
+ case 'o':
+ if (strcmp(node, NAME_OSTYPE) == 0) {
+ return KERN_OSTYPE;
+ }
+
+ if (strcmp(node, NAME_OSRELEASE) == 0) {
+ return KERN_OSRELEASE;
+ }
+ return -1;
+ }
+
+ return -1;
+}
+
+/*
+ * Handle parsing of 'hw.*' node names
+ *
+ * @node: Node name to parse
+ * @is_str: Set to true if string
+ */
+static int
+hw_node(const char *node, bool *is_str)
+{
+ switch (*node) {
+ case 'p':
+ if (strcmp(node, NAME_PAGESIZE) == 0) {
+ *is_str = false;
+ return HW_PAGESIZE;
+ }
+
+ return -1;
+ case 'n':
+ if (strcmp(node, NAME_NCPU) == 0) {
+ *is_str = false;
+ return HW_NCPU;
+ }
+
+ return -1;
+ case 'm':
+ if (strcmp(node, NAME_MACHINE) == 0) {
+ return HW_MACHINE;
+ }
+ return -1;
+ }
+
+ return -1;
+}
+
+/*
+ * Handle parsing of 'proc.*' node names
+ *
+ * @node: Node name to parse
+ * @is_str: Set to true if string
+ */
+static int
+proc_node(const char *node, bool *is_str)
+{
+ switch (*node) {
+ case 'c':
+ if (strcmp(node, NAME_COUNT) == 0) {
+ *is_str = false;
+ return PROC_COUNT;
+ }
+
+ return -1;
+ }
+
+ return -1;
+}
+
+/*
+ * Convert string node to a sysctl name
+ * definition.
+ *
+ * @name: Name to convert
+ * @is_str: Set to true if string
+ *
+ * Convert to int def
+ * /
+ * kern.ostype
+ * ^^ name
+ *
+ * --
+ * Returns a less than zero value on failure
+ * (e.g., entry not found).
+ */
+static int
+node_to_def(int name, const char *node, bool *is_str)
+{
+ int retval;
+ bool dmmy;
+
+ /*
+ * If the caller did not set `is_str' just
+ * set it to a dummy value. Otherwise, we will
+ * make it *default* to a 'true' value.
+ */
+ if (is_str == NULL) {
+ is_str = &dmmy;
+ } else {
+ *is_str = true;
+ }
+
+ switch (name) {
+ case NAME_DEF_KERN:
+ return kern_node(node, is_str);
+ case NAME_DEF_HW:
+ return hw_node(node, is_str);
+ case NAME_DEF_PROC:
+ return proc_node(node, is_str);
+ }
+
+ return -1;
+}
+
+int
+main(int argc, char **argv)
+{
+ struct sysctl_args args;
+ char *var, *p;
+ int type, error;
+ int root, name;
+ size_t oldlen;
+ bool is_str;
+ char buf[BUF_SIZE];
+
+ if (argc < 2) {
+ printf("sysctl: usage: sysctl <var>\n");
+ return -1;
+ }
+
+ var = argv[1];
+ p = strtok(var, ".");
+
+ if (p == NULL) {
+ printf("sysctl: bad var \"%s\"\n", p);
+ return -1;
+ }
+
+ if ((root = name_to_def(p)) < 0) {
+ printf("sysctl: bad var \"%s\"\n", p);
+ return root;
+ }
+
+ p = strtok(NULL, ".");
+ if (p == NULL) {
+ printf("sysctl: bad var \"%s\"\n", p);
+ return -1;
+ }
+
+ if ((name = node_to_def(root, p, &is_str)) < 0) {
+ printf("sysctl: bad var \"%s\"\n", p);
+ return name;
+ }
+
+ memset(buf, 0, sizeof(buf));
+ oldlen = sizeof(buf);
+ args.name = &name;
+ args.nlen = 1;
+ args.oldp = buf;
+ args.oldlenp = &oldlen;
+ args.newp = NULL;
+ args.newlen = 0;
+
+ if ((error = sysctl(&args)) != 0) {
+ printf("sysctl returned %d\n", error);
+ return error;
+ }
+
+ varbuf_print(buf, is_str);
+ return 0;
+}
diff --git a/usr.bin/whoami/Makefile b/usr.bin/whoami/Makefile
new file mode 100644
index 0000000..ced9ae2
--- /dev/null
+++ b/usr.bin/whoami/Makefile
@@ -0,0 +1,6 @@
+include user.mk
+
+CFILES = $(shell find . -name "*.c")
+
+$(ROOT)/base/usr/bin/whoami:
+ gcc $(CFILES) -o $@ $(INTERNAL_CFLAGS)
diff --git a/usr.bin/whoami/whoami.c b/usr.bin/whoami/whoami.c
new file mode 100644
index 0000000..c3adcf0
--- /dev/null
+++ b/usr.bin/whoami/whoami.c
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+
+int
+main(void)
+{
+ printf("%s\f\n", getlogin());
+ return 0;
+}
diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile
new file mode 100644
index 0000000..870848c
--- /dev/null
+++ b/usr.sbin/Makefile
@@ -0,0 +1,10 @@
+LDSCRIPT =
+USRDIR =
+ROOT =
+ARGS = -I$(ROOT)/builddeps LDSCRIPT=$(LDSCRIPT) USRDIR=$(USRDIR) ROOT=$(ROOT)
+
+.PHONY: all
+all:
+ make -C init/ $(ARGS)
+ make -C install/ $(ARGS)
+ make -C inject/ $(ARGS)
diff --git a/usr.sbin/init/Makefile b/usr.sbin/init/Makefile
index bee9f3a..5f95379 100644
--- a/usr.sbin/init/Makefile
+++ b/usr.sbin/init/Makefile
@@ -2,5 +2,5 @@ include user.mk
CFILES = $(shell find . -name "*.c")
-init:
+$(ROOT)/base/usr/sbin/init:
gcc $(CFILES) -o $@ $(INTERNAL_CFLAGS)
diff --git a/usr.sbin/init/main.c b/usr.sbin/init/main.c
index a136740..12bb98c 100644
--- a/usr.sbin/init/main.c
+++ b/usr.sbin/init/main.c
@@ -27,8 +27,68 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
+#include <sys/spawn.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+
+#define log_trace(fmt, ...) printf("[init]: " fmt, ##__VA_ARGS__)
+#define log_error(fmt, ...) printf("[error]: " fmt, ##__VA_ARGS__)
+
+#define SHELL_PATH "/usr/bin/osh"
+#define LOGIN_PATH "/usr/bin/login"
+#define INIT_RC_PATH "/usr/rc/init.rc"
+
+static void
+init_hostname(void)
+{
+ char hostname[128];
+ size_t len;
+ FILE *fp;
+
+ fp = fopen("/etc/hostname", "r");
+ if (fp == NULL) {
+ log_error("[init]: error opening /etc/hostname\n");
+ return;
+ }
+
+ len = fread(hostname, sizeof(char), sizeof(hostname), fp);
+ if (len == 0) {
+ log_error("[init]: error reading /etc/hostname\n");
+ fclose(fp);
+ return;
+ }
+
+ hostname[len - 1] = '\0';
+ if (sethostname(hostname, len) < 0) {
+ log_error("[init]: error setting hostname\n");
+ log_error("[init]: tried to set %s (len=%d)\n", hostname, len);
+ fclose(fp);
+ return;
+ }
+
+ log_trace("hostname -> %s\n", hostname);
+ fclose(fp);
+}
+
int
-main(void)
+main(int argc, char **argv)
{
+ char *login_argv[] = { LOGIN_PATH, NULL };
+ char *start_argv[] = { SHELL_PATH, INIT_RC_PATH, NULL };
+ char *envp[] = { NULL };
+
+ /* Initialize the system hostname */
+ init_hostname();
+
+ /* Start the init.rc */
+ log_trace("init.rc up\n");
+ spawn(SHELL_PATH, start_argv, envp, 0);
+ start_argv[1] = NULL;
+
+ /* Start the login manager */
+ spawn(login_argv[0], login_argv, envp, 0);
+ for (;;);
return 0;
}
diff --git a/usr.sbin/inject/Makefile b/usr.sbin/inject/Makefile
new file mode 100644
index 0000000..7175ae9
--- /dev/null
+++ b/usr.sbin/inject/Makefile
@@ -0,0 +1,6 @@
+include user.mk
+
+CFILES = $(shell find . -name "*.c")
+
+$(ROOT)/base/usr/sbin/inject:
+ gcc $(CFILES) -o $@ $(INTERNAL_CFLAGS)
diff --git a/usr.sbin/inject/inject.c b/usr.sbin/inject/inject.c
new file mode 100644
index 0000000..514ca82
--- /dev/null
+++ b/usr.sbin/inject/inject.c
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/krq.h>
+#include <stdio.h>
+
+int
+main(int argc, char **argv)
+{
+ char *path = NULL;
+
+ if (argc > 1) {
+ path = argv[1];
+ }
+
+ return inject(path);
+}
diff --git a/usr.sbin/install/Makefile b/usr.sbin/install/Makefile
new file mode 100644
index 0000000..36c5969
--- /dev/null
+++ b/usr.sbin/install/Makefile
@@ -0,0 +1,6 @@
+include user.mk
+
+CFILES = $(shell find . -name "*.c")
+
+$(ROOT)/base/usr/sbin/install:
+ gcc $(CFILES) -o $@ $(INTERNAL_CFLAGS)
diff --git a/usr.sbin/install/install.c b/usr.sbin/install/install.c
new file mode 100644
index 0000000..803c864
--- /dev/null
+++ b/usr.sbin/install/install.c
@@ -0,0 +1,315 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/mman.h>
+#include <sys/disklabel.h>
+#include <sys/fbdev.h>
+#include <sys/reboot.h>
+#include <sys/spawn.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stddef.h>
+#include <string.h>
+#include <stdbool.h>
+
+#define TEXT_STYLE "\033[37;44m"
+#define INSTALLER_BG 0x00007F
+#define BLOCK_SIZE 512
+#define WAIT_KEY(VAR, C) while (((VAR = getchar())) != (C))
+
+static struct fbattr fb_attr;
+static void *fbmem;
+
+struct progress_bar {
+ bool dec;
+ uint8_t progress;
+};
+
+/*
+ * Clear the screen to a given background color
+ *
+ * @color: RGB value of chosen color
+ * @setattr: Sets default text style if true
+ */
+static void
+installer_clearscr(uint32_t color, bool setattr)
+{
+ size_t fb_size;
+ uint32_t *p;
+
+ if (setattr) {
+ puts(TEXT_STYLE);
+ }
+
+ puts("\033[H");
+ fb_size = fb_attr.height * fb_attr.pitch;
+ p = fbmem;
+
+ for (size_t i = 0; i < fb_size / sizeof(*p); ++i) {
+ p[i] = color;
+ }
+}
+
+static void
+pre_installer(void)
+{
+ char *argv[] = { "/usr/bin/osh", NULL };
+ char *envp[] = { NULL };
+ char c;
+ pid_t child = -1;
+
+ puts("[S]hell/[I]nstall");
+ for (;;) {
+ c = getchar();
+ if (c == 's') {
+ puts("\033[0m");
+ installer_clearscr(0x000000, false);
+ child = spawn(argv[0], argv, envp, 0);
+ installer_clearscr(INSTALLER_BG, true);
+ break;
+ } else if (c == 'i') {
+ break;
+ }
+ }
+
+ if (child > 0) {
+ waitpid(child, NULL, 0);
+ }
+}
+
+static void
+reboot_prompt(void)
+{
+ char c;
+
+ puts("Press 'r' to reboot");
+ WAIT_KEY(c, 'r');
+ cpu_reboot(REBOOT_RESET);
+}
+
+/*
+ * Create a progress bar animation for long
+ * operations.
+ *
+ * @bp: Pointer to a progress bar structure.
+ * @n: Number of blocks operated on.
+ * @max: Max blocks per bar update.
+ */
+static void
+progress_update(struct progress_bar *bp, size_t n, size_t max)
+{
+ /*
+ * We only want to update the progress bar
+ * per `max' blocks written.
+ */
+ if ((n > 0) && (n % max) != 0) {
+ return;
+ }
+
+ /* Add more '.' chars */
+ if (bp->progress < 8 && !bp->dec) {
+ fwrite(".", sizeof(char), 1, stdout);
+ } else if (bp->progress >= 8) {
+ bp->dec = true;
+ }
+
+ /* Remove '.' chars */
+ if (bp->dec && bp->progress > 0) {
+ fwrite("\b\f", sizeof(char), 2, stdout);
+ } else if (bp->progress == 0) {
+ bp->dec = false;
+ }
+
+ if (!bp->dec) {
+ ++bp->progress;
+ } else {
+ --bp->progress;
+ }
+}
+
+/*
+ * Wipe a number of blocks beginning at the current
+ * fd offset.
+ *
+ * @hdd_fd: Target drive fd
+ * @count: Number of bytes to wipe
+ */
+static void
+installer_wipe(int hdd_fd, uint32_t count)
+{
+ struct progress_bar bar = {0, 0};
+ size_t write_blocks, nblocks;
+ char buf[BLOCK_SIZE * 2];
+
+ if (__unlikely(count == 0)) {
+ puts("bad count for /dev/sd1");
+ reboot_prompt();
+ }
+
+ count = ALIGN_UP(count, sizeof(buf));
+ memset(buf, 0, sizeof(buf));
+ write_blocks = sizeof(buf) / BLOCK_SIZE;
+ nblocks = count / BLOCK_SIZE;
+
+ /* Zero that shit */
+ puts("zeroing...");
+ for (int i = 0; i < nblocks; i += write_blocks) {
+ write(hdd_fd, buf, sizeof(buf));
+ progress_update(&bar, i, 256);
+ }
+
+ lseek(hdd_fd, 0, SEEK_SET);
+ puts("OK");
+}
+
+/*
+ * Write data to the drive.
+ *
+ * @hdd: HDD file descriptor
+ * @p: Data pointer
+ * @len: Length of data.
+ */
+static void
+installer_write(int hdd_fd, int file_fd, void *p, size_t len)
+{
+ size_t nblocks;
+ struct progress_bar bar = {0, 0};
+ char buf[BLOCK_SIZE];
+ char *bufp;
+
+ len = ALIGN_UP(len, BLOCK_SIZE);
+ nblocks = len / BLOCK_SIZE;
+ bufp = (p == NULL || len < BLOCK_SIZE) ? buf : p;
+
+ if (len < BLOCK_SIZE) {
+ memcpy(buf, p, len);
+ }
+
+ for (size_t i = 0; i < nblocks; ++i) {
+ if (file_fd > 0) {
+ read(file_fd, bufp, BLOCK_SIZE);
+ }
+
+ write(hdd_fd, bufp, BLOCK_SIZE);
+ progress_update(&bar, i, 128);
+ }
+
+ puts("OK");
+}
+
+static void
+installer_run(void)
+{
+ struct stat hdd_sb, iso_sb;
+ struct disklabel label;
+ const char *hdd_path = "/dev/sd1";
+ const char *iso_path = "/boot/Hyra.iso";
+ char buf[256];
+ int hdd_fd, iso_fd, error;
+ int n;
+ char c;
+
+ pre_installer();
+
+ if ((hdd_fd = open(hdd_path, O_RDWR)) < 0) {
+ puts("No available devices to target!");
+ reboot_prompt();
+ }
+ if (stat(hdd_path, &hdd_sb) < 0) {
+ puts("hdd stat() failure\n");
+ reboot_prompt();
+ }
+
+ snprintf(buf, sizeof(buf), "/dev/sd1 (%d sectors) [a]", hdd_sb.st_size);
+ puts("Please choose which device to target");
+ puts(buf);
+ WAIT_KEY(c, 'a');
+
+ /* Wait for y/n option */
+ puts("\033[37;41m!! DRIVE WILL BE WIPED !!" TEXT_STYLE);
+ puts("Are you sure? [y/n]");
+ WAIT_KEY(c, 'y') {
+ if (c == 'n') {
+ reboot_prompt();
+ }
+ }
+
+ if ((iso_fd = open(iso_path, O_RDONLY)) < 0) {
+ puts("failed to read install data\n");
+ reboot_prompt();
+ }
+ if (stat(iso_path, &iso_sb) < 0) {
+ puts("hdd stat() failure\n");
+ reboot_prompt();
+ }
+
+ /* Prepare the parition table */
+ label.magic = DISK_MAG;
+ label.sect_size = BLOCK_SIZE;
+
+ installer_wipe(hdd_fd, iso_sb.st_size + sizeof(label));
+ puts("writing install data");
+ installer_write(hdd_fd, iso_fd, NULL, iso_sb.st_size);
+ puts("writing parition table");
+ installer_write(hdd_fd, -1, &label, sizeof(label));
+
+ puts("\nInstallation complete!");
+ reboot_prompt();
+}
+
+int
+main(void)
+{
+ int fb_fd, fbattr_fd, prot;
+ size_t fb_size;
+
+ if ((fb_fd = open("/dev/fb0", O_RDWR)) < 0) {
+ puts("FATAL: failed to open /dev/fb0");
+ return fb_fd;
+ }
+ if ((fbattr_fd = open("/ctl/fb0/attr", O_RDONLY)) < 0) {
+ puts("FATAL: failed to open /ctl/fb0/attr");
+ return fbattr_fd;
+ }
+
+ read(fbattr_fd, &fb_attr, sizeof(fb_attr));
+ fb_size = fb_attr.height * fb_attr.pitch;
+
+ prot = PROT_READ | PROT_WRITE;
+ fbmem = mmap(NULL, fb_size, prot, MAP_SHARED, fb_fd, 0);
+
+ installer_clearscr(INSTALLER_BG, true);
+ puts("Welcome to Hyra/" _OSARCH " v" _OSVER "!");
+ installer_run();
+ return 0;
+}
diff --git a/usr.sbin/link.ld b/usr.sbin/link.ld
index 9fad881..5e99291 100644
--- a/usr.sbin/link.ld
+++ b/usr.sbin/link.ld
@@ -23,4 +23,9 @@ SECTIONS
*(.bss.*)
__bss_end = .;
}
+
+ /DISCARD/ : {
+ *(.eh_frame)
+ *(.note .note.*)
+ }
}