From c0ad63c27f1808e67da7ca06606dfaf1d9afa3d4 Mon Sep 17 00:00:00 2001 From: Maksymilian Wilczynski Date: Mon, 12 Oct 2026 18:17:43 +0200 Subject: [PATCH] clean up and standardize types across the codebase, unify atomic operations, improve UTF-8 handling throughout the system, remove obsolete input and named queue interfaces, redesign process preparation and creation, tighten IPC and service boundaries, improve error handling and reliability, minor documentation cleanup, fix doom surfaces, fix panic in ini parsing. --- OVMF/OVMF_VARS_4M_d.fd | Bin 540672 -> 540672 bytes code/build/build.c | 12 +- .../include/ntlite/internal/session_control.h | 9 +- code/drivers/audio/hda/hda.h | 8 +- code/drivers/audio/hda/hda_codec.c | 9 +- code/drivers/audio/hda/hda_transport.c | 10 +- code/drivers/bus/usb/usb.h | 4 +- code/drivers/bus/usb/usb_hid.c | 14 +- code/drivers/bus/usb/usb_storage.c | 7 +- code/drivers/display/vmwaresvga/vmwaresvga.c | 89 +- code/drivers/display/vmwaresvga/vmwaresvga.h | 16 +- code/drivers/fs/ext4/ext4_driver.c | 10 +- code/drivers/fs/fat/fat.c | 35 +- code/drivers/fs/fat/fat_directory.c | 197 +- code/drivers/fs/fat/fat_driver.c | 25 +- code/drivers/fs/fat/fat_file.c | 3 +- code/drivers/fs/fat/fat_internal.h | 15 +- code/drivers/input/ps2/ps2.c | 4 +- code/drivers/input/ps2/ps2.h | 4 +- code/drivers/net/lwip/lwip_stack.c | 57 +- code/drivers/storage/ahci/ahci.c | 16 +- code/drivers/storage/ahci/ahci.h | 2 +- code/kernel/hal/hal_main.c | 72 +- code/kernel/hal/hal_output.c | 52 +- code/kernel/hal/hal_screen.c | 50 +- code/kernel/hal/hal_serial.c | 13 +- code/kernel/include/kernel/hal/hal_api.h | 21 +- code/kernel/include/kernel/io_volume.h | 3 +- code/kernel/include/kernel/ke_apc.h | 2 +- code/kernel/include/kernel/ke_audio.h | 10 +- code/kernel/include/kernel/ke_cpu.h | 8 +- code/kernel/include/kernel/ke_debug.h | 7 +- code/kernel/include/kernel/ke_display.h | 3 +- code/kernel/include/kernel/ke_id.h | 4 +- code/kernel/include/kernel/ke_input.h | 72 +- code/kernel/include/kernel/ke_irql.h | 3 +- code/kernel/include/kernel/ke_mutex.h | 7 +- code/kernel/include/kernel/ke_named_queue.h | 12 - code/kernel/include/kernel/ke_process.h | 15 +- code/kernel/include/kernel/ke_sched.h | 11 +- code/kernel/include/kernel/ke_thread.h | 31 +- code/kernel/include/kernel/ke_timer.h | 5 +- code/kernel/include/kernel/ke_trap.h | 3 +- code/kernel/include/kernel/ke_work.h | 2 +- code/kernel/include/kernel/mm_file_cache.h | 3 +- code/kernel/include/kernel/mm_pte.h | 7 +- code/kernel/include/kernel/mm_swap.h | 5 +- code/kernel/include/kernel/mm_virt.h | 3 +- code/kernel/include/kernel/syscall_nr.h | 228 +- code/kernel/io_device.c | 13 +- code/kernel/io_dma.c | 162 +- code/kernel/io_file.c | 9 +- code/kernel/io_pipe.c | 14 +- code/kernel/io_volume.c | 5 +- code/kernel/ke_apc.c | 8 +- code/kernel/ke_audio.c | 143 +- code/kernel/ke_boot_config.c | 10 +- code/kernel/ke_cpu.c | 12 +- code/kernel/ke_debug.c | 77 +- code/kernel/ke_display.c | 209 +- code/kernel/ke_dpc.c | 10 +- code/kernel/ke_event.c | 25 +- code/kernel/ke_hal.c | 3 +- code/kernel/ke_handle.c | 29 +- code/kernel/ke_id.c | 9 +- code/kernel/ke_input.c | 511 +-- code/kernel/ke_irp.c | 10 +- code/kernel/ke_irql.c | 3 +- code/kernel/ke_mutex.c | 14 +- code/kernel/ke_port.c | 95 +- code/kernel/ke_process.c | 46 +- code/kernel/ke_sched.c | 105 +- code/kernel/ke_settings.c | 7 +- code/kernel/ke_smp.c | 105 +- code/kernel/ke_surface.c | 24 +- code/kernel/ke_syscall.c | 597 +--- code/kernel/ke_thread.c | 56 +- code/kernel/ke_timer.c | 3 +- code/kernel/ke_trap.c | 5 +- code/kernel/ke_work.c | 25 +- code/kernel/kmain.c | 47 +- code/kernel/ldr_image.c | 3 +- code/kernel/mm_file_cache.c | 27 +- code/kernel/mm_phys.c | 21 +- code/kernel/mm_pool.c | 17 +- code/kernel/mm_pte.c | 46 +- code/kernel/mm_swap.c | 13 +- code/kernel/mm_virt.c | 118 +- code/kernel/ob_object.c | 18 +- code/public/freestanding/intrin.h | 17 + code/public/freestanding/stddef.h | 4 + code/public/ntlite/atomic.h | 98 + code/public/ntlite/audio.h | 11 +- code/public/ntlite/boot_info.h | 14 + code/public/ntlite/console_service.h | 23 +- code/public/ntlite/ddi/display.h | 20 +- code/public/ntlite/ddi/input_device.h | 2 +- code/public/ntlite/ddi/io_driver.h | 2 +- code/public/ntlite/ddk/async.h | 3 +- code/public/ntlite/ddk/audio.h | 7 +- code/public/ntlite/ddk/debug.h | 2 +- code/public/ntlite/ddk/display.h | 26 +- code/public/ntlite/ddk/dpc.h | 2 +- code/public/ntlite/ddk/event.h | 9 +- code/public/ntlite/ddk/irp.h | 2 +- code/public/ntlite/ddk/irql.h | 3 +- code/public/ntlite/ddk/mutex.h | 5 +- code/public/ntlite/ddk/object.h | 7 +- code/public/ntlite/ddk/sync.h | 16 +- code/public/ntlite/desktop.h | 24 +- code/public/ntlite/display.h | 10 +- code/public/ntlite/exception.h | 15 +- code/public/ntlite/file_stat.h | 6 + code/public/ntlite/handle.h | 8 +- code/public/ntlite/input.h | 19 +- code/public/ntlite/input_service.h | 2 +- code/public/ntlite/memory_map.h | 8 + code/public/ntlite/port.h | 7 + code/public/ntlite/posix_service.h | 10 +- code/public/ntlite/posix_signal.h | 3 +- code/public/ntlite/process_loader.h | 50 +- code/public/ntlite/security.h | 5 +- code/public/ntlite/status.h | 3 + code/public/ntlite/string_view.h | 148 +- code/public/ntlite/syscall.h | 124 +- code/public/ntlite/time.h | 5 + code/user/apps/doom/doom.c | 89 +- code/user/apps/doom/doom_audio.c | 29 +- code/user/apps/edit/edit.c | 2 +- code/user/apps/httpget/httpget.c | 10 +- code/user/apps/ping/ping.c | 6 +- code/user/apps/resolve/resolve.c | 7 +- code/user/apps/shell_native/shell_native.c | 111 +- code/user/apps/shell_posix/shell_posix.c | 32 +- code/user/apps/shell_win32/shell_win32.c | 30 +- code/user/apps/tcpserve/tcpserve.c | 6 +- code/user/apps/udp/udp.c | 14 +- code/user/apps/video/video.c | 17 +- code/user/personalities/posix/posix_dir.c | 10 +- code/user/personalities/posix/posix_dlfcn.c | 8 +- code/user/personalities/posix/posix_fd.c | 43 +- code/user/personalities/posix/posix_fd.h | 6 +- .../user/personalities/posix/posix_internal.h | 6 +- code/user/personalities/posix/posix_io.c | 38 +- code/user/personalities/posix/posix_mman.c | 4 +- code/user/personalities/posix/posix_path.c | 9 +- code/user/personalities/posix/posix_runtime.c | 10 +- code/user/personalities/posix/posix_signal.c | 15 +- code/user/personalities/posix/posix_socket.c | 63 +- code/user/personalities/posix/posix_thread.c | 12 +- code/user/personalities/win32/kernel32.c | 242 +- code/user/personalities/win32/winmm_waveout.c | 52 +- code/user/runtime/heap.c | 8 +- code/user/runtime/native_exception.c | 38 +- code/user/runtime/native_start.c | 3 +- code/user/runtime/posix_start.c | 3 +- code/user/syscall/console_service.c | 19 +- code/user/syscall/desktop.c | 2 +- code/user/syscall/input_service.c | 14 +- code/user/syscall/module_loader.c | 40 +- code/user/syscall/module_loader_internal.h | 3 +- code/user/syscall/path.c | 22 +- code/user/syscall/process_loader.c | 199 +- code/user/syscall/syscall_nr.inc | 228 +- code/user/syscall/syscall_stubs.asm | 18 +- code/user/system/dwm/dwm.c | 20 +- code/user/system/init/service_config.c | 3 +- code/user/system/posixsrv/posixsrv.c | 183 +- code/user/system/rssrv/console.c | 8 + code/user/system/rssrv/display.c | 8 +- code/user/system/rssrv/input.c | 32 +- code/user/system/rssrv/namespace.c | 3 +- code/user/system/rssrv/resource_service.c | 8 +- code/user/system/rssrv/rssrv.c | 2 +- code/user/system/rssrv/rssrv_internal.h | 19 +- code/user/system/rssrv/session.c | 4 +- code/user/system/rssrv/volume.c | 9 +- code/user/tests/all_tests/all_tests.c | 10 +- .../anonymous_queue_smoke.c | 138 + .../tests/apc_return_smoke/apc_return_smoke.c | 6 +- code/user/tests/async_apc/async_apc.c | 6 +- code/user/tests/async_cancel/async_cancel.c | 6 +- .../audio_stream_smoke/audio_stream_smoke.c | 17 + .../cwd_userspace_smoke/cwd_userspace_smoke.c | 8 +- .../tests/dirty_writeback/dirty_writeback.c | 18 +- .../exception_dispatcher_smoke.c | 8 +- .../tests/exception_smoke/exception_smoke.c | 15 +- .../tests/file_lock_smoke/file_lock_smoke.c | 29 +- .../tests/file_stat_smoke/file_stat_smoke.c | 19 +- .../tests/input_api_smoke/input_api_smoke.c | 122 - .../input_model_smoke/input_model_smoke.c | 135 - code/user/tests/mmap_smoke/mmap_smoke.c | 6 +- .../module_load_smoke/module_load_smoke.c | 17 +- .../named_queue_lifetime_smoke.c | 197 - .../native_exception_runtime_smoke.c | 9 +- .../native_raise_exception_smoke.c | 9 +- code/user/tests/net_irp_smoke/net_irp_smoke.c | 16 +- .../pcache_invalidate/pcache_invalidate.c | 10 +- .../pcache_mmap_read_reuse.c | 6 +- .../pcache_multithread/pcache_multithread.c | 10 +- .../tests/pcache_pressure/pcache_pressure.c | 13 +- code/user/tests/pcache_smoke/pcache_smoke.c | 4 +- .../tests/posix_fd_smoke/posix_fd_smoke.c | 3 +- .../posix_process_smoke/posix_process_smoke.c | 3 +- .../posix_service_smoke/posix_service_smoke.c | 11 +- .../posix_signal_smoke/posix_signal_smoke.c | 3 +- .../process_builder_smoke.c | 113 +- .../process_exit_multithread_smoke.c | 3 +- .../process_protection_smoke.c | 6 +- .../tests/realpath_smoke/realpath_smoke.c | 6 +- .../resource_service_smoke.c | 17 + .../tests/security_smoke/security_smoke.c | 14 - code/user/tests/shared_mmap/shared_mmap.c | 18 +- .../socket_addr_smoke/socket_addr_smoke.c | 8 +- .../tests/stats_abi_smoke/stats_abi_smoke.c | 6 +- .../surface_invalid_rect.c | 4 +- .../surface_multithread_commit.c | 8 +- code/user/tests/surface_smoke/surface_smoke.c | 4 +- .../syscall_contract_smoke.c | 85 +- .../thread_name_smoke/thread_name_smoke.c | 26 +- code/user/tests/timer_smoke/timer_smoke.c | 14 +- .../win32_thread_suspend_smoke.c | 4 +- .../winmm_waveout_smoke/winmm_waveout_smoke.c | 14 +- .../would_block_smoke/would_block_smoke.c | 8 +- docs.md | 4 +- ntlite_remaining_roadmap.md | 3154 +++++++++++++++++ 226 files changed, 6562 insertions(+), 4147 deletions(-) delete mode 100644 code/kernel/include/kernel/ke_named_queue.h create mode 100644 code/public/ntlite/atomic.h create mode 100644 code/user/tests/anonymous_queue_smoke/anonymous_queue_smoke.c delete mode 100644 code/user/tests/input_api_smoke/input_api_smoke.c delete mode 100644 code/user/tests/input_model_smoke/input_model_smoke.c delete mode 100644 code/user/tests/named_queue_lifetime_smoke/named_queue_lifetime_smoke.c create mode 100644 ntlite_remaining_roadmap.md diff --git a/OVMF/OVMF_VARS_4M_d.fd b/OVMF/OVMF_VARS_4M_d.fd index b8233de92a247cba309f4720809a4931cf58af7f..9a9904a720eecdad13438f29e9738b629da6ac2b 100644 GIT binary patch delta 2089 zcma)7Uu;ul6#ovr?Yddq9ZrMWz^xfw|K0BHuD5iswYP)^0-9_AVc;CCD1v~gse+Lu zlqh>~iGh<4NJwma=qrRv!izD{@Bk(f-2*SkzXW1}FKFVc^ZV29c6Ya^efjmCbAIQX z``vSXU$r_=tqxo{7{l301Q%lGIc|&pz)?>ua*QVbVXuIF0^SlZBj9ZTvjX-DC<-VEct^ki0q;_{`vO`Uyfto%fVZ&@V;L+T2Q+UhgG#&S>Qtn7#6dmyngcpSoSCeEom=X zU-O6}KHkucwOH_+5n3G}tB8*%#827?!Ly@7SZr4ygdY`#Ab?%QFn{h~&+P$ET#(s= z0l{W-H^{>S%nhHyu01!Thl?KmzEH;R~(M4 zNIA4~oT)@nPlUn1or&F`;J2EEPZMkLn{?RKxCr<0GSOrhMsY_{#7FRcYBS}$?3Cay zA7Y)d66|nz8A}U88V_jGyv#mX!hM)cj-S^WPldt6{V73WwLZg#BT036c6Ri%!gicZ z1@Z5~@#kk`pNC1q`z{s8FPjDS^?N%!QXt1yH`n3i{F{y(c&PX#^){A{N`f36!5el~ zib=y4&bY0^&detsVTi133x78+(kLklQzM2agIg3@Wwuy9stN+Xr^gIs{^2_{_+4a>hyS z4iS6i>{xS>+4ec%a(4*vJE8xvJEhq7avGtTmG4J?{Hfz=Hp1r`OR)sqokhp*o%^fF z9+2+mPbJI4u1dCZEoWOFjM6)_57oX@-Rv%-+Vp_Uj4_j6Xj)A+Qc;`kbCdB9h%wnf`x2Pe(D?+W9dxg4 zpLCDWiE*-nk;3K*{b^;>uemTeZ4Ov=iL3pUKhySC{>)+r+8aWdwl{<_pP1Jsz|tnb P+9tr(CcwT-fWrU)h@>n0 diff --git a/code/build/build.c b/code/build/build.c index 5860c24..176f3f8 100644 --- a/code/build/build.c +++ b/code/build/build.c @@ -78,7 +78,7 @@ void create_layout() { "obj\\shell_win32", "obj\\shell_posix", "obj\\shell_native", "obj\\async_apc", "obj\\async_cancel", "obj\\apc_return_smoke", "obj\\edit", "obj\\httpget", "obj\\tcpserve", "obj\\udp", "obj\\resolve", "obj\\ping", "obj\\doom", "obj\\video", "obj\\memtop", - "obj\\file_stat_smoke", "obj\\handle_dup_smoke", "obj\\security_smoke", "obj\\port_ipc_smoke", "obj\\resource_service_smoke", "obj\\named_queue_lifetime_smoke", "obj\\input_api_smoke", "obj\\input_model_smoke", "obj\\input_service_smoke", "obj\\console_service_smoke", "obj\\desktop_smoke", "obj\\timer_smoke", "obj\\stats_abi_smoke", "obj\\realpath_smoke", "obj\\file_lock_smoke", "obj\\process_protection_smoke", "obj\\process_exit_multithread_smoke", "obj\\thread_name_smoke", "obj\\process_builder_smoke", "obj\\socket_addr_smoke", "obj\\syscall_contract_smoke", "obj\\would_block_smoke", "obj\\audio_stream_smoke", "obj\\winmm_waveout_smoke", "obj\\win32_condition_variable_smoke", "obj\\win32_mutex_smoke", "obj\\win32_thread_suspend_smoke", "obj\\win32_module_lookup_smoke", "obj\\cwd_userspace_smoke", "obj\\mmap_smoke", "obj\\pcache_smoke", "obj\\pcache_pressure", "obj\\dirty_writeback", "obj\\shared_mmap", + "obj\\file_stat_smoke", "obj\\handle_dup_smoke", "obj\\security_smoke", "obj\\port_ipc_smoke", "obj\\resource_service_smoke", "obj\\anonymous_queue_smoke", "obj\\input_service_smoke", "obj\\console_service_smoke", "obj\\desktop_smoke", "obj\\timer_smoke", "obj\\stats_abi_smoke", "obj\\realpath_smoke", "obj\\file_lock_smoke", "obj\\process_protection_smoke", "obj\\process_exit_multithread_smoke", "obj\\thread_name_smoke", "obj\\process_builder_smoke", "obj\\socket_addr_smoke", "obj\\syscall_contract_smoke", "obj\\would_block_smoke", "obj\\audio_stream_smoke", "obj\\winmm_waveout_smoke", "obj\\win32_condition_variable_smoke", "obj\\win32_mutex_smoke", "obj\\win32_thread_suspend_smoke", "obj\\win32_module_lookup_smoke", "obj\\cwd_userspace_smoke", "obj\\mmap_smoke", "obj\\pcache_smoke", "obj\\pcache_pressure", "obj\\dirty_writeback", "obj\\shared_mmap", "obj\\pcache_invalidate", "obj\\pcache_mmap_read_reuse", "obj\\pcache_multithread", "obj\\all_tests", "obj\\posix_service_smoke", "obj\\surface_smoke", "obj\\surface_invalid_rect", "obj\\surface_multithread_commit", "obj\\surface_cross_process_smoke", "obj\\net_irp_smoke", "obj\\module_load_smoke", "obj\\posix_module_smoke", "obj\\posix_runtime_smoke", "obj\\posix_fd_smoke", "obj\\posix_process_smoke", "obj\\posix_signal_smoke", "obj\\posix_pipe_smoke", "obj\\exception_dispatcher_smoke", "obj\\native_exception_runtime_smoke", "obj\\native_raise_exception_smoke", "obj\\exception_smoke", "obj\\win32_exception_smoke", "obj\\win32_seh_smoke" }; @@ -119,7 +119,7 @@ bool clean_build_object_directories(void) { "obj\\init", "obj\\rssrv", "obj\\posixsrv", "obj\\dwm", "obj\\shell_win32", "obj\\shell_posix", "obj\\shell_native", "obj\\async_apc", "obj\\async_cancel", "obj\\apc_return_smoke", "obj\\edit", "obj\\httpget", "obj\\tcpserve", "obj\\udp", "obj\\resolve", "obj\\ping", - "obj\\doom", "obj\\video", "obj\\memtop", "obj\\file_stat_smoke", "obj\\handle_dup_smoke", "obj\\security_smoke", "obj\\port_ipc_smoke", "obj\\resource_service_smoke", "obj\\named_queue_lifetime_smoke", "obj\\input_api_smoke", "obj\\input_model_smoke", "obj\\input_service_smoke", "obj\\console_service_smoke", "obj\\desktop_smoke", "obj\\timer_smoke", "obj\\stats_abi_smoke", "obj\\realpath_smoke", "obj\\file_lock_smoke", "obj\\process_protection_smoke", "obj\\process_exit_multithread_smoke", "obj\\thread_name_smoke", "obj\\process_builder_smoke", "obj\\socket_addr_smoke", "obj\\syscall_contract_smoke", "obj\\would_block_smoke", "obj\\audio_stream_smoke", "obj\\winmm_waveout_smoke", "obj\\win32_condition_variable_smoke", "obj\\win32_mutex_smoke", "obj\\win32_thread_suspend_smoke", "obj\\win32_module_lookup_smoke", "obj\\cwd_userspace_smoke", "obj\\mmap_smoke", "obj\\pcache_smoke", "obj\\pcache_pressure", "obj\\dirty_writeback", "obj\\shared_mmap", + "obj\\doom", "obj\\video", "obj\\memtop", "obj\\file_stat_smoke", "obj\\handle_dup_smoke", "obj\\security_smoke", "obj\\port_ipc_smoke", "obj\\resource_service_smoke", "obj\\anonymous_queue_smoke", "obj\\input_service_smoke", "obj\\console_service_smoke", "obj\\desktop_smoke", "obj\\timer_smoke", "obj\\stats_abi_smoke", "obj\\realpath_smoke", "obj\\file_lock_smoke", "obj\\process_protection_smoke", "obj\\process_exit_multithread_smoke", "obj\\thread_name_smoke", "obj\\process_builder_smoke", "obj\\socket_addr_smoke", "obj\\syscall_contract_smoke", "obj\\would_block_smoke", "obj\\audio_stream_smoke", "obj\\winmm_waveout_smoke", "obj\\win32_condition_variable_smoke", "obj\\win32_mutex_smoke", "obj\\win32_thread_suspend_smoke", "obj\\win32_module_lookup_smoke", "obj\\cwd_userspace_smoke", "obj\\mmap_smoke", "obj\\pcache_smoke", "obj\\pcache_pressure", "obj\\dirty_writeback", "obj\\shared_mmap", "obj\\pcache_invalidate", "obj\\pcache_mmap_read_reuse", "obj\\pcache_multithread", "obj\\all_tests", "obj\\posix_service_smoke", "obj\\surface_smoke", "obj\\surface_invalid_rect", "obj\\surface_multithread_commit", "obj\\surface_cross_process_smoke", "obj\\net_irp_smoke", "obj\\module_load_smoke", "obj\\posix_module_smoke", "obj\\posix_runtime_smoke", "obj\\posix_fd_smoke", "obj\\posix_process_smoke", "obj\\posix_signal_smoke", "obj\\posix_pipe_smoke", "obj\\exception_dispatcher_smoke", "obj\\native_exception_runtime_smoke", "obj\\native_raise_exception_smoke", "obj\\exception_smoke", "obj\\win32_exception_smoke", "obj\\win32_seh_smoke" }; @@ -477,9 +477,7 @@ int main(int argc, char** argv) { {"tests\\security_smoke", NATIVE_EXE_LIBS_LITERAL, "security_smoke", NATIVE_EXE_ENTRY_LITERAL}, {"tests\\port_ipc_smoke", NATIVE_EXE_LIBS_LITERAL, "port_ipc_smoke", NATIVE_EXE_ENTRY_LITERAL}, {"tests\\resource_service_smoke", NATIVE_EXE_LIBS_LITERAL, "resource_service_smoke", NATIVE_EXE_ENTRY_LITERAL}, - {"tests\\named_queue_lifetime_smoke", NATIVE_EXE_LIBS_LITERAL, "named_queue_lifetime_smoke", NATIVE_EXE_ENTRY_LITERAL}, - {"tests\\input_api_smoke", NATIVE_EXE_LIBS_LITERAL, "input_api_smoke", NATIVE_EXE_ENTRY_LITERAL}, - {"tests\\input_model_smoke", NATIVE_EXE_LIBS_LITERAL, "input_model_smoke", NATIVE_EXE_ENTRY_LITERAL}, + {"tests\\anonymous_queue_smoke", NATIVE_EXE_LIBS_LITERAL, "anonymous_queue_smoke", NATIVE_EXE_ENTRY_LITERAL}, {"tests\\input_service_smoke", NATIVE_EXE_LIBS_LITERAL, "input_service_smoke", NATIVE_EXE_ENTRY_LITERAL}, {"tests\\console_service_smoke", NATIVE_EXE_LIBS_LITERAL, "console_service_smoke", NATIVE_EXE_ENTRY_LITERAL}, {"tests\\desktop_smoke", NATIVE_EXE_LIBS_LITERAL, "desktop_smoke", NATIVE_EXE_ENTRY_LITERAL}, @@ -637,9 +635,7 @@ int main(int argc, char** argv) { "bin\\security_smoke.exe", "bin\\port_ipc_smoke.exe", "bin\\resource_service_smoke.exe", - "bin\\named_queue_lifetime_smoke.exe", - "bin\\input_api_smoke.exe", - "bin\\input_model_smoke.exe", + "bin\\anonymous_queue_smoke.exe", "bin\\input_service_smoke.exe", "bin\\console_service_smoke.exe", "bin\\desktop_smoke.exe", diff --git a/code/common/include/ntlite/internal/session_control.h b/code/common/include/ntlite/internal/session_control.h index 2962d18..1307707 100644 --- a/code/common/include/ntlite/internal/session_control.h +++ b/code/common/include/ntlite/internal/session_control.h @@ -3,10 +3,11 @@ #pragma once #include +#include #include #include -#define NTL_SESSION_DWM_PROTOCOL_VERSION 1u +#define NTL_SESSION_DWM_PROTOCOL_VERSION 2u #define NTL_SESSION_DWM_FLAG_NONE 0x00000000u #define NTL_SESSION_DWM_OP_SET_KEYBOARD_FOCUS 1u @@ -23,7 +24,7 @@ typedef struct NTL_SESSION_DWM_REQUEST { uint32_t Version; uint32_t Operation; uint32_t Flags; - uint32_t Enabled; + bool Enabled; uint32_t Reserved[3]; } NTL_SESSION_DWM_REQUEST; @@ -46,3 +47,7 @@ typedef struct NTL_SESSION_DWM_EVENT { INPUT_KEY_RECORD Key; INPUT_MOUSE_EVENT Mouse; } NTL_SESSION_DWM_EVENT; + +// RSSRV and DWM must agree on the private session-control payload. +_Static_assert(sizeof(NTL_SESSION_DWM_REQUEST) == 32u, "NTL_SESSION_DWM_REQUEST size changed"); +_Static_assert(offsetof(NTL_SESSION_DWM_REQUEST, Enabled) == 16u, "NTL_SESSION_DWM_REQUEST Enabled offset changed"); diff --git a/code/drivers/audio/hda/hda.h b/code/drivers/audio/hda/hda.h index b307394..4621c1c 100644 --- a/code/drivers/audio/hda/hda.h +++ b/code/drivers/audio/hda/hda.h @@ -207,8 +207,8 @@ typedef struct HDA_OUTPUT_STREAM { uint8_t NextCompletionBdl; bool Prepared; bool Running; - volatile long PendingPeriodInterrupts; - volatile long PendingDmaErrors; + volatile int32_t PendingPeriodInterrupts; + volatile int32_t PendingDmaErrors; HDA_OUTPUT_RING Rings[HDA_MAX_LOGICAL_ENDPOINTS]; uint8_t ActiveRingIndex; IO_DMA_BUFFER* BdlDma; @@ -249,8 +249,8 @@ struct HDA_CONTROLLER { bool SurpriseRemoved; SPINLOCK StateLock; KDPC Dpc; - volatile long IsrActive; - volatile long DpcActive; + volatile int32_t IsrActive; + volatile int32_t DpcActive; IO_DMA_ADAPTER* DmaAdapter; IO_DMA_BUFFER* CorbDma; IO_DMA_BUFFER* RirbDma; diff --git a/code/drivers/audio/hda/hda_codec.c b/code/drivers/audio/hda/hda_codec.c index 0f6953a..7a7f7ca 100644 --- a/code/drivers/audio/hda/hda_codec.c +++ b/code/drivers/audio/hda/hda_codec.c @@ -1,5 +1,6 @@ // Generic Intel HDA codec discovery and analog render routing. +#include #include "hda.h" #include @@ -211,9 +212,9 @@ struct HDA_CODEC_STATE { EVENT* PollFinished; THREAD* WorkThread; THREAD* PollThread; - volatile long WorkRunning; - volatile long PollRunning; - volatile long ShuttingDown; + volatile int32_t WorkRunning; + volatile int32_t PollRunning; + volatile int32_t ShuttingDown; uint64_t PendingUnsolicitedTags; bool PollRequested; bool HavePollingPins; @@ -1586,7 +1587,7 @@ static void HdaCodecEndpointStop(void* providerContext) { // Stores or applies one endpoint hardware gain and mute request. static KSTATUS HdaCodecEndpointSetGain(void* providerContext, int32_t gainMilliDb, - uint8_t muted) { + bool muted) { HDA_LOGICAL_ENDPOINT* endpoint = (HDA_LOGICAL_ENDPOINT*)providerContext; if (!endpoint || !endpoint->State || !endpoint->HasHardwareGain) return NTLSTATUS_INVALID_PARAMETER; diff --git a/code/drivers/audio/hda/hda_transport.c b/code/drivers/audio/hda/hda_transport.c index d0b6e98..1d9ce4a 100644 --- a/code/drivers/audio/hda/hda_transport.c +++ b/code/drivers/audio/hda/hda_transport.c @@ -1,5 +1,7 @@ // Intel High Definition Audio controller transport, command rings, output DMA, and recovery. +#include +#include #include "hda.h" #include @@ -1079,7 +1081,7 @@ uint64_t HdaQueryOutputPositionFrames(HDA_CONTROLLER* controller) { void HdaHandleInterrupt(HDA_CONTROLLER* controller) { if (!controller) return; - _InterlockedIncrement(&controller->IsrActive); + NtlAtomicIncrement32(&controller->IsrActive); if (!controller->Started || controller->Removed || controller->SurpriseRemoved) goto finished; @@ -1137,7 +1139,7 @@ void HdaHandleInterrupt(HDA_CONTROLLER* controller) { (void)KeInsertQueueDpc(&controller->Dpc, (void*)0, (void*)0); finished: - _InterlockedDecrement(&controller->IsrActive); + NtlAtomicDecrement32(&controller->IsrActive); } // Promotes an error to stream or controller recovery using the one-second threshold. @@ -1166,7 +1168,7 @@ void HdaDpcRoutine(KDPC* dpc, void* context, void* systemArgument1, void* system HDA_CONTROLLER* controller = (HDA_CONTROLLER*)context; if (!controller) return; - _InterlockedIncrement(&controller->DpcActive); + NtlAtomicIncrement32(&controller->DpcActive); if (controller->Removed || controller->SurpriseRemoved) goto finished; @@ -1253,7 +1255,7 @@ void HdaDpcRoutine(KDPC* dpc, void* context, void* systemArgument1, void* system } finished: - _InterlockedDecrement(&controller->DpcActive); + NtlAtomicDecrement32(&controller->DpcActive); } // Recovers one isolated stream error without disturbing the codec topology. diff --git a/code/drivers/bus/usb/usb.h b/code/drivers/bus/usb/usb.h index 427f003..9c170b0 100644 --- a/code/drivers/bus/usb/usb.h +++ b/code/drivers/bus/usb/usb.h @@ -636,7 +636,7 @@ struct USB_STORAGE_EXTENSION { uint32_t BlockSize; uint64_t BlockCount; USB_STORAGE_FLUSH_CAPABILITY FlushCapability; - volatile long IoBusy; + volatile int32_t IoBusy; }; struct USB_STORAGE_DISK_EXTENSION { @@ -723,7 +723,7 @@ typedef struct XHCI_DEVICE { bool MassRemoveLockReady; bool MassSurpriseRemoved; bool MassRemoved; - volatile long MassIoBusy; + volatile int32_t MassIoBusy; uint64_t InputContextDeviceAddress; uint64_t DeviceContextDeviceAddress; diff --git a/code/drivers/bus/usb/usb_hid.c b/code/drivers/bus/usb/usb_hid.c index 509909b..378f1ba 100644 --- a/code/drivers/bus/usb/usb_hid.c +++ b/code/drivers/bus/usb/usb_hid.c @@ -77,9 +77,9 @@ void XhciProcessBootKeyboardReport(XHCI_DEVICE* dev, const uint8_t* report, uint void XhciProcessBootMouseReport(XHCI_DEVICE* dev, const uint8_t* report, uint16_t len) { if (len < 3) return; - int dx = (int)(int8_t)report[1]; - int dy = (int)(int8_t)report[2]; - int wheel = (len >= 4) ? (int)(int8_t)report[3] : 0; + int32_t dx = (int32_t)(int8_t)report[1]; + int32_t dy = (int32_t)(int8_t)report[2]; + int32_t wheel = (len >= 4) ? (int32_t)(int8_t)report[3] : 0; uint8_t buttons = report[0] & 0x07u; if (dx || dy || wheel || buttons != dev->HidLastButtons) { @@ -397,9 +397,9 @@ static void UsbHidProcessBootKeyboardReport(USB_HID_EXTENSION* ext, const uint8_ static void UsbHidProcessBootMouseReport(USB_HID_EXTENSION* ext, const uint8_t* report, uint16_t len) { if (!ext || len < 3) return; - int dx = (int)(int8_t)report[1]; - int dy = (int)(int8_t)report[2]; - int wheel = (len >= 4) ? (int)(int8_t)report[3] : 0; + int32_t dx = (int32_t)(int8_t)report[1]; + int32_t dy = (int32_t)(int8_t)report[2]; + int32_t wheel = (len >= 4) ? (int32_t)(int8_t)report[3] : 0; uint8_t buttons = report[0] & 0x07u; if (dx || dy || wheel || buttons != ext->LastButtons) { @@ -421,7 +421,7 @@ static void UsbHidProcessQemuTabletReport(USB_HID_EXTENSION* ext, const uint8_t* uint8_t buttons = report[0] & 0x07u; uint16_t x = (uint16_t)report[1] | ((uint16_t)report[2] << 8); uint16_t y = (uint16_t)report[3] | ((uint16_t)report[4] << 8); - int wheel = (len >= 6) ? (int)(int8_t)report[5] : 0; + int32_t wheel = (len >= 6) ? (int32_t)(int8_t)report[5] : 0; if (x != ext->LastAbsoluteX || y != ext->LastAbsoluteY || wheel || buttons != ext->LastButtons) { diff --git a/code/drivers/bus/usb/usb_storage.c b/code/drivers/bus/usb/usb_storage.c index cd2c890..0bf217a 100644 --- a/code/drivers/bus/usb/usb_storage.c +++ b/code/drivers/bus/usb/usb_storage.c @@ -5,6 +5,9 @@ // interface stack and issues BOT transfers through USB URBs. The xHCI HCD // still backs the bulk URBs with the existing per-device rings; later stages // will make endpoint rings fully pipe-owned and asynchronous. +#include +#include +#include #include "usb.h" DRIVER_OBJECT gUsbStorageDriver; @@ -464,14 +467,14 @@ static KSTATUS UsbStorageSynchronizeCache(USB_STORAGE_EXTENSION* storage) { void UsbStorageAcquireIo(USB_STORAGE_EXTENSION* storage) { if (!storage) return; XHCI_CONTROLLER* xhci = storage->Device ? (XHCI_CONTROLLER*)storage->Device->Hcd->ControllerContext : (XHCI_CONTROLLER*)0; - while (_InterlockedCompareExchange(&storage->IoBusy, 1, 0) != 0) { + while (NtlAtomicCompareExchange32(&storage->IoBusy, 1, 0) != 0) { if (xhci) UsbDelayUs(xhci->Ddk, 10); } } // Implements the storage Release Io operation. void UsbStorageReleaseIo(USB_STORAGE_EXTENSION* storage) { - if (storage) _InterlockedExchange(&storage->IoBusy, 0); + if (storage) NtlAtomicExchange32(&storage->IoBusy, 0); } // Implements the storage Dispatch Read Write operation. diff --git a/code/drivers/display/vmwaresvga/vmwaresvga.c b/code/drivers/display/vmwaresvga/vmwaresvga.c index 1d21eb6..8ff0276 100644 --- a/code/drivers/display/vmwaresvga/vmwaresvga.c +++ b/code/drivers/display/vmwaresvga/vmwaresvga.c @@ -1,5 +1,6 @@ // VMware SVGA-II PCI display provider. This file owns adapter setup, FIFO command submission, framebuffer presentation, and cursor integration. +#include #include "vmwaresvga.h" const NTL_DDK* gVmwSvgaDdk; @@ -211,7 +212,7 @@ static void VmwSvgaWakeFifo(VMWSVGA_DEVICE* v) { VmwSvgaWriteReg(v, SVGA_REG_SYNC, 1u); } -static KSTATUS VmwSvgaUploadCursorImage(VMWSVGA_DEVICE* v, int width, int height, int hotX, int hotY, const uint32_t* argbPixels); +static KSTATUS VmwSvgaUploadCursorImage(VMWSVGA_DEVICE* v, int32_t width, int32_t height, int32_t hotX, int32_t hotY, const uint32_t* argbPixels); // Implements the premultiply Argb For Bgra operation. static uint32_t VmwSvgaPremultiplyArgbForBgra(uint32_t argb) { @@ -275,7 +276,7 @@ static void VmwSvgaCursorSetVisible(VMWSVGA_DEVICE* v, bool visible) { } // Implements the cursor Set Position operation. -static void VmwSvgaCursorSetPosition(VMWSVGA_DEVICE* v, int x, int y) { +static void VmwSvgaCursorSetPosition(VMWSVGA_DEVICE* v, int32_t x, int32_t y) { if (!v || !v->HardwareCursor) return; v->CursorX = x; @@ -284,7 +285,7 @@ static void VmwSvgaCursorSetPosition(VMWSVGA_DEVICE* v, int x, int y) { } // Implements the define Alpha Cursor operation. -static KSTATUS VmwSvgaDefineAlphaCursor(VMWSVGA_DEVICE* v, int width, int height, int hotX, int hotY, const uint32_t* argbPixels) { +static KSTATUS VmwSvgaDefineAlphaCursor(VMWSVGA_DEVICE* v, int32_t width, int32_t height, int32_t hotX, int32_t hotY, const uint32_t* argbPixels) { KSTATUS st = VmwSvgaFifoWrite(v, SVGA_CMD_DEFINE_ALPHA_CURSOR); if (!KSUCCESS(st)) return st; st = VmwSvgaFifoWrite(v, 0u); // cursor id, reserved @@ -298,8 +299,8 @@ static KSTATUS VmwSvgaDefineAlphaCursor(VMWSVGA_DEVICE* v, int width, int height st = VmwSvgaFifoWrite(v, (uint32_t)height); if (!KSUCCESS(st)) return st; - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { + for (int32_t y = 0; y < height; y++) { + for (int32_t x = 0; x < width; x++) { uint32_t p = argbPixels[(size_t)y * (size_t)width + (size_t)x]; st = VmwSvgaFifoWrite(v, VmwSvgaPremultiplyArgbForBgra(p)); if (!KSUCCESS(st)) return st; @@ -310,9 +311,9 @@ static KSTATUS VmwSvgaDefineAlphaCursor(VMWSVGA_DEVICE* v, int width, int height } static uint32_t VmwSvgaPackMonoCursorDword(const uint8_t* bits, - int width, - int height, - int y, + int32_t width, + int32_t height, + int32_t y, uint32_t dwordIndex) { // SVGA classic cursor scanlines are padded to 32 bits. QEMU stores @@ -343,7 +344,7 @@ static uint32_t VmwSvgaPackMonoCursorDword(const uint8_t* bits, } // Implements the classic Cursor Width operation. -static int VmwSvgaClassicCursorWidth(int width) { +static int32_t VmwSvgaClassicCursorWidth(int32_t width) { // QEMU's VMware SVGA implementation reads classic cursor masks as // SVGA dword-padded scanlines, then passes the raw byte stream to @@ -358,9 +359,9 @@ static int VmwSvgaClassicCursorWidth(int width) { } static void VmwSvgaBuildMonoCursorBits(const uint32_t* argbPixels, - int sourceWidth, - int encodedWidth, - int height, + int32_t sourceWidth, + int32_t encodedWidth, + int32_t height, uint8_t* andBits, uint8_t* xorBits) { @@ -372,8 +373,8 @@ static void VmwSvgaBuildMonoCursorBits(const uint32_t* argbPixels, // This intentionally discards antialiasing, but avoids QEMU/GTK's // fragile 32bpp classic host-cursor path. - for (int y = 0; y < height; y++) { - for (int x = 0; x < encodedWidth; x++) { + for (int32_t y = 0; y < height; y++) { + for (int32_t x = 0; x < encodedWidth; x++) { size_t out = (size_t)y * (size_t)encodedWidth + (size_t)x; if (x >= sourceWidth) { andBits[out] = 1u; @@ -400,8 +401,8 @@ static void VmwSvgaBuildMonoCursorBits(const uint32_t* argbPixels, } // Implements the define Classic Cursor operation. -static KSTATUS VmwSvgaDefineClassicCursor(VMWSVGA_DEVICE* v, int width, int height, int hotX, int hotY, const uint32_t* argbPixels) { - int encodedWidth = VmwSvgaClassicCursorWidth(width); +static KSTATUS VmwSvgaDefineClassicCursor(VMWSVGA_DEVICE* v, int32_t width, int32_t height, int32_t hotX, int32_t hotY, const uint32_t* argbPixels) { + int32_t encodedWidth = VmwSvgaClassicCursorWidth(width); uint32_t dwordsPerRow = ((uint32_t)encodedWidth + 31u) / 32u; size_t pixelCount = (size_t)encodedWidth * (size_t)height; @@ -438,14 +439,14 @@ static KSTATUS VmwSvgaDefineClassicCursor(VMWSVGA_DEVICE* v, int width, int heig st = VmwSvgaFifoWrite(v, 1u); // XOR mask depth if (!KSUCCESS(st)) goto done; - for (int y = 0; y < height; y++) { + for (int32_t y = 0; y < height; y++) { for (uint32_t d = 0; d < dwordsPerRow; d++) { st = VmwSvgaFifoWrite(v, VmwSvgaPackMonoCursorDword(andBits, encodedWidth, height, y, d)); if (!KSUCCESS(st)) goto done; } } - for (int y = 0; y < height; y++) { + for (int32_t y = 0; y < height; y++) { for (uint32_t d = 0; d < dwordsPerRow; d++) { st = VmwSvgaFifoWrite(v, VmwSvgaPackMonoCursorDword(xorBits, encodedWidth, height, y, d)); if (!KSUCCESS(st)) goto done; @@ -461,7 +462,7 @@ done: } // Implements the upload Cursor Image operation. -static KSTATUS VmwSvgaUploadCursorImage(VMWSVGA_DEVICE* v, int width, int height, int hotX, int hotY, const uint32_t* argbPixels) { +static KSTATUS VmwSvgaUploadCursorImage(VMWSVGA_DEVICE* v, int32_t width, int32_t height, int32_t hotX, int32_t hotY, const uint32_t* argbPixels) { if (!v || !v->HardwareCursor || !argbPixels || width <= 0 || height <= 0) return NTLSTATUS_INVALID_PARAMETER; if (width > 256 || height > 256 || hotX < 0 || hotY < 0 || hotX >= width || hotY >= height) @@ -479,7 +480,7 @@ static KSTATUS VmwSvgaUploadCursorImage(VMWSVGA_DEVICE* v, int width, int height } // Implements the define Cursor Image operation. -static KSTATUS VmwSvgaDefineCursorImage(VMWSVGA_DEVICE* v, int width, int height, int hotX, int hotY, const uint32_t* argbPixels) { +static KSTATUS VmwSvgaDefineCursorImage(VMWSVGA_DEVICE* v, int32_t width, int32_t height, int32_t hotX, int32_t hotY, const uint32_t* argbPixels) { if (!v || !v->HardwareCursor || !argbPixels || width <= 0 || height <= 0) return NTLSTATUS_INVALID_PARAMETER; if (width > 256 || height > 256 || hotX < 0 || hotY < 0 || hotX >= width || hotY >= height) @@ -568,7 +569,7 @@ static KSTATUS VmwSvgaDefineGmrfb(VMWSVGA_DEVICE* v) { } // Blits gmrfb To Screen. -static KSTATUS VmwSvgaBlitGmrfbToScreen(VMWSVGA_DEVICE* v, int x, int y, int w, int h) { +static KSTATUS VmwSvgaBlitGmrfbToScreen(VMWSVGA_DEVICE* v, int32_t x, int32_t y, int32_t w, int32_t h) { DISPLAY_RECT r = { x, y, w, h }; if (!VmwSvgaClipRect(v, &r)) return NTLSTATUS_SUCCESS; @@ -659,7 +660,7 @@ static KSTATUS VmwSvgaInitializeScreenObject(VMWSVGA_DEVICE* v) { } // Implements the legacy Update operation. -static KSTATUS VmwSvgaLegacyUpdate(VMWSVGA_DEVICE* v, int x, int y, int w, int h) { +static KSTATUS VmwSvgaLegacyUpdate(VMWSVGA_DEVICE* v, int32_t x, int32_t y, int32_t w, int32_t h) { DISPLAY_RECT r = { x, y, w, h }; if (!VmwSvgaClipRect(v, &r)) return NTLSTATUS_SUCCESS; @@ -680,7 +681,7 @@ static KSTATUS VmwSvgaLegacyUpdate(VMWSVGA_DEVICE* v, int x, int y, int w, int h } // Updates the requested value. -static KSTATUS VmwSvgaUpdate(VMWSVGA_DEVICE* v, int x, int y, int w, int h) { +static KSTATUS VmwSvgaUpdate(VMWSVGA_DEVICE* v, int32_t x, int32_t y, int32_t w, int32_t h) { DISPLAY_RECT r = { x, y, w, h }; if (!VmwSvgaClipRect(v, &r)) return NTLSTATUS_SUCCESS; @@ -841,7 +842,7 @@ static void VmwSvgaCopyToShadowAndFramebuffer(uint32_t* shadow, } // Implements the display pitched blit operation. -static KSTATUS VmwSvgaDisplayBlit(DISPLAY_DEVICE* device, int x, int y, int w, int h, const uint32_t* pixels, uint32_t pitchBytes) { +static KSTATUS VmwSvgaDisplayBlit(DISPLAY_DEVICE* device, int32_t x, int32_t y, int32_t w, int32_t h, const uint32_t* pixels, uint32_t pitchBytes) { VMWSVGA_DEVICE* v = VmwSvgaFromDisplay(device); if (!v || !v->Backbuffer || !pixels || w <= 0 || h <= 0 || pitchBytes < (uint32_t)w * sizeof(uint32_t)) @@ -857,7 +858,7 @@ static KSTATUS VmwSvgaDisplayBlit(DISPLAY_DEVICE* device, int x, int y, int w, i int srcOffsetX = r.X - x; int srcOffsetY = r.Y - y; size_t bytesPerRow = (size_t)r.W * sizeof(uint32_t); - for (int row = 0; row < r.H; row++) { + for (int32_t row = 0; row < r.H; row++) { uint32_t* dst = v->Backbuffer + ((size_t)(r.Y + row) * v->Stride) + r.X; const uint8_t* sourceRow = (const uint8_t*)pixels + (size_t)(srcOffsetY + row) * pitchBytes; const uint32_t* src = (const uint32_t*)sourceRow + srcOffsetX; @@ -868,10 +869,10 @@ static KSTATUS VmwSvgaDisplayBlit(DISPLAY_DEVICE* device, int x, int y, int w, i // Presents one pitched rectangle without a shadow-to-framebuffer second pass. static KSTATUS VmwSvgaDisplayPresentPitched(DISPLAY_DEVICE* device, - int x, - int y, - int w, - int h, + int32_t x, + int32_t y, + int32_t w, + int32_t h, const uint32_t* pixels, uint32_t pitchBytes) { VMWSVGA_DEVICE* v = VmwSvgaFromDisplay(device); @@ -894,7 +895,7 @@ static KSTATUS VmwSvgaDisplayPresentPitched(DISPLAY_DEVICE* device, v->Stats.ScanoutBytes += bytes; v->Stats.BytesCopied += bytes * 2u; - for (int row = 0; row < r.H; row++) { + for (int32_t row = 0; row < r.H; row++) { const uint8_t* sourceRow = (const uint8_t*)pixels + (size_t)(srcOffsetY + row) * pitchBytes; const uint32_t* source = (const uint32_t*)sourceRow + srcOffsetX; @@ -914,7 +915,7 @@ static KSTATUS VmwSvgaDisplayPresentPitched(DISPLAY_DEVICE* device, } // Implements the display Fill Rect operation. -static KSTATUS VmwSvgaDisplayFillRect(DISPLAY_DEVICE* device, int x, int y, int w, int h, uint32_t color) { +static KSTATUS VmwSvgaDisplayFillRect(DISPLAY_DEVICE* device, int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color) { VMWSVGA_DEVICE* v = VmwSvgaFromDisplay(device); if (!v || !v->Backbuffer || w <= 0 || h <= 0) return NTLSTATUS_INVALID_PARAMETER; @@ -935,7 +936,7 @@ static KSTATUS VmwSvgaDisplayFillRect(DISPLAY_DEVICE* device, int x, int y, int } // Implements the display Copy Rect operation. -static KSTATUS VmwSvgaDisplayCopyRect(DISPLAY_DEVICE* device, int dstX, int dstY, int srcX, int srcY, int w, int h) { +static KSTATUS VmwSvgaDisplayCopyRect(DISPLAY_DEVICE* device, int32_t dstX, int32_t dstY, int32_t srcX, int32_t srcY, int32_t w, int32_t h) { VMWSVGA_DEVICE* v = VmwSvgaFromDisplay(device); if (!v || !v->Backbuffer || w <= 0 || h <= 0) return NTLSTATUS_INVALID_PARAMETER; @@ -956,13 +957,13 @@ static KSTATUS VmwSvgaDisplayCopyRect(DISPLAY_DEVICE* device, int dstX, int dstY size_t rowBytes = (size_t)w * sizeof(uint32_t); if (dstY > srcY) { - for (int row = h - 1; row >= 0; row--) { + for (int32_t row = h - 1; row >= 0; row--) { uint32_t* dst = v->Backbuffer + ((size_t)(dstY + row) * v->Stride) + dstX; uint32_t* src = v->Backbuffer + ((size_t)(srcY + row) * v->Stride) + srcX; memmove(dst, src, rowBytes); } } else { - for (int row = 0; row < h; row++) { + for (int32_t row = 0; row < h; row++) { uint32_t* dst = v->Backbuffer + ((size_t)(dstY + row) * v->Stride) + dstX; uint32_t* src = v->Backbuffer + ((size_t)(srcY + row) * v->Stride) + srcX; memmove(dst, src, rowBytes); @@ -972,7 +973,7 @@ static KSTATUS VmwSvgaDisplayCopyRect(DISPLAY_DEVICE* device, int dstX, int dstY } // Implements the display Read Rect operation. -static KSTATUS VmwSvgaDisplayReadRect(DISPLAY_DEVICE* device, int x, int y, int w, int h, uint32_t* outPixels, uint32_t outStride) { +static KSTATUS VmwSvgaDisplayReadRect(DISPLAY_DEVICE* device, int32_t x, int32_t y, int32_t w, int32_t h, uint32_t* outPixels, uint32_t outStride) { VMWSVGA_DEVICE* v = VmwSvgaFromDisplay(device); if (!v || !v->Backbuffer || !outPixels || outStride == 0 || w <= 0 || h <= 0) return NTLSTATUS_INVALID_PARAMETER; @@ -986,7 +987,7 @@ static KSTATUS VmwSvgaDisplayReadRect(DISPLAY_DEVICE* device, int x, int y, int int dstOffsetX = r.X - x; int dstOffsetY = r.Y - y; - for (int row = 0; row < r.H; row++) { + for (int32_t row = 0; row < r.H; row++) { const uint32_t* src = v->Backbuffer + ((size_t)(r.Y + row) * v->Stride) + r.X; uint32_t* dst = outPixels + ((size_t)(dstOffsetY + row) * outStride) + dstOffsetX; memcpy(dst, src, (size_t)r.W * sizeof(uint32_t)); @@ -1015,7 +1016,7 @@ static KSTATUS VmwSvgaDisplayPresent(DISPLAY_DEVICE* device, const DISPLAY_RECT* v->Stats.BytesCopied += bytes; v->Stats.ScanoutBytes += bytes; size_t pixelCount = (size_t)r.W; - for (int row = 0; row < r.H; row++) { + for (int32_t row = 0; row < r.H; row++) { uint32_t* dst = (uint32_t*)(uintptr_t)(v->Framebuffer + ((size_t)(r.Y + row) * v->BytesPerLine) + ((size_t)r.X * sizeof(uint32_t))); @@ -1046,7 +1047,7 @@ static KSTATUS VmwSvgaDisplaySync(DISPLAY_DEVICE* device) { } // Implements the display Set Cursor Image operation. -static KSTATUS VmwSvgaDisplaySetCursorImage(DISPLAY_DEVICE* device, int width, int height, int hotX, int hotY, const uint32_t* argbPixels) { +static KSTATUS VmwSvgaDisplaySetCursorImage(DISPLAY_DEVICE* device, int32_t width, int32_t height, int32_t hotX, int32_t hotY, const uint32_t* argbPixels) { VMWSVGA_DEVICE* v = VmwSvgaFromDisplay(device); if (!v || !v->HardwareCursor) return NTLSTATUS_NOT_SUPPORTED; @@ -1054,7 +1055,7 @@ static KSTATUS VmwSvgaDisplaySetCursorImage(DISPLAY_DEVICE* device, int width, i } // Implements the display Set Cursor Position operation. -static KSTATUS VmwSvgaDisplaySetCursorPosition(DISPLAY_DEVICE* device, int x, int y) { +static KSTATUS VmwSvgaDisplaySetCursorPosition(DISPLAY_DEVICE* device, int32_t x, int32_t y) { VMWSVGA_DEVICE* v = VmwSvgaFromDisplay(device); if (!v) return NTLSTATUS_INVALID_PARAMETER; @@ -1073,7 +1074,7 @@ static void VmwSvgaRestoreCursor(VMWSVGA_DEVICE* v) { DISPLAY_RECT r = { v->CursorDrawX, v->CursorDrawY, VMWSVGA_CURSOR_W, VMWSVGA_CURSOR_H }; if (VmwSvgaClipRect(v, &r)) { - for (int row = 0; row < r.H; row++) { + for (int32_t row = 0; row < r.H; row++) { volatile uint8_t* dstBytes = v->Framebuffer + ((size_t)(r.Y + row) * v->BytesPerLine) + ((size_t)r.X * sizeof(uint32_t)); uint32_t* src = v->Backbuffer + ((size_t)(r.Y + row) * v->Stride) + r.X; memcpy((void*)dstBytes, src, (size_t)r.W * sizeof(uint32_t)); @@ -1091,17 +1092,17 @@ static void VmwSvgaDrawCursor(VMWSVGA_DEVICE* v) { if (!v || !v->Backbuffer || !v->Framebuffer) return; - int x = v->CursorX; - int y = v->CursorY; + int32_t x = v->CursorX; + int32_t y = v->CursorY; v->CursorDrawX = x; v->CursorDrawY = y; DISPLAY_RECT dirty = { x, y, VMWSVGA_CURSOR_W, VMWSVGA_CURSOR_H }; - for (int row = 0; row < VMWSVGA_CURSOR_H; row++) { + for (int32_t row = 0; row < VMWSVGA_CURSOR_H; row++) { int fy = y + row; if ((unsigned)fy >= v->Height) continue; - for (int col = 0; col < VMWSVGA_CURSOR_W; col++) { + for (int32_t col = 0; col < VMWSVGA_CURSOR_W; col++) { int fx = x + col; if ((unsigned)fx >= v->Width) continue; diff --git a/code/drivers/display/vmwaresvga/vmwaresvga.h b/code/drivers/display/vmwaresvga/vmwaresvga.h index a096d77..90b1eb1 100644 --- a/code/drivers/display/vmwaresvga/vmwaresvga.h +++ b/code/drivers/display/vmwaresvga/vmwaresvga.h @@ -192,18 +192,18 @@ typedef struct VMWSVGA_DEVICE { bool CursorVisible; uint32_t* CursorImageArgb; size_t CursorImageBytes; - int CursorImageWidth; - int CursorImageHeight; - int CursorHotX; - int CursorHotY; + int32_t CursorImageWidth; + int32_t CursorImageHeight; + int32_t CursorHotX; + int32_t CursorHotY; bool Started; bool Removed; bool SurpriseRemoved; - int CursorX; - int CursorY; - int CursorDrawX; - int CursorDrawY; + int32_t CursorX; + int32_t CursorY; + int32_t CursorDrawX; + int32_t CursorDrawY; bool CursorDrawn; DISPLAY_STATS Stats; diff --git a/code/drivers/fs/ext4/ext4_driver.c b/code/drivers/fs/ext4/ext4_driver.c index 7ca4255..3ce0f44 100644 --- a/code/drivers/fs/ext4/ext4_driver.c +++ b/code/drivers/fs/ext4/ext4_driver.c @@ -838,11 +838,11 @@ static KSTATUS Ext4ReadDir(IO_VOLUME_PROVIDER* provider, void* dirCtx, KDIR_ENTR } memset(out, 0, sizeof(*out)); - size_t nameLength = entry->name_length; - if (nameLength >= SYS_DIR_NAME_MAX) - nameLength = SYS_DIR_NAME_MAX - 1; - memcpy(out->Name, entry->name, nameLength); - out->Name[nameLength] = '\0'; + // ext4 allows arbitrary non-NUL filename bytes. Project malformed UTF-8 + // to U+FFFD only for enumeration; keep raw bytes for ext4 metadata lookups. + // The displayed name is not a reversible path for malformed disk names. + (void)SysSvCopyUtf8WithReplacement( + SysSvFromParts(entry->name, entry->name_length), out->Name, sizeof(out->Name)); out->Attr = entry->inode_type == EXT4_DE_DIR ? SYS_FILE_ATTR_DIRECTORY : SYS_FILE_ATTR_ARCHIVE; diff --git a/code/drivers/fs/fat/fat.c b/code/drivers/fs/fat/fat.c index 5930eaa..60d834d 100644 --- a/code/drivers/fs/fat/fat.c +++ b/code/drivers/fs/fat/fat.c @@ -1,5 +1,6 @@ // FAT allocation, cache, and metadata core shared by the filesystem operation translation units. +#include #include "fat_internal.h" // FAT sector cache (fixes #1 and #5) @@ -169,7 +170,7 @@ uint32_t FatEocMark(FAT_VOL* v) { return v && v->Variant == FatVariant16 ? FAT16_EOC_MARK : FAT32_EOC_MARK; } -int FatIsDataCluster(FAT_VOL* v, uint32_t cluster) { +bool FatIsDataCluster(FAT_VOL* v, uint32_t cluster) { return v && cluster >= 2u && cluster < FatEocThreshold(v); } @@ -185,49 +186,49 @@ void FatSetDirentFirstCluster(FAT_VOL* v, DIRENT* d, uint32_t cluster) { d->FirstClusterHi = (v && v->Variant == FatVariant32) ? (uint16_t)(cluster >> 16) : 0u; } -int FatIsFixedRoot(FAT_VOL* v, uint32_t dirCluster) { +bool FatIsFixedRoot(FAT_VOL* v, uint32_t dirCluster) { return v && v->Variant == FatVariant16 && dirCluster == FAT_FIXED_ROOT_CLUSTER; } uint64_t FatClusterToLba(FAT_VOL* v, uint32_t cluster); uint32_t FatEntry(FAT_VOL* v, uint32_t cluster); -int FatDirectoryFirstSegment(FAT_VOL* v, uint32_t dirCluster, +bool FatDirectoryFirstSegment(FAT_VOL* v, uint32_t dirCluster, uint32_t* cursor, uint64_t* lba, uint32_t* sectors) { - if (!v || !cursor || !lba || !sectors) return 0; + if (!v || !cursor || !lba || !sectors) return false; if (FatIsFixedRoot(v, dirCluster)) { - if (!v->RootDirSectors) return 0; + if (!v->RootDirSectors) return false; *cursor = 0; *lba = v->RootDirStart; *sectors = 1; - return 1; + return true; } - if (!FatIsDataCluster(v, dirCluster)) return 0; + if (!FatIsDataCluster(v, dirCluster)) return false; *cursor = dirCluster; *lba = FatClusterToLba(v, dirCluster); *sectors = v->SectorsPerCluster; - return 1; + return true; } -int FatDirectoryNextSegment(FAT_VOL* v, uint32_t dirCluster, +bool FatDirectoryNextSegment(FAT_VOL* v, uint32_t dirCluster, uint32_t* cursor, uint64_t* lba, uint32_t* sectors) { - if (!v || !cursor || !lba || !sectors) return 0; + if (!v || !cursor || !lba || !sectors) return false; if (FatIsFixedRoot(v, dirCluster)) { uint32_t next = *cursor + 1u; - if (next >= v->RootDirSectors) return 0; + if (next >= v->RootDirSectors) return false; *cursor = next; *lba = v->RootDirStart + next; *sectors = 1; - return 1; + return true; } uint32_t next = FatEntry(v, *cursor); - if (!FatIsDataCluster(v, next)) return 0; + if (!FatIsDataCluster(v, next)) return false; *cursor = next; *lba = FatClusterToLba(v, next); *sectors = v->SectorsPerCluster; - return 1; + return true; } // Implements the cluster To Lba operation. @@ -314,7 +315,7 @@ KSTATUS FatWriteEntry(FAT_VOL* v, uint32_t cluster, uint32_t value) { } // Cluster allocation (fix #3: next-free hint + two-phase wrap-around scan). -uint32_t FatAllocClusterEx(FAT_VOL* v, int zeroCluster) { +uint32_t FatAllocClusterEx(FAT_VOL* v, bool zeroCluster) { if (v->TotalClusters == 0) return 0; uint32_t total = v->TotalClusters; @@ -353,7 +354,7 @@ uint32_t FatAllocCluster(FAT_VOL* v) { return FatAllocClusterEx(v, 1); } -uint32_t FatAppendClusterEx(FAT_VOL* v, uint32_t lastCluster, int zeroCluster) { +uint32_t FatAppendClusterEx(FAT_VOL* v, uint32_t lastCluster, bool zeroCluster) { uint32_t newCluster = FatAllocClusterEx(v, zeroCluster); if (!newCluster) return 0; if (!KSUCCESS(FatWriteEntry(v, lastCluster, newCluster))) return 0; @@ -390,7 +391,7 @@ void FatFreeClusterChain(FAT_VOL* v, uint32_t start) { // FAT timestamp and DIRENT metadata helpers. // Implements the fat Is Leap Year operation. -static int FatIsLeapYear(uint32_t year) { +static bool FatIsLeapYear(uint32_t year) { return ((year % 4u) == 0u && (year % 100u) != 0u) || ((year % 400u) == 0u); } diff --git a/code/drivers/fs/fat/fat_directory.c b/code/drivers/fs/fat/fat_directory.c index f4889af..9c07a78 100644 --- a/code/drivers/fs/fat/fat_directory.c +++ b/code/drivers/fs/fat/fat_directory.c @@ -1,18 +1,19 @@ // FAT name handling, path traversal, and directory mutation. +#include #include "fat_internal.h" // 8.3 name matching / assembly. -static int MatchRaw83(const char dirName[8], const char dirExt[3], +static bool MatchRaw83(const char dirName[8], const char dirExt[3], const char wantName[8], const char wantExt[3]) { return memcmp(dirName, wantName, 8) == 0 && memcmp(dirExt, wantExt, 3) == 0; } static char FatUpperChar(char c); -static int FatIsLowerChar(char c); -static int FatIsValidShortChar(char c); +static bool FatIsLowerChar(char c); +static bool FatIsValidShortChar(char c); // Implements the make83 Name V operation. static void Make83NameV(SYS_STRING_VIEW src, char name8[8], char ext3[3]) { @@ -53,40 +54,40 @@ static char FatUpperChar(char c) { } // Implements the fat Is Lower Char operation. -static int FatIsLowerChar(char c) { +static bool FatIsLowerChar(char c) { return c >= 'a' && c <= 'z'; } // Implements the fat Is Valid Short Char operation. -static int FatIsValidShortChar(char c) { +static bool FatIsValidShortChar(char c) { c = FatUpperChar(c); - if (c >= 'A' && c <= 'Z') return 1; - if (c >= '0' && c <= '9') return 1; + if (c >= 'A' && c <= 'Z') return true; + if (c >= '0' && c <= '9') return true; switch (c) { case '$': case '%': case '\'': case '-': case '_': case '@': case '~': case '`': case '!': case '(': case ')': case '{': case '}': case '^': case '#': case '&': - return 1; + return true; default: - return 0; + return false; } } // True when the user spelling can refer to a normal FAT 8.3 entry. This is // intentionally structural and case-insensitive: "doom1.wad" must still open // an existing short entry named DOOM1.WAD. Leading-dot names are not 8.3. -static int FatNameIsStructurallyShort83V(SYS_STRING_VIEW name) { - if (!SysSvIsValid(name) || name.Length == 0) return 0; - if (name.Data[0] == '.') return 0; +static bool FatNameIsStructurallyShort83V(SYS_STRING_VIEW name) { + if (!SysSvIsValid(name) || name.Length == 0) return false; + if (name.Data[0] == '.') return false; size_t dot = (size_t)-1; for (size_t i = 0; i < name.Length; i++) { char c = name.Data[i]; if (c == '.') { - if (dot != (size_t)-1) return 0; + if (dot != (size_t)-1) return false; dot = i; } else if (!FatIsValidShortChar(c)) { - return 0; + return false; } } @@ -96,17 +97,17 @@ static int FatNameIsStructurallyShort83V(SYS_STRING_VIEW name) { } // Implements the fat Component Has Lower V operation. -static int FatComponentHasLowerV(SYS_STRING_VIEW name, size_t start, size_t end) { +static bool FatComponentHasLowerV(SYS_STRING_VIEW name, size_t start, size_t end) { for (size_t i = start; i < end; i++) - if (FatIsLowerChar(name.Data[i])) return 1; - return 0; + if (FatIsLowerChar(name.Data[i])) return true; + return false; } // Implements the fat Component Has Upper V operation. -static int FatComponentHasUpperV(SYS_STRING_VIEW name, size_t start, size_t end) { +static bool FatComponentHasUpperV(SYS_STRING_VIEW name, size_t start, size_t end) { for (size_t i = start; i < end; i++) - if (name.Data[i] >= 'A' && name.Data[i] <= 'Z') return 1; - return 0; + if (name.Data[i] >= 'A' && name.Data[i] <= 'Z') return true; + return false; } static void FatSplitShortNameV(SYS_STRING_VIEW name, size_t* baseEnd, @@ -128,19 +129,19 @@ static void FatSplitShortNameV(SYS_STRING_VIEW name, size_t* baseEnd, // Creation uses LFN only when the spelling cannot be represented by a short // entry, or when mixed-case 8.3 spelling would be lost. Plain lowercase // 8.3 names are represented with FAT's NT case bits, not VFAT LFN entries. -static int FatNameNeedsLfnForCreateV(SYS_STRING_VIEW name) { - if (!FatNameIsStructurallyShort83V(name)) return 1; +static bool FatNameNeedsLfnForCreateV(SYS_STRING_VIEW name) { + if (!FatNameIsStructurallyShort83V(name)) return true; size_t baseEnd, extStart, extEnd; FatSplitShortNameV(name, &baseEnd, &extStart, &extEnd); - int baseLower = FatComponentHasLowerV(name, 0, baseEnd); - int baseUpper = FatComponentHasUpperV(name, 0, baseEnd); - int extLower = FatComponentHasLowerV(name, extStart, extEnd); - int extUpper = FatComponentHasUpperV(name, extStart, extEnd); + bool baseLower = FatComponentHasLowerV(name, 0, baseEnd); + bool baseUpper = FatComponentHasUpperV(name, 0, baseEnd); + bool extLower = FatComponentHasLowerV(name, extStart, extEnd); + bool extUpper = FatComponentHasUpperV(name, extStart, extEnd); - if ((baseLower && baseUpper) || (extLower && extUpper)) return 1; - return 0; + if ((baseLower && baseUpper) || (extLower && extUpper)) return true; + return false; } // Implements the fat Short Name Case Flags V operation. @@ -211,15 +212,15 @@ static void MakeLegacyBrokenDot83(const char* src, char name8[8], char ext3[3]) ext3[ei++] = FatUpperChar(*p); } -static int ShortNameExistsRaw(FAT_VOL* v, uint32_t dirCluster, +static bool ShortNameExistsRaw(FAT_VOL* v, uint32_t dirCluster, const char name8[8], const char ext3[3]) { uint8_t* clusterBuf = (uint8_t*)MmAllocatePool(v->BytesPerCluster); - if (!clusterBuf) return 1; // fail closed; caller should pick another alias + if (!clusterBuf) return true; // fail closed; caller should pick another alias uint32_t cursor; uint64_t lba; uint32_t sectors; if (!FatDirectoryFirstSegment(v, dirCluster, &cursor, &lba, §ors)) { MmFreePool(clusterBuf); - return 0; + return false; } do { @@ -228,17 +229,17 @@ static int ShortNameExistsRaw(FAT_VOL* v, uint32_t dirCluster, uint32_t entries = (sectors * v->BytesPerSector) / sizeof(DIRENT); DIRENT* dir = (DIRENT*)clusterBuf; for (uint32_t i = 0; i < entries; i++) { - if (dir[i].Name[0] == 0x00) { MmFreePool(clusterBuf); return 0; } + if (dir[i].Name[0] == 0x00) { MmFreePool(clusterBuf); return false; } if ((uint8_t)dir[i].Name[0] == 0xE5 || dir[i].Attr == ATTR_LFN) continue; if (MatchRaw83(dir[i].Name, dir[i].Ext, name8, ext3)) { MmFreePool(clusterBuf); - return 1; + return true; } } } while (FatDirectoryNextSegment(v, dirCluster, &cursor, &lba, §ors)); MmFreePool(clusterBuf); - return 0; + return false; } // Implements the fat Alias Append Char operation. @@ -310,7 +311,7 @@ static void MakeAliasAttempt(const char stem[9], const char extIn[3], memcpy(name8 + keep, suffix, suffixLen); } -static int MakeUniqueShortAliasV(FAT_VOL* v, uint32_t dirCluster, +static bool MakeUniqueShortAliasV(FAT_VOL* v, uint32_t dirCluster, SYS_STRING_VIEW longName, char name8[8], char ext3[3]) { char stem[9], extCandidate[3]; @@ -318,9 +319,9 @@ static int MakeUniqueShortAliasV(FAT_VOL* v, uint32_t dirCluster, for (uint32_t attempt = 1; attempt < 1000000; attempt++) { MakeAliasAttempt(stem, extCandidate, attempt, name8, ext3); - if (!ShortNameExistsRaw(v, dirCluster, name8, ext3)) return 1; + if (!ShortNameExistsRaw(v, dirCluster, name8, ext3)) return true; } - return 0; + return false; } // Creates legacy Broken Dot83 V. @@ -400,44 +401,44 @@ void FatLfnAppend(char* lfn, const LFN_ENTRY* e) { } // Directory search and view-native path traversal. -static int FatIsPathSep(char c) { +static bool FatIsPathSep(char c) { return c == '\\' || c == '/'; } // Tests whether a non-empty FAT path view points at plausible kernel-owned storage. -static int FatStringViewDataIsPlausible(SYS_STRING_VIEW view) { - if (!SysSvIsValid(view)) return 0; - if (view.Length == 0) return 1; +static bool FatStringViewDataIsPlausible(SYS_STRING_VIEW view) { + if (!SysSvIsValid(view)) return false; + if (view.Length == 0) return true; uintptr_t data = (uintptr_t)view.Data; return data >= 0x10000u; } // Implements the name View Is Valid operation. -static int FatNameViewIsValid(SYS_STRING_VIEW name) { - if (!FatStringViewDataIsPlausible(name)) return 0; - if (name.Length == 0 || name.Length >= FAT_MAX_LFN) return 0; +static bool FatNameViewIsValid(SYS_STRING_VIEW name) { + if (!FatStringViewDataIsPlausible(name)) return false; + if (name.Length == 0 || name.Length >= FAT_MAX_LFN) return false; for (size_t i = 0; i < name.Length; i++) { char c = name.Data[i]; - if (c == '\0' || FatIsPathSep(c)) return 0; + if (c == '\0' || FatIsPathSep(c)) return false; } - return 1; + return true; } // Implements the lfn Match V operation. -static int LfnMatchV(const char* lfn, SYS_STRING_VIEW want) { - if (!lfn || !SysSvIsValid(want)) return 0; +static bool LfnMatchV(const char* lfn, SYS_STRING_VIEW want) { + if (!lfn || !SysSvIsValid(want)) return false; size_t i = 0; while (lfn[i] && i < want.Length) { char a = lfn[i], b = want.Data[i]; if (a >= 'A' && a <= 'Z') a += 32; if (b >= 'A' && b <= 'Z') b += 32; - if (a != b) return 0; + if (a != b) return false; i++; } return lfn[i] == '\0' && i == want.Length; } -static int MatchName83V(const char* dirName, const char* dirExt, +static bool MatchName83V(const char* dirName, const char* dirExt, SYS_STRING_VIEW want) { char buf[13]; int n = 0; @@ -449,14 +450,14 @@ static int MatchName83V(const char* dirName, const char* dirExt, buf[n++] = (char)(dirExt[i] >= 'A' && dirExt[i] <= 'Z' ? dirExt[i]+32 : dirExt[i]); } - if ((size_t)n != want.Length) return 0; + if ((size_t)n != want.Length) return false; for (int i = 0; i < n; i++) { char a = buf[i], b = want.Data[i]; if (a >= 'A' && a <= 'Z') a += 32; if (b >= 'A' && b <= 'Z') b += 32; - if (a != b) return 0; + if (a != b) return false; } - return 1; + return true; } // Returns the path cache canonical byte used for hashes and comparisons. @@ -719,21 +720,21 @@ bool FatBuildReadRun(FAT_FILE* f, uint32_t logicalIndex, return true; } -static int SearchDirV(FAT_VOL* v, uint32_t dirCluster, SYS_STRING_VIEW name, +static bool SearchDirV(FAT_VOL* v, uint32_t dirCluster, SYS_STRING_VIEW name, uint32_t* outCluster, uint32_t* outSize, uint8_t* outAttr, uint64_t* outDirentLba, uint32_t* outDirentOffset) { - if (!FatNameViewIsValid(name)) return 0; + if (!FatNameViewIsValid(name)) return false; uint8_t* clusterBuf = (uint8_t*)MmAllocatePool(v->BytesPerCluster); - if (!clusterBuf) return 0; + if (!clusterBuf) return false; - char lfn[FAT_MAX_LFN]; int hasLfn = 0; memset(lfn, 0, sizeof(lfn)); + char lfn[FAT_MAX_LFN]; bool hasLfn = false; memset(lfn, 0, sizeof(lfn)); char wantName83[8], wantExt83[3]; - int allowShortMatch = FatNameIsStructurallyShort83V(name); + bool allowShortMatch = FatNameIsStructurallyShort83V(name); Make83NameV(name, wantName83, wantExt83); uint64_t clustersRead = 0; uint64_t entriesScanned = 0; - int found = 0; + bool found = false; uint32_t cursor; uint64_t lba; uint32_t sectors; if (!FatDirectoryFirstSegment(v, dirCluster, &cursor, &lba, §ors)) @@ -749,16 +750,16 @@ static int SearchDirV(FAT_VOL* v, uint32_t dirCluster, SYS_STRING_VIEW name, for (uint32_t i = 0; i < entries; i++) { entriesScanned++; if (dir[i].Name[0] == 0x00) goto done; - if ((uint8_t)dir[i].Name[0] == 0xE5) { hasLfn = 0; memset(lfn, 0, sizeof(lfn)); continue; } - if (dir[i].Attr == ATTR_LFN) { FatLfnAppend(lfn, (LFN_ENTRY*)&dir[i]); hasLfn = 1; continue; } + if ((uint8_t)dir[i].Name[0] == 0xE5) { hasLfn = false; memset(lfn, 0, sizeof(lfn)); continue; } + if (dir[i].Attr == ATTR_LFN) { FatLfnAppend(lfn, (LFN_ENTRY*)&dir[i]); hasLfn = true; continue; } - int match = 0; + bool match = false; if (hasLfn && lfn[0]) match = LfnMatchV(lfn, name); if (!match && allowShortMatch) match = MatchName83V(dir[i].Name, dir[i].Ext, name); if (!match && allowShortMatch) match = MatchRaw83(dir[i].Name, dir[i].Ext, wantName83, wantExt83); - hasLfn = 0; memset(lfn, 0, sizeof(lfn)); + hasLfn = false; memset(lfn, 0, sizeof(lfn)); if (match) { *outCluster = FatDirentFirstCluster(v, &dir[i]); @@ -769,7 +770,7 @@ static int SearchDirV(FAT_VOL* v, uint32_t dirCluster, SYS_STRING_VIEW name, if (outDirentLba) *outDirentLba = lba + eb / v->BytesPerSector; if (outDirentOffset) *outDirentOffset = (uint32_t)(eb % v->BytesPerSector); } - found = 1; + found = true; goto done; } } @@ -781,22 +782,22 @@ done: return found; } -static int FatPathNextComponent(SYS_STRING_VIEW path, size_t* cursor, +static bool FatPathNextComponent(SYS_STRING_VIEW path, size_t* cursor, SYS_STRING_VIEW* component) { - if (!cursor || !component || !SysSvIsValid(path)) return 0; + if (!cursor || !component || !SysSvIsValid(path)) return false; size_t pos = *cursor; while (pos < path.Length && FatIsPathSep(path.Data[pos])) pos++; if (pos >= path.Length) { *cursor = pos; *component = SYS_SV_NULL; - return 0; + return false; } size_t start = pos; while (pos < path.Length && !FatIsPathSep(path.Data[pos])) pos++; *cursor = pos; *component = SysSvFromParts(path.Data + start, pos - start); - return 1; + return true; } KSTATUS FatLookupPathV(FAT_VOL* v, SYS_STRING_VIEW path, @@ -884,20 +885,20 @@ static KSTATUS SplitPathV(SYS_STRING_VIEW path, SYS_STRING_VIEW* parent, } // Find a free DIRENT slot. -static int FindFreeDirent(FAT_VOL* v, uint32_t dirCluster, +static bool FindFreeDirent(FAT_VOL* v, uint32_t dirCluster, uint64_t* outLba, uint32_t* outOffset) { uint8_t* clusterBuf = (uint8_t*)MmAllocatePool(v->BytesPerCluster); - if (!clusterBuf) return 0; + if (!clusterBuf) return false; uint32_t cursor; uint64_t lba; uint32_t sectors; if (!FatDirectoryFirstSegment(v, dirCluster, &cursor, &lba, §ors)) { MmFreePool(clusterBuf); - return 0; + return false; } do { if (!KSUCCESS(FatReadSectors(v, lba, sectors, clusterBuf))) { - MmFreePool(clusterBuf); return 0; + MmFreePool(clusterBuf); return false; } uint32_t entries = (sectors * v->BytesPerSector) / sizeof(DIRENT); DIRENT* dir = (DIRENT*)clusterBuf; @@ -907,46 +908,46 @@ static int FindFreeDirent(FAT_VOL* v, uint32_t dirCluster, uint64_t eb = (uint64_t)i * sizeof(DIRENT); *outLba = lba + eb / v->BytesPerSector; *outOffset = (uint32_t)(eb % v->BytesPerSector); - MmFreePool(clusterBuf); return 1; + MmFreePool(clusterBuf); return true; } } } while (FatDirectoryNextSegment(v, dirCluster, &cursor, &lba, §ors)); if (FatIsFixedRoot(v, dirCluster)) { MmFreePool(clusterBuf); - return 0; + return false; } uint32_t newCluster = FatAppendCluster(v, cursor); if (newCluster) { *outLba = FatClusterToLba(v, newCluster); *outOffset = 0; - MmFreePool(clusterBuf); return 1; + MmFreePool(clusterBuf); return true; } MmFreePool(clusterBuf); - return 0; + return false; } // Find a contiguous run of free directory entries. // VFAT LFN creation requires N long-name entries to immediately precede the // final short entry, so the run must live inside one readable/writeable segment. -static int FindFreeDirentRun(FAT_VOL* v, uint32_t dirCluster, uint32_t needed, +static bool FindFreeDirentRun(FAT_VOL* v, uint32_t dirCluster, uint32_t needed, uint64_t* outLba, uint32_t* outFirstIndex, uint32_t* outSectors) { - if (!needed) return 0; + if (!needed) return false; uint8_t* clusterBuf = (uint8_t*)MmAllocatePool(v->BytesPerCluster); - if (!clusterBuf) return 0; + if (!clusterBuf) return false; uint32_t cursor; uint64_t lba; uint32_t sectors; if (!FatDirectoryFirstSegment(v, dirCluster, &cursor, &lba, §ors)) { MmFreePool(clusterBuf); - return 0; + return false; } do { if (!KSUCCESS(FatReadSectors(v, lba, sectors, clusterBuf))) { MmFreePool(clusterBuf); - return 0; + return false; } uint32_t entries = (sectors * v->BytesPerSector) / sizeof(DIRENT); @@ -963,7 +964,7 @@ static int FindFreeDirentRun(FAT_VOL* v, uint32_t dirCluster, uint32_t needed, *outFirstIndex = runStart; *outSectors = sectors; MmFreePool(clusterBuf); - return 1; + return true; } } else { runLen = 0; @@ -973,7 +974,7 @@ static int FindFreeDirentRun(FAT_VOL* v, uint32_t dirCluster, uint32_t needed, if (FatIsFixedRoot(v, dirCluster)) { MmFreePool(clusterBuf); - return 0; + return false; } uint32_t newCluster = FatAppendCluster(v, cursor); @@ -982,11 +983,11 @@ static int FindFreeDirentRun(FAT_VOL* v, uint32_t dirCluster, uint32_t needed, *outFirstIndex = 0; *outSectors = v->SectorsPerCluster; MmFreePool(clusterBuf); - return 1; + return true; } MmFreePool(clusterBuf); - return 0; + return false; } // Finds the parent directory and final component for a create or delete path. @@ -1039,7 +1040,7 @@ KSTATUS FatCreateDirent(FAT_VOL* v, SYS_STRING_VIEW path, } } - int useLfn = FatNameNeedsLfnForCreateV(filenameView); + bool useLfn = FatNameNeedsLfnForCreateV(filenameView); char shortName[8], shortExt[3]; if (useLfn) { s = DeleteLegacyBrokenDotAliasV(v, parentCluster, filenameView); @@ -1152,17 +1153,17 @@ KSTATUS FatInitializeDirectoryCluster(FAT_VOL* v, uint32_t dirCluster, } // Tests whether a FAT directory entry is one of the synthetic dot entries. -static int FatIsDotEntry(const DIRENT* d) { - if (!d || !(d->Attr & ATTR_DIRECTORY)) return 0; - if (d->Name[0] != '.') return 0; +static bool FatIsDotEntry(const DIRENT* d) { + if (!d || !(d->Attr & ATTR_DIRECTORY)) return false; + if (d->Name[0] != '.') return false; if (d->Name[1] == ' ') - return 1; + return true; return d->Name[1] == '.' && d->Name[2] == ' '; } // Tests whether a directory contains any real child entries. KSTATUS FatDirectoryIsEmpty(FAT_VOL* v, uint32_t dirCluster, - int* emptyOut) { + bool* emptyOut) { if (!v || !emptyOut) return NTLSTATUS_INVALID_PARAMETER; @@ -1209,12 +1210,12 @@ KSTATUS FatDeleteDirentRunV(FAT_VOL* v, uint32_t parentCluster, if (!clusterBuf) return NTLSTATUS_NO_MEMORY; char lfn[FAT_MAX_LFN]; - int hasLfn = 0; + bool hasLfn = false; uint32_t lfnStart = 0; memset(lfn, 0, sizeof(lfn)); char wantName83[8], wantExt83[3]; - int allowShortMatch = FatNameIsStructurallyShort83V(name); + bool allowShortMatch = FatNameIsStructurallyShort83V(name); Make83NameV(name, wantName83, wantExt83); uint32_t cursor; uint64_t lba; uint32_t sectors; @@ -1229,20 +1230,20 @@ KSTATUS FatDeleteDirentRunV(FAT_VOL* v, uint32_t parentCluster, uint32_t entries = (sectors * v->BytesPerSector) / sizeof(DIRENT); DIRENT* dir = (DIRENT*)clusterBuf; - hasLfn = 0; + hasLfn = false; memset(lfn, 0, sizeof(lfn)); for (uint32_t i = 0; i < entries; i++) { uint8_t first = (uint8_t)dir[i].Name[0]; if (first == 0x00) { MmFreePool(clusterBuf); return NTLSTATUS_NOT_FOUND; } - if (first == 0xE5) { hasLfn = 0; memset(lfn, 0, sizeof(lfn)); continue; } + if (first == 0xE5) { hasLfn = false; memset(lfn, 0, sizeof(lfn)); continue; } if (dir[i].Attr == ATTR_LFN) { if (!hasLfn) lfnStart = i; FatLfnAppend(lfn, (LFN_ENTRY*)&dir[i]); - hasLfn = 1; + hasLfn = true; continue; } - int match = 0; + bool match = false; if (hasLfn && lfn[0]) match = LfnMatchV(lfn, name); if (!match && allowShortMatch) match = MatchName83V(dir[i].Name, dir[i].Ext, name); @@ -1259,7 +1260,7 @@ KSTATUS FatDeleteDirentRunV(FAT_VOL* v, uint32_t parentCluster, return s; } - hasLfn = 0; + hasLfn = false; memset(lfn, 0, sizeof(lfn)); } } while (FatDirectoryNextSegment(v, parentCluster, &cursor, &lba, §ors)); diff --git a/code/drivers/fs/fat/fat_driver.c b/code/drivers/fs/fat/fat_driver.c index d448831..6016b95 100644 --- a/code/drivers/fs/fat/fat_driver.c +++ b/code/drivers/fs/fat/fat_driver.c @@ -1,5 +1,6 @@ // FAT mount discovery, BPB validation, and driver lifetime. +#include #include "fat_internal.h" // FAT BPB validation. @@ -12,18 +13,18 @@ typedef struct FAT_BPB_LAYOUT { uint32_t TotalClusters; } FAT_BPB_LAYOUT; -static int FatComputeBpbLayout(const BPB* bpb, FAT_BPB_LAYOUT* out) { - if (!bpb || !out) return 0; +static bool FatComputeBpbLayout(const BPB* bpb, FAT_BPB_LAYOUT* out) { + if (!bpb || !out) return false; memset(out, 0, sizeof(*out)); - if (bpb->BytesPerSector != 512) return 0; - if (bpb->SectorsPerCluster == 0) return 0; - if (bpb->NumFats == 0) return 0; - if (bpb->ReservedSectors == 0) return 0; + if (bpb->BytesPerSector != 512) return false; + if (bpb->SectorsPerCluster == 0) return false; + if (bpb->NumFats == 0) return false; + if (bpb->ReservedSectors == 0) return false; uint32_t totalSectors = bpb->TotalSectors16 ? bpb->TotalSectors16 : bpb->TotalSectors32; uint32_t fatSize = bpb->FatSize16 ? bpb->FatSize16 : bpb->FatSize32; - if (!totalSectors || !fatSize) return 0; + if (!totalSectors || !fatSize) return false; uint32_t rootDirSectors = ((uint32_t)bpb->RootEntryCount * sizeof(DIRENT) + (uint32_t)bpb->BytesPerSector - 1u) / @@ -31,7 +32,7 @@ static int FatComputeBpbLayout(const BPB* bpb, FAT_BPB_LAYOUT* out) { uint32_t metadataSectors = (uint32_t)bpb->ReservedSectors + (uint32_t)bpb->NumFats * fatSize + rootDirSectors; - if (totalSectors <= metadataSectors) return 0; + if (totalSectors <= metadataSectors) return false; uint32_t dataSectors = totalSectors - metadataSectors; uint32_t totalClusters = dataSectors / bpb->SectorsPerCluster; @@ -44,10 +45,10 @@ static int FatComputeBpbLayout(const BPB* bpb, FAT_BPB_LAYOUT* out) { variant = FatVariant32; if (variant == FatVariant16) { - if (bpb->FatSize16 == 0 || bpb->RootEntryCount == 0) return 0; + if (bpb->FatSize16 == 0 || bpb->RootEntryCount == 0) return false; } else if (variant == FatVariant32) { - if (bpb->FatSize16 != 0 || bpb->RootEntryCount != 0) return 0; - if (bpb->FatSize32 == 0 || bpb->RootCluster < 2) return 0; + if (bpb->FatSize16 != 0 || bpb->RootEntryCount != 0) return false; + if (bpb->FatSize32 == 0 || bpb->RootCluster < 2) return false; } out->Variant = variant; @@ -56,7 +57,7 @@ static int FatComputeBpbLayout(const BPB* bpb, FAT_BPB_LAYOUT* out) { out->RootDirSectors = rootDirSectors; out->DataSectors = dataSectors; out->TotalClusters = totalClusters; - return 1; + return true; } diff --git a/code/drivers/fs/fat/fat_file.c b/code/drivers/fs/fat/fat_file.c index 1b37d78..37ea773 100644 --- a/code/drivers/fs/fat/fat_file.c +++ b/code/drivers/fs/fat/fat_file.c @@ -1,5 +1,6 @@ // FAT synchronous file and directory operations. +#include #include "fat_internal.h" // Open / Close. @@ -323,7 +324,7 @@ KSTATUS FatRemoveDir(IO_VOLUME_PROVIDER* fs, SYS_STRING_VIEW path) { if (!direntLba) return NTLSTATUS_ACCESS_DENIED; if (!(attr & ATTR_DIRECTORY)) return NTLSTATUS_ACCESS_DENIED; - int empty = 0; + bool empty = false; s = FatDirectoryIsEmpty(v, cluster, &empty); if (!KSUCCESS(s)) return s; if (!empty) return NTLSTATUS_ACCESS_DENIED; diff --git a/code/drivers/fs/fat/fat_internal.h b/code/drivers/fs/fat/fat_internal.h index 0c5f439..7aa8b98 100644 --- a/code/drivers/fs/fat/fat_internal.h +++ b/code/drivers/fs/fat/fat_internal.h @@ -2,6 +2,7 @@ #pragma once +#include #include "fat.h" #include "fat_disk.h" @@ -99,21 +100,21 @@ KSTATUS FatReadSectors(FAT_VOL* volume, uint64_t lba, uint32_t count, void* buff KSTATUS FatWriteSectors(FAT_VOL* volume, uint64_t lba, uint32_t count, const void* buffer); const char* FatVariantName(FAT_VARIANT variant); uint32_t FatEocMark(FAT_VOL* volume); -int FatIsDataCluster(FAT_VOL* volume, uint32_t cluster); +bool FatIsDataCluster(FAT_VOL* volume, uint32_t cluster); uint32_t FatDirentFirstCluster(FAT_VOL* volume, const DIRENT* dirent); void FatSetDirentFirstCluster(FAT_VOL* volume, DIRENT* dirent, uint32_t cluster); -int FatIsFixedRoot(FAT_VOL* volume, uint32_t dirCluster); -int FatDirectoryFirstSegment(FAT_VOL* volume, uint32_t dirCluster, +bool FatIsFixedRoot(FAT_VOL* volume, uint32_t dirCluster); +bool FatDirectoryFirstSegment(FAT_VOL* volume, uint32_t dirCluster, uint32_t* cursorOut, uint64_t* lbaOut, uint32_t* sectorsOut); -int FatDirectoryNextSegment(FAT_VOL* volume, uint32_t dirCluster, +bool FatDirectoryNextSegment(FAT_VOL* volume, uint32_t dirCluster, uint32_t* cursorInOut, uint64_t* lbaOut, uint32_t* sectorsOut); uint64_t FatClusterToLba(FAT_VOL* volume, uint32_t cluster); uint32_t FatEntry(FAT_VOL* volume, uint32_t cluster); KSTATUS FatWriteEntry(FAT_VOL* volume, uint32_t cluster, uint32_t value); void FatFlushCache(FAT_VOL* volume); -uint32_t FatAllocClusterEx(FAT_VOL* volume, int zeroCluster); +uint32_t FatAllocClusterEx(FAT_VOL* volume, bool zeroCluster); uint32_t FatAllocCluster(FAT_VOL* volume); -uint32_t FatAppendClusterEx(FAT_VOL* volume, uint32_t lastCluster, int zeroCluster); +uint32_t FatAppendClusterEx(FAT_VOL* volume, uint32_t lastCluster, bool zeroCluster); uint32_t FatAppendCluster(FAT_VOL* volume, uint32_t lastCluster); uint32_t FatGetChainEnd(FAT_VOL* volume, uint32_t start, uint32_t* countOut); void FatFreeClusterChain(FAT_VOL* volume, uint32_t start); @@ -145,7 +146,7 @@ KSTATUS FatCreateDirent(FAT_VOL* volume, SYS_STRING_VIEW path, uint64_t* direntLbaOut, uint32_t* direntOffsetOut); KSTATUS FatInitializeDirectoryCluster(FAT_VOL* volume, uint32_t dirCluster, uint32_t parentCluster); -KSTATUS FatDirectoryIsEmpty(FAT_VOL* volume, uint32_t dirCluster, int* emptyOut); +KSTATUS FatDirectoryIsEmpty(FAT_VOL* volume, uint32_t dirCluster, bool* emptyOut); KSTATUS FatDeleteDirentRunV(FAT_VOL* volume, uint32_t parentCluster, SYS_STRING_VIEW name); void FatCacheInvalidate(void); diff --git a/code/drivers/input/ps2/ps2.c b/code/drivers/input/ps2/ps2.c index 7a763bf..658f62c 100644 --- a/code/drivers/input/ps2/ps2.c +++ b/code/drivers/input/ps2/ps2.c @@ -216,8 +216,8 @@ static void Ps2MouseIsr(void) { if (flags & 0xC0u) return; - int dx = (int)gMousePacket[1] - ((flags & 0x10u) ? 256 : 0); - int dy = (int)gMousePacket[2] - ((flags & 0x20u) ? 256 : 0); + int32_t dx = (int32_t)gMousePacket[1] - ((flags & 0x10u) ? 256 : 0); + int32_t dy = (int32_t)gMousePacket[2] - ((flags & 0x20u) ? 256 : 0); dy = -dy; int next = (gMouseDpcTail + 1) & (int)MOUSE_DPC_QUEUE_MASK; diff --git a/code/drivers/input/ps2/ps2.h b/code/drivers/input/ps2/ps2.h index 857206c..b068fa5 100644 --- a/code/drivers/input/ps2/ps2.h +++ b/code/drivers/input/ps2/ps2.h @@ -33,8 +33,8 @@ #define MOUSE_DPC_QUEUE_MASK (MOUSE_DPC_QUEUE_SIZE - 1u) typedef struct PS2_MOUSE_PACKET { - int dx; - int dy; + int32_t dx; + int32_t dy; uint8_t buttons; } PS2_MOUSE_PACKET; diff --git a/code/drivers/net/lwip/lwip_stack.c b/code/drivers/net/lwip/lwip_stack.c index f86e5c2..86c50ca 100644 --- a/code/drivers/net/lwip/lwip_stack.c +++ b/code/drivers/net/lwip/lwip_stack.c @@ -1,5 +1,6 @@ // lwIP stack provider glue. This file bridges the kernel network-device model into lwIP. +#include #include #include #include @@ -77,7 +78,7 @@ typedef struct LWIP_ATTACH_CTX { typedef struct LWIP_RESOLVE_CONTEXT { SEMAPHORE* Done; - volatile long References; + volatile int32_t References; err_t Result; u8_t DnsType; ip_addr_t Address; @@ -167,12 +168,12 @@ static KSTATUS LwipErrToStatus(err_t err) { } // Implements the ascii Is Digit operation. -static int LwipAsciiIsDigit(char c) { +static bool LwipAsciiIsDigit(char c) { return c >= '0' && c <= '9'; } // Parses ipv4 Part. -static int LwipParseIpv4Part(const char** cursor, uint8_t* out) { +static bool LwipParseIpv4Part(const char** cursor, uint8_t* out) { const char* p = *cursor; uint32_t value = 0; uint32_t digits = 0; @@ -180,37 +181,37 @@ static int LwipParseIpv4Part(const char** cursor, uint8_t* out) { while (LwipAsciiIsDigit(*p)) { value = value * 10u + (uint32_t)(*p - '0'); if (value > 255u) - return 0; + return false; p++; digits++; } if (digits == 0) - return 0; + return false; *out = (uint8_t)value; *cursor = p; - return 1; + return true; } // Parses ipv4. -static int LwipParseIpv4(const char* text, ip4_addr_t* out) { +static bool LwipParseIpv4(const char* text, ip4_addr_t* out) { if (!text || !out) - return 0; + return false; const char* p = text; uint8_t a = 0, b = 0, c = 0, d = 0; - if (!LwipParseIpv4Part(&p, &a) || *p++ != '.') return 0; - if (!LwipParseIpv4Part(&p, &b) || *p++ != '.') return 0; - if (!LwipParseIpv4Part(&p, &c) || *p++ != '.') return 0; - if (!LwipParseIpv4Part(&p, &d) || *p != '\0') return 0; + if (!LwipParseIpv4Part(&p, &a) || *p++ != '.') return false; + if (!LwipParseIpv4Part(&p, &b) || *p++ != '.') return false; + if (!LwipParseIpv4Part(&p, &c) || *p++ != '.') return false; + if (!LwipParseIpv4Part(&p, &d) || *p != '\0') return false; IP4_ADDR(out, a, b, c, d); - return 1; + return true; } static KSTATUS LwipGetSettingIpv4(const char* section, const char* key, - ip4_addr_t* out, int required) { + ip4_addr_t* out, bool required) { char value[LWIP_SETTINGS_VALUE_MAX]; KSTATUS st = KeGetSettingString(section, key, value, sizeof(value)); if (st == NTLSTATUS_NOT_FOUND && !required) @@ -225,7 +226,7 @@ static KSTATUS LwipGetSettingIpv4(const char* section, const char* key, } static KSTATUS LwipGetSettingIpv6(const char* section, const char* key, - ip6_addr_t* out, int required) { + ip6_addr_t* out, bool required) { char value[LWIP_SETTINGS_VALUE_MAX]; KSTATUS st = KeGetSettingString(section, key, value, sizeof(value)); if (st == NTLSTATUS_NOT_FOUND && !required) @@ -308,13 +309,13 @@ static KSTATUS LwipLoadConfigFromSection(const char* section, LWIP_NETIF_CONFIG* if (strcmp(mode, "static") == 0) { cfg->UseDhcp = 0; - st = LwipGetSettingIpv4(section, "address", &cfg->Address, 1); + st = LwipGetSettingIpv4(section, "address", &cfg->Address, true); if (!KSUCCESS(st)) return st; - st = LwipGetSettingIpv4(section, "netmask", &cfg->Netmask, 1); + st = LwipGetSettingIpv4(section, "netmask", &cfg->Netmask, true); if (!KSUCCESS(st)) return st; - st = LwipGetSettingIpv4(section, "gateway", &cfg->Gateway, 1); + st = LwipGetSettingIpv4(section, "gateway", &cfg->Gateway, true); if (!KSUCCESS(st)) return st; - st = LwipGetSettingIpv4(section, "dns", &cfg->Dns4, 1); + st = LwipGetSettingIpv4(section, "dns", &cfg->Dns4, true); if (!KSUCCESS(st)) return st; cfg->HasDns4 = 1; return LwipLoadIpv6Config(section, cfg); @@ -322,7 +323,7 @@ static KSTATUS LwipLoadConfigFromSection(const char* section, LWIP_NETIF_CONFIG* if (strcmp(mode, "dhcp") == 0) { cfg->UseDhcp = 1; - st = LwipGetSettingIpv4(section, "dns", &cfg->Dns4, 0); + st = LwipGetSettingIpv4(section, "dns", &cfg->Dns4, false); if (KSUCCESS(st)) cfg->HasDns4 = 1; else if (st != NTLSTATUS_NOT_FOUND) @@ -449,7 +450,7 @@ static KSTATUS LwipSocketAllocateWithConn(uint32_t domain, uint32_t type, uint32 // Returns whether the IRP cancel token has been set by SysCancelIo. -static int LwipSocketCancelRequested(volatile uint8_t* cancelRequested) { +static bool LwipSocketCancelRequested(volatile uint8_t* cancelRequested) { return cancelRequested && *cancelRequested; } @@ -552,22 +553,22 @@ static KSTATUS LwipSocketWaitAccept(LWIP_PROVIDER_SOCKET* s, } } -static int LwipSocketAddressValid(const SYS_SOCKET_ADDRESS_K* address, size_t addressSize) { +static bool LwipSocketAddressValid(const SYS_SOCKET_ADDRESS_K* address, size_t addressSize) { if (!address || addressSize != sizeof(SYS_SOCKET_ADDRESS_K)) - return 0; + return false; if (address->Size != sizeof(SYS_SOCKET_ADDRESS_K)) - return 0; + return false; return address->Family == SYS_AF_INET || address->Family == SYS_AF_INET6; } -static int LwipSocketFamilyMatches(const LWIP_PROVIDER_SOCKET* s, const SYS_SOCKET_ADDRESS_K* address) { +static bool LwipSocketFamilyMatches(const LWIP_PROVIDER_SOCKET* s, const SYS_SOCKET_ADDRESS_K* address) { if (!s || !address) - return 0; + return false; if (s->Domain == SYS_AF_INET) return address->Family == SYS_AF_INET; if (s->Domain == SYS_AF_INET6) return address->Family == SYS_AF_INET6; - return 0; + return false; } // Implements the socket Make Ip operation. @@ -1613,7 +1614,7 @@ static void LwipResolveRelease(LWIP_RESOLVE_CONTEXT* context) { if (!context) return; - if (_InterlockedDecrement(&context->References) != 0) + if (NtlAtomicDecrement32(&context->References) != 0) return; if (context->Done) diff --git a/code/drivers/storage/ahci/ahci.c b/code/drivers/storage/ahci/ahci.c index bf52a9c..0ee47ea 100644 --- a/code/drivers/storage/ahci/ahci.c +++ b/code/drivers/storage/ahci/ahci.c @@ -266,7 +266,7 @@ static void AhciIsr(void) { if (!pending) continue; - for (int i = 0; i < ctl->driveCount; i++) { + for (uint32_t i = 0; i < ctl->driveCount; i++) { AHCI_DRIVE* drv = &ctl->drives[i]; uint32_t portBit = (1u << drv->portNo); if (!(pending & portBit)) continue; @@ -676,7 +676,7 @@ static KSTATUS AhciDispatchControllerPnp(DEVICE_OBJECT* device, IRP* irp) { case IRP_MN_SURPRISE_REMOVAL: ctl->surpriseRemoved = true; device->PnpState = DevicePnpSurpriseRemoved; - for (int i = 0; i < ctl->driveCount; i++) { + for (uint32_t i = 0; i < ctl->driveCount; i++) { AHCI_DRIVE* drv = &ctl->drives[i]; drv->surpriseRemoved = true; if (drv->deviceObject) @@ -689,7 +689,7 @@ static KSTATUS AhciDispatchControllerPnp(DEVICE_OBJECT* device, IRP* irp) { case IRP_MN_REMOVE_DEVICE: ctl->removed = true; device->PnpState = DevicePnpDeleted; - for (int i = 0; i < ctl->driveCount; i++) { + for (uint32_t i = 0; i < ctl->driveCount; i++) { AHCI_DRIVE* drv = &ctl->drives[i]; if (drv->deviceObject) { (void)IoUnregisterDiskDevice(drv->deviceObject->u.Disk.DiskIndex); @@ -974,7 +974,7 @@ static KSTATUS AhciStartController(AHCI_CONTROLLER* ctl) { DbgPrint("AHCI: port %u: link up, initializing...\n", portno); - int driveIdx = ctl->driveCount; + uint32_t driveIdx = ctl->driveCount; AHCI_DRIVE* drv = &ctl->drives[driveIdx]; memset(drv, 0, sizeof(*drv)); drv->magic = AHCI_DRIVE_MAGIC; @@ -1119,11 +1119,11 @@ static KSTATUS AhciStartController(AHCI_CONTROLLER* ctl) { ctl->pciInfo.Address.Function); if (!ctl->pollingMode) { - DbgPrint("AHCI: ISR registered on IRQ %u (%s); controller drives=%d\n", - (unsigned)ctl->irq, ctl->usingMsi ? "MSI" : "INTx", ctl->driveCount); + DbgPrint("AHCI: ISR registered on IRQ %u (%s); controller drives=%u\n", + (unsigned)ctl->irq, ctl->usingMsi ? "MSI" : "INTx", (unsigned)ctl->driveCount); } else { - DbgPrint("AHCI: controller started in polling mode; controller drives=%d\n", - ctl->driveCount); + DbgPrint("AHCI: controller started in polling mode; controller drives=%u\n", + (unsigned)ctl->driveCount); } ctl->started = true; ctl->fdo->PnpState = DevicePnpStarted; diff --git a/code/drivers/storage/ahci/ahci.h b/code/drivers/storage/ahci/ahci.h index 36a219a..ee311dd 100644 --- a/code/drivers/storage/ahci/ahci.h +++ b/code/drivers/storage/ahci/ahci.h @@ -378,7 +378,7 @@ struct AHCI_CONTROLLER { bool pollingMode; AHCI_DRIVE drives[MAX_AHCI_DRIVES]; - int driveCount; + uint32_t driveCount; bool started; bool surpriseRemoved; diff --git a/code/kernel/hal/hal_main.c b/code/kernel/hal/hal_main.c index 1099498..a54d04d 100644 --- a/code/kernel/hal/hal_main.c +++ b/code/kernel/hal/hal_main.c @@ -13,7 +13,7 @@ #include #include -void HalScreenInitialize(void* (*AllocatePool)(size_t size), uintptr_t fb, int w, int h, int stride, +void HalScreenInitialize(void* (*AllocatePool)(size_t size), uintptr_t fb, int32_t w, int32_t h, int32_t stride, HAL_SCREEN* out); void HalPciInitialize(HAL_PCI* out); void HalRtcInitialize(HAL_RTC* out); @@ -215,15 +215,15 @@ static uint64_t AcpiReadU64(const void* table, uint32_t tableLen, uint32_t off) } // Decodes one ACPI Generic Address Structure used for fixed power registers. -static int AcpiReadGasRegister(const void* table, uint32_t tableLen, uint32_t off, +static bool AcpiReadGasRegister(const void* table, uint32_t tableLen, uint32_t off, ACPI_FIXED_REGISTER* out) { - if (!out || off + sizeof(ACPI_GAS) > tableLen) return 0; + if (!out || off + sizeof(ACPI_GAS) > tableLen) return false; ACPI_GAS gas; memcpy(&gas, (const uint8_t*)table + off, sizeof(gas)); - if (!gas.Address || gas.RegisterBitOffset != 0) return 0; - if (gas.AddressSpaceId != 0x00 && gas.AddressSpaceId != 0x01) return 0; - if (gas.AddressSpaceId == 0x01 && gas.Address > 0xFFFFu) return 0; + if (!gas.Address || gas.RegisterBitOffset != 0) return false; + if (gas.AddressSpaceId != 0x00 && gas.AddressSpaceId != 0x01) return false; + if (gas.AddressSpaceId == 0x01 && gas.Address > 0xFFFFu) return false; uint8_t widthBytes = 0; if (gas.AccessSize >= 1 && gas.AccessSize <= 3) @@ -236,24 +236,24 @@ static int AcpiReadGasRegister(const void* table, uint32_t tableLen, uint32_t of out->AddressSpaceId = gas.AddressSpaceId; out->WidthBytes = widthBytes; out->Address = gas.Address; - return 1; + return true; } // Decodes one byte-wide ACPI GAS used by the HW-reduced Sleep Control register. -static int AcpiReadByteGasRegister(const void* table, uint32_t tableLen, uint32_t off, +static bool AcpiReadByteGasRegister(const void* table, uint32_t tableLen, uint32_t off, ACPI_FIXED_REGISTER* out) { - if (!out || off + sizeof(ACPI_GAS) > tableLen) return 0; + if (!out || off + sizeof(ACPI_GAS) > tableLen) return false; ACPI_GAS gas; memcpy(&gas, (const uint8_t*)table + off, sizeof(gas)); - if (!gas.Address || gas.RegisterBitOffset != 0 || gas.RegisterBitWidth != 8) return 0; - if (gas.AddressSpaceId != 0x00 && gas.AddressSpaceId != 0x01) return 0; - if (gas.AddressSpaceId == 0x01 && gas.Address > 0xFFFFu) return 0; + if (!gas.Address || gas.RegisterBitOffset != 0 || gas.RegisterBitWidth != 8) return false; + if (gas.AddressSpaceId != 0x00 && gas.AddressSpaceId != 0x01) return false; + if (gas.AddressSpaceId == 0x01 && gas.Address > 0xFFFFu) return false; out->AddressSpaceId = gas.AddressSpaceId; out->WidthBytes = 1u; out->Address = gas.Address; - return 1; + return true; } // Reads one ACPI fixed register while preserving the address-space distinction. @@ -341,9 +341,9 @@ static KSTATUS AcpiEnableModeForShutdown(void) { } } -static int AmlParsePkgLength(const uint8_t* p, const uint8_t* end, +static bool AmlParsePkgLength(const uint8_t* p, const uint8_t* end, const uint8_t** body, const uint8_t** pkgEnd) { - if (p >= end) return 0; + if (p >= end) return false; const uint8_t* start = p; uint8_t first = *p++; @@ -355,21 +355,21 @@ static int AmlParsePkgLength(const uint8_t* p, const uint8_t* end, } else { length = first & 0x0F; for (uint32_t i = 0; i < follow; i++) { - if (p >= end) return 0; + if (p >= end) return false; length |= ((uint32_t)*p++) << (4 + i * 8); } } - if (start + length < p || start + length > end) return 0; + if (start + length < p || start + length > end) return false; *body = p; *pkgEnd = start + length; - return 1; + return true; } static const uint8_t* AmlParseNameString(const uint8_t* p, const uint8_t* end, - int* lastSegmentIsS5) { + bool* lastSegmentIsS5) { uint32_t segments = 1; - *lastSegmentIsS5 = 0; + *lastSegmentIsS5 = false; // Optional root or parent prefixes: '\\', '^', '^^', ... while (p < end && (*p == 0x5C || *p == 0x5E)) p++; @@ -389,7 +389,7 @@ static const uint8_t* AmlParseNameString(const uint8_t* p, const uint8_t* end, for (uint32_t s = 0; s < segments; s++) { if (p + 4 > end) return NULL; if (s == segments - 1 && memcmp(p, "_S5_", 4) == 0) - *lastSegmentIsS5 = 1; + *lastSegmentIsS5 = true; p += 4; } @@ -397,9 +397,9 @@ static const uint8_t* AmlParseNameString(const uint8_t* p, const uint8_t* end, } // Implements the aml Read Integer operation. -static int AmlReadInteger(const uint8_t** pp, const uint8_t* end, uint8_t* out) { +static bool AmlReadInteger(const uint8_t** pp, const uint8_t* end, uint8_t* out) { const uint8_t* p = *pp; - if (p >= end) return 0; + if (p >= end) return false; switch (*p++) { case 0x00: // ZeroOp @@ -409,43 +409,43 @@ static int AmlReadInteger(const uint8_t** pp, const uint8_t* end, uint8_t* out) *out = 1; break; case 0x0A: // BytePrefix - if (p >= end) return 0; + if (p >= end) return false; *out = *p++; break; case 0x0B: // WordPrefix - if (p + 2 > end) return 0; + if (p + 2 > end) return false; *out = p[0]; p += 2; break; case 0x0C: // DWordPrefix - if (p + 4 > end) return 0; + if (p + 4 > end) return false; *out = p[0]; p += 4; break; case 0x0E: // QWordPrefix - if (p + 8 > end) return 0; + if (p + 8 > end) return false; *out = p[0]; p += 8; break; default: - return 0; + return false; } *pp = p; - return 1; + return true; } // Scan DSDT AML for the _S5_ Name object and extract SLP_TYP values. Real // firmware commonly encodes the name as "\\_S5_" instead of just "_S5_", and // package integers may be ZeroOp/OneOp/Byte/Word/DWord/QWord constants. -static int ScanAmlForS5(const uint8_t* aml, uint32_t len, const char* source) { +static bool ScanAmlForS5(const uint8_t* aml, uint32_t len, const char* source) { const uint8_t* end = aml + len; for (const uint8_t* cur = aml; cur < end; cur++) { if (*cur != 0x08) continue; // NameOp const uint8_t* p = cur + 1; - int isS5 = 0; + bool isS5 = false; p = AmlParseNameString(p, end, &isS5); if (!p || !isS5 || p >= end || *p != 0x12) continue; // PackageOp @@ -467,14 +467,14 @@ static int ScanAmlForS5(const uint8_t* aml, uint32_t len, const char* source) { HalDbgPrint("HAL: _S5_ source=%s SLP_TYP_A=0x%02x SLP_TYP_B=0x%02x\n", source ? source : "AML", gSlpTypA, gSlpTypB); - return 1; + return true; } - return 0; + return false; } // Validates checksum. -static int validate_checksum(void* table, uint32_t length) { +static bool validate_checksum(void* table, uint32_t length) { uint8_t sum = 0; uint8_t* bytes = (uint8_t*)table; for (uint32_t i = 0; i < length; i++) { @@ -627,7 +627,7 @@ static void WalkAcpi(uintptr_t rsdpPhys) { ACPISDTHeader* sdtHeader = NULL; uint32_t count = 0; - int isXsdt = 0; + bool isXsdt = false; // Safely find and validate the XSDT (ACPI 2.0+). if (rsdp->Revision >= 2 && rsdp->XsdtAddress != 0) { @@ -639,7 +639,7 @@ static void WalkAcpi(uintptr_t rsdpPhys) { sdtHeader = &xsdt->Header; count = (xsdt->Header.Length - sizeof(ACPISDTHeader)) / 8; - isXsdt = 1; + isXsdt = true; } } diff --git a/code/kernel/hal/hal_output.c b/code/kernel/hal/hal_output.c index 401dee0..f0ed7c9 100644 --- a/code/kernel/hal/hal_output.c +++ b/code/kernel/hal/hal_output.c @@ -1,5 +1,6 @@ // HAL output router. This file fans typed text streams out to registered physical and runtime endpoints. +#include #include #include #include @@ -10,26 +11,26 @@ typedef struct HAL_OUTPUT_SLOT { HAL_OUTPUT_ENDPOINT Endpoint; - volatile long Published; + volatile int32_t Published; } HAL_OUTPUT_SLOT; static HAL_OUTPUT_SLOT gOutputSlots[HAL_OUTPUT_ENDPOINT_CAPACITY]; -static volatile long gOutputRegistrationLock; -static volatile long long gOutputRecordCount; -static volatile long long gOutputByteCount; -static volatile long long gOutputEndpointWriteCount; -static volatile long long gOutputEmergencyRecordCount; -static volatile long gOutputEndpointCount; +static volatile int32_t gOutputRegistrationLock; +static volatile int64_t gOutputRecordCount; +static volatile int64_t gOutputByteCount; +static volatile int64_t gOutputEndpointWriteCount; +static volatile int64_t gOutputEmergencyRecordCount; +static volatile int32_t gOutputEndpointCount; // Acquires the endpoint registration lock. static void HalOutputAcquireRegistrationLock(void) { - while (_InterlockedCompareExchange(&gOutputRegistrationLock, 1, 0) != 0) + while (NtlAtomicCompareExchange32(&gOutputRegistrationLock, 1, 0) != 0) _mm_pause(); } // Releases the endpoint registration lock. static void HalOutputReleaseRegistrationLock(void) { - _InterlockedExchange(&gOutputRegistrationLock, 0); + NtlAtomicExchange32(&gOutputRegistrationLock, 0); } // Registers one output endpoint in the fixed endpoint table. @@ -42,13 +43,13 @@ static KSTATUS HalOutputRegisterEndpoint(const HAL_OUTPUT_ENDPOINT* endpoint, ui HalOutputAcquireRegistrationLock(); for (uint32_t i = 0; i < HAL_OUTPUT_ENDPOINT_CAPACITY; i++) { HAL_OUTPUT_SLOT* slot = &gOutputSlots[i]; - if (_InterlockedCompareExchange(&slot->Published, 0, 0) != 0) + if (NtlAtomicLoad32(&slot->Published) != 0) continue; slot->Endpoint = *endpoint; _ReadWriteBarrier(); - _InterlockedExchange(&slot->Published, 1); - _InterlockedIncrement(&gOutputEndpointCount); + NtlAtomicExchange32(&slot->Published, 1); + NtlAtomicIncrement32(&gOutputEndpointCount); *endpointIdOut = i; status = NTLSTATUS_SUCCESS; break; @@ -64,10 +65,10 @@ static KSTATUS HalOutputSetEndpointStreams(uint32_t endpointId, uint32_t streamM return NTLSTATUS_INVALID_PARAMETER; HAL_OUTPUT_SLOT* slot = &gOutputSlots[endpointId]; - if (_InterlockedCompareExchange(&slot->Published, 0, 0) == 0) + if (NtlAtomicLoad32(&slot->Published) == 0) return NTLSTATUS_NOT_FOUND; - _InterlockedExchange((volatile long*)&slot->Endpoint.StreamMask, (long)streamMask); + NtlAtomicExchangeU32(&slot->Endpoint.StreamMask, streamMask); return NTLSTATUS_SUCCESS; } @@ -78,18 +79,17 @@ static void HalOutputWrite(const HAL_OUTPUT_RECORD* record) { (record->Stream & (record->Stream - 1u)) != 0) return; - _InterlockedIncrement64(&gOutputRecordCount); - _InterlockedExchangeAdd64(&gOutputByteCount, (long long)record->Length); + NtlAtomicIncrement64(&gOutputRecordCount); + NtlAtomicExchangeAdd64(&gOutputByteCount, (int64_t)record->Length); if (record->Flags & HAL_OUTPUT_WRITE_EMERGENCY) - _InterlockedIncrement64(&gOutputEmergencyRecordCount); + NtlAtomicIncrement64(&gOutputEmergencyRecordCount); for (uint32_t i = 0; i < HAL_OUTPUT_ENDPOINT_CAPACITY; i++) { HAL_OUTPUT_SLOT* slot = &gOutputSlots[i]; - if (_InterlockedCompareExchange(&slot->Published, 0, 0) == 0) + if (NtlAtomicLoad32(&slot->Published) == 0) continue; - uint32_t streams = (uint32_t)_InterlockedCompareExchange( - (volatile long*)&slot->Endpoint.StreamMask, 0, 0); + uint32_t streams = NtlAtomicLoadU32(&slot->Endpoint.StreamMask); if ((streams & record->Stream) == 0) continue; if ((record->Flags & HAL_OUTPUT_WRITE_EMERGENCY) && @@ -102,7 +102,7 @@ static void HalOutputWrite(const HAL_OUTPUT_RECORD* record) { continue; write(context, record); - _InterlockedIncrement64(&gOutputEndpointWriteCount); + NtlAtomicIncrement64(&gOutputEndpointWriteCount); } } @@ -112,11 +112,11 @@ static void HalOutputQueryStats(HAL_OUTPUT_STATS* statsOut) { return; memset(statsOut, 0, sizeof(*statsOut)); - statsOut->RecordCount = (uint64_t)_InterlockedCompareExchange64(&gOutputRecordCount, 0, 0); - statsOut->ByteCount = (uint64_t)_InterlockedCompareExchange64(&gOutputByteCount, 0, 0); - statsOut->EndpointWriteCount = (uint64_t)_InterlockedCompareExchange64(&gOutputEndpointWriteCount, 0, 0); - statsOut->EmergencyRecordCount = (uint64_t)_InterlockedCompareExchange64(&gOutputEmergencyRecordCount, 0, 0); - statsOut->EndpointCount = (uint32_t)_InterlockedCompareExchange(&gOutputEndpointCount, 0, 0); + statsOut->RecordCount = (uint64_t)NtlAtomicLoad64(&gOutputRecordCount); + statsOut->ByteCount = (uint64_t)NtlAtomicLoad64(&gOutputByteCount); + statsOut->EndpointWriteCount = (uint64_t)NtlAtomicLoad64(&gOutputEndpointWriteCount); + statsOut->EmergencyRecordCount = (uint64_t)NtlAtomicLoad64(&gOutputEmergencyRecordCount); + statsOut->EndpointCount = (uint32_t)NtlAtomicLoad32(&gOutputEndpointCount); } // Initializes the fixed HAL output endpoint table. diff --git a/code/kernel/hal/hal_screen.c b/code/kernel/hal/hal_screen.c index 40933cd..bed6038 100644 --- a/code/kernel/hal/hal_screen.c +++ b/code/kernel/hal/hal_screen.c @@ -26,12 +26,12 @@ static int gCursorX = 0, gCursorY = 0; #include // see font8x16.h // Internals. -static inline uint32_t* ScreenBackbufferPixel(int x, int y) { +static inline uint32_t* ScreenBackbufferPixel(int32_t x, int32_t y) { return gBackbuffer ? (gBackbuffer + y * gStride + x) : (gFb + y * gStride + x); } // Updates pixel Raw. -static inline void PutPixelRaw(int x, int y, uint32_t color) { +static inline void PutPixelRaw(int32_t x, int32_t y, uint32_t color) { if ((unsigned)x >= (unsigned)gWidth || (unsigned)y >= (unsigned)gHeight) return; @@ -60,26 +60,26 @@ static void ScreenFlushRows(int startY, int rows) { // HAL_SCREEN implementations. static void ScreenClear(uint32_t color) { - for (int y = 0; y < gHeight; y++) - for (int x = 0; x < gWidth; x++) + for (int32_t y = 0; y < gHeight; y++) + for (int32_t x = 0; x < gWidth; x++) ScreenBackbufferPixel(x, y)[0] = color; ScreenFlushRows(0, gHeight); if (!gBackbuffer) { // ScreenFlushRows is a no-op without a backbuffer. - for (int y = 0; y < gHeight; y++) - for (int x = 0; x < gWidth; x++) + for (int32_t y = 0; y < gHeight; y++) + for (int32_t x = 0; x < gWidth; x++) gFb[y * gStride + x] = color; } gCursorX = 0; gCursorY = 0; } // Implements the screen Put Pixel operation. -static void ScreenPutPixel(int x, int y, uint32_t color) { +static void ScreenPutPixel(int32_t x, int32_t y, uint32_t color) { PutPixelRaw(x, y, color); } // Implements the screen Blit operation. -static void ScreenBlit(int x, int y, int w, int h, const uint32_t* pixels) { +static void ScreenBlit(int32_t x, int32_t y, int32_t w, int32_t h, const uint32_t* pixels) { // Calculate clipping bounds. int startX = (x < 0) ? 0 : x; int startY = (y < 0) ? 0 : y; @@ -101,7 +101,7 @@ static void ScreenBlit(int x, int y, int w, int h, const uint32_t* pixels) { // Calculate bytes to copy per row. size_t bytesPerRow = (size_t)drawW * sizeof(uint32_t); - for (int row = 0; row < drawH; row++) { + for (int32_t row = 0; row < drawH; row++) { uint32_t* shadow = ScreenBackbufferPixel(startX, startY + row); uint32_t* fb = gFb + ((startY + row) * gStride) + startX; const uint32_t* src = pixels + ((srcOffsetY + row) * w) + srcOffsetX; @@ -128,7 +128,7 @@ static KSTATUS ScreenGetFramebuffer(HAL_FRAMEBUFFER_INFO* outInfo) { } // Implements the screen Scroll operation. -static void ScreenScroll(int lines) { +static void ScreenScroll(int32_t lines) { if (lines <= 0) return; if (lines >= gHeight) { @@ -145,8 +145,8 @@ static void ScreenScroll(int lines) { // Fill the revealed rows with the console background. Do not memset here: // the background color is not zero. uint32_t* fill = shadow + (gHeight - lines) * gStride; - for (int y = 0; y < lines; y++) { - for (int x = 0; x < gWidth; x++) + for (int32_t y = 0; y < lines; y++) { + for (int32_t x = 0; x < gWidth; x++) fill[y * gStride + x] = SCREEN_DEFAULT_BG; } @@ -154,11 +154,11 @@ static void ScreenScroll(int lines) { } // Implements the screen Put Char operation. -static void ScreenPutChar(char c, int x, int y, uint32_t fg, uint32_t bg) { +static void ScreenPutChar(char c, int32_t x, int32_t y, uint32_t fg, uint32_t bg) { const uint8_t* glyph = Font8x16 + ((uint8_t)c * 16); - for (int row = 0; row < 16; row++) { + for (int32_t row = 0; row < 16; row++) { uint8_t bits = glyph[row]; - for (int col = 0; col < 8; col++) { + for (int32_t col = 0; col < 8; col++) { uint32_t color = (bits & (0x80u >> col)) ? fg : bg; PutPixelRaw(x + col, y + row, color); } @@ -234,10 +234,10 @@ static int gCursorDrawX = -1; // -1 = cursor not yet drawn static int gCursorDrawY = -1; // Save the background rectangle [x,y,CURSOR_W,CURSOR_H] from the framebuffer. -static void CursorSaveBg(int x, int y) { - for (int row = 0; row < CURSOR_H; row++) { +static void CursorSaveBg(int32_t x, int32_t y) { + for (int32_t row = 0; row < CURSOR_H; row++) { int fy = y + row; - for (int col = 0; col < CURSOR_W; col++) { + for (int32_t col = 0; col < CURSOR_W; col++) { int fx = x + col; uint32_t px = 0; if ((unsigned)fx < (unsigned)gWidth && (unsigned)fy < (unsigned)gHeight) @@ -248,10 +248,10 @@ static void CursorSaveBg(int x, int y) { } // Restore the background previously saved by CursorSaveBg. -static void CursorRestoreBg(int x, int y) { - for (int row = 0; row < CURSOR_H; row++) { +static void CursorRestoreBg(int32_t x, int32_t y) { + for (int32_t row = 0; row < CURSOR_H; row++) { int fy = y + row; - for (int col = 0; col < CURSOR_W; col++) { + for (int32_t col = 0; col < CURSOR_W; col++) { int fx = x + col; if ((unsigned)fx < (unsigned)gWidth && (unsigned)fy < (unsigned)gHeight) { uint32_t px = gBackbuffer @@ -287,13 +287,13 @@ static inline uint32_t BlendPixel(uint32_t dst, uint32_t src) { // Draw the arrow sprite at (x,y), saving the background first. // Called from hal_mouse.c via extern declaration. -void HalScreenDrawCursor(int x, int y) { +void HalScreenDrawCursor(int32_t x, int32_t y) { CursorSaveBg(x, y); gCursorDrawX = x; gCursorDrawY = y; - for (int row = 0; row < CURSOR_H; row++) { + for (int32_t row = 0; row < CURSOR_H; row++) { int fy = y + row; - for (int col = 0; col < CURSOR_W; col++) { + for (int32_t col = 0; col < CURSOR_W; col++) { uint32_t src = gCursorPixels[row][col]; if ((src >> 24) == 0) continue; // fully transparent, skip int fx = x + col; @@ -341,7 +341,7 @@ void HalScreenRegisterOutputEndpoint(HAL_OUTPUT_ROUTER* output) { } // Populate the HAL_SCREEN vtable. -void HalScreenInitialize(void* (*AllocatePool)(size_t size), uintptr_t fb, int w, int h, int stride, +void HalScreenInitialize(void* (*AllocatePool)(size_t size), uintptr_t fb, int32_t w, int32_t h, int32_t stride, HAL_SCREEN* out) { gFb = (uint32_t*)fb; gWidth = w; diff --git a/code/kernel/hal/hal_serial.c b/code/kernel/hal/hal_serial.c index 1b814e4..2db70f0 100644 --- a/code/kernel/hal/hal_serial.c +++ b/code/kernel/hal/hal_serial.c @@ -1,5 +1,6 @@ // HAL serial output endpoint. This file owns polling COM1 writes and subscribes serial to HAL text streams. +#include #include #include #include @@ -17,8 +18,8 @@ #define HAL_SERIAL_TIMER_SPIN_CAP 65536u #define HAL_SERIAL_EARLY_SPINS 4096u -static volatile long gSerialWriteLock; -static volatile long gSerialPresent; +static volatile int32_t gSerialWriteLock; +static volatile int32_t gSerialPresent; // Probes the 16550 scratch register without relying on line-status values from an absent port. static bool HalSerialProbeCom1(void) { @@ -69,20 +70,20 @@ static bool HalSerialWriteByte(uint8_t value) { // Acquires the normal serial writer lock. static void HalSerialAcquireWriteLock(void) { - while (_InterlockedCompareExchange(&gSerialWriteLock, 1, 0) != 0) + while (NtlAtomicCompareExchange32(&gSerialWriteLock, 1, 0) != 0) _mm_pause(); } // Releases the normal serial writer lock. static void HalSerialReleaseWriteLock(void) { - _InterlockedExchange(&gSerialWriteLock, 0); + NtlAtomicExchange32(&gSerialWriteLock, 0); } // Writes one output record through polling COM1. static void HalSerialWriteRecord(void* context, const HAL_OUTPUT_RECORD* record) { (void)context; if (!record || !record->Buffer || - _InterlockedCompareExchange(&gSerialPresent, 0, 0) == 0) + NtlAtomicLoad32(&gSerialPresent) == 0) return; bool emergency = (record->Flags & HAL_OUTPUT_WRITE_EMERGENCY) != 0; @@ -102,7 +103,7 @@ static void HalSerialWriteRecord(void* context, const HAL_OUTPUT_RECORD* record) } if (!responsive) - _InterlockedExchange(&gSerialPresent, 0); + NtlAtomicExchange32(&gSerialPresent, 0); if (!emergency) { HalSerialReleaseWriteLock(); diff --git a/code/kernel/include/kernel/hal/hal_api.h b/code/kernel/include/kernel/hal/hal_api.h index 33a03c8..9140466 100644 --- a/code/kernel/include/kernel/hal/hal_api.h +++ b/code/kernel/include/kernel/hal/hal_api.h @@ -2,6 +2,7 @@ #pragma once #include +#include #include #include @@ -17,9 +18,9 @@ typedef struct HAL_PROCESSOR_INFO { uint32_t FirmwareId; uint32_t HardwareId; uint32_t Flags; - uint8_t Enabled; - uint8_t IsBootProcessor; - uint8_t Startable; + bool Enabled; + bool IsBootProcessor; + bool Startable; uint8_t Reserved; } HAL_PROCESSOR_INFO; @@ -56,17 +57,17 @@ typedef struct HAL_FRAMEBUFFER_INFO { typedef struct HAL_SCREEN { void (*Clear)(uint32_t color); - void (*PutPixel)(int x, int y, uint32_t color); - void (*Blit)(int x, int y, int w, int h, const uint32_t* pixels); - void (*GetSize)(int* width, int* height); + void (*PutPixel)(int32_t x, int32_t y, uint32_t color); + void (*Blit)(int32_t x, int32_t y, int32_t w, int32_t h, const uint32_t* pixels); + void (*GetSize)(int32_t* width, int32_t* height); KSTATUS (*GetFramebuffer)(HAL_FRAMEBUFFER_INFO* outInfo); - void (*Scroll)(int lines); - void (*PutChar)(char c, int x, int y, uint32_t fg, uint32_t bg); + void (*Scroll)(int32_t lines); + void (*PutChar)(char c, int32_t x, int32_t y, uint32_t fg, uint32_t bg); // Writes only to the boot framebuffer text console. Stream routing belongs to HAL_OUTPUT_ROUTER. void (*PrintChar)(char c, uint32_t fg, uint32_t bg); // Legacy software cursor hooks kept for early-boot/panic fallback only. // Normal input cursor ownership lives in ke_display. - void (*DrawCursor)(int x, int y); + void (*DrawCursor)(int32_t x, int32_t y); void (*EraseCursor)(void); } HAL_SCREEN; @@ -219,7 +220,7 @@ typedef struct HAL_INIT_PARAMS { uint64_t PhysicalWindowBase; // Debug. - int Debug; + bool Debug; void* (*AllocatePool)(size_t size); } HAL_INIT_PARAMS; diff --git a/code/kernel/include/kernel/io_volume.h b/code/kernel/include/kernel/io_volume.h index 55df4f9..8dd6535 100644 --- a/code/kernel/include/kernel/io_volume.h +++ b/code/kernel/include/kernel/io_volume.h @@ -2,6 +2,7 @@ #pragma once +#include #include #include #include @@ -41,7 +42,7 @@ struct IO_VOLUME { uint32_t InstanceId; uint32_t State; uint32_t RemovalReason; - uint8_t RegistryLinked; + bool RegistryLinked; IO_VOLUME_SOURCE Source; DEVICE_OBJECT* BackingDevice; IO_VOLUME_PROVIDER Provider; diff --git a/code/kernel/include/kernel/ke_apc.h b/code/kernel/include/kernel/ke_apc.h index 1d6b911..8c53342 100644 --- a/code/kernel/include/kernel/ke_apc.h +++ b/code/kernel/include/kernel/ke_apc.h @@ -65,7 +65,7 @@ struct KAPC { void* NormalContext; void* SystemArgument1; void* SystemArgument2; - volatile long Inserted; + volatile int32_t Inserted; }; void KiInitializeApcState(THREAD* thread); diff --git a/code/kernel/include/kernel/ke_audio.h b/code/kernel/include/kernel/ke_audio.h index 4e081d1..447ab16 100644 --- a/code/kernel/include/kernel/ke_audio.h +++ b/code/kernel/include/kernel/ke_audio.h @@ -59,10 +59,10 @@ typedef struct KE_AUDIO_ENDPOINT_INFO { IO_AUDIO_FORMAT HardwareFormat; uint32_t PeriodCount; uint32_t FramesPerPeriod; - uint8_t Available; - uint8_t IsDefault; - uint8_t HardwareRunning; - uint8_t MasterMuted; + bool Available; + bool IsDefault; + bool HardwareRunning; + bool MasterMuted; int32_t MasterHardwareGainMilliDb; float MasterRequestedVolume; uint32_t StreamCount; @@ -108,7 +108,7 @@ typedef struct KE_AUDIO_STREAM_INFO { KSTATUS FailureStatus; float Volume; float Pan; - uint8_t Muted; + bool Muted; } KE_AUDIO_STREAM_INFO; // Initializes endpoint registry and mixer ownership before drivers load. diff --git a/code/kernel/include/kernel/ke_cpu.h b/code/kernel/include/kernel/ke_cpu.h index de05b5d..c9d8fc3 100644 --- a/code/kernel/include/kernel/ke_cpu.h +++ b/code/kernel/include/kernel/ke_cpu.h @@ -37,8 +37,8 @@ typedef struct KCPU { uint32_t FirmwareId; uint32_t BootProcessorIndex; - volatile long State; - uint8_t IsBootProcessor; + volatile int32_t State; + bool IsBootProcessor; // CPUID/MADT-derived SMT topology. PackageId/CoreId/ThreadId are decoded // from the processor hardware ID using CPUID leaf 0x1F/0x0B when available, with a @@ -54,7 +54,7 @@ typedef struct KCPU { uint32_t PhysicalCoreIndex; uint64_t CoreSiblingMask; uint64_t PackageMask; - uint8_t IsPrimaryThread; + bool IsPrimaryThread; uint8_t SmtThreadCount; uint16_t TopologyFlags; @@ -67,7 +67,7 @@ typedef struct KCPU { KIRQL CurrentIrql; uint32_t InterruptDepth; - volatile long ReschedulePending; + volatile int32_t ReschedulePending; TSS64 Tss; uint64_t Gdt[KE_GDT_ENTRIES]; diff --git a/code/kernel/include/kernel/ke_debug.h b/code/kernel/include/kernel/ke_debug.h index de7bdce..78a9796 100644 --- a/code/kernel/include/kernel/ke_debug.h +++ b/code/kernel/include/kernel/ke_debug.h @@ -4,6 +4,7 @@ #define KE_DEBUG_H #include +#include #include #include #include @@ -16,8 +17,8 @@ __declspec(dllexport) void DbgPrint(const char* fmt, ...); __declspec(dllexport) void KePrintConsole(const char* buf, size_t len); KSTATUS KeFlushDebugLogToSystemRoot(const char* relativePath); -__declspec(dllexport) _Noreturn void KePanic(int code, const char* fmt, ...); -__declspec(dllexport) int KeHandleNmiIpi(void); +__declspec(dllexport) _Noreturn void KePanic(int32_t code, const char* fmt, ...); +__declspec(dllexport) bool KeHandleNmiIpi(void); // Fault dispatch - called from the IDT fault stubs (ke_syscall.asm). @@ -57,7 +58,7 @@ typedef struct KFAULT_FRAME { __declspec(dllexport) KSTATUS KeFaultDispatchFrame(KFAULT_FRAME* frame); // Compatibility wrapper for any older assembly/object still calling this. -__declspec(dllexport) _Noreturn void KeFaultDispatch(int vector, uintptr_t rip, +__declspec(dllexport) _Noreturn void KeFaultDispatch(uint32_t vector, uintptr_t rip, uintptr_t errCode, uintptr_t cr2, uintptr_t cs); diff --git a/code/kernel/include/kernel/ke_display.h b/code/kernel/include/kernel/ke_display.h index 294479b..1995268 100644 --- a/code/kernel/include/kernel/ke_display.h +++ b/code/kernel/include/kernel/ke_display.h @@ -2,6 +2,7 @@ #pragma once #include +#include #include #ifndef DISPLAY_API @@ -28,7 +29,7 @@ typedef struct DISPLAY_CLOCK_OBJECT { uint64_t PeriodTicks; uint64_t NextTick; uint64_t LastFrameGeneration; - uint8_t Linked; + bool Linked; } DISPLAY_CLOCK_OBJECT; void KeDisplayInitialize(const HAL_API* hal); diff --git a/code/kernel/include/kernel/ke_id.h b/code/kernel/include/kernel/ke_id.h index d7bdefd..fd5bcc9 100644 --- a/code/kernel/include/kernel/ke_id.h +++ b/code/kernel/include/kernel/ke_id.h @@ -6,10 +6,10 @@ #include typedef struct KE_ID_ALLOCATOR { - volatile long long Next; + volatile int64_t Next; } KE_ID_ALLOCATOR; -#define KE_ID_ALLOCATOR_INIT(first_) { (long long)(uint32_t)(first_) } +#define KE_ID_ALLOCATOR_INIT(first_) { (int64_t)(uint32_t)(first_) } // Allocates one 32-bit identifier and never reuses an issued value during the boot. KSTATUS KeAllocateId32(KE_ID_ALLOCATOR* allocator, uint32_t* idOut); diff --git a/code/kernel/include/kernel/ke_input.h b/code/kernel/include/kernel/ke_input.h index 47c83a9..0b5b6a0 100644 --- a/code/kernel/include/kernel/ke_input.h +++ b/code/kernel/include/kernel/ke_input.h @@ -8,9 +8,7 @@ #include #include #include -#include -typedef struct SYS_GLOBAL_INPUT_STATE SYS_GLOBAL_INPUT_STATE; typedef struct SYS_INPUT_MANAGER_EVENT SYS_INPUT_MANAGER_EVENT; typedef struct KE_INPUT_MANAGER_EVENT { @@ -22,54 +20,23 @@ typedef struct KE_INPUT_MANAGER_EVENT { } Data; } KE_INPUT_MANAGER_EVENT; -typedef struct INPUT_KEY_QUEUE_ENTRY { - INPUT_KEY Key; - char Character; - uint32_t Modifiers; -} INPUT_KEY_QUEUE_ENTRY; - typedef struct KE_INPUT_CONTEXT { const HAL_API* Hal; - volatile uint8_t KeyDown[256]; - volatile uint32_t Modifiers; - volatile long long Generation; - - INPUT_KEY_QUEUE_ENTRY KeyQueue[256]; - volatile int KeyHead; - volatile int KeyTail; - volatile INPUT_KEY RepeatKey; - volatile uint64_t RepeatNextTick; - KDPC RepeatDpc; - - INPUT_MOUSE_EVENT MouseQueue[128]; - volatile int MouseHead; - volatile int MouseTail; - KE_INPUT_MANAGER_EVENT ManagerQueue[256]; - volatile int ManagerHead; - volatile int ManagerTail; - volatile long long ManagerSequence; + volatile int32_t ManagerHead; + volatile int32_t ManagerTail; + volatile int64_t ManagerSequence; EVENT* ManagerWaitEvent; - volatile int MouseX; - volatile int MouseY; - volatile int MouseBoundsWidth; - volatile int MouseBoundsHeight; - volatile int MouseWheel; - volatile uint8_t MouseButtons; - - EVENT* WaitEvent; + volatile int32_t MouseX; + volatile int32_t MouseY; + volatile int32_t MouseBoundsWidth; + volatile int32_t MouseBoundsHeight; } KE_INPUT_CONTEXT; typedef struct KE_INPUT_MANAGER { OBJECT Header; } KE_INPUT_MANAGER; -typedef struct INPUT_QUEUE { - OBJECT Header; - KE_INPUT_CONTEXT* Context; - EVENT* WaitEvent; -} INPUT_QUEUE; - void KeInputInitialize(const HAL_API* hal); KSTATUS KeInputReferenceManager(KE_INPUT_MANAGER** managerOut); KSTATUS KeInputManagerReadEvent(KE_INPUT_MANAGER* manager, uint64_t timeoutMs, SYS_INPUT_MANAGER_EVENT* eventOut); @@ -78,26 +45,7 @@ __declspec(dllexport) KSTATUS KeInputRegisterDevice(const char* name, uint32_t f __declspec(dllexport) void KeInputSubmitKey(const INPUT_KEY_EVENT* event); __declspec(dllexport) void KeInputSubmitMouse(const INPUT_MOUSE_EVENT* event); __declspec(dllexport) void KeInputSubmitMouseAbsolute(const INPUT_MOUSE_ABSOLUTE_EVENT* event); -__declspec(dllexport) void KeInputSubmitMouseAbsoluteScaled(uint32_t rawX, uint32_t rawY, uint32_t maxX, uint32_t maxY, int wheel, uint8_t buttons); +__declspec(dllexport) void KeInputSubmitMouseAbsoluteScaled(uint32_t rawX, uint32_t rawY, uint32_t maxX, uint32_t maxY, int32_t wheel, uint8_t buttons); -INPUT_KEY KeInputKbPoll(void); -KSTATUS KeInputWaitKey(uint8_t alertable, uint64_t timeoutMs); -void KeInputTimerTick(void); -bool KeInputIsKeyDown(INPUT_KEY key); -bool KeInputPeekEvent(INPUT_KEY_RECORD* record); -bool KeInputReadEvent(INPUT_KEY_RECORD* record); -KSTATUS KeInputOpenQueue(INPUT_QUEUE** out); -bool KeInputQueuePeekEvent(INPUT_QUEUE* queue, INPUT_KEY_RECORD* record); -bool KeInputQueueReadEvent(INPUT_QUEUE* queue, INPUT_KEY_RECORD* record); -KSTATUS KeInputQueueWaitKey(INPUT_QUEUE* queue, uint8_t alertable, uint64_t timeoutMs); -EVENT* KeInputQueueWaitEvent(INPUT_QUEUE* queue); -INPUT_KEY KeInputReadKey(void); -char KeInputReadChar(void); -uint32_t KeInputGetModifiers(void); -KSTATUS KeInputQueryGlobalState(SYS_GLOBAL_INPUT_STATE* out); - -bool KeInputMousePeek(INPUT_MOUSE_EVENT* event); -bool KeInputMouseRead(INPUT_MOUSE_EVENT* event); -void KeInputMouseGetPosition(int* x, int* y); -void KeInputMouseSetPosition(int x, int y); -void KeInputMouseSetBounds(int w, int h); +void KeInputMouseSetPosition(int32_t x, int32_t y); +void KeInputMouseSetBounds(int32_t w, int32_t h); diff --git a/code/kernel/include/kernel/ke_irql.h b/code/kernel/include/kernel/ke_irql.h index 58961a5..ed5c4c2 100644 --- a/code/kernel/include/kernel/ke_irql.h +++ b/code/kernel/include/kernel/ke_irql.h @@ -6,6 +6,7 @@ // The level constants are DDK-visible because current synchronization and IRP // layouts expose KIRQL. Kernel routines remain declared here. +#include #include // Return the current processor's software IRQL. @@ -24,7 +25,7 @@ __declspec(dllexport) void KeAssertIrql(KIRQL expected); __declspec(dllexport) void KeAssertIrqlAtOrBelow(KIRQL maxIrql); // Return nonzero when the current context may block. -__declspec(dllexport) int KeCanWait(void); +__declspec(dllexport) bool KeCanWait(void); // Return a diagnostic name for an IRQL. __declspec(dllexport) const char* KeIrqlName(KIRQL irql); diff --git a/code/kernel/include/kernel/ke_mutex.h b/code/kernel/include/kernel/ke_mutex.h index 52b3aad..7ac37e3 100644 --- a/code/kernel/include/kernel/ke_mutex.h +++ b/code/kernel/include/kernel/ke_mutex.h @@ -1,6 +1,7 @@ // Kernel mutex declarations shared by implementation files and kernel clients. #pragma once +#include #include #include @@ -18,8 +19,8 @@ typedef struct MUTEX { LIST_ENTRY OwnerLink; KE_SCHEDULING_CLASS DonationClass; uint32_t DonationPriority; - uint8_t HasDonation; - uint8_t Abandoned; + bool HasDonation; + bool Abandoned; } MUTEX; // Tests whether a mutex acquisition transferred ownership to the caller. @@ -31,7 +32,7 @@ __declspec(dllexport) KSTATUS KeAcquireMutex(MUTEX* mutex, uint64_t timeoutMilli __declspec(dllexport) void KeReleaseMutex(MUTEX* mutex); // Removes one blocked waiter while preserving priority-inheritance state. -int KiMutexAbortWait(MUTEX* mutex, struct THREAD* thread); +bool KiMutexAbortWait(MUTEX* mutex, struct THREAD* thread); // Releases every mutex owned by a terminating thread as abandoned. void KiMutexRundownThread(struct THREAD* thread); diff --git a/code/kernel/include/kernel/ke_named_queue.h b/code/kernel/include/kernel/ke_named_queue.h deleted file mode 100644 index 90982b0..0000000 --- a/code/kernel/include/kernel/ke_named_queue.h +++ /dev/null @@ -1,12 +0,0 @@ -// Named queue registry hooks shared by process handle cleanup and syscalls. - -#pragma once - -#include -#include - -struct PROCESS; -struct OBJECT; - -// Removes registry entries owned by the given process handle/object pair. -void KeNamedQueueUnpublishHandle(struct PROCESS* process, SYS_HANDLE handle, struct OBJECT* object); diff --git a/code/kernel/include/kernel/ke_process.h b/code/kernel/include/kernel/ke_process.h index 9cfd08a..5170015 100644 --- a/code/kernel/include/kernel/ke_process.h +++ b/code/kernel/include/kernel/ke_process.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -57,14 +58,14 @@ typedef struct PROCESS { struct SECURITY_CONTEXT* SecurityContext; int32_t ExitCode; - volatile long Terminating; - volatile long Exited; - volatile long ResourcesReleased; + volatile int32_t Terminating; + volatile int32_t Exited; + volatile int32_t ResourcesReleased; // Userspace image construction is authorized by SYS_PROCESS_RIGHT_BUILD. // BuildOperations closes the race between memory construction and the one-way start transition. - volatile long BuildState; - volatile long BuildOperations; + volatile int32_t BuildState; + volatile int32_t BuildOperations; // Main executable image. Used for cleanup. uintptr_t ImageBase; @@ -124,8 +125,8 @@ void KeStopProcessConstruction(PROCESS* process); KSTATUS KeExitProcess(PROCESS* process, int32_t exitCode); KSTATUS KeTerminateProcess(PROCESS* process, int32_t exitCode); void KeCompleteProcessExitIfLastThread(PROCESS* process); -int KeIsProcessTerminating(PROCESS* process); -int KeIsProcessTerminationProtected(PROCESS* process); +bool KeIsProcessTerminating(PROCESS* process); +bool KeIsProcessTerminationProtected(PROCESS* process); KSTATUS KeGetProcessExitCode(PROCESS* process, int32_t* exitCodeOut); void KeQueryExceptionStats(struct SYS_EXCEPTION_STATS* statsOut); diff --git a/code/kernel/include/kernel/ke_sched.h b/code/kernel/include/kernel/ke_sched.h index 1379d3e..4c1dde7 100644 --- a/code/kernel/include/kernel/ke_sched.h +++ b/code/kernel/include/kernel/ke_sched.h @@ -1,6 +1,7 @@ // Kernel ke sched declarations shared by implementation files and kernel clients. #pragma once +#include // The scheduler combines dynamic normal queues with static real-time queues. // Per-processor timer IRQs drive KeSchedulerTick. #include @@ -55,8 +56,8 @@ typedef struct KE_THREAD_SCHEDULER_SNAPSHOT { uint32_t WaitObjectType; KSTATUS WaitStatus; struct MUTEX* WaitingMutex; - uint8_t Queued; - uint8_t AlertableWait; + bool Queued; + bool AlertableWait; } KE_THREAD_SCHEDULER_SNAPSHOT; // Copies scheduler-owned thread state into a stable diagnostic snapshot. @@ -64,7 +65,7 @@ void KiSchedulerSnapshotThread(THREAD* thread, KE_THREAD_SCHEDULER_SNAPSHOT* sna // Selects waiters and refreshes donations while the mutex lock is held. struct THREAD* KiSchedulerSelectMutexWaiter(struct MUTEX* mutex); -int KiSchedulerRefreshMutexDonation(struct MUTEX* mutex); +bool KiSchedulerRefreshMutexDonation(struct MUTEX* mutex); // Updates one thread's normal-class base priority. KSTATUS KiSchedulerSetNormalPriority(THREAD* thread, THREAD_PRIORITY priority); @@ -82,7 +83,7 @@ struct MUTEX* KiSchedulerFirstOwnedMutex(THREAD* thread); void KiSchedulerNotePriorityPropagation(uint32_t depth); // Recomputes effective rank and optionally returns a referenced upstream mutex. -int KiSchedulerRefreshEffectivePriority(THREAD* thread, struct MUTEX** waitingMutexOut); +bool KiSchedulerRefreshEffectivePriority(THREAD* thread, struct MUTEX** waitingMutexOut); // Called once by KernelMain before any threads are created. void KeInitializeScheduler(void); @@ -107,7 +108,7 @@ void KeSchedulerTick(void); // is cheap and can be called from interrupt context. KiScheduleFromInterrupt // consumes the pending request and performs the context switch, if needed. void KiRequestReschedule(void); -int KiIsReschedulePending(void); +bool KiIsReschedulePending(void); void KiScheduleFromInterrupt(void); void KiSchedulerNoteVoluntaryBlock(THREAD* thread); void KiSchedulerMarkThreadWaiting(THREAD* thread); diff --git a/code/kernel/include/kernel/ke_thread.h b/code/kernel/include/kernel/ke_thread.h index 4271c73..8f9a4a9 100644 --- a/code/kernel/include/kernel/ke_thread.h +++ b/code/kernel/include/kernel/ke_thread.h @@ -6,6 +6,7 @@ // ke_thread.h - thread model // A THREAD is the unit of scheduling. Every thread belongs to exactly one process. #include +#include #include #include #include @@ -131,8 +132,8 @@ struct THREAD { // Scheduler hardening state. Queued means membership in a scheduler ready // queue only; wait-object membership is tracked by WaitListHead. Idle // threads are CPU-local and never enter a global ready queue. - uint8_t Queued; - uint8_t IsIdleThread; + bool Queued; + bool IsIdleThread; uint16_t SchedulerFlags; uint32_t LastCpu; uint64_t AffinityMask; @@ -143,9 +144,9 @@ struct THREAD { // FpuContext. KSTATUS WaitStatus; LIST_ENTRY* WaitListHead; - uint8_t AlertableWait; + bool AlertableWait; LIST_ENTRY TimeoutLink; - uint8_t TimeoutArmed; + bool TimeoutArmed; uint64_t TimeoutTick; KSTATUS TimeoutStatus; @@ -157,9 +158,9 @@ struct THREAD { // when that thread reaches PASSIVE_LEVEL with APC delivery enabled. LIST_ENTRY ApcQueue; SPINLOCK ApcLock; - volatile long KernelApcPending; + volatile int32_t KernelApcPending; int32_t KernelApcDisable; - uint8_t ApcDeliveryActive; + bool ApcDeliveryActive; // Non-NULL while executing on a syscall/interrupt/fault entry frame. // The pointed-to frame lives on this thread's kernel stack. @@ -170,10 +171,10 @@ struct THREAD { // user-mode dispatcher stub. LIST_ENTRY UserApcQueue; SPINLOCK UserApcLock; - volatile long UserApcPending; + volatile int32_t UserApcPending; uintptr_t UserApcDispatcher; - uint8_t UserApcDeliverable; - uint8_t UserApcDeliveryActive; + bool UserApcDeliverable; + bool UserApcDeliveryActive; uintptr_t UserApcFrameUserAddress; uint64_t UserApcReturnToken; uint64_t UserApcSavedRip; @@ -192,12 +193,12 @@ struct THREAD { // Synchronous kernel work can temporarily retain caller-owned stack or // process memory. Process termination must wait until that ownership has // returned instead of freeing the thread underneath an active driver. - volatile long TerminationDeferralCount; + volatile int32_t TerminationDeferralCount; // Set once terminal thread teardown begins. Interrupts may continue to // run, but interrupt-return scheduling must not abandon this kernel stack // before KeFinalizeThreadExit and process-exit publication complete. - volatile long ExitInProgress; + volatile int32_t ExitInProgress; }; // Creates and queues a user thread. On success, *out receives a referenced @@ -224,7 +225,7 @@ __declspec(dllexport) KSTATUS KeCreateKernelThread(uintptr_t fn, uintptr_t arg, THREAD** out); // Mark a thread dead and deschedule it permanently. -__declspec(dllexport) void KeExitThread(THREAD* thread, int code); +__declspec(dllexport) void KeExitThread(THREAD* thread, int32_t code); // Publishes completion, releases per-thread user memory, and removes one dead // thread from its process live-thread set. Retained handles remain waitable. @@ -251,7 +252,7 @@ KSTATUS KeCommitBlockThread(THREAD* thread, uint8_t alertable); KSTATUS KeBlockThread(THREAD* thread, OBJECT* waitObject, uint8_t alertable); void KeUnblockThreadWithStatus(THREAD* thread, KSTATUS status); void KeUnblockThreadFromWaitListWithStatus(THREAD* thread, KSTATUS status); -int KeAbortThreadWait(THREAD* thread, KSTATUS status); +bool KeAbortThreadWait(THREAD* thread, KSTATUS status); void KeUnblockThread(THREAD* thread); KSTATUS KeAlertThread(THREAD* thread); void KiWakeThreadForKernelApc(THREAD* thread); @@ -260,8 +261,8 @@ void KiWakeThreadForUserApc(THREAD* thread); // Defers process termination while kernel work owns caller-backed storage. void KeEnterTerminationDeferral(void); void KeLeaveTerminationDeferral(void); -int KeThreadTerminationDeferred(const THREAD* thread); -int KeThreadExitInProgress(const THREAD* thread); +bool KeThreadTerminationDeferred(const THREAD* thread); +bool KeThreadExitInProgress(const THREAD* thread); // Assembly stub - implemented in ke_switch.asm. void KeSwitchContext(THREAD* from, THREAD* to); diff --git a/code/kernel/include/kernel/ke_timer.h b/code/kernel/include/kernel/ke_timer.h index c67a45a..0ac6509 100644 --- a/code/kernel/include/kernel/ke_timer.h +++ b/code/kernel/include/kernel/ke_timer.h @@ -3,6 +3,7 @@ #pragma once // ke_timer.h - waitable one-shot and periodic timers. #include +#include #include #include #include @@ -15,8 +16,8 @@ typedef struct KTIMER { LIST_ENTRY Link; uint64_t DueMicroseconds; uint64_t PeriodMicroseconds; - uint8_t Active; - uint8_t Linked; + bool Active; + bool Linked; } KTIMER; // Creates an unset waitable timer object. diff --git a/code/kernel/include/kernel/ke_trap.h b/code/kernel/include/kernel/ke_trap.h index 73817e3..a380038 100644 --- a/code/kernel/include/kernel/ke_trap.h +++ b/code/kernel/include/kernel/ke_trap.h @@ -8,6 +8,7 @@ // and KiExitTrapFrame commits those values back to the hardware IRET slots. #include +#include #include #define KTRAP_FRAME_MAGIC 0x4650545241544B31ULL @@ -80,7 +81,7 @@ typedef struct SYS_TRAP_FRAME_INFO_K { void KiEnterTrapFrame(KTRAP_FRAME* frame); void KiExitTrapFrame(KTRAP_FRAME* frame); KTRAP_FRAME* KeGetCurrentTrapFrame(void); -int KeTrapFrameFromUser(const KTRAP_FRAME* frame); +bool KeTrapFrameFromUser(const KTRAP_FRAME* frame); void KiValidateSyscallTrapFrame(uint64_t nr, uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3); KSTATUS KeQueryCurrentTrapFrame(SYS_TRAP_FRAME_INFO_K* out); diff --git a/code/kernel/include/kernel/ke_work.h b/code/kernel/include/kernel/ke_work.h index 379ebb3..08c36db 100644 --- a/code/kernel/include/kernel/ke_work.h +++ b/code/kernel/include/kernel/ke_work.h @@ -15,7 +15,7 @@ typedef struct KWORK_ITEM { LIST_ENTRY Link; KWORK_ROUTINE Routine; void* Context; - volatile long Queued; + volatile int32_t Queued; } KWORK_ITEM; void KeInitializeWorkItem(KWORK_ITEM* item, KWORK_ROUTINE routine, void* context); diff --git a/code/kernel/include/kernel/mm_file_cache.h b/code/kernel/include/kernel/mm_file_cache.h index 626b80b..2fdeadc 100644 --- a/code/kernel/include/kernel/mm_file_cache.h +++ b/code/kernel/include/kernel/mm_file_cache.h @@ -4,6 +4,7 @@ #pragma once #include +#include #include #include #include @@ -59,7 +60,7 @@ void MmFileCacheReleasePage(MM_FILE_CACHE_PAGE* page); // Returns a retained coherent cache page suitable for a shared writable mapping. KSTATUS MmFileCacheGetWritablePage(FILE_OBJECT* file, uint64_t offset, - uint64_t fileSizeBefore, int needExistingBytes, + uint64_t fileSizeBefore, bool needExistingBytes, MM_FILE_CACHE_PAGE* outPage); // Marks one coherent cache page dirty after a shared writable mapping is written. diff --git a/code/kernel/include/kernel/mm_pte.h b/code/kernel/include/kernel/mm_pte.h index 83c8497..af15792 100644 --- a/code/kernel/include/kernel/mm_pte.h +++ b/code/kernel/include/kernel/mm_pte.h @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -87,10 +88,10 @@ __declspec(dllexport) KSTATUS MmPteProtectRange(uintptr_t virt, size_t size, uin // Tests and clears the hardware accessed bit on one mapped 4 KiB leaf. // Returns nonzero when the page had been accessed and was given a second chance. -__declspec(dllexport) int MmPteTestAndClearAccessed(uintptr_t virt); +__declspec(dllexport) bool MmPteTestAndClearAccessed(uintptr_t virt); // Tests and clears the hardware dirty bit on one mapped 4 KiB leaf. -__declspec(dllexport) int MmPteTestAndClearDirty(uintptr_t virt); +__declspec(dllexport) bool MmPteTestAndClearDirty(uintptr_t virt); // Translates a virtual address through the current shared page tables. __declspec(dllexport) PHYSICAL_ADDRESS MmPteTranslate(uintptr_t virt); @@ -106,7 +107,7 @@ void MmPteFlushLocal(uintptr_t virt); void MmPteFlushKernelAddress(uintptr_t virt); // Handles the fixed-vector NMI used for kernel TLB shootdowns. -int MmPteHandleShootdownNmi(void); +bool MmPteHandleShootdownNmi(void); // Returns page-table operation counters used by memory telemetry. void MmPteQueryStats(MM_PTE_STATS* out); diff --git a/code/kernel/include/kernel/mm_swap.h b/code/kernel/include/kernel/mm_swap.h index 9aef4fb..afa96dd 100644 --- a/code/kernel/include/kernel/mm_swap.h +++ b/code/kernel/include/kernel/mm_swap.h @@ -3,12 +3,13 @@ #pragma once #include +#include #include #include #include typedef struct MM_SWAP_STATS { - uint64_t Enabled; + bool Enabled; uint64_t TotalSlots; uint64_t UsedSlots; uint64_t FreeSlots; @@ -23,7 +24,7 @@ typedef struct MM_SWAP_STATS { } MM_SWAP_STATS; KSTATUS MmSwapInitialize(const char* relativePath, uint64_t sizeBytes); -int MmSwapIsEnabled(void); +bool MmSwapIsEnabled(void); KSTATUS MmSwapPageOut(PHYSICAL_ADDRESS frame, uint32_t* slotOut); KSTATUS MmSwapPageIn(uint32_t slot, PHYSICAL_ADDRESS frame); void MmSwapFreeSlot(uint32_t slot); diff --git a/code/kernel/include/kernel/mm_virt.h b/code/kernel/include/kernel/mm_virt.h index 73a482f..3694c2e 100644 --- a/code/kernel/include/kernel/mm_virt.h +++ b/code/kernel/include/kernel/mm_virt.h @@ -4,6 +4,7 @@ // mm_virt.h - virtual memory manager (VMM) // Manages the single shared PML4, kernel pool, and VAD-backed user VM ownership. #include +#include #include #include #include @@ -92,7 +93,7 @@ __declspec(dllexport) void MmUnpinUserPage(MM_PINNED_USER_PAGE* page); // Locks a user buffer for asynchronous kernel access. The containing VADs stay // alive and protected, and resident PFNs stay pinned until the buffer is // unlocked. WriteAccess describes kernel writes to the user buffer. -__declspec(dllexport) KSTATUS MmLockUserBuffer(PROCESS* process, void* userAddress, size_t size, int writeAccess, MM_LOCKED_USER_BUFFER** out); +__declspec(dllexport) KSTATUS MmLockUserBuffer(PROCESS* process, void* userAddress, size_t size, bool writeAccess, MM_LOCKED_USER_BUFFER** out); __declspec(dllexport) void MmUnlockUserBuffer(MM_LOCKED_USER_BUFFER* buffer); size_t MmLockedUserBufferPageCount(const MM_LOCKED_USER_BUFFER* buffer); PHYSICAL_ADDRESS MmLockedUserBufferPageFrame(const MM_LOCKED_USER_BUFFER* buffer, size_t index); diff --git a/code/kernel/include/kernel/syscall_nr.h b/code/kernel/include/kernel/syscall_nr.h index a16bbb0..d74a317 100644 --- a/code/kernel/include/kernel/syscall_nr.h +++ b/code/kernel/include/kernel/syscall_nr.h @@ -68,145 +68,129 @@ #define SYS_SIGNAL_EVENT 53 #define SYS_CLEAR_EVENT 54 -// Queues and named queues. +// Anonymous queues. #define SYS_CREATE_QUEUE 55 #define SYS_SEND_QUEUE 56 #define SYS_RECV_QUEUE 57 -#define SYS_PUBLISH_QUEUE 58 -#define SYS_OPEN_NAMED_QUEUE 59 -#define SYS_OPEN_NAMED_QUEUE_OWNER_PROCESS 60 -#define SYS_UNPUBLISH_QUEUE 61 // LPC-like Ports and well-known system services. -#define SYS_CREATE_PORT 62 -#define SYS_CONNECT_PORT 63 -#define SYS_ACCEPT_PORT 64 -#define SYS_SEND_PORT 65 -#define SYS_RECEIVE_PORT 66 -#define SYS_CALL_PORT 67 -#define SYS_REPLY_PORT 68 -#define SYS_DISCONNECT_PORT 69 -#define SYS_OPEN_PORT_PEER_PROCESS 70 -#define SYS_REGISTER_SYSTEM_SERVICE 71 -#define SYS_UNREGISTER_SYSTEM_SERVICE 72 -#define SYS_CONNECT_SYSTEM_SERVICE 73 +#define SYS_CREATE_PORT 58 +#define SYS_CONNECT_PORT 59 +#define SYS_ACCEPT_PORT 60 +#define SYS_SEND_PORT 61 +#define SYS_RECEIVE_PORT 62 +#define SYS_CALL_PORT 63 +#define SYS_REPLY_PORT 64 +#define SYS_DISCONNECT_PORT 65 +#define SYS_OPEN_PORT_PEER_PROCESS 66 +#define SYS_REGISTER_SYSTEM_SERVICE 67 +#define SYS_UNREGISTER_SYSTEM_SERVICE 68 +#define SYS_CONNECT_SYSTEM_SERVICE 69 // Mounted-volume manager control plane. -#define SYS_QUERY_VOLUMES 74 -#define SYS_OPEN_VOLUME 75 -#define SYS_WAIT_VOLUME_CHANGE 76 +#define SYS_QUERY_VOLUMES 70 +#define SYS_OPEN_VOLUME 71 +#define SYS_WAIT_VOLUME_CHANGE 72 // Files and directories. -#define SYS_OPEN_FILE_AT 77 -#define SYS_OPEN_DIR_AT 78 -#define SYS_STAT_AT 79 -#define SYS_DELETE_AT 80 -#define SYS_RENAME_AT 81 -#define SYS_CREATE_DIR_AT 82 -#define SYS_REMOVE_DIR_AT 83 -#define SYS_READ_FILE 84 -#define SYS_WRITE_FILE 85 -#define SYS_READ_FILE_ASYNC 86 -#define SYS_WRITE_FILE_ASYNC 87 -#define SYS_CANCEL_IO 88 -#define SYS_SEEK_FILE 89 -#define SYS_GET_FILE_POS 90 -#define SYS_GET_FILE_SIZE 91 -#define SYS_GET_FILE_STAT 92 -#define SYS_TRUNCATE_FILE 93 -#define SYS_FLUSH_FILE 94 -#define SYS_LOCK_FILE 95 -#define SYS_UNLOCK_FILE 96 -#define SYS_QUERY_FILE_LOCK 97 -#define SYS_SET_FILE_STATUS 98 -#define SYS_CREATE_PIPE 99 -#define SYS_NOTIFY_PIPE 100 -#define SYS_READ_DIR 101 +#define SYS_OPEN_FILE_AT 73 +#define SYS_OPEN_DIR_AT 74 +#define SYS_STAT_AT 75 +#define SYS_DELETE_AT 76 +#define SYS_RENAME_AT 77 +#define SYS_CREATE_DIR_AT 78 +#define SYS_REMOVE_DIR_AT 79 +#define SYS_READ_FILE 80 +#define SYS_WRITE_FILE 81 +#define SYS_READ_FILE_ASYNC 82 +#define SYS_WRITE_FILE_ASYNC 83 +#define SYS_CANCEL_IO 84 +#define SYS_SEEK_FILE 85 +#define SYS_GET_FILE_POS 86 +#define SYS_GET_FILE_SIZE 87 +#define SYS_GET_FILE_STAT 88 +#define SYS_TRUNCATE_FILE 89 +#define SYS_FLUSH_FILE 90 +#define SYS_LOCK_FILE 91 +#define SYS_UNLOCK_FILE 92 +#define SYS_QUERY_FILE_LOCK 93 +#define SYS_SET_FILE_STATUS 94 +#define SYS_CREATE_PIPE 95 +#define SYS_NOTIFY_PIPE 96 +#define SYS_READ_DIR 97 // Console, display, and surfaces. -#define SYS_WRITE_CONSOLE 102 -#define SYS_GET_CONSOLE_INFO 103 -#define SYS_SET_CONSOLE_CURSOR 104 -#define SYS_FILL_CONSOLE_CHARACTER 105 -#define SYS_FILL_CONSOLE_ATTRIBUTE 106 -#define SYS_PUT_SCREEN_CHAR 107 -#define SYS_CLEAR_SCREEN 108 -#define SYS_GET_SCREEN_SIZE 109 -#define SYS_QUERY_DISPLAY_INFO 110 -#define SYS_QUERY_DISPLAY_MANAGER_INFO 111 -#define SYS_PRESENT_DISPLAY_MANAGER_SURFACE 112 -#define SYS_SET_DISPLAY_MANAGER_CURSOR 113 -#define SYS_WRITE_DISPLAY_MANAGER_CONSOLE 114 -#define SYS_QUERY_DISPLAY_MANAGER_CONSOLE_INFO 115 -#define SYS_SET_DISPLAY_MANAGER_CONSOLE_CURSOR 116 -#define SYS_FILL_DISPLAY_MANAGER_CONSOLE_CHARACTER 117 -#define SYS_FILL_DISPLAY_MANAGER_CONSOLE_ATTRIBUTE 118 -#define SYS_PUT_DISPLAY_MANAGER_CONSOLE_CHARACTER 119 -#define SYS_CLEAR_DISPLAY_MANAGER_CONSOLE 120 -#define SYS_OPEN_DISPLAY_CLOCK 121 -#define SYS_CREATE_SURFACE 122 -#define SYS_MAP_SURFACE 123 -#define SYS_UNMAP_SURFACE 124 -#define SYS_QUERY_SURFACE 125 -#define SYS_COMMIT_SURFACE 126 +#define SYS_WRITE_CONSOLE 98 +#define SYS_GET_CONSOLE_INFO 99 +#define SYS_SET_CONSOLE_CURSOR 100 +#define SYS_FILL_CONSOLE_CHARACTER 101 +#define SYS_FILL_CONSOLE_ATTRIBUTE 102 +#define SYS_PUT_SCREEN_CHAR 103 +#define SYS_CLEAR_SCREEN 104 +#define SYS_GET_SCREEN_SIZE 105 +#define SYS_QUERY_DISPLAY_INFO 106 +#define SYS_QUERY_DISPLAY_MANAGER_INFO 107 +#define SYS_PRESENT_DISPLAY_MANAGER_SURFACE 108 +#define SYS_SET_DISPLAY_MANAGER_CURSOR 109 +#define SYS_WRITE_DISPLAY_MANAGER_CONSOLE 110 +#define SYS_QUERY_DISPLAY_MANAGER_CONSOLE_INFO 111 +#define SYS_SET_DISPLAY_MANAGER_CONSOLE_CURSOR 112 +#define SYS_FILL_DISPLAY_MANAGER_CONSOLE_CHARACTER 113 +#define SYS_FILL_DISPLAY_MANAGER_CONSOLE_ATTRIBUTE 114 +#define SYS_PUT_DISPLAY_MANAGER_CONSOLE_CHARACTER 115 +#define SYS_CLEAR_DISPLAY_MANAGER_CONSOLE 116 +#define SYS_OPEN_DISPLAY_CLOCK 117 +#define SYS_CREATE_SURFACE 118 +#define SYS_MAP_SURFACE 119 +#define SYS_UNMAP_SURFACE 120 +#define SYS_QUERY_SURFACE 121 +#define SYS_COMMIT_SURFACE 122 // Native render audio. -#define SYS_AUDIO_ENUMERATE_ENDPOINTS 127 -#define SYS_AUDIO_OPEN_STREAM 128 -#define SYS_AUDIO_WRITE 129 -#define SYS_AUDIO_ARM_WAIT 130 -#define SYS_AUDIO_QUERY_STREAM 131 -#define SYS_AUDIO_START 132 -#define SYS_AUDIO_PAUSE 133 -#define SYS_AUDIO_RESUME 134 -#define SYS_AUDIO_DRAIN 135 -#define SYS_AUDIO_STOP 136 -#define SYS_AUDIO_RESET 137 -#define SYS_AUDIO_SET_VOLUME 138 -#define SYS_AUDIO_SET_MASTER_VOLUME 139 +#define SYS_AUDIO_ENUMERATE_ENDPOINTS 123 +#define SYS_AUDIO_OPEN_STREAM 124 +#define SYS_AUDIO_WRITE 125 +#define SYS_AUDIO_ARM_WAIT 126 +#define SYS_AUDIO_QUERY_STREAM 127 +#define SYS_AUDIO_START 128 +#define SYS_AUDIO_PAUSE 129 +#define SYS_AUDIO_RESUME 130 +#define SYS_AUDIO_DRAIN 131 +#define SYS_AUDIO_STOP 132 +#define SYS_AUDIO_RESET 133 +#define SYS_AUDIO_SET_VOLUME 134 +#define SYS_AUDIO_SET_MASTER_VOLUME 135 // Input. -#define SYS_READ_INPUT_MANAGER_EVENT 140 -#define SYS_POLL_KEYBOARD 141 -#define SYS_IS_KEY_DOWN 142 -#define SYS_READ_KEYBOARD_CHAR 143 -#define SYS_READ_KEY 144 -#define SYS_GET_KEY_MODIFIERS 145 -#define SYS_QUERY_GLOBAL_INPUT_STATE 146 -#define SYS_OPEN_INPUT_QUEUE 147 -#define SYS_READ_INPUT_EVENT 148 -#define SYS_PEEK_INPUT_EVENT 149 -#define SYS_PEEK_MOUSE 150 -#define SYS_READ_MOUSE 151 -#define SYS_GET_MOUSE_POS 152 +#define SYS_READ_INPUT_MANAGER_EVENT 136 // Time, debug, and system information. -#define SYS_CREATE_TIMER 153 -#define SYS_SET_TIMER 154 -#define SYS_CANCEL_TIMER 155 -#define SYS_GET_TICK 156 -#define SYS_GET_TICK_FREQUENCY 157 -#define SYS_GET_SYSTEM_TIME 158 -#define SYS_PRINT_DEBUG 159 -#define SYS_QUERY_TRAP_FRAME 160 -#define SYS_GET_MEMORY_STATUS 161 -#define SYS_QUERY_KERNEL_STATS 162 +#define SYS_CREATE_TIMER 137 +#define SYS_SET_TIMER 138 +#define SYS_CANCEL_TIMER 139 +#define SYS_GET_TICK 140 +#define SYS_GET_TICK_FREQUENCY 141 +#define SYS_GET_SYSTEM_TIME 142 +#define SYS_PRINT_DEBUG 143 +#define SYS_QUERY_TRAP_FRAME 144 +#define SYS_GET_MEMORY_STATUS 145 +#define SYS_QUERY_KERNEL_STATS 146 // Native sockets and polling. -#define SYS_CREATE_SOCKET 163 -#define SYS_SOCKET_IO_ASYNC 164 -#define SYS_BIND_SOCKET 165 -#define SYS_LISTEN_SOCKET 166 -#define SYS_ACCEPT_SOCKET 167 -#define SYS_CONNECT_SOCKET 168 -#define SYS_SEND_SOCKET 169 -#define SYS_RECV_SOCKET 170 -#define SYS_SHUTDOWN_SOCKET 171 -#define SYS_SEND_TO_SOCKET 172 -#define SYS_RECV_FROM_SOCKET 173 -#define SYS_RESOLVE_HOST 174 -#define SYS_SET_SOCKET_OPTION 175 -#define SYS_GET_SOCKET_OPTION 176 -#define SYS_POLL 177 +#define SYS_CREATE_SOCKET 147 +#define SYS_SOCKET_IO_ASYNC 148 +#define SYS_BIND_SOCKET 149 +#define SYS_LISTEN_SOCKET 150 +#define SYS_ACCEPT_SOCKET 151 +#define SYS_CONNECT_SOCKET 152 +#define SYS_SEND_SOCKET 153 +#define SYS_RECV_SOCKET 154 +#define SYS_SHUTDOWN_SOCKET 155 +#define SYS_SEND_TO_SOCKET 156 +#define SYS_RECV_FROM_SOCKET 157 +#define SYS_RESOLVE_HOST 158 +#define SYS_SET_SOCKET_OPTION 159 +#define SYS_GET_SOCKET_OPTION 160 +#define SYS_POLL 161 -#define SYS_MAX 178 +#define SYS_MAX 162 diff --git a/code/kernel/io_device.c b/code/kernel/io_device.c index 500b9b4..26995f3 100644 --- a/code/kernel/io_device.c +++ b/code/kernel/io_device.c @@ -1,5 +1,7 @@ // I/O manager. This file owns driver objects, device stacks, PnP dispatch, and the disk namespace. +#include +#include #include #include #include @@ -87,6 +89,7 @@ __declspec(dllexport) void IoInitializeDriverObject(DRIVER_OBJECT* driver, // Registers driver Object. __declspec(dllexport) KSTATUS IoRegisterDriverObject(DRIVER_OBJECT* driver) { if (!driver) return NTLSTATUS_INVALID_PARAMETER; + if (!SysSvIsUtf8Text(driver->Name)) return NTLSTATUS_INVALID_PARAMETER; if (gDriverObjectCount >= MAX_DRIVER_OBJECTS) return NTLSTATUS_NO_MEMORY; @@ -193,7 +196,7 @@ __declspec(dllexport) KSTATUS IoCreatePhysicalDevice(DRIVER_OBJECT* busDriver, if (idCount && ids) { for (size_t i = 0; i < idCount; i++) { - if (!SysSvIsValid(ids[i].Id) || ids[i].Id.Length >= IO_MAX_DEVICE_ID_CHARS) { + if (!SysSvIsUtf8Text(ids[i].Id) || ids[i].Id.Length >= IO_MAX_DEVICE_ID_CHARS) { IoDeleteDevice(pdo); return NTLSTATUS_INVALID_PARAMETER; } @@ -255,7 +258,7 @@ __declspec(dllexport) KSTATUS IoRegisterPhysicalDevice(DEVICE_OBJECT* pdo) { __declspec(dllexport) KSTATUS IoSetDeviceName(DEVICE_OBJECT* device, SYS_STRING_VIEW name) { - if (!device || !SysSvIsValid(name)) + if (!device || !SysSvIsUtf8Text(name)) return NTLSTATUS_INVALID_PARAMETER; if (name.Length >= IO_MAX_DEVICE_NAME_CHARS) return NTLSTATUS_BUFFER_TOO_SMALL; @@ -397,7 +400,7 @@ __declspec(dllexport) KSTATUS IoForwardIrpToLower(DEVICE_OBJECT* device, IRP* ir if (currentDevice == device) { cur->DeviceObject = (DEVICE_OBJECT*)0; - _InterlockedDecrement(&device->OutstandingIrps); + NtlAtomicDecrement32(&device->OutstandingIrps); IoDereferenceDeviceObject(device); } @@ -693,13 +696,13 @@ __declspec(dllexport) KSTATUS IoCallDriver(DEVICE_OBJECT* device, IRP* irp) { ios->DeviceObject = device; IoReferenceDeviceObject(device); - _InterlockedIncrement(&device->OutstandingIrps); + NtlAtomicIncrement32(&device->OutstandingIrps); KSTATUS st = dispatch(device, irp); if (st != NTLSTATUS_PENDING) { if (ios->DeviceObject == device) { ios->DeviceObject = (DEVICE_OBJECT*)0; - _InterlockedDecrement(&device->OutstandingIrps); + NtlAtomicDecrement32(&device->OutstandingIrps); IoDereferenceDeviceObject(device); } } diff --git a/code/kernel/io_dma.c b/code/kernel/io_dma.c index 32f6065..f9ad26a 100644 --- a/code/kernel/io_dma.c +++ b/code/kernel/io_dma.c @@ -1,5 +1,7 @@ // Kernel DMA core. This file owns device adapters, common buffers, pinned memory, streaming mappings, and bounce copies. +#include +#include #include #include #include @@ -21,8 +23,8 @@ #define IO_DMA_MAP_TAG MM_POOL_TAG('d', 'm', 'a', 'm') struct IO_DMA_ADAPTER { - volatile long RefCount; - volatile long Removed; + volatile int32_t RefCount; + volatile int32_t Removed; SPINLOCK Lock; DEVICE_OBJECT* Device; IO_DMA_REQUIREMENTS Requirements; @@ -44,7 +46,7 @@ typedef enum MM_PINNED_MEMORY_ORIGIN { } MM_PINNED_MEMORY_ORIGIN; struct MM_PINNED_MEMORY { - volatile long RefCount; + volatile int32_t RefCount; MM_PINNED_MEMORY_ORIGIN Origin; IO_DMA_DIRECTION Direction; void* Address; @@ -63,38 +65,38 @@ struct IO_DMA_MAPPING { size_t Length; IO_DMA_DIRECTION Direction; uint32_t SegmentCount; - int DeviceOwnsMapping; + bool DeviceOwnsMapping; IO_DMA_SEGMENT Segments[1]; }; typedef struct IO_DMA_INTERNAL_STATS { - volatile long long AdaptersCreated; - volatile long long AdaptersDestroyed; - volatile long long AdaptersInUse; - volatile long long CommonBuffersAllocated; - volatile long long CommonBuffersFreed; - volatile long long CommonBuffersInUse; - volatile long long CommonBufferBytesInUse; - volatile long long PinnedKernelRanges; - volatile long long PinnedUserRanges; - volatile long long PinnedRangesReleased; - volatile long long PinnedRangesInUse; - volatile long long PinnedPagesInUse; - volatile long long StreamingMappings; - volatile long long StreamingMappingsUnmapped; - volatile long long StreamingMappingsInUse; - volatile long long DirectMappings; - volatile long long BounceMappings; - volatile long long BounceBytesCopiedToDevice; - volatile long long BounceBytesCopiedToCpu; - volatile long long FailedAllocations; - volatile long long FailedMappings; + volatile int64_t AdaptersCreated; + volatile int64_t AdaptersDestroyed; + volatile int64_t AdaptersInUse; + volatile int64_t CommonBuffersAllocated; + volatile int64_t CommonBuffersFreed; + volatile int64_t CommonBuffersInUse; + volatile int64_t CommonBufferBytesInUse; + volatile int64_t PinnedKernelRanges; + volatile int64_t PinnedUserRanges; + volatile int64_t PinnedRangesReleased; + volatile int64_t PinnedRangesInUse; + volatile int64_t PinnedPagesInUse; + volatile int64_t StreamingMappings; + volatile int64_t StreamingMappingsUnmapped; + volatile int64_t StreamingMappingsInUse; + volatile int64_t DirectMappings; + volatile int64_t BounceMappings; + volatile int64_t BounceBytesCopiedToDevice; + volatile int64_t BounceBytesCopiedToCpu; + volatile int64_t FailedAllocations; + volatile int64_t FailedMappings; } IO_DMA_INTERNAL_STATS; static IO_DMA_INTERNAL_STATS gIoDmaStats; // Returns whether a size is a nonzero power of two. -static int IoDmaIsPowerOfTwo(size_t value) { +static bool IoDmaIsPowerOfTwo(size_t value) { return value && ((value & (value - 1)) == 0); } @@ -125,7 +127,7 @@ static KSTATUS IoDmaNormalizeRequirements(const IO_DMA_REQUIREMENTS* source, } // Returns whether two normalized requirement sets are identical. -static int IoDmaRequirementsEqual(const IO_DMA_REQUIREMENTS* left, +static bool IoDmaRequirementsEqual(const IO_DMA_REQUIREMENTS* left, const IO_DMA_REQUIREMENTS* right) { return left && right && left->MaximumDeviceAddress == right->MaximumDeviceAddress && @@ -136,37 +138,37 @@ static int IoDmaRequirementsEqual(const IO_DMA_REQUIREMENTS* left, } // Returns whether device removal has closed the adapter to new ownership. -static int IoDmaAdapterRemoved(const IO_DMA_ADAPTER* adapter) { +static bool IoDmaAdapterRemoved(const IO_DMA_ADAPTER* adapter) { if (!adapter) - return 1; - return _InterlockedCompareExchange((volatile long*)&adapter->Removed, 0, 0) != 0; + return true; + return NtlAtomicLoad32(&adapter->Removed) != 0; } // Adds one reference to an adapter that is already stabilized by its owner. static void IoDmaReferenceAdapter(IO_DMA_ADAPTER* adapter) { if (adapter) - _InterlockedIncrement(&adapter->RefCount); + NtlAtomicIncrement32(&adapter->RefCount); } // Adds a reference only while the adapter still accepts new DMA ownership. -static int IoDmaTryReferenceAdapter(IO_DMA_ADAPTER* adapter) { +static bool IoDmaTryReferenceAdapter(IO_DMA_ADAPTER* adapter) { if (!adapter) - return 0; + return false; uintptr_t flags = KeAcquireSpinlock(&adapter->Lock); if (adapter->Removed) { KeReleaseSpinlock(&adapter->Lock, flags); - return 0; + return false; } - _InterlockedIncrement(&adapter->RefCount); + NtlAtomicIncrement32(&adapter->RefCount); KeReleaseSpinlock(&adapter->Lock, flags); - return 1; + return true; } // Releases one adapter reference and destroys the detached adapter at zero. static void IoDmaPutAdapterImpl(IO_DMA_ADAPTER* adapter) { if (!adapter) return; - long remaining = _InterlockedDecrement(&adapter->RefCount); + int32_t remaining = NtlAtomicDecrement32(&adapter->RefCount); if (remaining > 0) return; if (remaining < 0) @@ -177,8 +179,8 @@ static void IoDmaPutAdapterImpl(IO_DMA_ADAPTER* adapter) { KePanic(KE_PANIC_CODE_GENERIC, "DMA: attached adapter reached zero references adapter=0x%016llX\n", (unsigned long long)(uintptr_t)adapter); - _InterlockedIncrement64(&gIoDmaStats.AdaptersDestroyed); - _InterlockedDecrement64(&gIoDmaStats.AdaptersInUse); + NtlAtomicIncrement64(&gIoDmaStats.AdaptersDestroyed); + NtlAtomicDecrement64(&gIoDmaStats.AdaptersInUse); MmFreePool(adapter); } @@ -217,7 +219,7 @@ static KSTATUS IoDmaGetAdapterImpl(DEVICE_OBJECT* device, IO_DMA_ADAPTER* candidate = (IO_DMA_ADAPTER*)MmAllocatePoolTag(sizeof(*candidate), IO_DMA_ADAPTER_TAG); if (!candidate) { - _InterlockedIncrement64(&gIoDmaStats.FailedAllocations); + NtlAtomicIncrement64(&gIoDmaStats.FailedAllocations); return NTLSTATUS_NO_MEMORY; } memset(candidate, 0, sizeof(*candidate)); @@ -249,8 +251,8 @@ static KSTATUS IoDmaGetAdapterImpl(DEVICE_OBJECT* device, device->DmaAdapter = candidate; KeReleaseSpinlock(&device->Lock, flags); - _InterlockedIncrement64(&gIoDmaStats.AdaptersCreated); - _InterlockedIncrement64(&gIoDmaStats.AdaptersInUse); + NtlAtomicIncrement64(&gIoDmaStats.AdaptersCreated); + NtlAtomicIncrement64(&gIoDmaStats.AdaptersInUse); *outAdapter = candidate; return NTLSTATUS_SUCCESS; } @@ -324,7 +326,7 @@ static KSTATUS IoDmaAllocateCommonBufferInternal(IO_DMA_ADAPTER* adapter, allocationBoundary, MmPfnUseDma); if (!physical) { - _InterlockedIncrement64(&gIoDmaStats.FailedAllocations); + NtlAtomicIncrement64(&gIoDmaStats.FailedAllocations); return NTLSTATUS_NO_MEMORY; } @@ -332,7 +334,7 @@ static KSTATUS IoDmaAllocateCommonBufferInternal(IO_DMA_ADAPTER* adapter, if (!buffer) { for (size_t i = 0; i < pageCount; ++i) (void)MmPfnFree(physical + i * PAGE_SIZE); - _InterlockedIncrement64(&gIoDmaStats.FailedAllocations); + NtlAtomicIncrement64(&gIoDmaStats.FailedAllocations); return NTLSTATUS_NO_MEMORY; } @@ -348,7 +350,7 @@ static KSTATUS IoDmaAllocateCommonBufferInternal(IO_DMA_ADAPTER* adapter, MmFreePool(buffer); for (size_t i = 0; i < pageCount; ++i) (void)MmPfnFree(physical + i * PAGE_SIZE); - _InterlockedIncrement64(&gIoDmaStats.FailedAllocations); + NtlAtomicIncrement64(&gIoDmaStats.FailedAllocations); return NTLSTATUS_FAILURE; } @@ -359,9 +361,9 @@ static KSTATUS IoDmaAllocateCommonBufferInternal(IO_DMA_ADAPTER* adapter, (void)MmPfnFree(physical + i * PAGE_SIZE); return NTLSTATUS_OBJECT_DEAD; } - _InterlockedIncrement64(&gIoDmaStats.CommonBuffersAllocated); - _InterlockedIncrement64(&gIoDmaStats.CommonBuffersInUse); - _InterlockedExchangeAdd64(&gIoDmaStats.CommonBufferBytesInUse, (long long)allocationSize); + NtlAtomicIncrement64(&gIoDmaStats.CommonBuffersAllocated); + NtlAtomicIncrement64(&gIoDmaStats.CommonBuffersInUse); + NtlAtomicExchangeAdd64(&gIoDmaStats.CommonBufferBytesInUse, (int64_t)allocationSize); *outBuffer = buffer; return NTLSTATUS_SUCCESS; } @@ -389,9 +391,9 @@ static void IoDmaFreeCommonBufferImpl(IO_DMA_BUFFER* buffer) { for (size_t i = 0; i < pageCount; ++i) (void)MmPfnFree(physical + i * PAGE_SIZE); - _InterlockedIncrement64(&gIoDmaStats.CommonBuffersFreed); - _InterlockedDecrement64(&gIoDmaStats.CommonBuffersInUse); - _InterlockedExchangeAdd64(&gIoDmaStats.CommonBufferBytesInUse, -(long long)allocationSize); + NtlAtomicIncrement64(&gIoDmaStats.CommonBuffersFreed); + NtlAtomicDecrement64(&gIoDmaStats.CommonBuffersInUse); + NtlAtomicExchangeAdd64(&gIoDmaStats.CommonBufferBytesInUse, -(int64_t)allocationSize); IoDmaPutAdapterImpl(adapter); } @@ -411,7 +413,7 @@ static size_t IoDmaGetCommonBufferSizeImpl(const IO_DMA_BUFFER* buffer) { } // Returns whether a pinned access mode can satisfy a mapping direction. -static int IoDmaDirectionAllows(IO_DMA_DIRECTION pinned, IO_DMA_DIRECTION mapped) { +static bool IoDmaDirectionAllows(IO_DMA_DIRECTION pinned, IO_DMA_DIRECTION mapped) { if (pinned == IoDmaBidirectional) return mapped == IoDmaToDevice || mapped == IoDmaFromDevice || mapped == IoDmaBidirectional; return pinned == mapped; @@ -432,7 +434,7 @@ static MM_PINNED_MEMORY* IoDmaAllocatePinnedMemory(size_t pageCount) { static void IoDmaReleasePinnedMemory(MM_PINNED_MEMORY* memory) { if (!memory) return; - long remaining = _InterlockedDecrement(&memory->RefCount); + int32_t remaining = NtlAtomicDecrement32(&memory->RefCount); if (remaining > 0) return; if (remaining < 0) @@ -446,16 +448,16 @@ static void IoDmaReleasePinnedMemory(MM_PINNED_MEMORY* memory) { for (size_t i = 0; i < memory->PageCount; ++i) (void)MmPfnUnpin(memory->Frames[i]); } - _InterlockedIncrement64(&gIoDmaStats.PinnedRangesReleased); - _InterlockedDecrement64(&gIoDmaStats.PinnedRangesInUse); - _InterlockedExchangeAdd64(&gIoDmaStats.PinnedPagesInUse, -(long long)memory->PageCount); + NtlAtomicIncrement64(&gIoDmaStats.PinnedRangesReleased); + NtlAtomicDecrement64(&gIoDmaStats.PinnedRangesInUse); + NtlAtomicExchangeAdd64(&gIoDmaStats.PinnedPagesInUse, -(int64_t)memory->PageCount); MmFreePool(memory); } // Adds one mapping reference to pinned memory. static void IoDmaReferencePinnedMemory(MM_PINNED_MEMORY* memory) { if (memory) - _InterlockedIncrement(&memory->RefCount); + NtlAtomicIncrement32(&memory->RefCount); } // Pins a kernel virtual range for later DMA mapping. @@ -480,7 +482,7 @@ static KSTATUS IoDmaPinKernelMemoryImpl(void* address, MM_PINNED_MEMORY* memory = IoDmaAllocatePinnedMemory(pageCount); if (!memory) { - _InterlockedIncrement64(&gIoDmaStats.FailedAllocations); + NtlAtomicIncrement64(&gIoDmaStats.FailedAllocations); return NTLSTATUS_NO_MEMORY; } @@ -515,9 +517,9 @@ static KSTATUS IoDmaPinKernelMemoryImpl(void* address, return status; } - _InterlockedIncrement64(&gIoDmaStats.PinnedKernelRanges); - _InterlockedIncrement64(&gIoDmaStats.PinnedRangesInUse); - _InterlockedExchangeAdd64(&gIoDmaStats.PinnedPagesInUse, (long long)pageCount); + NtlAtomicIncrement64(&gIoDmaStats.PinnedKernelRanges); + NtlAtomicIncrement64(&gIoDmaStats.PinnedRangesInUse); + NtlAtomicExchangeAdd64(&gIoDmaStats.PinnedPagesInUse, (int64_t)pageCount); *outMemory = memory; return NTLSTATUS_SUCCESS; } @@ -549,7 +551,7 @@ static KSTATUS IoDmaPinUserMemoryImpl(struct PROCESS* process, MM_PINNED_MEMORY* memory = IoDmaAllocatePinnedMemory(pageCount); if (!memory) { MmUnlockUserBuffer(locked); - _InterlockedIncrement64(&gIoDmaStats.FailedAllocations); + NtlAtomicIncrement64(&gIoDmaStats.FailedAllocations); return NTLSTATUS_NO_MEMORY; } @@ -564,9 +566,9 @@ static KSTATUS IoDmaPinUserMemoryImpl(struct PROCESS* process, for (size_t i = 0; i < pageCount; ++i) memory->Frames[i] = MmLockedUserBufferPageFrame(locked, i); - _InterlockedIncrement64(&gIoDmaStats.PinnedUserRanges); - _InterlockedIncrement64(&gIoDmaStats.PinnedRangesInUse); - _InterlockedExchangeAdd64(&gIoDmaStats.PinnedPagesInUse, (long long)pageCount); + NtlAtomicIncrement64(&gIoDmaStats.PinnedUserRanges); + NtlAtomicIncrement64(&gIoDmaStats.PinnedRangesInUse); + NtlAtomicExchangeAdd64(&gIoDmaStats.PinnedPagesInUse, (int64_t)pageCount); *outMemory = memory; return NTLSTATUS_SUCCESS; } @@ -842,7 +844,7 @@ static KSTATUS IoDmaMapPinnedMemoryImpl(IO_DMA_ADAPTER* adapter, if (!KSUCCESS(status)) { if (status != NTLSTATUS_NOT_SUPPORTED || (flags & IO_DMA_MAP_NO_BOUNCE)) { - _InterlockedIncrement64(&gIoDmaStats.FailedMappings); + NtlAtomicIncrement64(&gIoDmaStats.FailedMappings); return status; } @@ -852,7 +854,7 @@ static KSTATUS IoDmaMapPinnedMemoryImpl(IO_DMA_ADAPTER* adapter, 0, &bounce); if (!KSUCCESS(status)) { - _InterlockedIncrement64(&gIoDmaStats.FailedMappings); + NtlAtomicIncrement64(&gIoDmaStats.FailedMappings); return status; } status = IoDmaBuildContiguousSegments(bounce->DeviceAddress, @@ -862,7 +864,7 @@ static KSTATUS IoDmaMapPinnedMemoryImpl(IO_DMA_ADAPTER* adapter, &segmentCount); if (!KSUCCESS(status)) { IoDmaFreeCommonBufferImpl(bounce); - _InterlockedIncrement64(&gIoDmaStats.FailedMappings); + NtlAtomicIncrement64(&gIoDmaStats.FailedMappings); return status; } } @@ -870,7 +872,7 @@ static KSTATUS IoDmaMapPinnedMemoryImpl(IO_DMA_ADAPTER* adapter, IO_DMA_MAPPING* mapping = IoDmaAllocateMapping(segmentCount); if (!mapping) { IoDmaFreeCommonBufferImpl(bounce); - _InterlockedIncrement64(&gIoDmaStats.FailedMappings); + NtlAtomicIncrement64(&gIoDmaStats.FailedMappings); return NTLSTATUS_NO_MEMORY; } @@ -891,7 +893,7 @@ static KSTATUS IoDmaMapPinnedMemoryImpl(IO_DMA_ADAPTER* adapter, if (!KSUCCESS(status)) { MmFreePool(mapping); IoDmaFreeCommonBufferImpl(bounce); - _InterlockedIncrement64(&gIoDmaStats.FailedMappings); + NtlAtomicIncrement64(&gIoDmaStats.FailedMappings); return status; } @@ -909,12 +911,12 @@ static KSTATUS IoDmaMapPinnedMemoryImpl(IO_DMA_ADAPTER* adapter, } IoDmaReferencePinnedMemory(memory); - _InterlockedIncrement64(&gIoDmaStats.StreamingMappings); - _InterlockedIncrement64(&gIoDmaStats.StreamingMappingsInUse); + NtlAtomicIncrement64(&gIoDmaStats.StreamingMappings); + NtlAtomicIncrement64(&gIoDmaStats.StreamingMappingsInUse); if (bounce) - _InterlockedIncrement64(&gIoDmaStats.BounceMappings); + NtlAtomicIncrement64(&gIoDmaStats.BounceMappings); else - _InterlockedIncrement64(&gIoDmaStats.DirectMappings); + NtlAtomicIncrement64(&gIoDmaStats.DirectMappings); *outMapping = mapping; return NTLSTATUS_SUCCESS; } @@ -934,8 +936,8 @@ static KSTATUS IoDmaSynchronizeForDeviceImpl(IO_DMA_MAPPING* mapping) { mapping->Length); if (!KSUCCESS(status)) return status; - _InterlockedExchangeAdd64(&gIoDmaStats.BounceBytesCopiedToDevice, - (long long)mapping->Length); + NtlAtomicExchangeAdd64(&gIoDmaStats.BounceBytesCopiedToDevice, + (int64_t)mapping->Length); } _ReadWriteBarrier(); _mm_mfence(); @@ -961,8 +963,8 @@ static KSTATUS IoDmaSynchronizeForCpuImpl(IO_DMA_MAPPING* mapping) { mapping->Length); if (!KSUCCESS(status)) return status; - _InterlockedExchangeAdd64(&gIoDmaStats.BounceBytesCopiedToCpu, - (long long)mapping->Length); + NtlAtomicExchangeAdd64(&gIoDmaStats.BounceBytesCopiedToCpu, + (int64_t)mapping->Length); } mapping->DeviceOwnsMapping = 0; return NTLSTATUS_SUCCESS; @@ -989,8 +991,8 @@ static void IoDmaUnmapMemoryImpl(IO_DMA_MAPPING* mapping) { IoDmaFreeCommonBufferImpl(bounce); IoDmaReleasePinnedMemory(memory); IoDmaPutAdapterImpl(adapter); - _InterlockedIncrement64(&gIoDmaStats.StreamingMappingsUnmapped); - _InterlockedDecrement64(&gIoDmaStats.StreamingMappingsInUse); + NtlAtomicIncrement64(&gIoDmaStats.StreamingMappingsUnmapped); + NtlAtomicDecrement64(&gIoDmaStats.StreamingMappingsInUse); } // Returns the number of device segments in a streaming mapping. diff --git a/code/kernel/io_file.c b/code/kernel/io_file.c index 27d2af1..bd57314 100644 --- a/code/kernel/io_file.c +++ b/code/kernel/io_file.c @@ -1,5 +1,6 @@ // Kernel file I/O layer. This file owns file handles, filesystem dispatch, and asynchronous request plumbing. +#include #include #include #include @@ -58,13 +59,13 @@ typedef struct IO_FILE_LOCK_WAITER { uint64_t Length; uint32_t Mode; EVENT* Event; - uint8_t Armed; + bool Armed; } IO_FILE_LOCK_WAITER; static SPINLOCK gFileLockLock = SPINLOCK_INIT; static LIST_ENTRY gFileLocks; static LIST_ENTRY gFileLockWaiters; -static uint8_t gFileLocksInitialized; +static bool gFileLocksInitialized; // Initializes the global advisory file-lock lists on first use. static void KiIoEnsureFileLockState(void) { @@ -545,8 +546,8 @@ __declspec(dllexport) KSTATUS IoQueryAsyncFileRequest(IO_ASYNC_FILE_REQUEST_OBJE // Validates one canonical native path relative to an already-authoritative root capability. // Native relative paths use '/' only and never carry drive/CWD or parent-traversal semantics. static KSTATUS KiValidateNativeRelativePath(SYS_STRING_VIEW path, bool allowEmpty) { - if (!SysSvIsValid(path)) return NTLSTATUS_INVALID_PARAMETER; - if (SysSvContainsByte(path, '\0') || SysSvContainsByte(path, '\\')) + if (!SysSvIsUtf8Text(path)) return NTLSTATUS_INVALID_PARAMETER; + if (SysSvContainsByte(path, '\\')) return NTLSTATUS_INVALID_PARAMETER; if (path.Length == 0) return allowEmpty ? NTLSTATUS_SUCCESS : NTLSTATUS_INVALID_PARAMETER; diff --git a/code/kernel/io_pipe.c b/code/kernel/io_pipe.c index 8525dba..5eea26d 100644 --- a/code/kernel/io_pipe.c +++ b/code/kernel/io_pipe.c @@ -1,5 +1,7 @@ // Kernel pipe object lifetime, shared backing, readiness, and wait integration. +#include +#include #include #include #include @@ -20,7 +22,7 @@ typedef enum IO_PIPE_DIRECTION { } IO_PIPE_DIRECTION; typedef struct IO_PIPE_STATE { - volatile long RefCount; + volatile int32_t RefCount; SPINLOCK Lock; EVENT* ReadEvent; EVENT* WriteEvent; @@ -80,7 +82,7 @@ static void KiPipeRefreshEventsLocked(IO_PIPE_STATE* state) { // Releases shared pipe storage after both endpoint objects are gone. static void KiPipeStateRelease(IO_PIPE_STATE* state) { - if (!state || _InterlockedDecrement(&state->RefCount) != 0) + if (!state || NtlAtomicDecrement32(&state->RefCount) != 0) return; if (state->KernelView) { @@ -295,9 +297,9 @@ void IoPipeBeginWait(IO_PIPE_ENDPOINT* endpoint) { return; SYS_PIPE_SHARED_STATE* shared = endpoint->State->Shared; if (endpoint->Direction == IoPipeReadEnd) - _InterlockedIncrement(&shared->ReadWaiters); + NtlAtomicIncrement32(&shared->ReadWaiters); else - _InterlockedIncrement(&shared->WriteWaiters); + NtlAtomicIncrement32(&shared->WriteWaiters); uintptr_t flags = KeAcquireSpinlock(&endpoint->State->Lock); KiPipeRefreshEventsLocked(endpoint->State); @@ -310,9 +312,9 @@ void IoPipeEndWait(IO_PIPE_ENDPOINT* endpoint) { return; SYS_PIPE_SHARED_STATE* shared = endpoint->State->Shared; if (endpoint->Direction == IoPipeReadEnd) - _InterlockedDecrement(&shared->ReadWaiters); + NtlAtomicDecrement32(&shared->ReadWaiters); else - _InterlockedDecrement(&shared->WriteWaiters); + NtlAtomicDecrement32(&shared->WriteWaiters); } // Returns whether this endpoint is the read side of the pipe. diff --git a/code/kernel/io_volume.c b/code/kernel/io_volume.c index 191d808..9cc9e4d 100644 --- a/code/kernel/io_volume.c +++ b/code/kernel/io_volume.c @@ -1,5 +1,6 @@ // Mounted-volume core. This file owns volume identity, provider lifetime, and dead-volume dispatch semantics. +#include #include #include #include @@ -13,7 +14,7 @@ static KE_ID_ALLOCATOR gVolumeIds = KE_ID_ALLOCATOR_INIT(1); static SPINLOCK gVolumeRegistryLock = SPINLOCK_INIT; static LIST_ENTRY gVolumeRegistry; -static uint8_t gVolumeRegistryInitialized; +static bool gVolumeRegistryInitialized; static SPINLOCK gVolumeStatsLock = SPINLOCK_INIT; static IO_VOLUME_STATS gVolumeStats; static IO_VOLUME_MANAGER_STATS gVolumeManagerStats; @@ -21,7 +22,7 @@ static EVENT* gVolumeChangeEvent; static SPINLOCK gVolumeChangeLock = SPINLOCK_INIT; static uint64_t gVolumeGeneration; static IO_VOLUME_MANAGER gVolumeManagerObject; -static uint8_t gVolumeManagerObjectInitialized; +static bool gVolumeManagerObjectInitialized; static void KiIoVolumeAddStat(uint64_t* field, int64_t delta); diff --git a/code/kernel/ke_apc.c b/code/kernel/ke_apc.c index 19cb282..e7bd74d 100644 --- a/code/kernel/ke_apc.c +++ b/code/kernel/ke_apc.c @@ -1,5 +1,7 @@ // Kernel APC infrastructure. This file owns APC queuing, delivery, and user-mode return transitions. +#include +#include #include #include #include @@ -19,7 +21,7 @@ #define KI_RFLAGS_NT 0x4000ULL #define KI_RFLAGS_VM 0x20000ULL -static volatile long long gUserApcReturnTokenSeed; +static volatile int64_t gUserApcReturnTokenSeed; // Returns whether an address is in the canonical user half accepted by IRET. static bool KiUserAddressValid(uint64_t address) { @@ -49,7 +51,7 @@ static KSTATUS KiValidateUserApcResumeState(uint64_t rip, uint64_t rsp, uint64_t // Allocates a nonzero token for one active user APC return frame. static uint64_t KiNextUserApcReturnToken(THREAD* thread, uintptr_t frameAddress) { - uint64_t seed = (uint64_t)_InterlockedIncrement64(&gUserApcReturnTokenSeed); + uint64_t seed = (uint64_t)NtlAtomicIncrement64(&gUserApcReturnTokenSeed); uint64_t token = seed ^ (uint64_t)(uintptr_t)thread ^ (uint64_t)frameAddress ^ SYS_USER_APC_FRAME_MAGIC; if (token == 0) token = SYS_USER_APC_FRAME_MAGIC; @@ -113,7 +115,7 @@ __declspec(dllexport) bool KeInsertQueueApc(KAPC* apc, if (thread->State == ThreadDead || KeThreadExitInProgress(thread)) return false; - if (_InterlockedCompareExchange(&apc->Inserted, 1, 0) != 0) + if (NtlAtomicCompareExchange32(&apc->Inserted, 1, 0) != 0) return false; apc->SystemArgument1 = systemArgument1; diff --git a/code/kernel/ke_audio.c b/code/kernel/ke_audio.c index 2b78631..31e844d 100644 --- a/code/kernel/ke_audio.c +++ b/code/kernel/ke_audio.c @@ -1,5 +1,7 @@ // Kernel audio core. This file owns endpoint identity, logical streams, mixing, and provider timing handoff. +#include +#include #include #include #include @@ -52,8 +54,8 @@ struct KE_AUDIO_STREAM { LIST_ENTRY EndpointLink; IO_AUDIO_ENDPOINT* Endpoint; KE_AUDIO_STREAM_STATE State; - volatile long ProcessHandleCount; - volatile long CloseRequested; + volatile int32_t ProcessHandleCount; + volatile int32_t CloseRequested; IO_AUDIO_FORMAT Format; uint8_t* Ring; uint32_t FrameBytes; @@ -82,7 +84,7 @@ struct KE_AUDIO_STREAM { uint64_t WaitDiscontinuityGeneration; float Volume; float Pan; - uint8_t Muted; + bool Muted; float CurrentLeftGain; float CurrentRightGain; float TargetLeftGain; @@ -106,10 +108,10 @@ struct IO_AUDIO_ENDPOINT { IO_AUDIO_ENDPOINT_KIND Kind; KE_AUDIO_ENDPOINT_STATE State; uint32_t Capabilities; - uint8_t Available; - uint8_t Registered; - uint8_t HardwareRunning; - uint8_t SkipProviderStop; + bool Available; + bool Registered; + bool HardwareRunning; + bool SkipProviderStop; IO_AUDIO_REMOVAL_MODE RemovalMode; DEVICE_OBJECT* Device; void* ProviderContext; @@ -133,7 +135,7 @@ struct IO_AUDIO_ENDPOINT { float MasterGainStep; uint32_t MasterRampRemaining; uint64_t MasterGainGeneration; - uint8_t MasterMuted; + bool MasterMuted; KSTATUS FailureStatus; uint64_t PeriodsMixed; uint64_t PeriodsCompleted; @@ -151,23 +153,23 @@ struct IO_AUDIO_ENDPOINT { }; typedef struct KE_AUDIO_CORE_STATS { - volatile long long EndpointsRegistered; - volatile long long EndpointsUnregistered; - volatile long long StreamsCreated; - volatile long long StreamsDestroyed; - volatile long long Starts; - volatile long long Stops; - volatile long long Pauses; - volatile long long Resumes; - volatile long long Drains; - volatile long long Resets; + volatile int64_t EndpointsRegistered; + volatile int64_t EndpointsUnregistered; + volatile int64_t StreamsCreated; + volatile int64_t StreamsDestroyed; + volatile int64_t Starts; + volatile int64_t Stops; + volatile int64_t Pauses; + volatile int64_t Resumes; + volatile int64_t Drains; + volatile int64_t Resets; } KE_AUDIO_CORE_STATS; typedef struct KE_AUDIO_LIFECYCLE_SNAPSHOT { KE_AUDIO_ENDPOINT_STATE State; KSTATUS FailureStatus; IO_AUDIO_REMOVAL_MODE RemovalMode; - uint8_t SkipProviderStop; + bool SkipProviderStop; } KE_AUDIO_LIFECYCLE_SNAPSHOT; static SPINLOCK gAudioRegistryLock = SPINLOCK_INIT; @@ -175,7 +177,7 @@ static LIST_ENTRY gAudioEndpointList; static uint32_t gAudioEndpointCount; static KE_ID_ALLOCATOR gAudioEndpointIds = KE_ID_ALLOCATOR_INIT(1u); static uint32_t gAudioDefaultEndpointId; -static uint8_t gAudioInitialized; +static bool gAudioInitialized; static KE_AUDIO_CORE_STATS gAudioCoreStats; // Stops on one violated internal audio invariant. @@ -205,7 +207,7 @@ static uint64_t KiAudioQueryTicks(void) { } // Returns whether one float is finite without depending on a runtime math library. -static int KiAudioFloatIsFinite(float value) { +static bool KiAudioFloatIsFinite(float value) { union { float Float; uint32_t Bits; @@ -333,12 +335,12 @@ static uint32_t KiAudioFrameBytes(const IO_AUDIO_FORMAT* format) { } // Validates one logical source format accepted by the first mixer contract. -static int KiAudioValidateSourceFormat(const IO_AUDIO_FORMAT* format) { - if (!format) return 0; +static bool KiAudioValidateSourceFormat(const IO_AUDIO_FORMAT* format) { + if (!format) return false; if (format->ChannelCount != 1 && format->ChannelCount != 2) - return 0; + return false; if (format->SampleFormat != IoAudioSampleS16) - return 0; + return false; return format->SampleRate == 11025u || format->SampleRate == 22050u || format->SampleRate == 44100u || @@ -392,10 +394,10 @@ static KSTATUS KiAudioValidateRegistration( } // Returns whether one endpoint lifecycle transition is valid. -static int KiAudioEndpointTransitionAllowed(KE_AUDIO_ENDPOINT_STATE oldState, +static bool KiAudioEndpointTransitionAllowed(KE_AUDIO_ENDPOINT_STATE oldState, KE_AUDIO_ENDPOINT_STATE newState) { if (oldState == newState) - return 1; + return true; switch (oldState) { case KeAudioEndpointStarting: return newState == KeAudioEndpointReady || @@ -408,9 +410,9 @@ static int KiAudioEndpointTransitionAllowed(KE_AUDIO_ENDPOINT_STATE oldState, case KeAudioEndpointStopping: return newState == KeAudioEndpointRemoved; case KeAudioEndpointRemoved: - return 0; + return false; default: - return 0; + return false; } } @@ -471,7 +473,7 @@ static IO_AUDIO_ENDPOINT* KiAudioReferenceEndpointById(uint32_t endpointId) { // Signals one stream when an armed progress condition or terminal state is present. static void KiAudioRefreshStreamEventLocked(KE_AUDIO_STREAM* stream) { uint32_t writableFrames = KE_AUDIO_STREAM_RING_FRAMES - stream->BufferedFrames; - int signaled = stream->State == KeAudioStreamFailed || + bool signaled = stream->State == KeAudioStreamFailed || stream->State == KeAudioStreamClosed; if ((stream->WaitMask & KE_AUDIO_WAIT_WRITABLE) && writableFrames >= stream->WaitWritableFrames) @@ -525,21 +527,21 @@ static void KiAudioAdjustActiveStreams(IO_AUDIO_ENDPOINT* endpoint, int delta) { } // Returns whether one stream state contributes to the active hardware clock. -static int KiAudioStreamStateIsActive(KE_AUDIO_STREAM_STATE state) { +static bool KiAudioStreamStateIsActive(KE_AUDIO_STREAM_STATE state) { return state == KeAudioStreamRunning || state == KeAudioStreamDraining; } // Returns whether every frame targeted by one drain has reached a terminal disposition. -static int KiAudioDrainIsCompleteLocked(const KE_AUDIO_STREAM* stream) { +static bool KiAudioDrainIsCompleteLocked(const KE_AUDIO_STREAM* stream) { return stream->BufferedFrames == 0 && stream->FramesPresented + stream->FramesDiscarded >= stream->FramesSubmitted; } // Returns whether one logical stream transition is valid. -static int KiAudioStreamTransitionAllowed(KE_AUDIO_STREAM_STATE oldState, +static bool KiAudioStreamTransitionAllowed(KE_AUDIO_STREAM_STATE oldState, KE_AUDIO_STREAM_STATE newState) { if (oldState == newState) - return 1; + return true; if (newState == KeAudioStreamFailed || newState == KeAudioStreamClosed) return oldState != KeAudioStreamClosed; switch (oldState) { @@ -561,7 +563,7 @@ static int KiAudioStreamTransitionAllowed(KE_AUDIO_STREAM_STATE oldState, case KeAudioStreamFailed: case KeAudioStreamClosed: default: - return 0; + return false; } } @@ -571,8 +573,8 @@ static void KiAudioSetStreamStateLocked(KE_AUDIO_STREAM* stream, if (!KiAudioStreamTransitionAllowed(stream->State, newState)) KiAudioBug("KiAudioSetStreamStateLocked", "invalid stream transition", ((uint64_t)stream->State << 32) | (uint32_t)newState); - int oldActive = KiAudioStreamStateIsActive(stream->State); - int newActive = KiAudioStreamStateIsActive(newState); + bool oldActive = KiAudioStreamStateIsActive(stream->State); + bool newActive = KiAudioStreamStateIsActive(newState); stream->State = newState; if (oldActive != newActive) KiAudioAdjustActiveStreams(stream->Endpoint, newActive ? 1 : -1); @@ -656,8 +658,8 @@ static void KiAudioDecodeSourceFrame(const KE_AUDIO_STREAM* stream, } // Applies one final-handle close request while the stream mutex serializes state. -static int KiAudioApplyCloseRequestLocked(KE_AUDIO_STREAM* stream) { - if (!_InterlockedCompareExchange(&stream->CloseRequested, 0, 0) || +static bool KiAudioApplyCloseRequestLocked(KE_AUDIO_STREAM* stream) { + if (!NtlAtomicLoad32(&stream->CloseRequested) || stream->State == KeAudioStreamClosed) return stream->State == KeAudioStreamClosed; @@ -673,7 +675,7 @@ static int KiAudioApplyCloseRequestLocked(KE_AUDIO_STREAM* stream) { stream->ConsecutiveUnderrunPeriods = 0; stream->DiscontinuityGeneration++; KiAudioRefreshStreamEventLocked(stream); - return 1; + return true; } // Mixes one logical stream into one endpoint period. @@ -803,7 +805,7 @@ static void KiAudioWriteOutputPeriod(IO_AUDIO_ENDPOINT* endpoint, } // Reserves one free hardware period for passive-level mixing. -static int KiAudioReservePeriod(IO_AUDIO_ENDPOINT* endpoint, +static bool KiAudioReservePeriod(IO_AUDIO_ENDPOINT* endpoint, uint32_t* periodIndexOut, uint64_t* generationOut) { uintptr_t flags = KeAcquireSpinlock(&endpoint->NotifyLock); @@ -816,10 +818,10 @@ static int KiAudioReservePeriod(IO_AUDIO_ENDPOINT* endpoint, *periodIndexOut = i; *generationOut = endpoint->Periods[i].Generation; KeReleaseSpinlock(&endpoint->NotifyLock, flags); - return 1; + return true; } KeReleaseSpinlock(&endpoint->NotifyLock, flags); - return 0; + return false; } // Discards one contribution array after its mixed source frames cannot be presented. @@ -914,7 +916,7 @@ static int KiAudioMixOnePeriod(IO_AUDIO_ENDPOINT* endpoint) { } // Applies one hardware-completed period to per-stream presentation counters. -static int KiAudioReapOneCompletedPeriod(IO_AUDIO_ENDPOINT* endpoint) { +static bool KiAudioReapOneCompletedPeriod(IO_AUDIO_ENDPOINT* endpoint) { uint32_t periodIndex = 0; KE_AUDIO_PERIOD_STATE* period = (KE_AUDIO_PERIOD_STATE*)0; uintptr_t flags = KeAcquireSpinlock(&endpoint->NotifyLock); @@ -928,7 +930,7 @@ static int KiAudioReapOneCompletedPeriod(IO_AUDIO_ENDPOINT* endpoint) { } KeReleaseSpinlock(&endpoint->NotifyLock, flags); if (!period) - return 0; + return false; for (uint32_t i = 0; i < period->ContributionCount; i++) { KE_AUDIO_PERIOD_CONTRIBUTION* contribution = &period->Contributions[i]; @@ -952,7 +954,7 @@ static int KiAudioReapOneCompletedPeriod(IO_AUDIO_ENDPOINT* endpoint) { period->ContributionCount = 0; period->State = KE_AUDIO_PERIOD_STATE_FREE; KeReleaseSpinlock(&endpoint->NotifyLock, flags); - return 1; + return true; } // Counts periods ready for hardware consumption. @@ -1029,7 +1031,7 @@ static void KiAudioDiscardPeriodContributions(KE_AUDIO_PERIOD_STATE* period) { static void KiAudioStopHardware(IO_AUDIO_ENDPOINT* endpoint, int hardwareAccessible) { KiAudioAcquireMutex(endpoint->ControlLock, "KiAudioStopHardware"); uintptr_t flags = KeAcquireSpinlock(&endpoint->NotifyLock); - int wasRunning = endpoint->HardwareRunning; + bool wasRunning = endpoint->HardwareRunning != 0; endpoint->HardwareRunning = 0; endpoint->IdleDeadlineTicks = 0; KeReleaseSpinlock(&endpoint->NotifyLock, flags); @@ -1140,7 +1142,7 @@ static void KiAudioSnapshotLifecycle(IO_AUDIO_ENDPOINT* endpoint, } // Processes all currently available period and lifecycle work. -static int KiAudioProcessEndpoint(IO_AUDIO_ENDPOINT* endpoint, +static bool KiAudioProcessEndpoint(IO_AUDIO_ENDPOINT* endpoint, uint64_t* waitMillisecondsOut) { *waitMillisecondsOut = (uint64_t)-1; while (KiAudioReapOneCompletedPeriod(endpoint)) { @@ -1152,7 +1154,7 @@ static int KiAudioProcessEndpoint(IO_AUDIO_ENDPOINT* endpoint, if (lifecycle.State == KeAudioEndpointStopping || lifecycle.State == KeAudioEndpointFailed || lifecycle.State == KeAudioEndpointRemoved) { - int accessible = !lifecycle.SkipProviderStop && + bool accessible = !lifecycle.SkipProviderStop && lifecycle.RemovalMode != IoAudioSurpriseRemoval; KiAudioStopHardware(endpoint, accessible); if (lifecycle.State == KeAudioEndpointRemoved && KSUCCESS(failure)) @@ -1160,12 +1162,12 @@ static int KiAudioProcessEndpoint(IO_AUDIO_ENDPOINT* endpoint, if (KSUCCESS(failure)) failure = NTLSTATUS_DEVICE_ERROR; KiAudioFailAllStreams(endpoint, failure); - return 0; + return false; } uintptr_t flags = KeAcquireSpinlock(&endpoint->NotifyLock); uint32_t active = endpoint->ActiveStreamCount; - int running = endpoint->HardwareRunning; + bool running = endpoint->HardwareRunning != 0; uint64_t idleDeadline = endpoint->IdleDeadlineTicks; KeReleaseSpinlock(&endpoint->NotifyLock, flags); @@ -1182,7 +1184,7 @@ static int KiAudioProcessEndpoint(IO_AUDIO_ENDPOINT* endpoint, KiAudioSelectDefaultEndpointLocked(); KeReleaseSpinlock(&gAudioRegistryLock, registryFlags); KeSignalEvent(endpoint->WorkEvent); - return 1; + return true; } } } @@ -1221,7 +1223,7 @@ static int KiAudioProcessEndpoint(IO_AUDIO_ENDPOINT* endpoint, *waitMillisecondsOut = 1; } } - return 1; + return true; } // Runs one endpoint's real-time mixer and provider lifecycle loop. @@ -1275,7 +1277,7 @@ void KeAudioStreamHandleOpened(KE_AUDIO_STREAM* stream) { if (!stream) return; - long handleCount = _InterlockedIncrement(&stream->ProcessHandleCount); + int32_t handleCount = NtlAtomicIncrement32(&stream->ProcessHandleCount); if (handleCount <= 0) KiAudioBug("KeAudioStreamHandleOpened", "invalid process handle count", handleCount); } @@ -1285,13 +1287,13 @@ void KeAudioStreamHandleClosed(KE_AUDIO_STREAM* stream) { if (!stream) return; - long handleCount = _InterlockedDecrement(&stream->ProcessHandleCount); + int32_t handleCount = NtlAtomicDecrement32(&stream->ProcessHandleCount); if (handleCount < 0) KiAudioBug("KeAudioStreamHandleClosed", "process handle underflow", handleCount); if (handleCount != 0) return; - _InterlockedExchange(&stream->CloseRequested, 1); + NtlAtomicExchange32(&stream->CloseRequested, 1); KeSignalEvent(stream->WaitEvent); if (stream->Endpoint) KeSignalEvent(stream->Endpoint->WorkEvent); @@ -1315,7 +1317,7 @@ static void KiAudioStreamDestroy(OBJECT* object) { (uint64_t)(uintptr_t)stream->Lock->Owner); if (!KiAudioStreamTransitionAllowed(stream->State, KeAudioStreamClosed)) KiAudioBug("KiAudioStreamDestroy", "invalid terminal stream state", stream->State); - int wasActive = KiAudioStreamStateIsActive(stream->State); + bool wasActive = KiAudioStreamStateIsActive(stream->State); stream->State = KeAudioStreamClosed; if (wasActive) KiAudioAdjustActiveStreams(endpoint, -1); @@ -1328,7 +1330,7 @@ static void KiAudioStreamDestroy(OBJECT* object) { ObDereferenceObject(&stream->Lock->Header); if (endpoint) ObDereferenceObject(&endpoint->Header); - _InterlockedIncrement64(&gAudioCoreStats.StreamsDestroyed); + NtlAtomicIncrement64(&gAudioCoreStats.StreamsDestroyed); } // Registers one hardware-provider endpoint. @@ -1440,7 +1442,7 @@ static KSTATUS KiAudioRegisterEndpoint(const IO_AUDIO_ENDPOINT_REGISTRATION* reg KiAudioSelectDefaultEndpointLocked(); KeReleaseSpinlock(&gAudioRegistryLock, flags); - _InterlockedIncrement64(&gAudioCoreStats.EndpointsRegistered); + NtlAtomicIncrement64(&gAudioCoreStats.EndpointsRegistered); *outEndpoint = endpoint; return NTLSTATUS_SUCCESS; } @@ -1485,7 +1487,7 @@ static void KiAudioDdkUnregisterEndpoint(IO_AUDIO_ENDPOINT* endpoint, ObDereferenceObject(&endpoint->MixerThread->Header); endpoint->MixerThread = (THREAD*)0; - _InterlockedIncrement64(&gAudioCoreStats.EndpointsUnregistered); + NtlAtomicIncrement64(&gAudioCoreStats.EndpointsUnregistered); ObDereferenceObject(&endpoint->Header); ObDereferenceObject(&endpoint->Header); } @@ -1544,7 +1546,7 @@ static void KiAudioDdkNotifyPeriod(IO_AUDIO_ENDPOINT* endpoint, } // Updates one endpoint's jack-derived availability and default selection. -static void KiAudioDdkSetEndpointAvailable(IO_AUDIO_ENDPOINT* endpoint, uint8_t available) { +static void KiAudioDdkSetEndpointAvailable(IO_AUDIO_ENDPOINT* endpoint, bool available) { if (!endpoint || !(endpoint->Capabilities & IO_AUDIO_ENDPOINT_CAP_JACK_SENSE)) KiAudioBug("KiAudioDdkSetEndpointAvailable", "invalid availability update", 0); uintptr_t flags = KeAcquireSpinlock(&gAudioRegistryLock); @@ -1757,7 +1759,7 @@ KSTATUS KeAudioCreateStream(uint32_t endpointId, KeReleaseSpinlock(&endpoint->StreamListLock, streamFlags); KeReleaseSpinlock(&gAudioRegistryLock, registryFlags); - _InterlockedIncrement64(&gAudioCoreStats.StreamsCreated); + NtlAtomicIncrement64(&gAudioCoreStats.StreamsCreated); *outStream = stream; return NTLSTATUS_SUCCESS; } @@ -1839,7 +1841,7 @@ KSTATUS KeAudioWriteStream(KE_AUDIO_STREAM* stream, stream->FramesSubmitted += toWrite; *writtenFramesOut = toWrite; KiAudioRefreshStreamEventLocked(stream); - int active = KiAudioStreamStateIsActive(stream->State); + bool active = KiAudioStreamStateIsActive(stream->State); KiAudioReleaseMutex(stream->Lock); if (active) @@ -1862,7 +1864,7 @@ KSTATUS KeAudioStartStream(KE_AUDIO_STREAM* stream) { stream->FailureStatus = NTLSTATUS_SUCCESS; KiAudioRefreshStreamEventLocked(stream); KiAudioReleaseMutex(stream->Lock); - _InterlockedIncrement64(&gAudioCoreStats.Starts); + NtlAtomicIncrement64(&gAudioCoreStats.Starts); return NTLSTATUS_SUCCESS; } @@ -1880,7 +1882,7 @@ KSTATUS KeAudioPauseStream(KE_AUDIO_STREAM* stream) { KiAudioSetStreamStateLocked(stream, KeAudioStreamPaused); KiAudioRefreshStreamEventLocked(stream); KiAudioReleaseMutex(stream->Lock); - _InterlockedIncrement64(&gAudioCoreStats.Pauses); + NtlAtomicIncrement64(&gAudioCoreStats.Pauses); return NTLSTATUS_SUCCESS; } @@ -1898,7 +1900,7 @@ KSTATUS KeAudioResumeStream(KE_AUDIO_STREAM* stream) { KiAudioSetStreamStateLocked(stream, KeAudioStreamRunning); KiAudioRefreshStreamEventLocked(stream); KiAudioReleaseMutex(stream->Lock); - _InterlockedIncrement64(&gAudioCoreStats.Resumes); + NtlAtomicIncrement64(&gAudioCoreStats.Resumes); return NTLSTATUS_SUCCESS; } @@ -1921,7 +1923,7 @@ KSTATUS KeAudioDrainStream(KE_AUDIO_STREAM* stream) { KiAudioRefreshStreamEventLocked(stream); KiAudioReleaseMutex(stream->Lock); KeSignalEvent(stream->Endpoint->WorkEvent); - _InterlockedIncrement64(&gAudioCoreStats.Drains); + NtlAtomicIncrement64(&gAudioCoreStats.Drains); return NTLSTATUS_SUCCESS; } @@ -1945,7 +1947,7 @@ KSTATUS KeAudioStopStream(KE_AUDIO_STREAM* stream) { KiAudioRefreshStreamEventLocked(stream); KiAudioReleaseMutex(stream->Lock); KeSignalEvent(stream->Endpoint->WorkEvent); - _InterlockedIncrement64(&gAudioCoreStats.Stops); + NtlAtomicIncrement64(&gAudioCoreStats.Stops); return NTLSTATUS_SUCCESS; } @@ -1979,7 +1981,7 @@ KSTATUS KeAudioResetStream(KE_AUDIO_STREAM* stream) { KiAudioRefreshStreamEventLocked(stream); KiAudioReleaseMutex(stream->Lock); KeSignalEvent(stream->Endpoint->WorkEvent); - _InterlockedIncrement64(&gAudioCoreStats.Resets); + NtlAtomicIncrement64(&gAudioCoreStats.Resets); return NTLSTATUS_SUCCESS; } @@ -2027,7 +2029,9 @@ KSTATUS KeAudioSetMasterVolume(uint32_t endpointId, float volume, bool muted) { if (!endpoint) return NTLSTATUS_NOT_FOUND; + DbgPrint("audiodiag: master endpoint=%u waiting control lock\n", endpointId); KiAudioAcquireMutex(endpoint->ControlLock, "KeAudioSetMasterVolume"); + DbgPrint("audiodiag: master endpoint=%u acquired control lock\n", endpointId); float softwareGain = muted ? 0.0f : KiAudioSoftwareGainForVolume(endpoint, volume); uintptr_t registryFlags = KeAcquireSpinlock(&gAudioRegistryLock); @@ -2058,6 +2062,7 @@ KSTATUS KeAudioSetMasterVolume(uint32_t endpointId, float volume, bool muted) { KeReleaseSpinlock(&gAudioRegistryLock, registryFlags); KiAudioReleaseMutex(endpoint->ControlLock); + DbgPrint("audiodiag: master endpoint=%u update committed\n", endpointId); KeSignalEvent(endpoint->WorkEvent); ObDereferenceObject(&endpoint->Header); return NTLSTATUS_SUCCESS; diff --git a/code/kernel/ke_boot_config.c b/code/kernel/ke_boot_config.c index 9c9d29b..5055a65 100644 --- a/code/kernel/ke_boot_config.c +++ b/code/kernel/ke_boot_config.c @@ -29,8 +29,16 @@ static bool KiBootConfigSpanEquals(const char* begin, const char* end, const cha return begin == end && *text == '\0'; } -// Parses one unsigned decimal or 0x-prefixed hexadecimal value without accepting trailing junk. +// Parses an unsigned numeric configuration value with an optional inline comment. static KSTATUS KiBootConfigParseUInt32(const char* begin, const char* end, uint32_t* valueOut) { + KiBootConfigTrim(&begin, &end); + // Numeric debug values support inline comments without changing command-line text semantics. + for (const char* p = begin; p < end; ++p) { + if (*p == ';' || *p == '#') { + end = p; + break; + } + } KiBootConfigTrim(&begin, &end); if (begin == end || !valueOut) return NTLSTATUS_INVALID_PARAMETER; diff --git a/code/kernel/ke_cpu.c b/code/kernel/ke_cpu.c index 032078d..956667a 100644 --- a/code/kernel/ke_cpu.c +++ b/code/kernel/ke_cpu.c @@ -1,5 +1,7 @@ // CPU-local kernel state. This file owns bootstrap and per-CPU bookkeeping shared by scheduler and SMP code. +#include +#include #include #include #include @@ -44,8 +46,8 @@ KCPU gCpus[KE_MAX_CPUS]; uint32_t gCpuCount = 0; static uint8_t gBootstrapCpuStack[BOOTSTRAP_CPU_STACK_SIZE]; -static volatile long gKernelGsReady = 0; -static volatile long gWriteCombiningAvailable = 0; +static volatile int32_t gKernelGsReady = 0; +static volatile int32_t gWriteCombiningAvailable = 0; extern KCPU* KeReadCurrentCpuFromGs(void); @@ -103,7 +105,7 @@ void KeInitializeMemoryTypes(void) { uint32_t variableCount = (uint32_t)(__readmsr(IA32_MTRRCAP) & MTRR_CAP_VARIABLE_COUNT); if (KiMemoryTypePolicyInstalled(variableCount)) { - _InterlockedExchange(&gWriteCombiningAvailable, 1); + NtlAtomicExchange32(&gWriteCombiningAvailable, 1); return; } @@ -139,12 +141,12 @@ void KeInitializeMemoryTypes(void) { if (!installed) KeStopProcessor(); - _InterlockedExchange(&gWriteCombiningAvailable, 1); + NtlAtomicExchange32(&gWriteCombiningAvailable, 1); } // Reports whether PAT write-combining is available on the current platform. bool KeWriteCombiningAvailable(void) { - return _InterlockedCompareExchange(&gWriteCombiningAvailable, 0, 0) != 0; + return NtlAtomicLoad32(&gWriteCombiningAvailable) != 0; } // Loads kernel Gs Base. diff --git a/code/kernel/ke_debug.c b/code/kernel/ke_debug.c index 948a722..cc6ed55 100644 --- a/code/kernel/ke_debug.c +++ b/code/kernel/ke_debug.c @@ -1,5 +1,6 @@ // Kernel debugging and bugcheck support. This file owns panic reporting, process fault reporting, and diagnostic queries. +#include #include #include #include @@ -36,7 +37,7 @@ static char* gDebugLogBuffer; static size_t gDebugLogSize; static size_t gDebugLogWrite; static size_t gDebugLogUsed; -static volatile long gDebugLogFlushing; +static volatile int32_t gDebugLogFlushing; // Appends bytes to the in-memory debug log while its lock is held. static void KiDebugLogAppendLocked(const char* text, size_t len) { @@ -97,13 +98,13 @@ static void KiOutputVPrintf(uint32_t stream, uint32_t flags, KiOutputWrite(stream, flags, foreground, background, line, length); } -static volatile long gPanicInProgress; +static volatile int32_t gPanicInProgress; static volatile uintptr_t gPanicOwnerToken; -static volatile long gPanicStoppedCpuCount; +static volatile int32_t gPanicStoppedCpuCount; // Permanently stops a secondary processor that entered an active panic. static _Noreturn void KiPanicSecondaryHalt(void) { - _InterlockedIncrement(&gPanicStoppedCpuCount); + NtlAtomicIncrement32(&gPanicStoppedCpuCount); KeStopProcessor(); } @@ -119,12 +120,12 @@ static uintptr_t KiPanicCurrentToken(void) { } // Returns whether a fatal report is active. -static int KiPanicIsActive(void) { - return _InterlockedCompareExchange((volatile long*)&gPanicInProgress, 0, 0) != 0; +static bool KiPanicIsActive(void) { + return NtlAtomicLoad32(&gPanicInProgress) != 0; } // Returns whether the current processor owns the active fatal report. -static int KiPanicCurrentCpuOwnsReport(void) { +static bool KiPanicCurrentCpuOwnsReport(void) { uintptr_t token = KiPanicCurrentToken(); return token != 0 && gPanicOwnerToken == token; } @@ -133,7 +134,7 @@ static int KiPanicCurrentCpuOwnsReport(void) { static void KiBeginFatalReport(void) { (void)KeDisableInterrupts(); uintptr_t token = KiPanicCurrentToken(); - if (_InterlockedCompareExchange((volatile long*)&gPanicInProgress, 1, 0) == 0) { + if (NtlAtomicCompareExchange32(&gPanicInProgress, 1, 0) == 0) { gPanicOwnerToken = token; return; } @@ -142,14 +143,14 @@ static void KiBeginFatalReport(void) { } // Handles NMI-based panic stops and TLB shootdowns. -__declspec(dllexport) int KeHandleNmiIpi(void) { +__declspec(dllexport) bool KeHandleNmiIpi(void) { if (KiPanicIsActive()) KiPanicSecondaryHalt(); if (MmPteHandleShootdownNmi()) - return 1; + return true; - return 0; + return false; } // Initializes debug logging and initial output subscriptions. @@ -208,7 +209,7 @@ __declspec(dllexport) void DbgPrint(const char* fmt, ...) { if (!panic) { uintptr_t lockFlags = KeAcquireSpinlock(&gDebugPrintLock); - if (!_InterlockedCompareExchange((volatile long*)&gDebugLogFlushing, 0, 0)) + if (!NtlAtomicLoad32(&gDebugLogFlushing)) KiDebugLogAppendLocked(line, length); KeReleaseSpinlock(&gDebugPrintLock, lockFlags); } @@ -249,7 +250,7 @@ KSTATUS KeFlushDebugLogToSystemRoot(const char* relativePath) { if (!KeCanWait()) return NTLSTATUS_INVALID_IRQL; - if (_InterlockedCompareExchange((volatile long*)&gDebugLogFlushing, 1, 0) != 0) + if (NtlAtomicCompareExchange32(&gDebugLogFlushing, 1, 0) != 0) return NTLSTATUS_PENDING; size_t used; @@ -264,7 +265,7 @@ KSTATUS KeFlushDebugLogToSystemRoot(const char* relativePath) { FILE_OBJECT* file = (FILE_OBJECT*)0; KSTATUS st = KeCreateSystemRootFile(relativePath, &file); if (!KSUCCESS(st)) { - _InterlockedExchange((volatile long*)&gDebugLogFlushing, 0); + NtlAtomicExchange32(&gDebugLogFlushing, 0); return st; } @@ -292,7 +293,7 @@ KSTATUS KeFlushDebugLogToSystemRoot(const char* relativePath) { st = IoFlushFile(file); IoCloseFile(file); - _InterlockedExchange((volatile long*)&gDebugLogFlushing, 0); + NtlAtomicExchange32(&gDebugLogFlushing, 0); return st; } @@ -318,7 +319,7 @@ static void KiPanicPrintf(const char* format, ...) { } // Stops the system after reporting a fatal kernel error. -__declspec(dllexport) _Noreturn void KePanic(int code, const char* fmt, ...) { +__declspec(dllexport) _Noreturn void KePanic(int32_t code, const char* fmt, ...) { KiBeginFatalReport(); // Stop every other CPU before writing the final report. Panic-safe endpoints @@ -443,9 +444,9 @@ static void KiPrintAddressValue(PROCESS* proc, uintptr_t addr) { } // Checks whether a kernel virtual range is mapped before diagnostic reads. -static int KiKernelRangeReadable(uintptr_t addr, size_t size) { - if (!addr || size == 0) return 0; - if (size - 1u > (size_t)(UINTPTR_MAX - addr)) return 0; +static bool KiKernelRangeReadable(uintptr_t addr, size_t size) { + if (!addr || size == 0) return false; + if (size - 1u > (size_t)(UINTPTR_MAX - addr)) return false; uintptr_t start = addr & PAGE_MASK; uintptr_t last = (addr + size - 1u) & PAGE_MASK; @@ -454,41 +455,41 @@ static int KiKernelRangeReadable(uintptr_t addr, size_t size) { PHYSICAL_ADDRESS phys = 0; uint64_t flags = 0; if (!KSUCCESS(MmPteQueryPageFor(MmPteQueryDebug, page, &phys, &flags))) - return 0; + return false; if ((flags & PAGE_PRESENT) == 0) - return 0; + return false; if (page == last) break; if (page > UINTPTR_MAX - PAGE_SIZE) - return 0; + return false; } - return 1; + return true; } // Reads diagnostic memory from either user space or a mapped kernel range. -static int KiReadDiagnosticMemory(uintptr_t addr, void* out, size_t size) { - if (!out || !addr || size == 0) return 0; +static bool KiReadDiagnosticMemory(uintptr_t addr, void* out, size_t size) { + if (!out || !addr || size == 0) return false; if (addr >= KERNEL_BASE) { if (!KiKernelRangeReadable(addr, size)) - return 0; + return false; memcpy(out, (const void*)addr, size); - return 1; + return true; } if (!KSUCCESS(MmProbeUserRead((const void*)addr, size))) - return 0; + return false; memcpy(out, (const void*)addr, size); - return 1; + return true; } // Reads one diagnostic uint64_t. -static int KiUserReadU64(uintptr_t addr, uint64_t* out) { +static bool KiUserReadU64(uintptr_t addr, uint64_t* out) { return KiReadDiagnosticMemory(addr, out, sizeof(*out)); } // Reads one diagnostic byte. -static int KiUserReadU8(uintptr_t addr, uint8_t* out) { +static bool KiUserReadU8(uintptr_t addr, uint8_t* out) { return KiReadDiagnosticMemory(addr, out, sizeof(*out)); } @@ -518,7 +519,7 @@ static void KiDumpBytes(PROCESS* proc, const char* label, uintptr_t center) { } // Reads one diagnostic int32_t. -static int KiUserReadI32(uintptr_t addr, int32_t* out) { +static bool KiUserReadI32(uintptr_t addr, int32_t* out) { return KiReadDiagnosticMemory(addr, out, sizeof(*out)); } @@ -585,11 +586,11 @@ static void KiDumpModules(PROCESS* proc) { #if 0 // Returns whether a range is inside the current kernel stack. -static int KiKernelStackContains(const THREAD* thread, uintptr_t addr, size_t size) { +static bool KiKernelStackContains(const THREAD* thread, uintptr_t addr, size_t size) { if (!thread || !thread->KernelStackBase || !thread->KernelStackTop || !addr || size == 0) - return 0; + return false; if (size - 1u > (size_t)(UINTPTR_MAX - addr)) - return 0; + return false; uintptr_t base = (uintptr_t)thread->KernelStackBase; uintptr_t top = thread->KernelStackTop; @@ -598,9 +599,9 @@ static int KiKernelStackContains(const THREAD* thread, uintptr_t addr, size_t si } // Returns whether the fault frame is using the current kernel stack. -static int KiFaultFrameOnKernelStack(const THREAD* thread, const KFAULT_FRAME* f) { +static bool KiFaultFrameOnKernelStack(const THREAD* thread, const KFAULT_FRAME* f) { if (!f || !thread) - return 0; + return false; return KiKernelStackContains(thread, (uintptr_t)f->Rsp, sizeof(uint64_t)); } @@ -896,7 +897,7 @@ __declspec(dllexport) KSTATUS KeFaultDispatchFrame(KFAULT_FRAME* frame) { // The asm stubs collect the CPU exception frame values and call here. // KeFaultDispatchFrame owns the mode split and default unhandled policy. -__declspec(dllexport) _Noreturn void KeFaultDispatch(int vector, uintptr_t rip, +__declspec(dllexport) _Noreturn void KeFaultDispatch(uint32_t vector, uintptr_t rip, uintptr_t errCode, uintptr_t cr2, uintptr_t cs) { KFAULT_FRAME frame; diff --git a/code/kernel/ke_display.c b/code/kernel/ke_display.c index 5634868..9596fe1 100644 --- a/code/kernel/ke_display.c +++ b/code/kernel/ke_display.c @@ -1,5 +1,6 @@ // Kernel display manager. This file owns provider registration, framebuffer routing, dirty rectangles, text console state, and cursor delegation. +#include #include #include #include @@ -72,12 +73,12 @@ static SPINLOCK gDisplayLock = SPINLOCK_INIT; static MUTEX* gDisplayProviderMutex; static MUTEX* gDisplayOutputMutex; static KWORK_ITEM gDisplayCursorWorkItem; -static volatile long gDisplayCursorWorkInitialized; +static volatile int32_t gDisplayCursorWorkInitialized; static bool gDisplayTransitionInProgress; static uint64_t gDisplayGeneration; static LIST_ENTRY gDisplayClockList; static SPINLOCK gDisplayClockLock = SPINLOCK_INIT; -static volatile long gDisplayClockInitialized; +static volatile int32_t gDisplayClockInitialized; static uint64_t gDisplayFrameGeneration; static DISPLAY_DEVICE gBootFbDevice; @@ -106,27 +107,27 @@ static int gConsoleY; static uint16_t gConsoleAttribute; static char gConsoleCells[DISPLAY_CONSOLE_MAX_CELLS]; static uint8_t gConsoleAttrs[DISPLAY_CONSOLE_MAX_CELLS]; -static volatile long gDisplayConsoleSuspended; +static volatile int32_t gDisplayConsoleSuspended; static SPINLOCK gDisplayOutputLock = SPINLOCK_INIT; static DISPLAY_OUTPUT_GLYPH* gDisplayOutputBuffer; static size_t gDisplayOutputRead; static size_t gDisplayOutputWrite; static size_t gDisplayOutputUsed; static KWORK_ITEM gDisplayOutputWorkItem; -static volatile long gDisplayOutputWorkInitialized; +static volatile int32_t gDisplayOutputWorkInitialized; static KSTATUS KiDisplayPrintCharInternal(char c, uint32_t fg, uint32_t bg); static KSTATUS KiDisplayPrintGlyphsInternal(const DISPLAY_OUTPUT_GLYPH* glyphs, size_t count); static KSTATUS BootFbGetMode(DISPLAY_DEVICE* device, DISPLAY_MODE* outMode); -static KSTATUS BootFbBlit(DISPLAY_DEVICE* device, int x, int y, int w, int h, const uint32_t* pixels, uint32_t pitchBytes); -static KSTATUS BootFbPresentPitched(DISPLAY_DEVICE* device, int x, int y, int w, int h, const uint32_t* pixels, uint32_t pitchBytes); -static KSTATUS BootFbFillRect(DISPLAY_DEVICE* device, int x, int y, int w, int h, uint32_t color); -static KSTATUS BootFbCopyRect(DISPLAY_DEVICE* device, int dstX, int dstY, int srcX, int srcY, int w, int h); -static KSTATUS BootFbReadRect(DISPLAY_DEVICE* device, int x, int y, int w, int h, uint32_t* outPixels, uint32_t outStride); +static KSTATUS BootFbBlit(DISPLAY_DEVICE* device, int32_t x, int32_t y, int32_t w, int32_t h, const uint32_t* pixels, uint32_t pitchBytes); +static KSTATUS BootFbPresentPitched(DISPLAY_DEVICE* device, int32_t x, int32_t y, int32_t w, int32_t h, const uint32_t* pixels, uint32_t pitchBytes); +static KSTATUS BootFbFillRect(DISPLAY_DEVICE* device, int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color); +static KSTATUS BootFbCopyRect(DISPLAY_DEVICE* device, int32_t dstX, int32_t dstY, int32_t srcX, int32_t srcY, int32_t w, int32_t h); +static KSTATUS BootFbReadRect(DISPLAY_DEVICE* device, int32_t x, int32_t y, int32_t w, int32_t h, uint32_t* outPixels, uint32_t outStride); static KSTATUS BootFbPresent(DISPLAY_DEVICE* device, const DISPLAY_RECT* rects, size_t rectCount); static KSTATUS BootFbSync(DISPLAY_DEVICE* device); -static KSTATUS BootFbSetCursorImage(DISPLAY_DEVICE* device, int width, int height, int hotX, int hotY, const uint32_t* argbPixels); -static KSTATUS BootFbSetCursorPosition(DISPLAY_DEVICE* device, int x, int y); +static KSTATUS BootFbSetCursorImage(DISPLAY_DEVICE* device, int32_t width, int32_t height, int32_t hotX, int32_t hotY, const uint32_t* argbPixels); +static KSTATUS BootFbSetCursorPosition(DISPLAY_DEVICE* device, int32_t x, int32_t y); static KSTATUS BootFbShowCursor(DISPLAY_DEVICE* device, bool show); static KSTATUS BootFbQueryStats(DISPLAY_DEVICE* device, DISPLAY_STATS* outStats); @@ -235,7 +236,7 @@ static KSTATUS KiDisplayFallbackPrintChar(char c, uint32_t fg, uint32_t bg) { } static void KiDisplayQueueCursorWork(void) { - if (!_InterlockedCompareExchange(&gDisplayCursorWorkInitialized, 1, 1)) + if (!NtlAtomicCompareExchange32(&gDisplayCursorWorkInitialized, 1, 1)) return; if (KeIsWorkQueueStarted()) { (void)KeQueueWorkItem(&gDisplayCursorWorkItem); @@ -285,8 +286,8 @@ static void KiDisplayCursorWorker(void* context) { DISPLAY_DEVICE* device = (DISPLAY_DEVICE*)0; const DISPLAY_DEVICE_OPS* ops = (const DISPLAY_DEVICE_OPS*)0; uint32_t caps = 0; - int x = 0; - int y = 0; + int32_t x = 0; + int32_t y = 0; bool visible = false; bool drawn = false; bool suspended = false; @@ -497,7 +498,7 @@ static DISPLAY_RECT KiDisplayUnionDirtyRects(const DISPLAY_RECT* a, const DISPLA return result; } -static void KiDisplayAddDirtyForModeLocked(const DISPLAY_MODE* mode, int x, int y, int w, int h) { +static void KiDisplayAddDirtyForModeLocked(const DISPLAY_MODE* mode, int32_t x, int32_t y, int32_t w, int32_t h) { if (!mode || gDirtyAll || w <= 0 || h <= 0) return; @@ -533,13 +534,13 @@ static void KiDisplayAddDirtyForModeLocked(const DISPLAY_MODE* mode, int x, int gDirtyRects[gDirtyRectCount++] = rect; } -static void KiDisplayRecordBlitLocked(const DISPLAY_MODE* mode, int x, int y, int w, int h) { +static void KiDisplayRecordBlitLocked(const DISPLAY_MODE* mode, int32_t x, int32_t y, int32_t w, int32_t h) { gDisplayStats.BlitCount++; gDisplayStats.BytesCopied += (uint64_t)(uint32_t)w * (uint64_t)(uint32_t)h * sizeof(uint32_t); KiDisplayAddDirtyForModeLocked(mode, x, y, w, h); } -static void KiDisplayRecordDirectPresentLocked(const DISPLAY_MODE* mode, int x, int y, int w, int h) { +static void KiDisplayRecordDirectPresentLocked(const DISPLAY_MODE* mode, int32_t x, int32_t y, int32_t w, int32_t h) { if (!mode || w <= 0 || h <= 0) return; @@ -557,13 +558,13 @@ static void KiDisplayRecordDirectPresentLocked(const DISPLAY_MODE* mode, int x, gDisplayStats.BytesCopied += bytes; } -static void KiDisplayRecordFillLocked(const DISPLAY_MODE* mode, int x, int y, int w, int h) { +static void KiDisplayRecordFillLocked(const DISPLAY_MODE* mode, int32_t x, int32_t y, int32_t w, int32_t h) { gDisplayStats.FillRectCount++; gDisplayStats.BytesCopied += (uint64_t)(uint32_t)w * (uint64_t)(uint32_t)h * sizeof(uint32_t); KiDisplayAddDirtyForModeLocked(mode, x, y, w, h); } -static void KiDisplayRecordCopyLocked(const DISPLAY_MODE* mode, int dstX, int dstY, int w, int h) { +static void KiDisplayRecordCopyLocked(const DISPLAY_MODE* mode, int32_t dstX, int32_t dstY, int32_t w, int32_t h) { gDisplayStats.CopyRectCount++; gDisplayStats.BytesCopied += (uint64_t)(uint32_t)w * (uint64_t)(uint32_t)h * sizeof(uint32_t); KiDisplayAddDirtyForModeLocked(mode, dstX, dstY, w, h); @@ -656,8 +657,8 @@ static void KiDisplayRestoreCursorAfterDrawSnapshot(DISPLAY_DEVICE* device, if (!device || !ops || !ops->ShowCursor || !(caps & DISPLAY_CAP_SOFTWARE_CURSOR)) return; - int x = 0; - int y = 0; + int32_t x = 0; + int32_t y = 0; bool shouldShow = false; uintptr_t flags = KeAcquireSpinlock(&gDisplayLock); shouldShow = (device == gPrimaryDisplay) && @@ -719,9 +720,9 @@ static KSTATUS KiDisplayGetProviderSnapshot(DISPLAY_DEVICE** outDevice, static void KiDisplayBuildGlyphPixels(char c, uint32_t fg, uint32_t bg, uint32_t* outPixels) { const uint8_t* glyph = Font8x16 + ((uint8_t)c * 16u); - for (int row = 0; row < FONT_H; row++) { + for (int32_t row = 0; row < FONT_H; row++) { uint8_t bits = glyph[row]; - for (int col = 0; col < FONT_W; col++) + for (int32_t col = 0; col < FONT_W; col++) outPixels[row * FONT_W + col] = (bits & (0x80u >> col)) ? fg : bg; } } @@ -730,8 +731,8 @@ static KSTATUS KiDisplayBlitGlyphSnapshot(DISPLAY_DEVICE* device, const DISPLAY_DEVICE_OPS* ops, const DISPLAY_MODE* mode, char c, - int x, - int y, + int32_t x, + int32_t y, uint32_t fg, uint32_t bg) { if (!device || !ops || !ops->Blit || !mode) @@ -753,10 +754,10 @@ static KSTATUS KiDisplayBlitGlyphSnapshot(DISPLAY_DEVICE* device, static KSTATUS KiDisplayDrawBlitPitchedSnapshot(DISPLAY_DEVICE* device, const DISPLAY_DEVICE_OPS* ops, const DISPLAY_MODE* mode, - int x, - int y, - int w, - int h, + int32_t x, + int32_t y, + int32_t w, + int32_t h, const uint32_t* pixels, uint32_t pitchBytes) { if (!pixels || w <= 0 || h <= 0 || pitchBytes < (uint32_t)w * sizeof(uint32_t)) @@ -781,10 +782,10 @@ static KSTATUS KiDisplayDrawBlitPitchedSnapshot(DISPLAY_DEVICE* device, static KSTATUS KiDisplayFillRectSnapshot(DISPLAY_DEVICE* device, const DISPLAY_DEVICE_OPS* ops, const DISPLAY_MODE* mode, - int x, - int y, - int w, - int h, + int32_t x, + int32_t y, + int32_t w, + int32_t h, uint32_t color) { if (w <= 0 || h <= 0) return NTLSTATUS_INVALID_PARAMETER; @@ -804,12 +805,12 @@ static KSTATUS KiDisplayFillRectSnapshot(DISPLAY_DEVICE* device, static KSTATUS KiDisplayCopyRectSnapshot(DISPLAY_DEVICE* device, const DISPLAY_DEVICE_OPS* ops, const DISPLAY_MODE* mode, - int dstX, - int dstY, - int srcX, - int srcY, - int w, - int h) { + int32_t dstX, + int32_t dstY, + int32_t srcX, + int32_t srcY, + int32_t w, + int32_t h) { if (w <= 0 || h <= 0) return NTLSTATUS_INVALID_PARAMETER; if (!device || !ops || !ops->CopyRect || !mode) @@ -844,7 +845,7 @@ static void KiDisplayDirtyAll(void) { } // Implements the display Add Dirty operation. -static void KiDisplayAddDirty(int x, int y, int w, int h) { +static void KiDisplayAddDirty(int32_t x, int32_t y, int32_t w, int32_t h) { if (!KiDisplayHasDevice() || gDirtyAll || w <= 0 || h <= 0) return; @@ -946,7 +947,7 @@ static KSTATUS KiDisplayEndDraw(void) { } // Implements the display Draw Blit No Cursor operation. -static KSTATUS KiDisplayDrawBlitNoCursor(int x, int y, int w, int h, const uint32_t* pixels) { +static KSTATUS KiDisplayDrawBlitNoCursor(int32_t x, int32_t y, int32_t w, int32_t h, const uint32_t* pixels) { if (!pixels || w <= 0 || h <= 0) return NTLSTATUS_INVALID_PARAMETER; if (!KiDisplayHasDevice() || !gPrimaryDisplay->Ops->Blit) @@ -962,7 +963,7 @@ static KSTATUS KiDisplayDrawBlitNoCursor(int x, int y, int w, int h, const uint3 } // Implements the display Fill Rect No Cursor operation. -static KSTATUS KiDisplayFillRectNoCursor(int x, int y, int w, int h, uint32_t color) { +static KSTATUS KiDisplayFillRectNoCursor(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color) { if (w <= 0 || h <= 0) return NTLSTATUS_INVALID_PARAMETER; if (!KiDisplayHasDevice() || !gPrimaryDisplay->Ops->FillRect) @@ -978,7 +979,7 @@ static KSTATUS KiDisplayFillRectNoCursor(int x, int y, int w, int h, uint32_t co } // Implements the display Copy Rect No Cursor operation. -static KSTATUS KiDisplayCopyRectNoCursor(int dstX, int dstY, int srcX, int srcY, int w, int h) { +static KSTATUS KiDisplayCopyRectNoCursor(int32_t dstX, int32_t dstY, int32_t srcX, int32_t srcY, int32_t w, int32_t h) { if (w <= 0 || h <= 0) return NTLSTATUS_INVALID_PARAMETER; if (!KiDisplayHasDevice() || !gPrimaryDisplay->Ops->CopyRect) @@ -994,7 +995,7 @@ static KSTATUS KiDisplayCopyRectNoCursor(int dstX, int dstY, int srcX, int srcY, } // Implements the display Console Index operation. -static size_t KiDisplayConsoleIndex(int col, int row) { +static size_t KiDisplayConsoleIndex(int32_t col, int32_t row) { return (size_t)row * DISPLAY_CONSOLE_MAX_COLS + (size_t)col; } @@ -1054,8 +1055,8 @@ static void KiDisplayConsoleScrollCellsLocked(int cols, int rows, int cellLines) return; if (cellLines >= rows) { - for (int y = 0; y < rows; y++) { - for (int x = 0; x < cols; x++) { + for (int32_t y = 0; y < rows; y++) { + for (int32_t x = 0; x < cols; x++) { size_t i = KiDisplayConsoleIndex(x, y); gConsoleCells[i] = ' '; gConsoleAttrs[i] = (uint8_t)gConsoleAttribute; @@ -1064,8 +1065,8 @@ static void KiDisplayConsoleScrollCellsLocked(int cols, int rows, int cellLines) return; } - for (int y = 0; y < rows - cellLines; y++) { - for (int x = 0; x < cols; x++) { + for (int32_t y = 0; y < rows - cellLines; y++) { + for (int32_t x = 0; x < cols; x++) { size_t dst = KiDisplayConsoleIndex(x, y); size_t src = KiDisplayConsoleIndex(x, y + cellLines); gConsoleCells[dst] = gConsoleCells[src]; @@ -1073,8 +1074,8 @@ static void KiDisplayConsoleScrollCellsLocked(int cols, int rows, int cellLines) } } - for (int y = rows - cellLines; y < rows; y++) { - for (int x = 0; x < cols; x++) { + for (int32_t y = rows - cellLines; y < rows; y++) { + for (int32_t x = 0; x < cols; x++) { size_t i = KiDisplayConsoleIndex(x, y); gConsoleCells[i] = ' '; gConsoleAttrs[i] = (uint8_t)gConsoleAttribute; @@ -1083,13 +1084,13 @@ static void KiDisplayConsoleScrollCellsLocked(int cols, int rows, int cellLines) } // Implements the display Draw Char Pixels Locked operation. -static KSTATUS KiDisplayDrawCharPixelsLocked(char c, int x, int y, uint32_t fg, uint32_t bg) { +static KSTATUS KiDisplayDrawCharPixelsLocked(char c, int32_t x, int32_t y, uint32_t fg, uint32_t bg) { uint32_t glyphPixels[FONT_W * FONT_H]; const uint8_t* glyph = Font8x16 + ((uint8_t)c * 16u); - for (int row = 0; row < FONT_H; row++) { + for (int32_t row = 0; row < FONT_H; row++) { uint8_t bits = glyph[row]; - for (int col = 0; col < FONT_W; col++) + for (int32_t col = 0; col < FONT_W; col++) glyphPixels[row * FONT_W + col] = (bits & (0x80u >> col)) ? fg : bg; } @@ -1097,7 +1098,7 @@ static KSTATUS KiDisplayDrawCharPixelsLocked(char c, int x, int y, uint32_t fg, } // Implements the display Draw Console Cell Locked operation. -static KSTATUS KiDisplayDrawConsoleCellLocked(int col, int row, char c, uint16_t attr, bool updateCell) { +static KSTATUS KiDisplayDrawConsoleCellLocked(int32_t col, int32_t row, char c, uint16_t attr, bool updateCell) { if (updateCell) { size_t i = KiDisplayConsoleIndex(col, row); gConsoleCells[i] = c; @@ -1177,7 +1178,7 @@ KSTATUS KeDisplayFlushConsoleOutput(void) { // Queues the runtime display output worker when passive work is available. static void KiDisplayQueueOutputWorker(void) { - if (_InterlockedCompareExchange(&gDisplayOutputWorkInitialized, 1, 1) && + if (NtlAtomicCompareExchange32(&gDisplayOutputWorkInitialized, 1, 1) && KeIsWorkQueueStarted()) { (void)KeQueueWorkItem(&gDisplayOutputWorkItem); } @@ -1271,15 +1272,15 @@ void KeDisplayInitialize(const HAL_API* hal) { gDisplayClockLock = (SPINLOCK)SPINLOCK_INIT; LIST_INIT(&gDisplayClockList); gDisplayFrameGeneration = 0; - _InterlockedExchange(&gDisplayClockInitialized, 1); + NtlAtomicExchange32(&gDisplayClockInitialized, 1); if (!gDisplayProviderMutex) (void)KeMutexCreate(&gDisplayProviderMutex); if (!gDisplayOutputMutex) (void)KeMutexCreate(&gDisplayOutputMutex); KeInitializeWorkItem(&gDisplayCursorWorkItem, KiDisplayCursorWorker, 0); - _InterlockedExchange(&gDisplayCursorWorkInitialized, 1); + NtlAtomicExchange32(&gDisplayCursorWorkInitialized, 1); KeInitializeWorkItem(&gDisplayOutputWorkItem, KiDisplayOutputWorker, 0); - _InterlockedExchange(&gDisplayOutputWorkInitialized, 1); + NtlAtomicExchange32(&gDisplayOutputWorkInitialized, 1); gDisplayOutputRead = 0; gDisplayOutputWrite = 0; gDisplayOutputUsed = 0; @@ -1291,7 +1292,7 @@ void KeDisplayInitialize(const HAL_API* hal) { gConsoleX = 0; gConsoleY = 0; KiDisplayConsoleResetCellsLocked(); - _InterlockedExchange((volatile long*)&gDisplayConsoleSuspended, 0); + NtlAtomicExchange32(&gDisplayConsoleSuspended, 0); memset(&gBootFb, 0, sizeof(gBootFb)); memset(&gBootFbDevice, 0, sizeof(gBootFbDevice)); @@ -1353,7 +1354,7 @@ DISPLAY_API KSTATUS KeDisplayOpenClock(uint64_t periodMilliseconds, DISPLAY_CLOC return NTLSTATUS_INVALID_PARAMETER; *outClock = (DISPLAY_CLOCK_OBJECT*)0; - if (!_InterlockedCompareExchange(&gDisplayClockInitialized, 1, 1)) + if (!NtlAtomicCompareExchange32(&gDisplayClockInitialized, 1, 1)) return NTLSTATUS_NOT_SUPPORTED; uintptr_t displayFlags = KeAcquireSpinlock(&gDisplayLock); @@ -1429,7 +1430,7 @@ static void KiDisplaySignalFrameGeneration(uint64_t generation) { // Publishes a frame-ready event from the current primary display provider. DISPLAY_API void KeDisplayNotifyFrame(DISPLAY_DEVICE* device) { - if (!device || !_InterlockedCompareExchange(&gDisplayClockInitialized, 1, 1)) + if (!device || !NtlAtomicCompareExchange32(&gDisplayClockInitialized, 1, 1)) return; uintptr_t displayFlags = KeAcquireSpinlock(&gDisplayLock); @@ -1449,7 +1450,7 @@ DISPLAY_API void KeDisplayNotifyFrame(DISPLAY_DEVICE* device) { } DISPLAY_API void KeDisplayTick(void) { - if (!_InterlockedCompareExchange(&gDisplayClockInitialized, 1, 1)) + if (!NtlAtomicCompareExchange32(&gDisplayClockInitialized, 1, 1)) return; uintptr_t displayFlags = KeAcquireSpinlock(&gDisplayLock); @@ -1555,12 +1556,12 @@ static KSTATUS KiDisplaySeedNewPrimary(DISPLAY_DEVICE* oldDevice, DISPLAY_DEVICE } DISPLAY_API void KeDisplaySuspendConsole(bool suspend) { - _InterlockedExchange((volatile long*)&gDisplayConsoleSuspended, suspend ? 1 : 0); + NtlAtomicExchange32(&gDisplayConsoleSuspended, suspend ? 1 : 0); } // Implements the display Output Suspended operation. static bool KiDisplayOutputSuspended(void) { - return _InterlockedCompareExchange((volatile long*)&gDisplayConsoleSuspended, 0, 0) != 0; + return NtlAtomicLoad32(&gDisplayConsoleSuspended) != 0; } DISPLAY_API KSTATUS KeDisplaySetPrimary(DISPLAY_DEVICE* device) { @@ -1676,7 +1677,7 @@ DISPLAY_API KSTATUS KeDisplayGetMode(DISPLAY_MODE* outMode) { return s; } -DISPLAY_API KSTATUS KeDisplayGetSize(int* outWidth, int* outHeight) { +DISPLAY_API KSTATUS KeDisplayGetSize(int32_t* outWidth, int32_t* outHeight) { DISPLAY_MODE mode; KSTATUS s = KeDisplayGetMode(&mode); if (!KSUCCESS(s)) @@ -1770,7 +1771,7 @@ DISPLAY_API KSTATUS KeDisplayQueryInfo(KE_DISPLAY_INFO* outInfo) { return NTLSTATUS_SUCCESS; } -DISPLAY_API KSTATUS KeDisplayBlit(int x, int y, int w, int h, const uint32_t* pixels) { +DISPLAY_API KSTATUS KeDisplayBlit(int32_t x, int32_t y, int32_t w, int32_t h, const uint32_t* pixels) { bool providerHeld = false; KSTATUS s = KiDisplayAcquireProviderMutex(&providerHeld); if (!KSUCCESS(s)) @@ -1792,7 +1793,7 @@ DISPLAY_API KSTATUS KeDisplayBlit(int x, int y, int w, int h, const uint32_t* pi return s; } -DISPLAY_API KSTATUS KeDisplayBlitPitched(int x, int y, int w, int h, const uint32_t* pixels, uint32_t pitchBytes) { +DISPLAY_API KSTATUS KeDisplayBlitPitched(int32_t x, int32_t y, int32_t w, int32_t h, const uint32_t* pixels, uint32_t pitchBytes) { if (!pixels || w <= 0 || h <= 0 || pitchBytes < (uint32_t)w * sizeof(uint32_t)) return NTLSTATUS_INVALID_PARAMETER; @@ -1816,10 +1817,10 @@ DISPLAY_API KSTATUS KeDisplayBlitPitched(int x, int y, int w, int h, const uint3 return s; } -DISPLAY_API KSTATUS KeDisplayPresentPitched(int x, - int y, - int w, - int h, +DISPLAY_API KSTATUS KeDisplayPresentPitched(int32_t x, + int32_t y, + int32_t w, + int32_t h, const uint32_t* pixels, uint32_t pitchBytes) { if (!pixels || w <= 0 || h <= 0 || pitchBytes < (uint32_t)w * sizeof(uint32_t)) @@ -1857,7 +1858,7 @@ DISPLAY_API KSTATUS KeDisplayPresentPitched(int x, return s; } -DISPLAY_API KSTATUS KeDisplayFillRect(int x, int y, int w, int h, uint32_t color) { +DISPLAY_API KSTATUS KeDisplayFillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color) { bool providerHeld = false; KSTATUS s = KiDisplayAcquireProviderMutex(&providerHeld); if (!KSUCCESS(s)) @@ -1908,7 +1909,7 @@ DISPLAY_API KSTATUS KeDisplayClear(uint32_t color) { return s; } -DISPLAY_API KSTATUS KeDisplayCopyRect(int dstX, int dstY, int srcX, int srcY, int w, int h) { +DISPLAY_API KSTATUS KeDisplayCopyRect(int32_t dstX, int32_t dstY, int32_t srcX, int32_t srcY, int32_t w, int32_t h) { bool providerHeld = false; KSTATUS s = KiDisplayAcquireProviderMutex(&providerHeld); if (!KSUCCESS(s)) @@ -1929,7 +1930,7 @@ DISPLAY_API KSTATUS KeDisplayCopyRect(int dstX, int dstY, int srcX, int srcY, in return s; } -DISPLAY_API KSTATUS KeDisplayScroll(int lines) { +DISPLAY_API KSTATUS KeDisplayScroll(int32_t lines) { if (lines <= 0) return NTLSTATUS_SUCCESS; @@ -1972,7 +1973,7 @@ DISPLAY_API KSTATUS KeDisplayScroll(int lines) { return s; } -DISPLAY_API KSTATUS KeDisplayPutChar(char c, int x, int y, uint32_t fg, uint32_t bg) { +DISPLAY_API KSTATUS KeDisplayPutChar(char c, int32_t x, int32_t y, uint32_t fg, uint32_t bg) { bool providerHeld = false; KSTATUS s = KiDisplayAcquireProviderMutex(&providerHeld); if (!KSUCCESS(s)) @@ -2189,7 +2190,7 @@ DISPLAY_API KSTATUS KeDisplayGetConsoleInfo(KE_CONSOLE_INFO* outInfo) { return NTLSTATUS_SUCCESS; } -DISPLAY_API KSTATUS KeDisplaySetConsoleCursor(int col, int row) { +DISPLAY_API KSTATUS KeDisplaySetConsoleCursor(int32_t col, int32_t row) { bool providerHeld = false; KSTATUS s = KiDisplayAcquireProviderMutex(&providerHeld); if (!KSUCCESS(s)) @@ -2226,7 +2227,7 @@ DISPLAY_API KSTATUS KeDisplaySetConsoleCursor(int col, int row) { return NTLSTATUS_SUCCESS; } -DISPLAY_API KSTATUS KeDisplayFillConsoleChars(char c, int col, int row, size_t length, size_t* writtenOut) { +DISPLAY_API KSTATUS KeDisplayFillConsoleChars(char c, int32_t col, int32_t row, size_t length, size_t* writtenOut) { if (writtenOut) *writtenOut = 0; bool providerHeld = false; @@ -2258,8 +2259,8 @@ DISPLAY_API KSTATUS KeDisplayFillConsoleChars(char c, int col, int row, size_t l (void)KiDisplayHideCursorForDrawSnapshot(device, ops, caps); size_t written = 0; - int x = col; - int y = row; + int32_t x = col; + int32_t y = row; while (written < length && y < rows) { uint16_t attr = DISPLAY_CONSOLE_DEFAULT_ATTR; uintptr_t flags = KeAcquireSpinlock(&gDisplayLock); @@ -2295,7 +2296,7 @@ DISPLAY_API KSTATUS KeDisplayFillConsoleChars(char c, int col, int row, size_t l return KSUCCESS(s) ? fs : s; } -DISPLAY_API KSTATUS KeDisplayFillConsoleAttrs(uint16_t attr, int col, int row, size_t length, size_t* writtenOut) { +DISPLAY_API KSTATUS KeDisplayFillConsoleAttrs(uint16_t attr, int32_t col, int32_t row, size_t length, size_t* writtenOut) { if (writtenOut) *writtenOut = 0; bool providerHeld = false; @@ -2327,8 +2328,8 @@ DISPLAY_API KSTATUS KeDisplayFillConsoleAttrs(uint16_t attr, int col, int row, s (void)KiDisplayHideCursorForDrawSnapshot(device, ops, caps); size_t written = 0; - int x = col; - int y = row; + int32_t x = col; + int32_t y = row; while (written < length && y < rows) { char ch = ' '; uintptr_t flags = KeAcquireSpinlock(&gDisplayLock); @@ -2403,7 +2404,7 @@ DISPLAY_API KSTATUS KeDisplayPresent(const DISPLAY_RECT* rects, size_t rectCount return s; } -DISPLAY_API KSTATUS KeDisplaySetCursorImage(int width, int height, int hotX, int hotY, const uint32_t* argbPixels) { +DISPLAY_API KSTATUS KeDisplaySetCursorImage(int32_t width, int32_t height, int32_t hotX, int32_t hotY, const uint32_t* argbPixels) { bool providerHeld = false; KSTATUS s = KiDisplayAcquireProviderMutex(&providerHeld); if (!KSUCCESS(s)) @@ -2424,7 +2425,7 @@ DISPLAY_API KSTATUS KeDisplaySetCursorImage(int width, int height, int hotX, int return s; } -DISPLAY_API KSTATUS KeDisplaySetCursorPosition(int x, int y) { +DISPLAY_API KSTATUS KeDisplaySetCursorPosition(int32_t x, int32_t y) { uintptr_t flags = KeAcquireSpinlock(&gDisplayLock); gCursorX = x; gCursorY = y; @@ -2588,7 +2589,7 @@ static void BootFbCopyToShadowAndScanout(uint32_t* shadow, } // Implements the boot framebuffer pitched blit operation. -static KSTATUS BootFbBlit(DISPLAY_DEVICE* device, int x, int y, int w, int h, const uint32_t* pixels, uint32_t pitchBytes) { +static KSTATUS BootFbBlit(DISPLAY_DEVICE* device, int32_t x, int32_t y, int32_t w, int32_t h, const uint32_t* pixels, uint32_t pitchBytes) { BOOTFB_CONTEXT* fb = BootFbContext(device); if (!fb || !fb->Framebuffer || !pixels || w <= 0 || h <= 0 || pitchBytes < (uint32_t)w * sizeof(uint32_t)) @@ -2610,7 +2611,7 @@ static KSTATUS BootFbBlit(DISPLAY_DEVICE* device, int x, int y, int w, int h, co fb->Stats.BlitCount++; fb->Stats.BytesCopied += (uint64_t)(endX - startX) * (uint64_t)(endY - startY) * sizeof(uint32_t); - for (int row = 0; row < endY - startY; row++) { + for (int32_t row = 0; row < endY - startY; row++) { uint32_t* dst = dstBase + ((size_t)(startY + row) * fb->Stride) + startX; const uint8_t* sourceRow = (const uint8_t*)pixels + (size_t)(srcOffsetY + row) * pitchBytes; const uint32_t* src = (const uint32_t*)sourceRow + srcOffsetX; @@ -2621,10 +2622,10 @@ static KSTATUS BootFbBlit(DISPLAY_DEVICE* device, int x, int y, int w, int h, co // Presents one pitched source rectangle without a shadow-to-scanout second pass. static KSTATUS BootFbPresentPitched(DISPLAY_DEVICE* device, - int x, - int y, - int w, - int h, + int32_t x, + int32_t y, + int32_t w, + int32_t h, const uint32_t* pixels, uint32_t pitchBytes) { BOOTFB_CONTEXT* fb = BootFbContext(device); @@ -2651,7 +2652,7 @@ static KSTATUS BootFbPresentPitched(DISPLAY_DEVICE* device, fb->Stats.ScanoutBytes += bytes; fb->Stats.BytesCopied += fb->Backbuffer ? bytes * 2u : bytes; - for (int row = 0; row < endY - startY; row++) { + for (int32_t row = 0; row < endY - startY; row++) { const uint8_t* sourceRow = (const uint8_t*)pixels + (size_t)(srcOffsetY + row) * pitchBytes; const uint32_t* source = (const uint32_t*)sourceRow + srcOffsetX; uint32_t* scanout = fb->Framebuffer + ((size_t)(startY + row) * fb->Stride) + startX; @@ -2668,7 +2669,7 @@ static KSTATUS BootFbPresentPitched(DISPLAY_DEVICE* device, } // Implements the boot Fb Fill Rect operation. -static KSTATUS BootFbFillRect(DISPLAY_DEVICE* device, int x, int y, int w, int h, uint32_t color) { +static KSTATUS BootFbFillRect(DISPLAY_DEVICE* device, int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color) { BOOTFB_CONTEXT* fb = BootFbContext(device); if (!fb || !fb->Framebuffer || w <= 0 || h <= 0) return NTLSTATUS_INVALID_PARAMETER; @@ -2694,7 +2695,7 @@ static KSTATUS BootFbFillRect(DISPLAY_DEVICE* device, int x, int y, int w, int h } // Implements the boot Fb Copy Rect operation. -static KSTATUS BootFbCopyRect(DISPLAY_DEVICE* device, int dstX, int dstY, int srcX, int srcY, int w, int h) { +static KSTATUS BootFbCopyRect(DISPLAY_DEVICE* device, int32_t dstX, int32_t dstY, int32_t srcX, int32_t srcY, int32_t w, int32_t h) { BOOTFB_CONTEXT* fb = BootFbContext(device); if (!fb || !fb->Framebuffer || w <= 0 || h <= 0) return NTLSTATUS_INVALID_PARAMETER; @@ -2717,13 +2718,13 @@ static KSTATUS BootFbCopyRect(DISPLAY_DEVICE* device, int dstX, int dstY, int sr size_t rowBytes = (size_t)w * sizeof(uint32_t); if (dstY > srcY) { - for (int row = h - 1; row >= 0; row--) { + for (int32_t row = h - 1; row >= 0; row--) { uint32_t* dst = base + ((size_t)(dstY + row) * fb->Stride) + dstX; uint32_t* src = base + ((size_t)(srcY + row) * fb->Stride) + srcX; memmove(dst, src, rowBytes); } } else { - for (int row = 0; row < h; row++) { + for (int32_t row = 0; row < h; row++) { uint32_t* dst = base + ((size_t)(dstY + row) * fb->Stride) + dstX; uint32_t* src = base + ((size_t)(srcY + row) * fb->Stride) + srcX; memmove(dst, src, rowBytes); @@ -2733,7 +2734,7 @@ static KSTATUS BootFbCopyRect(DISPLAY_DEVICE* device, int dstX, int dstY, int sr } // Implements the boot Fb Read Rect operation. -static KSTATUS BootFbReadRect(DISPLAY_DEVICE* device, int x, int y, int w, int h, uint32_t* outPixels, uint32_t outStride) { +static KSTATUS BootFbReadRect(DISPLAY_DEVICE* device, int32_t x, int32_t y, int32_t w, int32_t h, uint32_t* outPixels, uint32_t outStride) { BOOTFB_CONTEXT* fb = BootFbContext(device); if (!fb || !fb->Framebuffer || !outPixels || outStride == 0 || w <= 0 || h <= 0) return NTLSTATUS_INVALID_PARAMETER; @@ -2749,7 +2750,7 @@ static KSTATUS BootFbReadRect(DISPLAY_DEVICE* device, int x, int y, int w, int h int dstOffsetX = r.X - x; int dstOffsetY = r.Y - y; - for (int row = 0; row < r.H; row++) { + for (int32_t row = 0; row < r.H; row++) { const uint32_t* src = srcBase + ((size_t)(r.Y + row) * fb->Stride) + r.X; uint32_t* dst = outPixels + ((size_t)(dstOffsetY + row) * outStride) + dstOffsetX; memcpy(dst, src, (size_t)r.W * sizeof(uint32_t)); @@ -2782,7 +2783,7 @@ static KSTATUS BootFbPresent(DISPLAY_DEVICE* device, const DISPLAY_RECT* rects, fb->Stats.BytesCopied += bytes; fb->Stats.ScanoutBytes += bytes; size_t pixelCount = (size_t)r.W; - for (int row = 0; row < r.H; row++) { + for (int32_t row = 0; row < r.H; row++) { uint32_t* dst = fb->Framebuffer + ((size_t)(r.Y + row) * fb->Stride) + r.X; uint32_t* src = fb->Backbuffer + ((size_t)(r.Y + row) * fb->Stride) + r.X; BootFbCopyToScanout(dst, src, pixelCount); @@ -2800,7 +2801,7 @@ static KSTATUS BootFbSync(DISPLAY_DEVICE* device) { } // Implements the boot Fb Set Cursor Image operation. -static KSTATUS BootFbSetCursorImage(DISPLAY_DEVICE* device, int width, int height, int hotX, int hotY, const uint32_t* argbPixels) { +static KSTATUS BootFbSetCursorImage(DISPLAY_DEVICE* device, int32_t width, int32_t height, int32_t hotX, int32_t hotY, const uint32_t* argbPixels) { (void)device; (void)width; (void)height; @@ -2811,7 +2812,7 @@ static KSTATUS BootFbSetCursorImage(DISPLAY_DEVICE* device, int width, int heigh } // Implements the boot Fb Set Cursor Position operation. -static KSTATUS BootFbSetCursorPosition(DISPLAY_DEVICE* device, int x, int y) { +static KSTATUS BootFbSetCursorPosition(DISPLAY_DEVICE* device, int32_t x, int32_t y) { BOOTFB_CONTEXT* fb = BootFbContext(device); if (fb) fb->Stats.CursorMoveCount++; (void)x; @@ -2846,7 +2847,7 @@ static void BootFbRestoreCursor(BOOTFB_CONTEXT* fb) { DISPLAY_RECT r = { gCursorDrawX, gCursorDrawY, DISPLAY_CURSOR_W, DISPLAY_CURSOR_H }; DISPLAY_MODE mode = { fb->Width, fb->Height, fb->Stride, DisplayPixelFormatX8R8G8B8 }; if (KiDisplayClipRect(&mode, &r)) { - for (int row = 0; row < r.H; row++) { + for (int32_t row = 0; row < r.H; row++) { uint32_t* dst = fb->Framebuffer + ((size_t)(r.Y + row) * fb->Stride) + r.X; uint32_t* src = fb->Backbuffer + ((size_t)(r.Y + row) * fb->Stride) + r.X; memcpy(dst, src, (size_t)r.W * sizeof(uint32_t)); @@ -2859,18 +2860,18 @@ static void BootFbRestoreCursor(BOOTFB_CONTEXT* fb) { } // Implements the boot Fb Draw Cursor operation. -static void BootFbDrawCursor(BOOTFB_CONTEXT* fb, int x, int y) { +static void BootFbDrawCursor(BOOTFB_CONTEXT* fb, int32_t x, int32_t y) { if (!fb || !fb->Framebuffer || !fb->Backbuffer) return; gCursorDrawX = x; gCursorDrawY = y; - for (int row = 0; row < DISPLAY_CURSOR_H; row++) { + for (int32_t row = 0; row < DISPLAY_CURSOR_H; row++) { int fy = y + row; if ((unsigned)fy >= fb->Height) continue; - for (int col = 0; col < DISPLAY_CURSOR_W; col++) { + for (int32_t col = 0; col < DISPLAY_CURSOR_W; col++) { int fx = x + col; if ((unsigned)fx >= fb->Width) continue; diff --git a/code/kernel/ke_dpc.c b/code/kernel/ke_dpc.c index 6e81e43..b2fe920 100644 --- a/code/kernel/ke_dpc.c +++ b/code/kernel/ke_dpc.c @@ -1,5 +1,7 @@ // Deferred procedure call infrastructure. This file owns DPC queuing and execution outside hard IRQ context. +#include +#include #include #include #include @@ -8,8 +10,8 @@ static LIST_ENTRY gDpcQueue; static SPINLOCK gDpcLock; -static volatile long gDpcInitialized = 0; -static volatile long gDpcActive = 0; +static volatile int32_t gDpcInitialized = 0; +static volatile int32_t gDpcActive = 0; // Initializes dpc System. void KeInitializeDpcSystem(void) { @@ -52,7 +54,7 @@ __declspec(dllexport) bool KeInsertQueueDpc(KDPC* dpc, // Coalesce repeated inserts of the same KDPC. The first insert owns the // argument pair until the DPC is popped by the dispatcher. - if (_InterlockedCompareExchange(&dpc->Inserted, 1, 0) != 0) + if (NtlAtomicCompareExchange32(&dpc->Inserted, 1, 0) != 0) return false; dpc->SystemArgument1 = systemArgument1; @@ -74,7 +76,7 @@ void KiDrainDpcQueue(void) { if (KeGetCurrentIrql() > DISPATCH_LEVEL) return; - if (_InterlockedCompareExchange(&gDpcActive, 1, 0) != 0) + if (NtlAtomicCompareExchange32(&gDpcActive, 1, 0) != 0) return; KIRQL oldIrql = KeGetCurrentIrql(); diff --git a/code/kernel/ke_event.c b/code/kernel/ke_event.c index 42ea070..5b6b4fc 100644 --- a/code/kernel/ke_event.c +++ b/code/kernel/ke_event.c @@ -1,5 +1,6 @@ // Kernel event primitive. This file owns manual-reset event state and waiter wakeups. +#include #include #include #include @@ -20,14 +21,14 @@ typedef struct KE_EVENT_WAIT_BLOCK { KE_EVENT_MULTI_WAIT_CONTEXT* Context; EVENT* Event; uint32_t Index; - uint8_t Armed; + bool Armed; } KE_EVENT_WAIT_BLOCK; struct KE_EVENT_MULTI_WAIT_CONTEXT { THREAD* Thread; KE_EVENT_WAIT_BLOCK* Blocks; uint32_t Count; - volatile long Completed; + volatile int32_t Completed; KSTATUS Status; uint32_t SelectedIndex; }; @@ -47,7 +48,7 @@ static void KiCompleteMultiWaitBlockLocked(KE_EVENT_WAIT_BLOCK* block) { if (!ctx) return; - if (_InterlockedCompareExchange(&ctx->Completed, 1, 0) != 0) + if (NtlAtomicCompareExchange32(&ctx->Completed, 1, 0) != 0) return; ctx->Status = NTLSTATUS_SUCCESS; @@ -81,7 +82,7 @@ static void KiRundownMultiWait(THREAD* thread, void* context, KSTATUS status) { if (!ctx) return; - if (_InterlockedCompareExchange(&ctx->Completed, 1, 0) == 0) { + if (NtlAtomicCompareExchange32(&ctx->Completed, 1, 0) == 0) { ctx->Status = status; ctx->SelectedIndex = 0; } @@ -96,7 +97,7 @@ static void KiRundownMultiWait(THREAD* thread, void* context, KSTATUS status) { } // Creates event. -__declspec(dllexport) KSTATUS KeEventCreate(EVENT_TYPE type, uint32_t initialState, EVENT** out) { +__declspec(dllexport) KSTATUS KeEventCreate(EVENT_TYPE type, bool initialState, EVENT** out) { if (!out) return NTLSTATUS_INVALID_PARAMETER; EVENT* e = (EVENT*)ObAllocateObject(ObjectTypeEvent, sizeof(EVENT)); if (!e) return NTLSTATUS_NO_MEMORY; @@ -145,7 +146,7 @@ __declspec(dllexport) void KeSignalEvent(EVENT* event) { LIST_ENTRY* node = event->MultiWaitList.Fwd; KE_EVENT_WAIT_BLOCK* block = CONTAINING_RECORD(node, KE_EVENT_WAIT_BLOCK, Link); KE_EVENT_MULTI_WAIT_CONTEXT* ctx = block->Context; - if (ctx && _InterlockedCompareExchange(&ctx->Completed, 0, 0) == 0) { + if (ctx && NtlAtomicLoad32(&ctx->Completed) == 0) { event->Signaled = 0; KiCompleteMultiWaitBlockLocked(block); KeReleaseSpinlock(&event->Lock, flags); @@ -182,7 +183,7 @@ __declspec(dllexport) KSTATUS KeTryWaitEvent(EVENT* event) { } // Waits for Event. -__declspec(dllexport) KSTATUS KeWaitForEvent(EVENT* event, uint8_t alertable) { +__declspec(dllexport) KSTATUS KeWaitForEvent(EVENT* event, bool alertable) { if (!event) return NTLSTATUS_INVALID_PARAMETER; if (!KeCanWait()) return NTLSTATUS_INVALID_IRQL; if (alertable && KeAreKernelApcsDisabled()) return NTLSTATUS_INVALID_PARAMETER; @@ -204,7 +205,7 @@ __declspec(dllexport) KSTATUS KeWaitForEvent(EVENT* event, uint8_t alertable) { // Waits for any of a set of events, returning the selected index. __declspec(dllexport) KSTATUS KeWaitForMultipleEvents(EVENT** events, uint32_t count, - uint8_t alertable, uint64_t timeoutMs, + bool alertable, uint64_t timeoutMs, uint32_t* signaledIndexOut) { if (!events || count == 0 || count > SYS_WAIT_MANY_MAX_HANDLES) return NTLSTATUS_INVALID_PARAMETER; @@ -255,18 +256,18 @@ __declspec(dllexport) KSTATUS KeWaitForMultipleEvents(EVENT** events, uint32_t c for (uint32_t i = 0; i < count; i++) { EVENT* event = events[i]; uintptr_t flags = KeAcquireSpinlock(&event->Lock); - if (_InterlockedCompareExchange(&ctx.Completed, 0, 0) == 0 && event->Signaled) { + if (NtlAtomicLoad32(&ctx.Completed) == 0 && event->Signaled) { if (event->Type == EventAutoReset) event->Signaled = 0; ctx.Status = NTLSTATUS_SUCCESS; ctx.SelectedIndex = i; - _InterlockedExchange(&ctx.Completed, 1); + NtlAtomicExchange32(&ctx.Completed, 1); KeReleaseSpinlock(&event->Lock, flags); KeUnblockThreadFromWaitListWithStatus(ctx.Thread, NTLSTATUS_SUCCESS); break; } - if (_InterlockedCompareExchange(&ctx.Completed, 0, 0) == 0) { + if (NtlAtomicLoad32(&ctx.Completed) == 0) { stackBlocks[i].Armed = 1; KeListInsertTailChecked(&event->MultiWaitList, &stackBlocks[i].Link); } @@ -274,7 +275,7 @@ __declspec(dllexport) KSTATUS KeWaitForMultipleEvents(EVENT** events, uint32_t c } KSTATUS waitStatus = KeCommitBlockThread(ctx.Thread, alertable); - if (_InterlockedCompareExchange(&ctx.Completed, 1, 0) == 0) + if (NtlAtomicCompareExchange32(&ctx.Completed, 1, 0) == 0) ctx.Status = waitStatus; KiCleanupMultiWaitBlocks(&ctx); diff --git a/code/kernel/ke_hal.c b/code/kernel/ke_hal.c index 695653b..22c165c 100644 --- a/code/kernel/ke_hal.c +++ b/code/kernel/ke_hal.c @@ -1,5 +1,6 @@ // Kernel HAL binding and generic platform-operation wrappers. +#include #include static const HAL_API* gHal; @@ -18,7 +19,7 @@ KSTATUS KeBindBootstrapHal(void) { } // Returns whether the initialized table contains every mandatory kernel platform service. -static int KiHalRuntimeTableValid(const HAL_API* hal) { +static bool KiHalRuntimeTableValid(const HAL_API* hal) { return hal && hal->Size == sizeof(HAL_API) && hal->Version == HAL_API_VERSION && hal->Processor.DisableInterrupts && hal->Processor.RestoreInterrupts && hal->Processor.EnableInterrupts && hal->Processor.WaitForInterrupt && diff --git a/code/kernel/ke_handle.c b/code/kernel/ke_handle.c index 1b08475..1957f3d 100644 --- a/code/kernel/ke_handle.c +++ b/code/kernel/ke_handle.c @@ -1,10 +1,11 @@ // Kernel capability handles. This file owns process-local handle identity, grant delegation, and revocation. +#include +#include #include #include #include #include -#include #include #include #include @@ -14,8 +15,8 @@ #include typedef struct CAPABILITY_GRANT { - volatile long RefCount; - volatile long Revoked; + volatile int32_t RefCount; + volatile int32_t Revoked; uint32_t Depth; SYS_RIGHTS MaximumAccess; OBJECT* Target; @@ -62,7 +63,7 @@ static KE_HANDLE_ENTRY* KiFindHandleLocked(KE_HANDLE_TABLE* table, SYS_HANDLE ha // Acquires one grant reference while its owner still stabilizes the pointer. static void KiReferenceGrant(CAPABILITY_GRANT* grant) { if (!grant) return; - long refs = _InterlockedIncrement(&grant->RefCount); + int32_t refs = NtlAtomicIncrement32(&grant->RefCount); if (refs <= 1) { KePanic(KE_PANIC_CODE_GENERIC, "capability: invalid grant reference grant=0x%016llX refs=%ld\n", @@ -74,7 +75,7 @@ static void KiReferenceGrant(CAPABILITY_GRANT* grant) { // Releases one grant and recursively drops its immutable parent chain. static void KiDereferenceGrant(CAPABILITY_GRANT* grant) { while (grant) { - long refs = _InterlockedDecrement(&grant->RefCount); + int32_t refs = NtlAtomicDecrement32(&grant->RefCount); if (refs > 0) return; if (refs < 0) { KePanic(KE_PANIC_CODE_GENERIC, @@ -92,17 +93,17 @@ static void KiDereferenceGrant(CAPABILITY_GRANT* grant) { } // Returns whether a grant and every ancestor remain unrevoked. -static int KiGrantIsActive(const CAPABILITY_GRANT* grant) { +static bool KiGrantIsActive(const CAPABILITY_GRANT* grant) { const CAPABILITY_GRANT* current = grant; uint32_t depth = 0; while (current) { - if (_InterlockedCompareExchange((volatile long*)¤t->Revoked, 0, 0) != 0) - return 0; + if (NtlAtomicLoad32(¤t->Revoked) != 0) + return false; current = current->Parent; if (++depth > KE_CAPABILITY_MAX_DEPTH) - return 0; + return false; } - return 1; + return true; } // Creates a root grant and takes the target object's lifetime reference. @@ -246,7 +247,7 @@ SYS_RIGHTS KeGetValidHandleRights(OBJECT_TYPE type) { return management | SYS_RIGHT_QUERY | SYS_RIGHT_WAIT | SYS_EVENT_RIGHT_SIGNAL; case ObjectTypeQueue: return management | SYS_RIGHT_QUERY | SYS_QUEUE_RIGHT_SEND | - SYS_QUEUE_RIGHT_RECEIVE | SYS_QUEUE_RIGHT_PUBLISH; + SYS_QUEUE_RIGHT_RECEIVE; case ObjectTypeFile: return management | SYS_RIGHT_QUERY | SYS_FILE_RIGHT_READ_DATA | SYS_FILE_RIGHT_WRITE_DATA | SYS_FILE_RIGHT_LOCK | @@ -271,8 +272,6 @@ SYS_RIGHTS KeGetValidHandleRights(OBJECT_TYPE type) { return management | SYS_RIGHT_QUERY | SYS_RIGHT_WAIT | SYS_SURFACE_RIGHT_MAP_READ | SYS_SURFACE_RIGHT_MAP_WRITE | SYS_SURFACE_RIGHT_COMMIT; - case ObjectTypeInputQueue: - return management | SYS_RIGHT_QUERY | SYS_RIGHT_WAIT | SYS_INPUT_QUEUE_RIGHT_READ; case ObjectTypeTimer: return management | SYS_RIGHT_QUERY | SYS_RIGHT_WAIT | SYS_TIMER_RIGHT_SET; case ObjectTypeDisplayClock: @@ -395,7 +394,6 @@ KSTATUS KeCloseHandle(PROCESS* process, SYS_HANDLE handle) { OBJECT* object = entry->Grant->Target; if (object && object->Type == ObjectTypeFile) closeStatus = IoFlushFile((FILE_OBJECT*)object); - KeNamedQueueUnpublishHandle(process, handle, object); KiHandleClosed(object); KiDereferenceGrant(entry->Grant); MmFreePool(entry); @@ -533,7 +531,7 @@ KSTATUS KeRevokeHandle(PROCESS* process, SYS_HANDLE handle) { return NTLSTATUS_CAPABILITY_REVOKED; } - _InterlockedExchange(&entry->Grant->Revoked, 1); + NtlAtomicExchange32(&entry->Grant->Revoked, 1); KeReleaseSpinlock(&table->Lock, flags); return NTLSTATUS_SUCCESS; } @@ -757,7 +755,6 @@ void KeRundownHandleTable(PROCESS* process) { if (!entry) break; OBJECT* object = entry->Grant->Target; - KeNamedQueueUnpublishHandle(process, entry->Handle, object); KiHandleClosed(object); KiDereferenceGrant(entry->Grant); MmFreePool(entry); diff --git a/code/kernel/ke_id.c b/code/kernel/ke_id.c index 177b81d..36c8573 100644 --- a/code/kernel/ke_id.c +++ b/code/kernel/ke_id.c @@ -1,5 +1,6 @@ // Kernel monotonic identifier allocator. Public identities stay 32-bit while the internal cursor detects exhaustion without wrapping. +#include #include #include @@ -9,12 +10,12 @@ KSTATUS KeAllocateId32(KE_ID_ALLOCATOR* allocator, uint32_t* idOut) { return NTLSTATUS_INVALID_PARAMETER; while (1) { - long long next = _InterlockedCompareExchange64(&allocator->Next, 0, 0); - if ((unsigned long long)next > 0xFFFFFFFFULL) + int64_t next = NtlAtomicLoad64(&allocator->Next); + if ((uint64_t)next > UINT32_MAX) return NTLSTATUS_ID_EXHAUSTED; - long long desired = next + 1; - if (_InterlockedCompareExchange64(&allocator->Next, desired, next) == next) { + int64_t desired = next + 1; + if (NtlAtomicCompareExchange64(&allocator->Next, desired, next) == next) { *idOut = (uint32_t)next; return NTLSTATUS_SUCCESS; } diff --git a/code/kernel/ke_input.c b/code/kernel/ke_input.c index c4bd0b4..a200349 100644 --- a/code/kernel/ke_input.c +++ b/code/kernel/ke_input.c @@ -1,5 +1,6 @@ // Kernel input core. This file owns canonical keyboard and mouse state shared by input drivers and syscalls. +#include #include #include #include @@ -11,62 +12,16 @@ #include #include #include -#include #include #include -#define KEY_QUEUE_SIZE 256u -#define KEY_QUEUE_MASK (KEY_QUEUE_SIZE - 1u) -#define MOUSE_QUEUE_SIZE 128u -#define MOUSE_QUEUE_MASK (MOUSE_QUEUE_SIZE - 1u) #define MANAGER_QUEUE_SIZE 256u #define MANAGER_QUEUE_MASK (MANAGER_QUEUE_SIZE - 1u) -#define MAX_INPUT_KEYS 256u -#define KEY_REPEAT_DELAY_US 500000ull -#define KEY_REPEAT_PERIOD_US 33000ull - -typedef struct KEY_CHAR_PAIR { - INPUT_KEY key; - char normal; - char shifted; -} KEY_CHAR_PAIR; static KE_INPUT_CONTEXT gDefaultInputContext; static KE_INPUT_MANAGER gInputManager; static KE_ID_ALLOCATOR gInputDeviceIds = KE_ID_ALLOCATOR_INIT(1u); -static char KiInputKeyToChar(KE_INPUT_CONTEXT* context, INPUT_KEY key); -static void KiInputServiceRepeat(KE_INPUT_CONTEXT* context); - -// Advances the physical input snapshot generation after visible state changes. -static void KiInputBumpGeneration(KE_INPUT_CONTEXT* context) { - _InterlockedIncrement64(&context->Generation); -} - -static const KEY_CHAR_PAIR gCharMap[] = { - { INPUT_KEY_1, '1', '!' }, { INPUT_KEY_2, '2', '@' }, - { INPUT_KEY_3, '3', '#' }, { INPUT_KEY_4, '4', '$' }, - { INPUT_KEY_5, '5', '%' }, { INPUT_KEY_6, '6', '^' }, - { INPUT_KEY_7, '7', '&' }, { INPUT_KEY_8, '8', '*' }, - { INPUT_KEY_9, '9', '(' }, { INPUT_KEY_0, '0', ')' }, - { INPUT_KEY_MINUS, '-', '_' }, { INPUT_KEY_EQUAL, '=', '+' }, - { INPUT_KEY_LEFT_BRACKET, '[', '{' }, { INPUT_KEY_RIGHT_BRACKET, ']', '}' }, - { INPUT_KEY_BACKSLASH, '\\', '|' }, { INPUT_KEY_SEMICOLON, ';', ':' }, - { INPUT_KEY_APOSTROPHE, '\'', '"' }, { INPUT_KEY_GRAVE, '`', '~' }, - { INPUT_KEY_COMMA, ',', '<' }, { INPUT_KEY_PERIOD, '.', '>' }, - { INPUT_KEY_SLASH, '/', '?' }, - { INPUT_KEY_SPACE, ' ', ' ' }, { INPUT_KEY_TAB, '\t', '\t' }, - { INPUT_KEY_ENTER, '\n', '\n' }, { INPUT_KEY_BACKSPACE, '\b', '\b' }, - { INPUT_KEY_KP_DIVIDE, '/', '/' }, { INPUT_KEY_KP_MULTIPLY, '*', '*' }, - { INPUT_KEY_KP_MINUS, '-', '-' }, { INPUT_KEY_KP_PLUS, '+', '+' }, - { INPUT_KEY_KP_ENTER, '\n', '\n' }, { INPUT_KEY_KP_1, '1', '1' }, - { INPUT_KEY_KP_2, '2', '2' }, { INPUT_KEY_KP_3, '3', '3' }, - { INPUT_KEY_KP_4, '4', '4' }, { INPUT_KEY_KP_5, '5', '5' }, - { INPUT_KEY_KP_6, '6', '6' }, { INPUT_KEY_KP_7, '7', '7' }, - { INPUT_KEY_KP_8, '8', '8' }, { INPUT_KEY_KP_9, '9', '9' }, - { INPUT_KEY_KP_0, '0', '0' }, { INPUT_KEY_KP_PERIOD, '.', '.' }, -}; - // Updates the waitable manager event to match queued physical input. static void KiInputRefreshManagerEvent(KE_INPUT_CONTEXT* context) { if (!context->ManagerWaitEvent) return; @@ -80,187 +35,30 @@ static void KiInputQueueManagerEvent(KE_INPUT_CONTEXT* context, const KE_INPUT_M int next = (context->ManagerTail + 1) & (int)MANAGER_QUEUE_MASK; if (next == context->ManagerHead) return; KE_INPUT_MANAGER_EVENT queued = *event; - queued.Sequence = (uint64_t)_InterlockedIncrement64(&context->ManagerSequence); + queued.Sequence = (uint64_t)NtlAtomicIncrement64(&context->ManagerSequence); context->ManagerQueue[context->ManagerTail] = queued; context->ManagerTail = next; KiInputRefreshManagerEvent(context); } -// Updates the waitable keyboard event to match queued keyboard input. -static void KiInputRefreshKeyboardEvent(KE_INPUT_CONTEXT* context) { - if (!context->WaitEvent) return; - if (context->KeyHead != context->KeyTail) KeSignalEvent(context->WaitEvent); - else KeClearEvent(context->WaitEvent); -} - -// Queues a keyboard event and wakes waiters when the event was accepted. -static void KiInputQueueKey(KE_INPUT_CONTEXT* context, INPUT_KEY key, char ch) { - if (key == INPUT_KEY_NONE) return; - int next = (context->KeyTail + 1) & (int)KEY_QUEUE_MASK; - if (next == context->KeyHead) return; - context->KeyQueue[context->KeyTail].Key = key; - context->KeyQueue[context->KeyTail].Character = ch; - context->KeyQueue[context->KeyTail].Modifiers = context->Modifiers; - context->KeyTail = next; - KiInputRefreshKeyboardEvent(context); -} - -// Implements the input Queue Has Space operation. -static bool KiInputQueueHasSpace(KE_INPUT_CONTEXT* context) { - int next = (context->KeyTail + 1) & (int)KEY_QUEUE_MASK; - return next != context->KeyHead; -} - -// Implements the input Key Is Repeatable operation. -static bool KiInputKeyIsRepeatable(INPUT_KEY key) { - if (key == INPUT_KEY_NONE) return false; - - switch (key) { - case INPUT_KEY_LEFT_SHIFT: - case INPUT_KEY_RIGHT_SHIFT: - case INPUT_KEY_LEFT_CTRL: - case INPUT_KEY_RIGHT_CTRL: - case INPUT_KEY_LEFT_ALT: - case INPUT_KEY_RIGHT_ALT: - case INPUT_KEY_LEFT_GUI: - case INPUT_KEY_RIGHT_GUI: - case INPUT_KEY_CAPS_LOCK: - case INPUT_KEY_NUM_LOCK: - return false; - default: - return true; - } -} - -// Implements the input Now Ticks operation. -static uint64_t KiInputNowTicks(KE_INPUT_CONTEXT* context) { - if (context->Hal && context->Hal->Timer.QueryTicks) - return context->Hal->Timer.QueryTicks(); - return 0; -} - -// Implements the input Start Repeat operation. -static void KiInputStartRepeat(KE_INPUT_CONTEXT* context, INPUT_KEY key, uint64_t now) { - if (!KiInputKeyIsRepeatable(key)) return; - context->RepeatKey = key; - context->RepeatNextTick = now + KEY_REPEAT_DELAY_US; -} - -// Implements the input Stop Repeat operation. -static void KiInputStopRepeat(KE_INPUT_CONTEXT* context, INPUT_KEY key) { - if (context->RepeatKey == key) { - context->RepeatKey = INPUT_KEY_NONE; - context->RepeatNextTick = 0; - } -} - -// Implements the input Service Repeat operation. -static void KiInputServiceRepeat(KE_INPUT_CONTEXT* context) { - INPUT_KEY key = context->RepeatKey; - if (!KiInputKeyIsRepeatable(key) || key >= MAX_INPUT_KEYS || !context->KeyDown[key]) { - context->RepeatKey = INPUT_KEY_NONE; - context->RepeatNextTick = 0; - return; - } - - uint64_t now = KiInputNowTicks(context); - if (now == 0 || now < context->RepeatNextTick) - return; - - if (!KiInputQueueHasSpace(context)) { - context->RepeatNextTick = now + KEY_REPEAT_PERIOD_US; - return; - } - - KiInputQueueKey(context, key, KiInputKeyToChar(context, key)); - context->RepeatNextTick = now + KEY_REPEAT_PERIOD_US; -} - -// Implements the input Repeat Dpc Routine operation. -static void KiInputRepeatDpcRoutine(KDPC* dpc, void* context, void* systemArgument1, void* systemArgument2) { - (void)dpc; - (void)systemArgument1; - (void)systemArgument2; - - KiInputServiceRepeat((KE_INPUT_CONTEXT*)context); -} - -// Implements the input Set Modifier operation. -static void KiInputSetModifier(KE_INPUT_CONTEXT* context, INPUT_KEY key, bool pressed) { - uint32_t bit = 0; - switch (key) { - case INPUT_KEY_LEFT_SHIFT: bit = INPUT_MOD_LSHIFT; break; - case INPUT_KEY_RIGHT_SHIFT: bit = INPUT_MOD_RSHIFT; break; - case INPUT_KEY_LEFT_CTRL: bit = INPUT_MOD_LCTRL; break; - case INPUT_KEY_RIGHT_CTRL: bit = INPUT_MOD_RCTRL; break; - case INPUT_KEY_LEFT_ALT: bit = INPUT_MOD_LALT; break; - case INPUT_KEY_RIGHT_ALT: bit = INPUT_MOD_RALT; break; - case INPUT_KEY_LEFT_GUI: bit = INPUT_MOD_LGUI; break; - case INPUT_KEY_RIGHT_GUI: bit = INPUT_MOD_RGUI; break; - default: break; - } - - if (bit) { - if (pressed) context->Modifiers |= bit; - else context->Modifiers &= ~bit; - } - - if (pressed && key == INPUT_KEY_CAPS_LOCK) context->Modifiers ^= INPUT_MOD_CAPS_LOCK; - if (pressed && key == INPUT_KEY_NUM_LOCK) context->Modifiers ^= INPUT_MOD_NUM_LOCK; -} - -// Implements the input Key To Char operation. -static char KiInputKeyToChar(KE_INPUT_CONTEXT* context, INPUT_KEY key) { - if (key >= INPUT_KEY_A && key <= INPUT_KEY_Z) { - bool shifted = (context->Modifiers & INPUT_MOD_SHIFT) != 0; - bool caps = (context->Modifiers & INPUT_MOD_CAPS_LOCK) != 0; - char c = (char)('a' + (key - INPUT_KEY_A)); - if (shifted ^ caps) c = (char)(c - 'a' + 'A'); - return c; - } - - bool shifted = (context->Modifiers & INPUT_MOD_SHIFT) != 0; - for (size_t i = 0; i < sizeof(gCharMap) / sizeof(gCharMap[0]); i++) { - if (gCharMap[i].key == key) - return shifted ? gCharMap[i].shifted : gCharMap[i].normal; - } - return 0; -} - // Implements the input Initialize operation. void KeInputInitialize(const HAL_API* hal) { KE_INPUT_CONTEXT* context = &gDefaultInputContext; ObInitializeObjectHeader(&gInputManager.Header, ObjectTypeInputManager); context->Hal = hal; - memset((void*)context->KeyDown, 0, sizeof(context->KeyDown)); - context->Modifiers = 0; - context->Generation = 0; - context->KeyHead = context->KeyTail = 0; - context->RepeatKey = INPUT_KEY_NONE; - context->RepeatNextTick = 0; - context->MouseHead = context->MouseTail = 0; context->ManagerHead = context->ManagerTail = 0; context->ManagerSequence = 0; context->MouseBoundsWidth = 1920; context->MouseBoundsHeight = 1080; - context->MouseWheel = 0; - context->MouseButtons = 0; - KeInitializeDpc(&context->RepeatDpc, KiInputRepeatDpcRoutine, context); - if (!context->WaitEvent) { - KSTATUS s = KeEventCreate(EventManualReset, 0, &context->WaitEvent); - if (!KSUCCESS(s)) - DbgPrint("Input: keyboard wait event create failed st=0x%x\n", s); - } if (!context->ManagerWaitEvent) { KSTATUS s = KeEventCreate(EventManualReset, 0, &context->ManagerWaitEvent); if (!KSUCCESS(s)) DbgPrint("Input: manager wait event create failed st=0x%x\n", s); } - KiInputRefreshKeyboardEvent(context); KiInputRefreshManagerEvent(context); - int w = 1920, h = 1080; + int32_t w = 1920, h = 1080; DISPLAY_MODE mode; if (KSUCCESS(KeDisplayGetMode(&mode))) { w = (int)mode.Width; @@ -332,43 +130,19 @@ __declspec(dllexport) void KeInputSubmitKey(const INPUT_KEY_EVENT* event) { KE_INPUT_CONTEXT* context = &gDefaultInputContext; if (!event) return; INPUT_KEY key = event->Key; - if (key == INPUT_KEY_NONE || key >= MAX_INPUT_KEYS) return; + if (key == INPUT_KEY_NONE || key >= 256u) return; - KE_INPUT_MANAGER_EVENT managerEvent; - memset(&managerEvent, 0, sizeof(managerEvent)); + // Forward physical transitions without interpreting key state or repeat policy. + KE_INPUT_MANAGER_EVENT managerEvent = {0}; managerEvent.Type = SYS_INPUT_MANAGER_EVENT_KEY; managerEvent.Data.Key = *event; KiInputQueueManagerEvent(context, &managerEvent); - - bool wasDown = context->KeyDown[key] != 0; - bool pressed = event->Pressed; - - if (pressed) { - // The input core owns typematic repeat. Treat repeated hardware make - // events as idempotent so PS/2 typematic and USB HID share one policy. - if (wasDown) - return; - - context->KeyDown[key] = 1u; - KiInputSetModifier(context, key, true); - KiInputBumpGeneration(context); - KiInputQueueKey(context, key, KiInputKeyToChar(context, key)); - KiInputStartRepeat(context, key, KiInputNowTicks(context)); - } else { - if (!wasDown) - return; - - context->KeyDown[key] = 0u; - KiInputSetModifier(context, key, false); - KiInputBumpGeneration(context); - KiInputStopRepeat(context, key); - } } // Implements the input Commit Mouse Position operation. -static bool KiInputCommitMousePosition(KE_INPUT_CONTEXT* context, int nx, int ny, uint8_t buttons) { - int bw = context->MouseBoundsWidth; - int bh = context->MouseBoundsHeight; +static void KiInputCommitMousePosition(KE_INPUT_CONTEXT* context, int32_t nx, int32_t ny) { + int32_t bw = context->MouseBoundsWidth; + int32_t bh = context->MouseBoundsHeight; if (bw < 1) bw = 1; if (bh < 1) bh = 1; @@ -377,11 +151,8 @@ static bool KiInputCommitMousePosition(KE_INPUT_CONTEXT* context, int nx, int ny if (nx >= bw) nx = bw - 1; if (ny >= bh) ny = bh - 1; - bool changed = (nx != context->MouseX) || (ny != context->MouseY) || (buttons != context->MouseButtons); context->MouseX = nx; context->MouseY = ny; - context->MouseButtons = buttons; - return changed; } // Implements the input Submit Mouse operation. @@ -395,23 +166,12 @@ __declspec(dllexport) void KeInputSubmitMouse(const INPUT_MOUSE_EVENT* event) { managerEvent.Data.Mouse = *event; KiInputQueueManagerEvent(context, &managerEvent); - int oldX = context->MouseX; - int oldY = context->MouseY; - int nx = oldX + event->dx; - int ny = oldY + event->dy; + int32_t oldX = context->MouseX; + int32_t oldY = context->MouseY; + int32_t nx = oldX + event->dx; + int32_t ny = oldY + event->dy; - bool changed = KiInputCommitMousePosition(context, nx, ny, event->buttons); - if (event->wheel != 0) { - context->MouseWheel += event->wheel; - changed = true; - } - if (changed) KiInputBumpGeneration(context); - - int next = (context->MouseTail + 1) & (int)MOUSE_QUEUE_MASK; - if (next != context->MouseHead) { - context->MouseQueue[context->MouseTail] = *event; - context->MouseTail = next; - } + KiInputCommitMousePosition(context, nx, ny); } // Implements the input Submit Mouse Absolute operation. @@ -419,8 +179,8 @@ __declspec(dllexport) void KeInputSubmitMouseAbsolute(const INPUT_MOUSE_ABSOLUTE KE_INPUT_CONTEXT* context = &gDefaultInputContext; if (!event) return; - int oldX = context->MouseX; - int oldY = context->MouseY; + int32_t oldX = context->MouseX; + int32_t oldY = context->MouseY; INPUT_MOUSE_EVENT managerMouse; managerMouse.dx = event->x - oldX; @@ -433,213 +193,42 @@ __declspec(dllexport) void KeInputSubmitMouseAbsolute(const INPUT_MOUSE_ABSOLUTE managerEvent.Data.Mouse = managerMouse; KiInputQueueManagerEvent(context, &managerEvent); - bool changed = KiInputCommitMousePosition(context, event->x, event->y, event->buttons); - if (event->wheel != 0) { - context->MouseWheel += event->wheel; - changed = true; - } - if (changed) KiInputBumpGeneration(context); - - INPUT_MOUSE_EVENT rel; - rel.dx = context->MouseX - oldX; - rel.dy = context->MouseY - oldY; - rel.wheel = event->wheel; - rel.buttons = event->buttons; - - int next = (context->MouseTail + 1) & (int)MOUSE_QUEUE_MASK; - if (next != context->MouseHead) { - context->MouseQueue[context->MouseTail] = rel; - context->MouseTail = next; - } + KiInputCommitMousePosition(context, event->x, event->y); } // Implements the input Submit Mouse Absolute Scaled operation. -__declspec(dllexport) void KeInputSubmitMouseAbsoluteScaled(uint32_t rawX, uint32_t rawY, uint32_t maxX, uint32_t maxY, int wheel, uint8_t buttons) { +__declspec(dllexport) void KeInputSubmitMouseAbsoluteScaled(uint32_t rawX, uint32_t rawY, uint32_t maxX, uint32_t maxY, int32_t wheel, uint8_t buttons) { KE_INPUT_CONTEXT* context = &gDefaultInputContext; - int bw = context->MouseBoundsWidth; - int bh = context->MouseBoundsHeight; + int32_t bw = context->MouseBoundsWidth; + int32_t bh = context->MouseBoundsHeight; if (bw < 1) bw = 1; if (bh < 1) bh = 1; INPUT_MOUSE_ABSOLUTE_EVENT ev; - ev.x = maxX ? (int)(((uint64_t)rawX * (uint64_t)(bw - 1)) / (uint64_t)maxX) : 0; - ev.y = maxY ? (int)(((uint64_t)rawY * (uint64_t)(bh - 1)) / (uint64_t)maxY) : 0; + ev.x = maxX ? (int32_t)(((uint64_t)rawX * (uint64_t)(bw - 1)) / (uint64_t)maxX) : 0; + ev.y = maxY ? (int32_t)(((uint64_t)rawY * (uint64_t)(bh - 1)) / (uint64_t)maxY) : 0; ev.wheel = wheel; ev.buttons = buttons; KeInputSubmitMouseAbsolute(&ev); } -// Returns the next queued key without consuming it. -INPUT_KEY KeInputKbPoll(void) { - KE_INPUT_CONTEXT* context = &gDefaultInputContext; - if (context->KeyHead == context->KeyTail) KiInputServiceRepeat(context); - return (context->KeyHead == context->KeyTail) ? INPUT_KEY_NONE : context->KeyQueue[context->KeyHead].Key; -} - -// Waits for keyboard input in one context or until the timeout expires. -static KSTATUS KiInputWaitKey(KE_INPUT_CONTEXT* context, uint8_t alertable, uint64_t timeoutMs) { - if (!context) - return NTLSTATUS_INVALID_PARAMETER; - - KiInputServiceRepeat(context); - if (context->KeyHead != context->KeyTail) - return NTLSTATUS_SUCCESS; - if (!context->WaitEvent) - return NTLSTATUS_FAILURE; - - while (true) { - EVENT* event = context->WaitEvent; - uint32_t selected = 0; - KSTATUS s = KeWaitForMultipleEvents(&event, 1, alertable, timeoutMs, &selected); - if (!KSUCCESS(s)) - return s; - - KiInputServiceRepeat(context); - if (context->KeyHead != context->KeyTail) - return NTLSTATUS_SUCCESS; - } -} - -// Waits for keyboard input in the default input context. -KSTATUS KeInputWaitKey(uint8_t alertable, uint64_t timeoutMs) { - return KiInputWaitKey(&gDefaultInputContext, alertable, timeoutMs); -} - -// Services typematic repeat from the scheduler timer bottom half. -void KeInputTimerTick(void) { - KE_INPUT_CONTEXT* context = &gDefaultInputContext; - INPUT_KEY key = context->RepeatKey; - if (!KiInputKeyIsRepeatable(key) || key >= MAX_INPUT_KEYS || !context->KeyDown[key]) - return; - - uint64_t now = KiInputNowTicks(context); - if (now != 0 && now >= context->RepeatNextTick) - KeInsertQueueDpc(&context->RepeatDpc, (void*)0, (void*)0); -} - -// Implements the input Is Key Down operation. -bool KeInputIsKeyDown(INPUT_KEY key) { - KE_INPUT_CONTEXT* context = &gDefaultInputContext; - if (key == INPUT_KEY_NONE || key >= MAX_INPUT_KEYS) return false; - return context->KeyDown[key] != 0; -} - -// Peeks one keyboard event from an input context without consuming it. -static bool KiInputPeekEvent(KE_INPUT_CONTEXT* context, INPUT_KEY_RECORD* record) { - if (!context || !record) return false; - if (context->KeyHead == context->KeyTail) KiInputServiceRepeat(context); - if (context->KeyHead == context->KeyTail) return false; - - INPUT_KEY_QUEUE_ENTRY entry = context->KeyQueue[context->KeyHead]; - record->Key = entry.Key; - record->Char = entry.Character; - record->Modifiers = entry.Modifiers; - return true; -} - -// Reads one keyboard event from an input context. -static bool KiInputReadEvent(KE_INPUT_CONTEXT* context, INPUT_KEY_RECORD* record) { - if (!KiInputPeekEvent(context, record)) return false; - context->KeyHead = (context->KeyHead + 1) & (int)KEY_QUEUE_MASK; - KiInputRefreshKeyboardEvent(context); - return true; -} - -// Peeks one keyboard event from the default input context. -bool KeInputPeekEvent(INPUT_KEY_RECORD* record) { - return KiInputPeekEvent(&gDefaultInputContext, record); -} - -// Reads one keyboard event from the default input context. -bool KeInputReadEvent(INPUT_KEY_RECORD* record) { - return KiInputReadEvent(&gDefaultInputContext, record); -} - -// Implements the input Read Key operation. -INPUT_KEY KeInputReadKey(void) { - INPUT_KEY_RECORD record; - return KeInputReadEvent(&record) ? record.Key : INPUT_KEY_NONE; -} - -// Implements the input Read Char operation. -char KeInputReadChar(void) { - INPUT_KEY_RECORD record; - return KeInputReadEvent(&record) ? record.Char : 0; -} - -// Implements the input Get Modifiers operation. -uint32_t KeInputGetModifiers(void) { - KE_INPUT_CONTEXT* context = &gDefaultInputContext; - return context->Modifiers; -} - -// Snapshots the current physical global input state without consuming events. -KSTATUS KeInputQueryGlobalState(SYS_GLOBAL_INPUT_STATE* out) { - KE_INPUT_CONTEXT* context = &gDefaultInputContext; - if (!out) return NTLSTATUS_INVALID_PARAMETER; - - out->Size = sizeof(*out); - out->Flags = SYS_GLOBAL_INPUT_STATE_FLAG_NONE; - out->Generation = (uint64_t)context->Generation; - out->Modifiers = context->Modifiers; - memset(out->KeyDown, 0, sizeof(out->KeyDown)); - - for (uint32_t key = 0; key < MAX_INPUT_KEYS; key++) { - if (context->KeyDown[key]) - out->KeyDown[key >> 3] |= (uint8_t)(1u << (key & 7u)); - } - - out->MouseX = context->MouseX; - out->MouseY = context->MouseY; - out->MouseWheel = context->MouseWheel; - out->MouseButtons = context->MouseButtons; - return NTLSTATUS_SUCCESS; -} - -// Implements the input Mouse Peek operation. -bool KeInputMousePeek(INPUT_MOUSE_EVENT* event) { - KE_INPUT_CONTEXT* context = &gDefaultInputContext; - if (!event) return false; - if (context->MouseHead == context->MouseTail) return false; - - *event = context->MouseQueue[context->MouseHead]; - return true; -} - -// Implements the input Mouse Read operation. -bool KeInputMouseRead(INPUT_MOUSE_EVENT* event) { - KE_INPUT_CONTEXT* context = &gDefaultInputContext; - if (!KeInputMousePeek(event)) return false; - context->MouseHead = (context->MouseHead + 1) & (int)MOUSE_QUEUE_MASK; - return true; -} - -// Implements the input Mouse Get Position operation. -void KeInputMouseGetPosition(int* x, int* y) { - KE_INPUT_CONTEXT* context = &gDefaultInputContext; - if (x) *x = context->MouseX; - if (y) *y = context->MouseY; -} - // Implements the input Mouse Set Position operation. -void KeInputMouseSetPosition(int x, int y) { +void KeInputMouseSetPosition(int32_t x, int32_t y) { KE_INPUT_CONTEXT* context = &gDefaultInputContext; - int bw = context->MouseBoundsWidth; - int bh = context->MouseBoundsHeight; + int32_t bw = context->MouseBoundsWidth; + int32_t bh = context->MouseBoundsHeight; if (bw < 1) bw = 1; if (bh < 1) bh = 1; if (x < 0) x = 0; if (y < 0) y = 0; if (x >= bw) x = bw - 1; if (y >= bh) y = bh - 1; - bool changed = (x != context->MouseX) || (y != context->MouseY); context->MouseX = x; context->MouseY = y; - if (changed) KiInputBumpGeneration(context); } // Implements the input Mouse Set Bounds operation. -void KeInputMouseSetBounds(int w, int h) { +void KeInputMouseSetBounds(int32_t w, int32_t h) { KE_INPUT_CONTEXT* context = &gDefaultInputContext; if (w < 1) w = 1; if (h < 1) h = 1; @@ -647,51 +236,3 @@ void KeInputMouseSetBounds(int w, int h) { context->MouseBoundsHeight = h; KeInputMouseSetPosition(context->MouseX, context->MouseY); } - -// Destroys an input queue handle. -static void KiInputQueueDestroy(OBJECT* object) { - INPUT_QUEUE* queue = CONTAINING_RECORD(object, INPUT_QUEUE, Header); - if (queue->WaitEvent) { - ObDereferenceObject(&queue->WaitEvent->Header); - queue->WaitEvent = (EVENT*)0; - } - queue->Context = (KE_INPUT_CONTEXT*)0; -} - -// Opens the default keyboard input queue as a waitable object. -KSTATUS KeInputOpenQueue(INPUT_QUEUE** out) { - KE_INPUT_CONTEXT* context = &gDefaultInputContext; - if (!out) return NTLSTATUS_INVALID_PARAMETER; - *out = (INPUT_QUEUE*)0; - if (!context->WaitEvent) return NTLSTATUS_FAILURE; - - INPUT_QUEUE* queue = (INPUT_QUEUE*)ObAllocateObject(ObjectTypeInputQueue, sizeof(INPUT_QUEUE)); - if (!queue) return NTLSTATUS_NO_MEMORY; - queue->Header.Destroy = KiInputQueueDestroy; - queue->Context = context; - ObReferenceObject(&context->WaitEvent->Header); - queue->WaitEvent = context->WaitEvent; - *out = queue; - return NTLSTATUS_SUCCESS; -} - -// Peeks one keyboard event from the queue's owning context. -bool KeInputQueuePeekEvent(INPUT_QUEUE* queue, INPUT_KEY_RECORD* record) { - return queue ? KiInputPeekEvent(queue->Context, record) : false; -} - -// Reads one keyboard event from the queue's owning context. -bool KeInputQueueReadEvent(INPUT_QUEUE* queue, INPUT_KEY_RECORD* record) { - return queue ? KiInputReadEvent(queue->Context, record) : false; -} - -// Waits for keyboard input in the queue's owning context. -KSTATUS KeInputQueueWaitKey(INPUT_QUEUE* queue, uint8_t alertable, uint64_t timeoutMs) { - return queue ? KiInputWaitKey(queue->Context, alertable, timeoutMs) - : NTLSTATUS_INVALID_PARAMETER; -} - -// Returns the event that wakes when keyboard input can be read. -EVENT* KeInputQueueWaitEvent(INPUT_QUEUE* queue) { - return queue ? queue->WaitEvent : (EVENT*)0; -} diff --git a/code/kernel/ke_irp.c b/code/kernel/ke_irp.c index d269f33..cc0053e 100644 --- a/code/kernel/ke_irp.c +++ b/code/kernel/ke_irp.c @@ -1,5 +1,7 @@ // I/O request packet infrastructure. This file owns IRP dispatch, queuing, cancellation, and remove-lock helpers. +#include +#include #include #include #include @@ -95,7 +97,7 @@ __declspec(dllexport) KSTATUS IoAcquireRemoveLock(IO_REMOVE_LOCK* lock, IRP* irp KeReleaseSpinlock(&lock->Lock, flags); return NTLSTATUS_OBJECT_DEAD; } - _InterlockedIncrement(&lock->IoCount); + NtlAtomicIncrement32(&lock->IoCount); KeReleaseSpinlock(&lock->Lock, flags); return NTLSTATUS_SUCCESS; } @@ -104,7 +106,7 @@ __declspec(dllexport) KSTATUS IoAcquireRemoveLock(IO_REMOVE_LOCK* lock, IRP* irp __declspec(dllexport) void IoReleaseRemoveLock(IO_REMOVE_LOCK* lock, IRP* irp) { (void)irp; if (!lock) return; - long left = _InterlockedDecrement(&lock->IoCount); + int32_t left = NtlAtomicDecrement32(&lock->IoCount); if (left == 0 && lock->Removing && lock->RemovedEvent) KeSignalEvent(lock->RemovedEvent); } @@ -116,7 +118,7 @@ __declspec(dllexport) void IoReleaseRemoveLockAndWait(IO_REMOVE_LOCK* lock, IRP* uintptr_t flags = KeAcquireSpinlock(&lock->Lock); lock->Removing = true; - long left = _InterlockedDecrement(&lock->IoCount); // release sentinel + int32_t left = NtlAtomicDecrement32(&lock->IoCount); // release sentinel bool done = (left == 0); KeReleaseSpinlock(&lock->Lock, flags); @@ -297,7 +299,7 @@ __declspec(dllexport) KSTATUS IoCompleteRequest(IRP* irp, KSTATUS status, size_t IO_STACK_LOCATION* ios = IoGetCurrentIrpStackLocation(irp); DEVICE_OBJECT* dev = ios ? ios->DeviceObject : (DEVICE_OBJECT*)0; if (dev) { - _InterlockedDecrement(&dev->OutstandingIrps); + NtlAtomicDecrement32(&dev->OutstandingIrps); IoDereferenceDeviceObject(dev); ios->DeviceObject = (DEVICE_OBJECT*)0; } diff --git a/code/kernel/ke_irql.c b/code/kernel/ke_irql.c index 79b9b83..000dd55 100644 --- a/code/kernel/ke_irql.c +++ b/code/kernel/ke_irql.c @@ -1,5 +1,6 @@ // Kernel IRQL model. This file owns the software interrupt-priority state used by IRQ and scheduler code. +#include #include #include #include @@ -87,6 +88,6 @@ __declspec(dllexport) void KeAssertIrqlAtOrBelow(KIRQL maxIrql) { } // Checks whether wait. -__declspec(dllexport) int KeCanWait(void) { +__declspec(dllexport) bool KeCanWait(void) { return KeGetCurrentIrql() == PASSIVE_LEVEL; } diff --git a/code/kernel/ke_mutex.c b/code/kernel/ke_mutex.c index 91fdb3a..8e24a35 100644 --- a/code/kernel/ke_mutex.c +++ b/code/kernel/ke_mutex.c @@ -36,8 +36,8 @@ static void KiMutexReferenceOwnerLocked(MUTEX* mutex, THREAD* owner) { } // Tests whether one scheduler link is currently attached to a list. -static int KiMutexWaiterIsLinked(const THREAD* thread) { - if (!thread) return 0; +static bool KiMutexWaiterIsLinked(const THREAD* thread) { + if (!thread) return false; return thread->SchedulerLink.Fwd != &thread->SchedulerLink || thread->SchedulerLink.Bwd != &thread->SchedulerLink; } @@ -48,7 +48,7 @@ static void KiMutexPropagatePriority(THREAD* thread) { for (uint32_t depth = 1; thread && depth <= KE_PRIORITY_INHERITANCE_MAX_DEPTH; depth++) { MUTEX* waitingMutex = (MUTEX*)0; - int changed = KiSchedulerRefreshEffectivePriority(thread, &waitingMutex); + bool changed = KiSchedulerRefreshEffectivePriority(thread, &waitingMutex); if (!changed) { if (releaseThread) ObDereferenceObject(&thread->Header); return; @@ -223,8 +223,8 @@ __declspec(dllexport) void KeReleaseMutex(MUTEX* mutex) { } // Removes one blocked mutex waiter during thread-wait rundown. -int KiMutexAbortWait(MUTEX* mutex, THREAD* thread) { - if (!mutex || !thread) return 0; +bool KiMutexAbortWait(MUTEX* mutex, THREAD* thread) { + if (!mutex || !thread) return false; uintptr_t flags = KeAcquireSpinlock(&mutex->Lock); if (thread->WaitingMutex != mutex || @@ -232,7 +232,7 @@ int KiMutexAbortWait(MUTEX* mutex, THREAD* thread) { thread->WaitListHead != &mutex->WaitList || !KiMutexWaiterIsLinked(thread)) { KeReleaseSpinlock(&mutex->Lock, flags); - return 0; + return false; } KeListRemoveChecked(&thread->SchedulerLink); @@ -245,7 +245,7 @@ int KiMutexAbortWait(MUTEX* mutex, THREAD* thread) { KiMutexPropagatePriority(owner); if (owner) ObDereferenceObject(&owner->Header); KiMutexPropagatePriority(thread); - return 1; + return true; } // Abandons one mutex owned by a terminating thread. diff --git a/code/kernel/ke_port.c b/code/kernel/ke_port.c index f1556ac..6131b6d 100644 --- a/code/kernel/ke_port.c +++ b/code/kernel/ke_port.c @@ -1,5 +1,6 @@ // Kernel Port IPC mechanism. Ports provide copied messaging, synchronous calls, waitability, and capability transfer. +#include #include #include #include @@ -67,17 +68,17 @@ struct PORT_RECEIVE_WAIT { SYS_HANDLE Handles[SYS_PORT_MAX_TRANSFER_HANDLES]; uint32_t HandleCount; KSTATUS Status; - volatile long Claimed; - volatile long Completed; + volatile int32_t Claimed; + volatile int32_t Completed; }; struct PORT_CALL { LIST_ENTRY Link; - volatile long RefCount; + volatile int32_t RefCount; PORT_CONNECTION* Connection; uint32_t MessageId; uint32_t CallerSide; - volatile long State; + volatile int32_t State; struct PROCESS* CallerProcess; EVENT* CompletionEvent; MM_LOCKED_USER_BUFFER* LockedReplyPayload; @@ -91,7 +92,7 @@ struct PORT_CALL { }; struct PORT_CONNECTION { - volatile long RefCount; + volatile int32_t RefCount; SPINLOCK Lock; struct PORT* Endpoints[2]; struct PROCESS* PeerProcesses[2]; @@ -133,22 +134,22 @@ struct PORT { static KE_ID_ALLOCATOR gPortMessageIds = KE_ID_ALLOCATOR_INIT(1u); typedef struct PORT_COUNTERS { - volatile long long ConnectionsCreated; - volatile long long DirectDeliveries; - volatile long long DirectBytes; - volatile long long QueuedMessages; - volatile long long QueuedBytes; - volatile long long QueuedDeliveries; - volatile long long RepliesCompleted; - volatile long long Disconnects; - volatile long long ProcessQuotaRejects; + volatile int64_t ConnectionsCreated; + volatile int64_t DirectDeliveries; + volatile int64_t DirectBytes; + volatile int64_t QueuedMessages; + volatile int64_t QueuedBytes; + volatile int64_t QueuedDeliveries; + volatile int64_t RepliesCompleted; + volatile int64_t Disconnects; + volatile int64_t ProcessQuotaRejects; } PORT_COUNTERS; static PORT_COUNTERS gPortCounters; // Reads one 64-bit diagnostic counter atomically without changing it. -static uint64_t KiPortReadCounter(volatile long long* counter) { - return (uint64_t)_InterlockedCompareExchange64(counter, 0, 0); +static uint64_t KiPortReadCounter(volatile int64_t* counter) { + return (uint64_t)NtlAtomicLoad64(counter); } // Copies current Port transport diagnostics for the kernel stats syscall. @@ -194,7 +195,7 @@ static KSTATUS KiPortChargeProcess(PROCESS* process, uint32_t payloadBytes, uint handleCount > PORT_PROCESS_HANDLE_LIMIT - process->PortOutstandingHandles) { KeReleaseSpinlock(&process->PortQuotaLock, flags); ObDereferenceObject(&process->Header); - _InterlockedIncrement64(&gPortCounters.ProcessQuotaRejects); + NtlAtomicIncrement64(&gPortCounters.ProcessQuotaRejects); return NTLSTATUS_LIMIT_REACHED; } process->PortOutstandingMessages++; @@ -236,7 +237,7 @@ static KSTATUS KiPortChargeCall(PROCESS* process) { if (process->PortOutstandingCalls >= PORT_PROCESS_CALL_LIMIT) { KeReleaseSpinlock(&process->PortQuotaLock, flags); ObDereferenceObject(&process->Header); - _InterlockedIncrement64(&gPortCounters.ProcessQuotaRejects); + NtlAtomicIncrement64(&gPortCounters.ProcessQuotaRejects); return NTLSTATUS_LIMIT_REACHED; } process->PortOutstandingCalls++; @@ -264,7 +265,7 @@ static void KiPortReleaseCallCharge(PROCESS* process) { static void KiPortDereferenceConnection(PORT_CONNECTION* connection) { if (!connection) return; - long refs = _InterlockedDecrement(&connection->RefCount); + int32_t refs = NtlAtomicDecrement32(&connection->RefCount); if (refs > 0) return; if (refs < 0) { @@ -418,7 +419,7 @@ static KSTATUS KiPortCreateMessage(PROCESS* senderProcess, message->Info.HandleCount = transferCount; message->Call = call; if (call) - _InterlockedIncrement(&call->RefCount); + NtlAtomicIncrement32(&call->RefCount); KSTATUS status = KiPortCaptureHandles(senderProcess, transfers, @@ -529,10 +530,10 @@ static KSTATUS KiPortDeliverDirect(PORT* targetEndpoint, receiver->Info = message->Info; receiver->Status = status; if (KSUCCESS(status)) { - _InterlockedIncrement64(&gPortCounters.DirectDeliveries); - _InterlockedExchangeAdd64(&gPortCounters.DirectBytes, (long long)message->Info.PayloadLength); + NtlAtomicIncrement64(&gPortCounters.DirectDeliveries); + NtlAtomicExchangeAdd64(&gPortCounters.DirectBytes, (int64_t)message->Info.PayloadLength); } - _InterlockedExchange(&receiver->Completed, 1); + NtlAtomicExchange32(&receiver->Completed, 1); KeSignalEvent(receiver->CompletionEvent); return status; } @@ -543,7 +544,7 @@ static void KiPortCompleteCallError(PORT_CALL* call, KSTATUS status) { return; call->Status = status; call->ReplyHandleCount = 0; - _InterlockedExchange(&call->State, PORT_CALL_COMPLETED); + NtlAtomicExchange32(&call->State, PORT_CALL_COMPLETED); KeSignalEvent(call->CompletionEvent); } @@ -586,7 +587,7 @@ static void KiPortMarkDisconnected(PORT* endpoint, bool removeEndpointPointer) { uintptr_t flags = KeAcquireSpinlock(&connection->Lock); if (!connection->Disconnected[side]) { connection->Disconnected[side] = 1; - _InterlockedIncrement64(&gPortCounters.Disconnects); + NtlAtomicIncrement64(&gPortCounters.Disconnects); } if (removeEndpointPointer && connection->Endpoints[side] == endpoint) connection->Endpoints[side] = (PORT*)0; @@ -594,9 +595,9 @@ static void KiPortMarkDisconnected(PORT* endpoint, bool removeEndpointPointer) { while (!LIST_IS_EMPTY(&endpoint->u.Endpoint.WaitingReceivers)) { LIST_ENTRY* link = KeListPopHeadChecked(&endpoint->u.Endpoint.WaitingReceivers); PORT_RECEIVE_WAIT* waiter = CONTAINING_RECORD(link, PORT_RECEIVE_WAIT, Link); - if (_InterlockedCompareExchange(&waiter->Claimed, 1, 0) == 0) { + if (NtlAtomicCompareExchange32(&waiter->Claimed, 1, 0) == 0) { waiter->Status = NTLSTATUS_PORT_DISCONNECTED; - _InterlockedExchange(&waiter->Completed, 1); + NtlAtomicExchange32(&waiter->Completed, 1); KeSignalEvent(waiter->CompletionEvent); } } @@ -618,7 +619,7 @@ static void KiPortMarkDisconnected(PORT* endpoint, bool removeEndpointPointer) { while (callLink != callHead) { PORT_CALL* call = CONTAINING_RECORD(callLink, PORT_CALL, Link); callLink = callLink->Fwd; - long state = _InterlockedCompareExchange(&call->State, 0, 0); + int32_t state = NtlAtomicLoad32(&call->State); if (state == PORT_CALL_REPLYING || state == PORT_CALL_COMPLETED || state == PORT_CALL_CANCELLED) continue; PORT_MESSAGE* queuedMessage = KiPortRemoveQueuedCallLocked(connection, call); @@ -626,7 +627,7 @@ static void KiPortMarkDisconnected(PORT* endpoint, bool removeEndpointPointer) { KeListInsertTailChecked(&abandonedMessages, &queuedMessage->Link); KeListRemoveChecked(&call->Link); call->Status = NTLSTATUS_PORT_DISCONNECTED; - _InterlockedExchange(&call->State, PORT_CALL_COMPLETED); + NtlAtomicExchange32(&call->State, PORT_CALL_COMPLETED); KeListInsertTailChecked(&completedCalls, &call->Link); } @@ -886,7 +887,7 @@ KSTATUS KePortConnect(PORT* listener, } KeListInsertTailChecked(&listener->u.Listener.Pending, &pending->Link); listener->u.Listener.PendingCount++; - _InterlockedIncrement64(&gPortCounters.ConnectionsCreated); + NtlAtomicIncrement64(&gPortCounters.ConnectionsCreated); KeSignalEvent(listener->u.Listener.WaitEvent); KeReleaseSpinlock(&listener->u.Listener.Lock, flags); @@ -999,7 +1000,7 @@ static KSTATUS KiPortSubmitMessage(PORT* endpoint, if (payloadLength > candidate->PayloadCapacity || message->CapturedCount > candidate->HandleCapacity) continue; - if (_InterlockedCompareExchange(&candidate->Claimed, 1, 0) != 0) + if (NtlAtomicCompareExchange32(&candidate->Claimed, 1, 0) != 0) continue; receiver = candidate; KeListRemoveChecked(&receiver->Link); @@ -1007,7 +1008,7 @@ static KSTATUS KiPortSubmitMessage(PORT* endpoint, } if (receiver) { if (message->Call) - _InterlockedExchange(&message->Call->State, PORT_CALL_DELIVERED); + NtlAtomicExchange32(&message->Call->State, PORT_CALL_DELIVERED); if (!ObTryReferenceObject(&peer->Header)) { receiver->Claimed = 0; KeListInsertHeadChecked(&peer->u.Endpoint.WaitingReceivers, &receiver->Link); @@ -1052,10 +1053,10 @@ static KSTATUS KiPortSubmitMessage(PORT* endpoint, peer->u.Endpoint.IncomingCount++; peer->u.Endpoint.IncomingBytes += message->Info.PayloadLength; peer->u.Endpoint.IncomingHandles += message->CapturedCount; - _InterlockedIncrement64(&gPortCounters.QueuedMessages); - _InterlockedExchangeAdd64(&gPortCounters.QueuedBytes, (long long)message->Info.PayloadLength); + NtlAtomicIncrement64(&gPortCounters.QueuedMessages); + NtlAtomicExchangeAdd64(&gPortCounters.QueuedBytes, (int64_t)message->Info.PayloadLength); if (message->Call) - _InterlockedExchange(&message->Call->State, PORT_CALL_PENDING); + NtlAtomicExchange32(&message->Call->State, PORT_CALL_PENDING); KeSignalEvent(peer->u.Endpoint.WaitEvent); if (peer->u.Endpoint.IncomingCount >= SYS_PORT_QUEUE_MESSAGE_LIMIT || peer->u.Endpoint.IncomingBytes >= SYS_PORT_QUEUE_BYTE_LIMIT || @@ -1160,7 +1161,7 @@ static KSTATUS KiPortDeliverQueued(PORT* endpoint, PORT_CONNECTION* connection = endpoint->u.Endpoint.Connection; bool completeCall = false; uintptr_t flags = KeAcquireSpinlock(&connection->Lock); - long callState = _InterlockedCompareExchange(&call->State, 0, 0); + int32_t callState = NtlAtomicLoad32(&call->State); if (callState != PORT_CALL_CANCELLED && callState != PORT_CALL_COMPLETED) { if (call->Link.Fwd != &call->Link && call->Link.Bwd != &call->Link) KeListRemoveChecked(&call->Link); @@ -1176,7 +1177,7 @@ static KSTATUS KiPortDeliverQueued(PORT* endpoint, if (message->CapturedCount) memcpy(handlesOut, handles, sizeof(SYS_HANDLE) * message->CapturedCount); *infoOut = message->Info; - _InterlockedIncrement64(&gPortCounters.QueuedDeliveries); + NtlAtomicIncrement64(&gPortCounters.QueuedDeliveries); return NTLSTATUS_SUCCESS; } @@ -1246,7 +1247,7 @@ KSTATUS KePortReceive(PORT* endpoint, KeListRemoveChecked(&message->Link); KiPortNoteDequeuedLocked(endpoint, message); if (message->Call) - _InterlockedExchange(&message->Call->State, PORT_CALL_DELIVERED); + NtlAtomicExchange32(&message->Call->State, PORT_CALL_DELIVERED); KeReleaseSpinlock(&connection->Lock, flags); result = KiPortDeliverQueued(endpoint, @@ -1292,7 +1293,7 @@ KSTATUS KePortReceive(PORT* endpoint, (uint32_t*)0); flags = KeAcquireSpinlock(&connection->Lock); - if (_InterlockedCompareExchange(&wait.Completed, 0, 0) != 0) { + if (NtlAtomicLoad32(&wait.Completed) != 0) { if (wait.Link.Fwd != &wait.Link || wait.Link.Bwd != &wait.Link) KeListRemoveChecked(&wait.Link); if (endpoint->u.Endpoint.IncomingCount == 0 && @@ -1308,7 +1309,7 @@ KSTATUS KePortReceive(PORT* endpoint, break; } - if (_InterlockedCompareExchange(&wait.Claimed, 0, 0) != 0) { + if (NtlAtomicLoad32(&wait.Claimed) != 0) { KeReleaseSpinlock(&connection->Lock, flags); EVENT* claimedEvents[1] = { wait.CompletionEvent }; (void)KeWaitForMultipleEvents(claimedEvents, @@ -1422,7 +1423,7 @@ static KSTATUS KiPortCreateCall(PORT* endpoint, static void KiPortReleaseCall(PORT_CALL* call) { if (!call) return; - long refs = _InterlockedDecrement(&call->RefCount); + int32_t refs = NtlAtomicDecrement32(&call->RefCount); if (refs > 0) return; if (refs < 0) { @@ -1530,7 +1531,7 @@ KSTATUS KePortCall(PORT* endpoint, if (!KSUCCESS(waitStatus)) { PORT_MESSAGE* queuedMessage = (PORT_MESSAGE*)0; uintptr_t flags = KeAcquireSpinlock(&call->Connection->Lock); - long state = _InterlockedCompareExchange(&call->State, 0, 0); + int32_t state = NtlAtomicLoad32(&call->State); if (state == PORT_CALL_REPLYING) { KeReleaseSpinlock(&call->Connection->Lock, flags); (void)KeWaitForMultipleEvents(events, 1, 0, SYS_PORT_TIMEOUT_INFINITE, (uint32_t*)0); @@ -1539,7 +1540,7 @@ KSTATUS KePortCall(PORT* endpoint, } else { if (call->Link.Fwd != &call->Link && call->Link.Bwd != &call->Link) KeListRemoveChecked(&call->Link); - _InterlockedExchange(&call->State, PORT_CALL_CANCELLED); + NtlAtomicExchange32(&call->State, PORT_CALL_CANCELLED); queuedMessage = KiPortRemoveQueuedCallLocked(call->Connection, call); KeReleaseSpinlock(&call->Connection->Lock, flags); if (queuedMessage) @@ -1602,7 +1603,7 @@ KSTATUS KePortReply(PORT* endpoint, } } - if (!call || _InterlockedCompareExchange(&call->State, 0, 0) != PORT_CALL_DELIVERED) { + if (!call || NtlAtomicLoad32(&call->State) != PORT_CALL_DELIVERED) { KeReleaseSpinlock(&connection->Lock, flags); for (uint32_t i = 0; i < transferCount; ++i) KeReleaseCapturedHandle(captured[i]); @@ -1615,7 +1616,7 @@ KSTATUS KePortReply(PORT* endpoint, return NTLSTATUS_BUFFER_TOO_SMALL; } - _InterlockedExchange(&call->State, PORT_CALL_REPLYING); + NtlAtomicExchange32(&call->State, PORT_CALL_REPLYING); KeListRemoveChecked(&call->Link); KeReleaseSpinlock(&connection->Lock, flags); @@ -1649,9 +1650,9 @@ KSTATUS KePortReply(PORT* endpoint, } if (KSUCCESS(status)) - _InterlockedIncrement64(&gPortCounters.RepliesCompleted); + NtlAtomicIncrement64(&gPortCounters.RepliesCompleted); call->Status = status; - _InterlockedExchange(&call->State, PORT_CALL_COMPLETED); + NtlAtomicExchange32(&call->State, PORT_CALL_COMPLETED); KeSignalEvent(call->CompletionEvent); return status; } diff --git a/code/kernel/ke_process.c b/code/kernel/ke_process.c index 52a57ce..04302c3 100644 --- a/code/kernel/ke_process.c +++ b/code/kernel/ke_process.c @@ -1,5 +1,7 @@ // Kernel process model. This file owns process lifetime, thread membership, and address-space metadata. +#include +#include #include #include #include @@ -145,8 +147,8 @@ size_t KeCopyProcessInfo(struct SYS_PROCESS_INFO* infoOut, size_t maxCount) { KeReleaseSpinlock(&p->ThreadLock, threadFlags); uint32_t handleCount = KeGetHandleCount(p); int32_t exitCode = p->ExitCode; - long terminating = p->Terminating; - long exited = p->Exited; + int32_t terminating = p->Terminating; + int32_t exited = p->Exited; info->ThreadCount = threadCount; info->HandleCount = handleCount; @@ -170,7 +172,7 @@ size_t KeCopyProcessInfo(struct SYS_PROCESS_INFO* infoOut, size_t maxCount) { } else if (terminating) { info->State = SYS_PROCESS_STATE_TERMINATING; info->Flags |= SYS_PROCESS_FLAG_TERMINATING; - } else if (_InterlockedCompareExchange(&p->BuildState, 0, 0) != KE_PROCESS_BUILD_STARTED) { + } else if (NtlAtomicLoad32(&p->BuildState) != KE_PROCESS_BUILD_STARTED) { info->State = SYS_PROCESS_STATE_CONSTRUCTING; info->Flags |= SYS_PROCESS_FLAG_CONSTRUCTING; } else { @@ -298,7 +300,7 @@ KSTATUS KeCopyProcessThreadInfo(uint32_t pid, struct SYS_THREAD_INFO* infoOut, s // both observe a process whose threads are already gone. static void KiProcessRundownResources(PROCESS* proc) { if (!proc) return; - if (_InterlockedExchange(&proc->ResourcesReleased, 1) != 0) + if (NtlAtomicExchange32(&proc->ResourcesReleased, 1) != 0) return; IoReleaseFileLocksForProcess(proc); @@ -377,6 +379,9 @@ KSTATUS KeCreateProcess(const char* name, SECURITY_CONTEXT* securityContext, PRO KiProcessInit(); if (!name || !securityContext || !out) return NTLSTATUS_INVALID_PARAMETER; + size_t nameLength = strlen(name); + if (!SysSvIsUtf8Text(SysSvFromParts(name, nameLength))) + return NTLSTATUS_INVALID_PARAMETER; uint32_t processId = 0; KSTATUS idStatus = KeAllocateId32(&gProcessIds, &processId); @@ -391,8 +396,14 @@ KSTATUS KeCreateProcess(const char* name, SECURITY_CONTEXT* securityContext, PRO proc->Flags = 0; proc->SecurityContext = securityContext; ObReferenceObject(&securityContext->Header); - strncpy(proc->Name, name, PROCESS_NAME_MAX - 1); - proc->Name[PROCESS_NAME_MAX - 1] = '\0'; + // Truncate the diagnostic name on a scalar boundary, never inside UTF-8. + size_t nameCopy = nameLength < PROCESS_NAME_MAX ? nameLength : PROCESS_NAME_MAX - 1u; + if (nameCopy < nameLength) { + while (nameCopy && (((uint8_t)name[nameCopy] & 0xC0u) == 0x80u)) + --nameCopy; + } + memcpy(proc->Name, name, nameCopy); + proc->Name[nameCopy] = '\0'; LIST_INIT(&proc->ThreadList); proc->ThreadCount = 0; @@ -463,8 +474,7 @@ static KSTATUS KiCopyEnvironmentStringView(PROCESS* proc, *baseOut = 0; *sizeOut = 0; - if (!SysSvIsValid(src)) return NTLSTATUS_INVALID_PARAMETER; - if (SysSvContainsByte(src, '\0')) return NTLSTATUS_INVALID_PARAMETER; + if (!SysSvIsUtf8Text(src)) return NTLSTATUS_INVALID_PARAMETER; if (src.Length == (size_t)-1) return NTLSTATUS_BUFFER_TOO_SMALL; size_t len = src.Length + 1; @@ -509,9 +519,7 @@ KSTATUS KeSetupUserProcessEnvironment(PROCESS* proc, SYS_STRING_VIEW imagePath, SYS_STRING_VIEW commandLine) { if (!proc || imagePath.Length == 0) return NTLSTATUS_INVALID_PARAMETER; - if (!SysSvIsValid(imagePath) || !SysSvIsValid(commandLine)) - return NTLSTATUS_INVALID_PARAMETER; - if (SysSvContainsByte(imagePath, '\0') || SysSvContainsByte(commandLine, '\0')) + if (!SysSvIsUtf8Text(imagePath) || !SysSvIsUtf8Text(commandLine)) return NTLSTATUS_INVALID_PARAMETER; SYS_STRING_VIEW finalCommandLine = commandLine.Length ? commandLine : imagePath; @@ -594,19 +602,19 @@ KSTATUS KeSetProcessLoaderData(PROCESS* proc, const void* loaderData) { // Stops new process-construction operations. Active builders defer final teardown until they leave. void KeStopProcessConstruction(PROCESS* process) { if (!process) return; - _InterlockedExchange(&process->BuildState, KE_PROCESS_BUILD_DEAD); + NtlAtomicExchange32(&process->BuildState, KE_PROCESS_BUILD_DEAD); } // Process exit / termination. void KeCompleteProcessExitIfLastThread(PROCESS* process) { if (!process) return; - if (_InterlockedCompareExchange(&process->BuildOperations, 0, 0) != 0) return; + if (NtlAtomicLoad32(&process->BuildOperations) != 0) return; uintptr_t flags = KeAcquireSpinlock(&process->ThreadLock); uint32_t threadCount = process->ThreadCount; KeReleaseSpinlock(&process->ThreadLock, flags); - if (threadCount == 0 && _InterlockedExchange(&process->Exited, 1) == 0) { + if (threadCount == 0 && NtlAtomicExchange32(&process->Exited, 1) == 0) { KiProcessRundownResources(process); if (process->ExitEvent) KeSignalEvent(process->ExitEvent); } @@ -624,9 +632,9 @@ KSTATUS KeTerminateProcess(PROCESS* process, int32_t exitCode) { if (KeIsProcessTerminationProtected(process)) return NTLSTATUS_ACCESS_DENIED; process->ExitCode = exitCode; - _InterlockedExchange(&process->Terminating, 1); + NtlAtomicExchange32(&process->Terminating, 1); KeStopProcessConstruction(process); - if (_InterlockedCompareExchange(&process->BuildOperations, 0, 0) != 0) + if (NtlAtomicLoad32(&process->BuildOperations) != 0) return NTLSTATUS_SUCCESS; uintptr_t flags = KeAcquireSpinlock(&process->ThreadLock); @@ -655,7 +663,7 @@ KSTATUS KeTerminateProcess(PROCESS* process, int32_t exitCode) { if (t->RunningCpu != KE_INVALID_CPU && t->RunningCpu != selfIndex && t->RunningCpu < gCpuCount) { KCPU* cpu = &gCpus[t->RunningCpu]; if (cpu->State == KeCpuOnline) { - _InterlockedExchange(&cpu->ReschedulePending, 1); + NtlAtomicExchange32(&cpu->ReschedulePending, 1); KeSendProcessorInterrupt(cpu->HardwareId, KE_VECTOR_RESCHEDULE_IPI); } } @@ -667,12 +675,12 @@ KSTATUS KeTerminateProcess(PROCESS* process, int32_t exitCode) { } // Checks whether process Terminating. -int KeIsProcessTerminating(PROCESS* process) { +bool KeIsProcessTerminating(PROCESS* process) { return process && process->Terminating != 0; } // Returns whether process termination is forbidden. -int KeIsProcessTerminationProtected(PROCESS* process) { +bool KeIsProcessTerminationProtected(PROCESS* process) { return process && (process->Flags & KE_PROCESS_FLAG_TERMINATION_PROTECTED) != 0; } diff --git a/code/kernel/ke_sched.c b/code/kernel/ke_sched.c index 469e745..5e2bd33 100644 --- a/code/kernel/ke_sched.c +++ b/code/kernel/ke_sched.c @@ -1,5 +1,6 @@ // Kernel scheduler. This file owns run queues, dispatch decisions, preemption, and CPU-local scheduling state. +#include #include #include #include @@ -15,7 +16,6 @@ #include #include #include -#include #include #include #include @@ -35,7 +35,7 @@ static KE_SCHED_STATS gSchedStats; static PROCESS* gIdleProcess; // Tests whether one scheduling rank outranks another. -static int KiRankOutranks(KE_SCHEDULING_CLASS leftClass, uint32_t leftPriority, +static bool KiRankOutranks(KE_SCHEDULING_CLASS leftClass, uint32_t leftPriority, KE_SCHEDULING_CLASS rightClass, uint32_t rightPriority) { if (leftClass != rightClass) return leftClass == KeSchedulingRealtime; @@ -43,15 +43,15 @@ static int KiRankOutranks(KE_SCHEDULING_CLASS leftClass, uint32_t leftPriority, } // Tests whether two scheduling ranks are equal. -static int KiRanksEqual(KE_SCHEDULING_CLASS leftClass, uint32_t leftPriority, +static bool KiRanksEqual(KE_SCHEDULING_CLASS leftClass, uint32_t leftPriority, KE_SCHEDULING_CLASS rightClass, uint32_t rightPriority) { return leftClass == rightClass && leftPriority == rightPriority; } // Compares two threads by effective scheduling rank. -static int KiSchedulerThreadOutranks(const THREAD* left, const THREAD* right) { - if (!left) return 0; - if (!right) return 1; +static bool KiSchedulerThreadOutranks(const THREAD* left, const THREAD* right) { + if (!left) return false; + if (!right) return true; return KiRankOutranks(left->EffectiveSchedulingClass, left->EffectivePriority, right->EffectiveSchedulingClass, right->EffectivePriority); } @@ -97,8 +97,8 @@ THREAD* KiSchedulerSelectMutexWaiter(MUTEX* mutex) { } // Refreshes one mutex donation while the caller holds the mutex lock. -int KiSchedulerRefreshMutexDonation(MUTEX* mutex) { - if (!mutex) return 0; +bool KiSchedulerRefreshMutexDonation(MUTEX* mutex) { + if (!mutex) return false; uintptr_t flags = KeAcquireSpinlock(&gSchedLock); THREAD* highest = (THREAD*)0; @@ -122,7 +122,7 @@ int KiSchedulerRefreshMutexDonation(MUTEX* mutex) { mutex->HasDonation = 0; } - int changed = oldHasDonation != mutex->HasDonation || + bool changed = oldHasDonation != mutex->HasDonation || oldClass != mutex->DonationClass || oldPriority != mutex->DonationPriority; KeReleaseSpinlock(&gSchedLock, flags); @@ -138,32 +138,32 @@ static uint64_t KiDefaultThreadAffinityMask(void) { } // Implements the thread Is Killed Locked operation. -static int KiThreadIsKilledLocked(const THREAD* thread) { +static bool KiThreadIsKilledLocked(const THREAD* thread) { return thread && !thread->IsIdleThread && thread->Owner && KeIsProcessTerminating(thread->Owner) && !KeThreadTerminationDeferred(thread); } // Implements the thread Can Run On Cpu Locked operation. -static int KiThreadCanRunOnCpuLocked(const THREAD* thread, const KCPU* cpu) { - if (!thread || !cpu) return 0; +static bool KiThreadCanRunOnCpuLocked(const THREAD* thread, const KCPU* cpu) { + if (!thread || !cpu) return false; if (thread->IsIdleThread) return thread == cpu->IdleThread; - if (cpu->CpuIndex >= 64) return 0; + if (cpu->CpuIndex >= 64) return false; return (thread->AffinityMask & (1ULL << cpu->CpuIndex)) != 0; } // Implements the cpu Is Idle Locked operation. -static int KiCpuIsIdleLocked(const KCPU* cpu) { +static bool KiCpuIsIdleLocked(const KCPU* cpu) { if (!cpu || cpu->State != KeCpuOnline) - return 0; + return false; THREAD* cur = cpu->CurrentThread; return (!cur || cur == cpu->IdleThread || cur->State != ThreadRunning); } // Implements the any Idle Primary For Thread Locked operation. -static int KiAnyIdlePrimaryForThreadLocked(const THREAD* thread) { +static bool KiAnyIdlePrimaryForThreadLocked(const THREAD* thread) { if (!thread || thread->IsIdleThread) - return 0; + return false; for (uint32_t i = 0; i < gCpuCount; i++) { KCPU* cpu = &gCpus[i]; @@ -174,17 +174,17 @@ static int KiAnyIdlePrimaryForThreadLocked(const THREAD* thread) { if (!KiCpuIsIdleLocked(cpu)) continue; if (KiThreadCanRunOnCpuLocked(thread, cpu)) - return 1; + return true; } - return 0; + return false; } // Checks whether defer To Idle Primary Locked. -static int KiShouldDeferToIdlePrimaryLocked(KCPU* cpu, const THREAD* thread) { +static bool KiShouldDeferToIdlePrimaryLocked(KCPU* cpu, const THREAD* thread) { if (!cpu || !thread || thread->IsIdleThread) - return 0; + return false; if (cpu->IsPrimaryThread || cpu->SmtThreadCount <= 1) - return 0; + return false; // Do not fill a secondary SMT sibling while any primary hardware thread // that can run this thread is idle. KiPokeRunnableCpusLocked targets those @@ -198,7 +198,7 @@ static void KiRequestRescheduleCpuLocked(KCPU* target) { if (!target || target->State != KeCpuOnline) return; - _InterlockedExchange(&target->ReschedulePending, 1); + NtlAtomicExchange32(&target->ReschedulePending, 1); KCPU* self = KeGetCurrentCpu(); if (self && target->CpuIndex != self->CpuIndex) { @@ -389,8 +389,8 @@ static void KiInsertReadyLocked(THREAD* thread) { } // Tests whether any ready thread outranks one running thread on a CPU. -static int KiAnyRunnableOutrankingThreadForCpuLocked(KCPU* cpu, const THREAD* current) { - if (!cpu || !current) return 0; +static bool KiAnyRunnableOutrankingThreadForCpuLocked(KCPU* cpu, const THREAD* current) { + if (!cpu || !current) return false; for (int p = (int)KE_REALTIME_PRIORITY_MAX; p >= 0; p--) { LIST_ENTRY* head = &gRealtimeRunQueue[p]; @@ -399,7 +399,7 @@ static int KiAnyRunnableOutrankingThreadForCpuLocked(KCPU* cpu, const THREAD* cu if (!KiThreadCanRunOnCpuLocked(thread, cpu)) continue; if (KiSchedulerThreadOutranks(thread, current)) - return 1; + return true; } } @@ -410,16 +410,16 @@ static int KiAnyRunnableOutrankingThreadForCpuLocked(KCPU* cpu, const THREAD* cu if (!KiThreadCanRunOnCpuLocked(thread, cpu)) continue; if (KiSchedulerThreadOutranks(thread, current)) - return 1; + return true; } } - return 0; + return false; } // Tests whether a real-time peer at the current or higher priority is ready. -static int KiAnyRealtimePeerForCpuLocked(KCPU* cpu, uint32_t minimumPriority) { - if (!cpu) return 0; +static bool KiAnyRealtimePeerForCpuLocked(KCPU* cpu, uint32_t minimumPriority) { + if (!cpu) return false; if (minimumPriority > KE_REALTIME_PRIORITY_MAX) minimumPriority = KE_REALTIME_PRIORITY_MAX; @@ -428,10 +428,10 @@ static int KiAnyRealtimePeerForCpuLocked(KCPU* cpu, uint32_t minimumPriority) { for (LIST_ENTRY* node = head->Fwd; node != head; node = node->Fwd) { THREAD* thread = CONTAINING_RECORD(node, THREAD, SchedulerLink); if (KiThreadCanRunOnCpuLocked(thread, cpu)) - return 1; + return true; } } - return 0; + return false; } // Computes one thread's effective rank from its base rank and mutex donations. @@ -458,13 +458,13 @@ static void KiComputeEffectiveRankLocked(THREAD* thread, } // Applies one effective-rank change and preserves ready-queue ordering. -static int KiApplyEffectiveRankLocked(THREAD* thread, +static bool KiApplyEffectiveRankLocked(THREAD* thread, KE_SCHEDULING_CLASS schedulingClass, uint32_t priority) { KE_SCHEDULING_CLASS oldClass = thread->EffectiveSchedulingClass; uint32_t oldPriority = thread->EffectivePriority; if (KiRanksEqual(oldClass, oldPriority, schedulingClass, priority)) - return 0; + return false; int wasQueued = thread->Queued != 0; if (wasQueued) { @@ -485,12 +485,12 @@ static int KiApplyEffectiveRankLocked(THREAD* thread, KiRequestRescheduleCpuLocked(cpu); } - return 1; + return true; } // Recomputes effective rank from current base rank and owned mutexes. -int KiSchedulerRefreshEffectivePriority(THREAD* thread, MUTEX** waitingMutexOut) { - if (!thread) return 0; +bool KiSchedulerRefreshEffectivePriority(THREAD* thread, MUTEX** waitingMutexOut) { + if (!thread) return false; uintptr_t flags = KeAcquireSpinlock(&gSchedLock); KE_SCHEDULING_CLASS oldClass = thread->EffectiveSchedulingClass; @@ -498,7 +498,7 @@ int KiSchedulerRefreshEffectivePriority(THREAD* thread, MUTEX** waitingMutexOut) KE_SCHEDULING_CLASS schedulingClass; uint32_t priority; KiComputeEffectiveRankLocked(thread, &schedulingClass, &priority); - int changed = KiApplyEffectiveRankLocked(thread, schedulingClass, priority); + bool changed = KiApplyEffectiveRankLocked(thread, schedulingClass, priority); if (changed) { if (KiRankOutranks(schedulingClass, priority, oldClass, oldPriority)) gSchedStats.PriorityInheritanceBoosts++; @@ -692,7 +692,7 @@ KSTATUS KiSchedulerSetNormalPriority(THREAD* thread, THREAD_PRIORITY priority) { return NTLSTATUS_SUCCESS; } -static int KiAnyRunnableForCpuLocked(KCPU* cpu); +static bool KiAnyRunnableForCpuLocked(KCPU* cpu); // Implements the any Runnable For Current Cpu operation. static int KiAnyRunnableForCurrentCpu(void) { @@ -856,7 +856,7 @@ void KeInitializeScheduler(void) { KePanic(KE_PANIC_CODE_INIT, "scheduler: failed to create CPU%u idle thread, status=%u\n", (unsigned)cpu->CpuIndex, s); - _InterlockedExchange(&cpu->State, KeCpuOnline); + NtlAtomicExchange32(&cpu->State, KeCpuOnline); } // Safely release the lock and clean up any dead threads natively post-switch. @@ -980,7 +980,7 @@ void KeUnreadyThread(THREAD* thread) { } // Implements the any Runnable For Cpu Locked operation. -static int KiAnyRunnableForCpuLocked(KCPU* cpu) { +static bool KiAnyRunnableForCpuLocked(KCPU* cpu) { if (!cpu) cpu = KeGetCurrentCpu(); for (uint32_t p = 0; p < KE_REALTIME_PRIORITY_COUNT; p++) { @@ -991,7 +991,7 @@ static int KiAnyRunnableForCpuLocked(KCPU* cpu) { continue; if (KiShouldDeferToIdlePrimaryLocked(cpu, thread)) continue; - return 1; + return true; } } @@ -1003,10 +1003,10 @@ static int KiAnyRunnableForCpuLocked(KCPU* cpu) { continue; if (KiShouldDeferToIdlePrimaryLocked(cpu, thread)) continue; - return 1; + return true; } } - return 0; + return false; } // Removes the first eligible thread from one ready queue. @@ -1053,13 +1053,13 @@ static THREAD* KiPickNextForCpu(KCPU* cpu) { void KiRequestReschedule(void) { KCPU* cpu = KeGetCurrentCpu(); if (cpu) - _InterlockedExchange(&cpu->ReschedulePending, 1); + NtlAtomicExchange32(&cpu->ReschedulePending, 1); } // Checks whether reschedule Pending. -int KiIsReschedulePending(void) { +bool KiIsReschedulePending(void) { KCPU* cpu = KeGetCurrentCpu(); - return cpu && _InterlockedCompareExchange(&cpu->ReschedulePending, 0, 0) != 0; + return cpu && NtlAtomicLoad32(&cpu->ReschedulePending) != 0; } // Implements the schedule operation. @@ -1069,7 +1069,7 @@ static void KiSchedule(void) { THREAD* idle = cpu->IdleThread; if (!cur) return; - _InterlockedExchange(&cpu->ReschedulePending, 0); + NtlAtomicExchange32(&cpu->ReschedulePending, 0); uintptr_t flags = KeAcquireSpinlock(&gSchedLock); KiArmPostSwitchUnlock(cur, flags); @@ -1296,14 +1296,14 @@ static THREAD* KiTakeExpiredTimeoutLocked(uint64_t nowTick, KSTATUS* statusOut) } // Reports whether one deadline is ready while the scheduler lock is held. -static int KiHasExpiredTimeoutLocked(uint64_t nowTick) { +static bool KiHasExpiredTimeoutLocked(uint64_t nowTick) { LIST_ENTRY* head = &gTimeoutList; for (LIST_ENTRY* node = head->Fwd; node != head; node = node->Fwd) { THREAD* thread = CONTAINING_RECORD(node, THREAD, TimeoutLink); if (thread->TimeoutTick <= nowTick) - return 1; + return true; } - return 0; + return false; } // Resolves expired deadlines at passive level through each object's abort path. @@ -1326,7 +1326,7 @@ static void KiTimeoutWorkRoutine(void* context) { } // Checks whether this CPU owns global scheduler tick duties. -static int KiCpuOwnsGlobalSchedulerTick(const KCPU* cpu) { +static bool KiCpuOwnsGlobalSchedulerTick(const KCPU* cpu) { // Periodic timer interrupts are per-CPU, but sleep deadlines, waitable timers, // input repeat, and priority aging are global scheduler services. Keep the // global clock single-sourced on the boot processor so enabling AP timers does not @@ -1367,7 +1367,6 @@ static void KiAgeReadyThreadsLocked(void) { // Runs scheduler work that must be single-sourced for the whole system. static int KiRunGlobalSchedulerTickLocked(void) { gSchedStats.Ticks++; - KeInputTimerTick(); KiAgeReadyThreadsLocked(); return KiHasExpiredTimeoutLocked(gSchedStats.Ticks); } diff --git a/code/kernel/ke_settings.c b/code/kernel/ke_settings.c index 2c14249..dbd726b 100644 --- a/code/kernel/ke_settings.c +++ b/code/kernel/ke_settings.c @@ -1,5 +1,6 @@ // Kernel settings reader. This file performs a direct lookup in the simple system settings file. +#include #include #include #include @@ -12,7 +13,7 @@ #define KE_SETTINGS_MAX_FILE_BYTES (64u * 1024u) // Returns whether one settings.ini character is horizontal whitespace. -static int KeSettingSpace(char c) { +static bool KeSettingSpace(char c) { return c == ' ' || c == '\t' || c == '\r'; } @@ -25,10 +26,10 @@ static void KeSettingTrim(const char** begin, const char** end) { } // Returns whether one immutable span exactly matches a NUL-terminated string. -static int KeSettingSpanEquals(const char* begin, const char* end, const char* text) { +static bool KeSettingSpanEquals(const char* begin, const char* end, const char* text) { while (begin < end && *text) { if (*begin++ != *text++) - return 0; + return false; } return begin == end && *text == '\0'; } diff --git a/code/kernel/ke_smp.c b/code/kernel/ke_smp.c index 4df76ee..7e64c32 100644 --- a/code/kernel/ke_smp.c +++ b/code/kernel/ke_smp.c @@ -4,6 +4,7 @@ // The kernel asks the HAL for platform processor identities, prepares the x86 // bootstrap state, asks the HAL to start each processor, and waits for it to // enter KiApKernelEntry() and park in kernel-owned state. +#include #include #include #include @@ -323,12 +324,12 @@ _Noreturn void KiApKernelEntry(KCPU* cpu) { KeLoadIdtForCurrentCpu(); if (gApBootBlock) gApBootBlock->Stage = AP_STAGE_IDT; - _InterlockedExchange(&cpu->State, KeCpuStarted); + NtlAtomicExchange32(&cpu->State, KeCpuStarted); cpu->CurrentIrql = PASSIVE_LEVEL; cpu->InterruptDepth = 0; cpu->ReschedulePending = 0; - _InterlockedExchange(&cpu->State, KeCpuParked); + NtlAtomicExchange32(&cpu->State, KeCpuParked); if (gApBootBlock) { gApBootBlock->Stage = AP_STAGE_PARK; gApBootBlock->State = AP_STATE_KERNEL_PARKED; @@ -337,7 +338,7 @@ _Noreturn void KiApKernelEntry(KCPU* cpu) { while (true) { cpu->SchedulerTicks++; - if (_InterlockedCompareExchange(&cpu->State, 0, 0) == KeCpuAdmissionRequested) { + if (NtlAtomicLoad32(&cpu->State) == KeCpuAdmissionRequested) { THREAD* idle = cpu->IdleThread; if (idle) { cpu->CurrentThread = idle; @@ -349,7 +350,7 @@ _Noreturn void KiApKernelEntry(KCPU* cpu) { // a current idle thread before they may be delivered. KeConfigureCurrentProcessorTimer(); - _InterlockedExchange(&cpu->State, KeCpuSchedulerActive); + NtlAtomicExchange32(&cpu->State, KeCpuSchedulerActive); KeEnableInterrupts(); KeStartInitialThread(idle); } @@ -360,7 +361,7 @@ _Noreturn void KiApKernelEntry(KCPU* cpu) { // changes between the poll and HLT: check once with interrupts // disabled, then use STI;HLT as a single sleep transition. uintptr_t interruptFlags = KeDisableInterrupts(); - if (_InterlockedCompareExchange(&cpu->State, 0, 0) == KeCpuAdmissionRequested) { + if (NtlAtomicLoad32(&cpu->State) == KeCpuAdmissionRequested) { KeRestoreInterrupts(interruptFlags); continue; } @@ -742,7 +743,7 @@ KSTATUS KeStartApplicationProcessors(void) { if (!KSUCCESS(st)) { DbgPrint("SMP: AP%u hardware=%u stack allocation failed status=0x%x; disabling AP and continuing\n", i, cpu->HardwareId, st); - _InterlockedExchange(&cpu->State, KeCpuAbsent); + NtlAtomicExchange32(&cpu->State, KeCpuAbsent); continue; } @@ -755,7 +756,7 @@ KSTATUS KeStartApplicationProcessors(void) { if (!KSUCCESS(st)) { DbgPrint("SMP: AP%u hardware=%u startup IPI failed status=0x%x; disabling AP and continuing\n", i, cpu->HardwareId, st); - _InterlockedExchange(&cpu->State, KeCpuAbsent); + NtlAtomicExchange32(&cpu->State, KeCpuAbsent); continue; } @@ -777,11 +778,11 @@ KSTATUS KeStartApplicationProcessors(void) { DbgPrint("SMP: AP%u failed to reach kernel park state hardware=%u state=%u stage=0x%x(%s) heartbeat=%llu last=%llu; disabling AP and continuing\n", i, cpu->HardwareId, gApBootBlock->State, gApBootBlock->Stage, KiApStageName(gApBootBlock->Stage), gApBootBlock->Heartbeat, lastHeartbeat); - _InterlockedExchange(&cpu->State, KeCpuAbsent); + NtlAtomicExchange32(&cpu->State, KeCpuAbsent); continue; } - if (_InterlockedCompareExchange(&cpu->State, 0, 0) != KeCpuParked) { + if (NtlAtomicLoad32(&cpu->State) != KeCpuParked) { KePanic(KE_PANIC_CODE_INIT, "SMP: AP%u boot block parked without CPU state transition state=%ld\n", i, cpu->State); @@ -812,7 +813,7 @@ KSTATUS KeAdmitApplicationProcessorsToScheduler(void) { KCPU* cpu = &gCpus[i]; if (cpu->State == KeCpuAbsent) continue; - if (_InterlockedCompareExchange(&cpu->State, 0, 0) != KeCpuParked) { + if (NtlAtomicLoad32(&cpu->State) != KeCpuParked) { KePanic(KE_PANIC_CODE_INIT, "SMP: AP%u cannot enter scheduler from state=%ld\n", i, cpu->State); @@ -824,7 +825,7 @@ KSTATUS KeAdmitApplicationProcessorsToScheduler(void) { return st; KiSmpPoolCheckpoint("smp after AP idle create", i); - _InterlockedExchange(&cpu->State, KeCpuAdmissionRequested); + NtlAtomicExchange32(&cpu->State, KeCpuAdmissionRequested); KiSmpPoolCheckpoint("smp before AP wake", i); // Parked secondary processors sleep with no periodic timer armed yet. The @@ -834,11 +835,11 @@ KSTATUS KeAdmitApplicationProcessorsToScheduler(void) { // the AP has installed its idle thread. KeSendProcessorInterrupt(cpu->HardwareId, KE_VECTOR_RESCHEDULE_IPI); - int active = 0; + bool active = false; uint64_t schedulerDeadline = KiSmpDeadlineAfter(AP_SCHED_TIMEOUT_US); for (;;) { - if (_InterlockedCompareExchange(&cpu->State, 0, 0) == KeCpuSchedulerActive) { - active = 1; + if (NtlAtomicLoad32(&cpu->State) == KeCpuSchedulerActive) { + active = true; break; } if (KiSmpQueryTicks() >= schedulerDeadline) @@ -852,7 +853,7 @@ KSTATUS KeAdmitApplicationProcessorsToScheduler(void) { i, cpu->HardwareId, cpu->State, cpu->SchedulerTicks); } - _InterlockedExchange(&cpu->State, KeCpuOnline); + NtlAtomicExchange32(&cpu->State, KeCpuOnline); KiSmpPoolCheckpoint("smp after AP scheduler active", i); admitted++; if (KiSmpDiagnosticsEnabled()) @@ -875,11 +876,11 @@ typedef struct SMP_AP_WORKER_ARG { uint32_t WorkerIndex; struct SMP_AP_WORK_TEST* Test; EVENT* StepEvent; - volatile long Ready; - volatile long WaitArmed; - volatile long Progress; - volatile long Done; - volatile long Failure; + volatile int32_t Ready; + volatile int32_t WaitArmed; + volatile int32_t Progress; + volatile int32_t Done; + volatile int32_t Failure; uint32_t WrongCpu; uint32_t WrongRunningCpu; } SMP_AP_WORKER_ARG; @@ -887,10 +888,10 @@ typedef struct SMP_AP_WORKER_ARG { struct SMP_AP_WORK_TEST { uint64_t Magic; uint32_t WorkerCount; - volatile long ReadyCount; - volatile long DoneCount; - volatile long FailureCount; - volatile long CpuHits[KE_MAX_CPUS]; + volatile int32_t ReadyCount; + volatile int32_t DoneCount; + volatile int32_t FailureCount; + volatile int32_t CpuHits[KE_MAX_CPUS]; SMP_AP_WORKER_ARG Workers[KE_MAX_CPUS]; }; @@ -901,14 +902,14 @@ static void KiSmpApControlledWorker(void* context) { KePanic(KE_PANIC_CODE_SELFTEST, "SMP AP worker received bad context 0x%016llX\n", (uint64_t)arg); } - _InterlockedExchange(&arg->Ready, 1); - _InterlockedIncrement(&arg->Test->ReadyCount); + NtlAtomicExchange32(&arg->Ready, 1); + NtlAtomicIncrement32(&arg->Test->ReadyCount); for (uint32_t round = 0; round < SMP_AP_WORKER_ROUNDS; round++) { - _InterlockedIncrement(&arg->WaitArmed); + NtlAtomicIncrement32(&arg->WaitArmed); KSTATUS waitStatus = KeWaitForEvent(arg->StepEvent, 0); if (!KSUCCESS(waitStatus)) { - _InterlockedExchange(&arg->Failure, 1); + NtlAtomicExchange32(&arg->Failure, 1); break; } @@ -916,49 +917,49 @@ static void KiSmpApControlledWorker(void* context) { THREAD* self = KeGetCurrentThread(); if (!cpu || cpu->CpuIndex != arg->CpuIndex) { arg->WrongCpu = cpu ? cpu->CpuIndex : KE_INVALID_CPU; - _InterlockedExchange(&arg->Failure, 1); + NtlAtomicExchange32(&arg->Failure, 1); break; } if (!self || self->RunningCpu != cpu->CpuIndex) { arg->WrongRunningCpu = self ? self->RunningCpu : KE_INVALID_CPU; - _InterlockedExchange(&arg->Failure, 1); + NtlAtomicExchange32(&arg->Failure, 1); break; } if (self && (self->AffinityMask & (1ULL << cpu->CpuIndex)) == 0) { arg->WrongCpu = cpu->CpuIndex; - _InterlockedExchange(&arg->Failure, 1); + NtlAtomicExchange32(&arg->Failure, 1); break; } if (cpu->CpuIndex < KE_MAX_CPUS) - _InterlockedIncrement(&arg->Test->CpuHits[cpu->CpuIndex]); + NtlAtomicIncrement32(&arg->Test->CpuHits[cpu->CpuIndex]); - _InterlockedIncrement(&arg->Progress); + NtlAtomicIncrement32(&arg->Progress); if ((round & 3u) == 0) KeYield(); } - if (_InterlockedCompareExchange(&arg->Failure, 0, 0) != 0) { - _InterlockedIncrement(&arg->Test->FailureCount); + if (NtlAtomicLoad32(&arg->Failure) != 0) { + NtlAtomicIncrement32(&arg->Test->FailureCount); } - _InterlockedExchange(&arg->Done, 1); - _InterlockedIncrement(&arg->Test->DoneCount); + NtlAtomicExchange32(&arg->Done, 1); + NtlAtomicIncrement32(&arg->Test->DoneCount); } // Waits for for Worker Counter. -static int KiWaitForWorkerCounter(volatile long* counter, long expected, const char* what) { +static bool KiWaitForWorkerCounter(volatile int32_t* counter, int32_t expected, const char* what) { uint64_t deadline = KiSmpDeadlineAfter(SMP_AP_WORKER_TIMEOUT_US); for (;;) { - if (_InterlockedCompareExchange(counter, 0, 0) >= expected) - return 1; + if (NtlAtomicLoad32(counter) >= expected) + return true; if (KiSmpQueryTicks() >= deadline) break; KeYield(); } - DbgPrint("SMP scheduler selftest timeout waiting for %s expected=%ld got=%ld\n", - what ? what : "counter", expected, _InterlockedCompareExchange(counter, 0, 0)); - return 0; + DbgPrint("SMP scheduler selftest timeout waiting for %s expected=%d got=%ld\n", + what ? what : "counter", expected, NtlAtomicLoad32(counter)); + return false; } // Runs application Processor Scheduler Self Tests. @@ -1019,7 +1020,7 @@ KSTATUS KeRunApplicationProcessorSchedulerSelfTests(void) { return NTLSTATUS_SUCCESS; } - if (!KiWaitForWorkerCounter(&test->ReadyCount, (long)workerCount, "AP worker ready")) { + if (!KiWaitForWorkerCounter(&test->ReadyCount, (int32_t)workerCount, "AP worker ready")) { KePanic(KE_PANIC_CODE_SELFTEST, "SMP scheduler selftest: workers failed to become ready ready=%ld workers=%u\n", test->ReadyCount, workerCount); } @@ -1027,7 +1028,7 @@ KSTATUS KeRunApplicationProcessorSchedulerSelfTests(void) { for (uint32_t round = 0; round < SMP_AP_WORKER_ROUNDS; round++) { for (uint32_t w = 0; w < workerCount; w++) { SMP_AP_WORKER_ARG* arg = &test->Workers[w]; - if (!KiWaitForWorkerCounter(&arg->WaitArmed, (long)(round + 1), "AP worker wait-armed")) { + if (!KiWaitForWorkerCounter(&arg->WaitArmed, (int32_t)(round + 1), "AP worker wait-armed")) { KePanic(KE_PANIC_CODE_SELFTEST, "SMP scheduler selftest: AP%u did not arm wait for round %u armed=%ld progress=%ld\n", arg->CpuIndex, round, arg->WaitArmed, arg->Progress); @@ -1037,13 +1038,13 @@ KSTATUS KeRunApplicationProcessorSchedulerSelfTests(void) { for (uint32_t w = 0; w < workerCount; w++) { SMP_AP_WORKER_ARG* arg = &test->Workers[w]; - if (!KiWaitForWorkerCounter(&arg->Progress, (long)(round + 1), "AP worker progress")) { + if (!KiWaitForWorkerCounter(&arg->Progress, (int32_t)(round + 1), "AP worker progress")) { KePanic(KE_PANIC_CODE_SELFTEST, "SMP scheduler selftest: AP%u no progress round=%u progress=%ld failure=%ld wrongCpu=%u wrongRunningCpu=%u\n", arg->CpuIndex, round, arg->Progress, arg->Failure, arg->WrongCpu, arg->WrongRunningCpu); } - if (_InterlockedCompareExchange(&arg->Failure, 0, 0) != 0) { + if (NtlAtomicLoad32(&arg->Failure) != 0) { KePanic(KE_PANIC_CODE_SELFTEST, "SMP scheduler selftest: AP%u worker failed round=%u wrongCpu=%u wrongRunningCpu=%u progress=%ld\n", arg->CpuIndex, round, arg->WrongCpu, arg->WrongRunningCpu, arg->Progress); @@ -1051,19 +1052,19 @@ KSTATUS KeRunApplicationProcessorSchedulerSelfTests(void) { } } - if (!KiWaitForWorkerCounter(&test->DoneCount, (long)workerCount, "AP worker done")) { - KePanic(KE_PANIC_CODE_SELFTEST, "SMP scheduler selftest: workers did not finish done=%ld workers=%u\n", + if (!KiWaitForWorkerCounter(&test->DoneCount, (int32_t)workerCount, "AP worker done")) { + KePanic(KE_PANIC_CODE_SELFTEST, "SMP scheduler selftest: workers did not finish done=%d workers=%u\n", test->DoneCount, workerCount); } - if (_InterlockedCompareExchange(&test->FailureCount, 0, 0) != 0) { - KePanic(KE_PANIC_CODE_SELFTEST, "SMP scheduler selftest: failure count=%ld\n", + if (NtlAtomicLoad32(&test->FailureCount) != 0) { + KePanic(KE_PANIC_CODE_SELFTEST, "SMP scheduler selftest: failure count=%d\n", test->FailureCount); } for (uint32_t w = 0; w < workerCount; w++) { SMP_AP_WORKER_ARG* arg = &test->Workers[w]; - long hits = (arg->CpuIndex < KE_MAX_CPUS) ? test->CpuHits[arg->CpuIndex] : 0; + int32_t hits = (arg->CpuIndex < KE_MAX_CPUS) ? test->CpuHits[arg->CpuIndex] : 0; if (hits <= 0) { KePanic(KE_PANIC_CODE_SELFTEST, "SMP scheduler selftest: AP%u recorded no CPU hits\n", arg->CpuIndex); diff --git a/code/kernel/ke_surface.c b/code/kernel/ke_surface.c index 9363c1c..398ecf0 100644 --- a/code/kernel/ke_surface.c +++ b/code/kernel/ke_surface.c @@ -23,8 +23,8 @@ typedef struct KE_SURFACE { uint32_t Flags; size_t PayloadSize; size_t PageCount; - uint8_t StatsRegistered; - uint8_t LastDamageValid; + bool StatsRegistered; + bool LastDamageValid; uint16_t Reserved; uint32_t Reserved2; uint64_t MapCount; @@ -45,10 +45,10 @@ static SPINLOCK gSurfaceStatsLock = SPINLOCK_INIT; static SYS_SURFACE_STATS gSurfaceStats; // Multiplies two sizes while rejecting overflow. -static int KiSurfaceMulSize(size_t a, size_t b, size_t* out) { - if (!out || (a != 0 && b > SIZE_MAX / a)) return 0; +static bool KiSurfaceMulSize(size_t a, size_t b, size_t* out) { + if (!out || (a != 0 && b > SIZE_MAX / a)) return false; *out = a * b; - return 1; + return true; } // Aligns one size upward while rejecting wraparound. @@ -73,7 +73,7 @@ static void KiSurfaceStatsRegister(KE_SURFACE* surface) { gSurfaceStats.LiveSurfaces++; gSurfaceStats.LivePages += surface->PageCount; gSurfaceStats.LiveBytes += surface->PayloadSize; - surface->StatsRegistered = 1; + surface->StatsRegistered = true; KeReleaseSpinlock(&gSurfaceStatsLock, irq); } @@ -87,7 +87,7 @@ static void KiSurfaceStatsUnregister(KE_SURFACE* surface) { ? gSurfaceStats.LivePages - surface->PageCount : 0; gSurfaceStats.LiveBytes = gSurfaceStats.LiveBytes >= surface->PayloadSize ? gSurfaceStats.LiveBytes - surface->PayloadSize : 0; - surface->StatsRegistered = 0; + surface->StatsRegistered = false; KeReleaseSpinlock(&gSurfaceStatsLock, irq); } @@ -145,8 +145,8 @@ static void KiSurfaceDestroy(OBJECT* object) { } // Clips one rectangle to the surface extent. -static int KiSurfaceClipRect(const KE_SURFACE* surface, const DISPLAY_RECT* input, DISPLAY_RECT* output) { - if (!surface || !output) return 0; +static bool KiSurfaceClipRect(const KE_SURFACE* surface, const DISPLAY_RECT* input, DISPLAY_RECT* output) { + if (!surface || !output) return false; DISPLAY_RECT r; if (input) { r = *input; @@ -156,7 +156,7 @@ static int KiSurfaceClipRect(const KE_SURFACE* surface, const DISPLAY_RECT* inpu r.W = (int32_t)surface->Width; r.H = (int32_t)surface->Height; } - if (r.W <= 0 || r.H <= 0) return 0; + if (r.W <= 0 || r.H <= 0) return false; int64_t x0 = r.X; int64_t y0 = r.Y; @@ -166,13 +166,13 @@ static int KiSurfaceClipRect(const KE_SURFACE* surface, const DISPLAY_RECT* inpu if (y0 < 0) y0 = 0; if (x1 > surface->Width) x1 = surface->Width; if (y1 > surface->Height) y1 = surface->Height; - if (x0 >= x1 || y0 >= y1) return 0; + if (x0 >= x1 || y0 >= y1) return false; output->X = (int32_t)x0; output->Y = (int32_t)y0; output->W = (int32_t)(x1 - x0); output->H = (int32_t)(y1 - y0); - return 1; + return true; } // Expands one damage bound to include another clipped rectangle. diff --git a/code/kernel/ke_syscall.c b/code/kernel/ke_syscall.c index 2d07c03..b9d2fad 100644 --- a/code/kernel/ke_syscall.c +++ b/code/kernel/ke_syscall.c @@ -24,6 +24,8 @@ // call: the object model already handled it; the dedicated syscall was only // adding a footgun where callers could bypass the unified refcount path. +#include +#include #include #include #include @@ -43,7 +45,6 @@ #include #include #include -#include #ifndef PROCESS_LAUNCH_DEBUG #define PROCESS_LAUNCH_DEBUG 0 @@ -83,7 +84,7 @@ static const HAL_API* gHal; typedef struct SYS_IO_STATUS_BLOCK_K { - volatile uint32_t Status; + volatile KSTATUS Status; volatile uint64_t Information; } SYS_IO_STATUS_BLOCK_K; @@ -259,7 +260,7 @@ static KSTATUS KiImportUserTextView(SYS_STRING_VIEW userView, bool nulTerminate) { KSTATUS s = KiImportUserStringView(userView, dst, dstSize, out, nulTerminate); if (!KSUCCESS(s)) return s; - if (out && SysSvContainsByte(*out, '\0')) return NTLSTATUS_INVALID_PARAMETER; + if (!out || !SysSvIsUtf8Text(*out)) return NTLSTATUS_INVALID_PARAMETER; return NTLSTATUS_SUCCESS; } @@ -300,11 +301,11 @@ static KSTATUS KiCopyInt32ToUser(int32_t* valueOut, int32_t value) { } // Implements the mul Size operation. -static int KiMulSize(size_t a, size_t b, size_t* out) { - if (!out) return 0; - if (a && b > ((size_t)-1) / a) return 0; +static bool KiMulSize(size_t a, size_t b, size_t* out) { + if (!out) return false; + if (a && b > ((size_t)-1) / a) return false; *out = a * b; - return 1; + return true; } static size_t KiAlignUpSize(size_t value, size_t alignment) { @@ -554,9 +555,6 @@ static KSTATUS KiHandleToWaitEvent(PROCESS* proc, SYS_HANDLE handle, EVENT** eve case ObjectTypeAsyncIo: event = IoGetAsyncRequestWaitEvent((IO_ASYNC_REQUEST_OBJECT*)obj); break; - case ObjectTypeInputQueue: - event = KeInputQueueWaitEvent(CONTAINING_RECORD(obj, INPUT_QUEUE, Header)); - break; case ObjectTypeTimer: event = KeTimerWaitEvent(CONTAINING_RECORD(obj, KTIMER, Header)); break; @@ -698,29 +696,6 @@ static uint64_t SyscallWaitMany(const SYS_WAIT_MANY_REQUEST* request) { return s; } -// Implements SYS_OPEN_INPUT_QUEUE. -static uint64_t SyscallOpenInputQueue(uint32_t flags, SYS_HANDLE* handleOut) { - KSTATUS privilegeStatus = KiRequirePrivilege(SYS_PRIV_GLOBAL_INPUT); - if (!KSUCCESS(privilegeStatus)) return privilegeStatus; - if (flags != 0) return NTLSTATUS_INVALID_PARAMETER; - if (!handleOut) return NTLSTATUS_INVALID_PARAMETER; - - INPUT_QUEUE* queue = (INPUT_QUEUE*)0; - KSTATUS s = KeInputOpenQueue(&queue); - if (!KSUCCESS(s)) return s; - - SYS_HANDLE handle = SYS_HANDLE_INVALID; - s = KiAllocHandleForCurrentWithRights(&queue->Header, KeGetValidHandleRights(ObjectTypeInputQueue), &handle); - if (!KSUCCESS(s)) return s; - - s = KiCopyHandleToUser(handleOut, handle); - if (!KSUCCESS(s)) { - PROCESS* proc = KeGetCurrentProcess(); - if (proc) KeCloseHandle(proc, handle); - } - return s; -} - // Returns the current thread ID. static uint64_t SyscallGetCurrentTid(void) { THREAD* t = KeGetCurrentThread(); @@ -2503,7 +2478,7 @@ static KSTATUS KiConsoleQueryInfo(SYS_CONSOLE_INFO* infoOut) { } // Updates the legacy text-console cursor after authorization. -static KSTATUS KiConsoleSetCursor(int x, int y) { +static KSTATUS KiConsoleSetCursor(int32_t x, int32_t y) { if (x < 0 || y < 0) return NTLSTATUS_INVALID_PARAMETER; return KeDisplaySetConsoleCursor(x, y); } @@ -2560,18 +2535,18 @@ static uint64_t SyscallClearScreen(uint32_t color) { } // Implements SYS_GET_SCREEN_SIZE. -static uint64_t SyscallGetScreenSize(int* widthOut, int* heightOut) { +static uint64_t SyscallGetScreenSize(int32_t* widthOut, int32_t* heightOut) { KSTATUS privilegeStatus = KiRequirePrivilege(SYS_PRIV_CONSOLE_ACCESS); if (!KSUCCESS(privilegeStatus)) return privilegeStatus; - int w = 0, h = 0; + int32_t w = 0, h = 0; KSTATUS gs = KeDisplayGetSize(&w, &h); if (!KSUCCESS(gs)) return gs; if (widthOut) { - KSTATUS s = KiCopyIntToUser(widthOut, w); + KSTATUS s = KiCopyInt32ToUser(widthOut, w); if (!KSUCCESS(s)) return s; } if (heightOut) { - KSTATUS s = KiCopyIntToUser(heightOut, h); + KSTATUS s = KiCopyInt32ToUser(heightOut, h); if (!KSUCCESS(s)) return s; } return NTLSTATUS_SUCCESS; @@ -2592,7 +2567,7 @@ static uint64_t SyscallGetConsoleInfo(SYS_CONSOLE_INFO* infoOut) { } // Implements SYS_SET_CONSOLE_CURSOR through the legacy ambient console privilege. -static uint64_t SyscallSetConsoleCursor(int x, int y) { +static uint64_t SyscallSetConsoleCursor(int32_t x, int32_t y) { KSTATUS privilegeStatus = KiRequirePrivilege(SYS_PRIV_CONSOLE_ACCESS); if (!KSUCCESS(privilegeStatus)) return privilegeStatus; return KiConsoleSetCursor(x, y); @@ -2612,19 +2587,6 @@ static uint64_t SyscallFillConsoleAttribute(const SYS_FILL_CONSOLE_ATTRIBUTE_REQ return KiConsoleFillAttribute(request); } -static KSTATUS KiReferenceInputQueueHandle(SYS_HANDLE handle, INPUT_QUEUE** queueOut) { - if (!queueOut) return NTLSTATUS_INVALID_PARAMETER; - *queueOut = (INPUT_QUEUE*)0; - if (handle == 0) return NTLSTATUS_SUCCESS; - - KSTATUS handleStatus = NTLSTATUS_SUCCESS; - OBJECT* object = KiHandleToObject(handle, ObjectTypeInputQueue, - SYS_INPUT_QUEUE_RIGHT_READ, &handleStatus); - if (!object) return handleStatus; - *queueOut = CONTAINING_RECORD(object, INPUT_QUEUE, Header); - return NTLSTATUS_SUCCESS; -} - // Implements the capability-scoped raw input-manager stream used by RSSRV. static uint64_t SyscallReadInputManagerEvent(const SYS_INPUT_MANAGER_READ_REQUEST* request) { SYS_INPUT_MANAGER_READ_REQUEST req; @@ -2645,209 +2607,6 @@ static uint64_t SyscallReadInputManagerEvent(const SYS_INPUT_MANAGER_READ_REQUES return CopyToUser(req.EventOut, &event, sizeof(event)); } -// Copies one keyboard input record to user mode. -static KSTATUS KiCopyInputRecordToUser(INPUT_KEY_RECORD* userRecord, const INPUT_KEY_RECORD* record) { - if (!userRecord || !record) return NTLSTATUS_INVALID_PARAMETER; - return CopyToUser(userRecord, record, sizeof(*record)); -} - -// Imports and validates a keyboard input event request. -static KSTATUS KiCopyInputEventRequest(const SYS_INPUT_EVENT_REQUEST* userRequest, SYS_INPUT_EVENT_REQUEST* req) { - KSTATUS s = KiCopySizedUserRequest(userRequest, req, sizeof(*req)); - if (!KSUCCESS(s)) return s; - if (req->Flags != SYS_INPUT_EVENT_FLAG_NONE) return NTLSTATUS_INVALID_PARAMETER; - if (!req->RecordOut) return NTLSTATUS_INVALID_PARAMETER; - return NTLSTATUS_SUCCESS; -} - -// Implements SYS_POLL_KEYBOARD. -static uint64_t SyscallKbPoll(INPUT_KEY* keyOut) { - KSTATUS privilegeStatus = KiRequirePrivilege(SYS_PRIV_GLOBAL_INPUT); - if (!KSUCCESS(privilegeStatus)) return privilegeStatus; - if (!keyOut) return NTLSTATUS_INVALID_PARAMETER; - - INPUT_KEY key = KeInputKbPoll(); - KSTATUS s = CopyToUser(keyOut, &key, sizeof(key)); - if (!KSUCCESS(s)) return s; - return (key == INPUT_KEY_NONE) ? NTLSTATUS_WOULD_BLOCK : NTLSTATUS_SUCCESS; -} - -// Implements SYS_IS_KEY_DOWN. -static uint64_t SyscallKbIsKeyDown(INPUT_KEY key, bool* downOut) { - KSTATUS privilegeStatus = KiRequirePrivilege(SYS_PRIV_GLOBAL_INPUT); - if (!KSUCCESS(privilegeStatus)) return privilegeStatus; - if (!downOut) return NTLSTATUS_INVALID_PARAMETER; - - bool down = KeInputIsKeyDown((INPUT_KEY)key); - return CopyToUser(downOut, &down, sizeof(down)); -} - -// Implements SYS_READ_KEYBOARD_CHAR. -static uint64_t SyscallKbReadChar(char* charOut) { - KSTATUS privilegeStatus = KiRequirePrivilege(SYS_PRIV_GLOBAL_INPUT); - if (!KSUCCESS(privilegeStatus)) return privilegeStatus; - if (!charOut) return NTLSTATUS_INVALID_PARAMETER; - - while (true) { - char ch = KeInputReadChar(); - if (ch) - return CopyToUser(charOut, &ch, sizeof(ch)); - - KSTATUS s = KeInputWaitKey(0, SYS_WAIT_TIMEOUT_INFINITE); - if (!KSUCCESS(s)) - return s; - } -} - -// Implements SYS_READ_KEY. -static uint64_t SyscallKbReadKey(INPUT_KEY* keyOut) { - KSTATUS privilegeStatus = KiRequirePrivilege(SYS_PRIV_GLOBAL_INPUT); - if (!KSUCCESS(privilegeStatus)) return privilegeStatus; - if (!keyOut) return NTLSTATUS_INVALID_PARAMETER; - - while (true) { - INPUT_KEY key = KeInputReadKey(); - if (key != INPUT_KEY_NONE) - return CopyToUser(keyOut, &key, sizeof(key)); - - KSTATUS s = KeInputWaitKey(0, SYS_WAIT_TIMEOUT_INFINITE); - if (!KSUCCESS(s)) - return s; - } -} - -// SYS_GET_KEY_MODIFIERS -> uint32_t INPUT_MOD_* flags. -static uint64_t SyscallKbGetModifiers(void) { - KSTATUS privilegeStatus = KiRequirePrivilege(SYS_PRIV_GLOBAL_INPUT); - if (!KSUCCESS(privilegeStatus)) return privilegeStatus; - return (uint64_t)KeInputGetModifiers(); -} - -// Implements SYS_QUERY_GLOBAL_INPUT_STATE. -static uint64_t SyscallQueryGlobalInputState(SYS_GLOBAL_INPUT_STATE* stateOut) { - KSTATUS privilegeStatus = KiRequirePrivilege(SYS_PRIV_GLOBAL_INPUT); - if (!KSUCCESS(privilegeStatus)) return privilegeStatus; - SYS_GLOBAL_INPUT_STATE state; - KSTATUS s = KiCopySizedUserRequest(stateOut, &state, sizeof(state)); - if (!KSUCCESS(s)) return s; - if (state.Flags != SYS_GLOBAL_INPUT_STATE_FLAG_NONE) return NTLSTATUS_INVALID_PARAMETER; - - s = KeInputQueryGlobalState(&state); - if (!KSUCCESS(s)) return s; - return CopyToUser(stateOut, &state, sizeof(state)); -} - -// Implements SYS_READ_INPUT_EVENT. -static uint64_t SyscallKbReadInputEvent(const SYS_INPUT_EVENT_REQUEST* request) { - SYS_INPUT_EVENT_REQUEST req; - KSTATUS s = KiCopyInputEventRequest(request, &req); - if (!KSUCCESS(s)) return s; - if (req.QueueHandle == SYS_HANDLE_INVALID) { - KSTATUS privilegeStatus = KiRequirePrivilege(SYS_PRIV_GLOBAL_INPUT); - if (!KSUCCESS(privilegeStatus)) return privilegeStatus; - } - - INPUT_QUEUE* queue = (INPUT_QUEUE*)0; - s = KiReferenceInputQueueHandle(req.QueueHandle, &queue); - if (!KSUCCESS(s)) return s; - - INPUT_KEY_RECORD record; - while (true) { - bool available = queue ? KeInputQueueReadEvent(queue, &record) - : KeInputReadEvent(&record); - if (available) { - s = KiCopyInputRecordToUser(req.RecordOut, &record); - break; - } - - if (req.TimeoutMilliseconds == 0) { - s = NTLSTATUS_WOULD_BLOCK; - break; - } - - s = queue ? KeInputQueueWaitKey(queue, 0, req.TimeoutMilliseconds) - : KeInputWaitKey(0, req.TimeoutMilliseconds); - if (!KSUCCESS(s)) - break; - } - - if (queue) ObDereferenceObject(&queue->Header); - return s; -} - -// Implements SYS_PEEK_INPUT_EVENT. -static uint64_t SyscallKbPeekInputEvent(const SYS_INPUT_EVENT_REQUEST* request) { - SYS_INPUT_EVENT_REQUEST req; - KSTATUS s = KiCopyInputEventRequest(request, &req); - if (!KSUCCESS(s)) return s; - if (req.QueueHandle == SYS_HANDLE_INVALID) { - KSTATUS privilegeStatus = KiRequirePrivilege(SYS_PRIV_GLOBAL_INPUT); - if (!KSUCCESS(privilegeStatus)) return privilegeStatus; - } - - INPUT_QUEUE* queue = (INPUT_QUEUE*)0; - s = KiReferenceInputQueueHandle(req.QueueHandle, &queue); - if (!KSUCCESS(s)) return s; - - INPUT_KEY_RECORD record; - while (true) { - bool available = queue ? KeInputQueuePeekEvent(queue, &record) - : KeInputPeekEvent(&record); - if (available) { - s = KiCopyInputRecordToUser(req.RecordOut, &record); - break; - } - - if (req.TimeoutMilliseconds == 0) { - s = NTLSTATUS_WOULD_BLOCK; - break; - } - - s = queue ? KeInputQueueWaitKey(queue, 0, req.TimeoutMilliseconds) - : KeInputWaitKey(0, req.TimeoutMilliseconds); - if (!KSUCCESS(s)) - break; - } - - if (queue) ObDereferenceObject(&queue->Header); - return s; -} - -// Implements SYS_PEEK_MOUSE. -static uint64_t SyscallMousePeek(INPUT_MOUSE_EVENT* eventOut) { - KSTATUS privilegeStatus = KiRequirePrivilege(SYS_PRIV_GLOBAL_INPUT); - if (!KSUCCESS(privilegeStatus)) return privilegeStatus; - if (!eventOut) return NTLSTATUS_INVALID_PARAMETER; - - INPUT_MOUSE_EVENT event; - if (!KeInputMousePeek(&event)) return NTLSTATUS_WOULD_BLOCK; - return CopyToUser(eventOut, &event, sizeof(event)); -} - -// Implements SYS_READ_MOUSE. -static uint64_t SyscallMouseRead(INPUT_MOUSE_EVENT* eventOut) { - KSTATUS privilegeStatus = KiRequirePrivilege(SYS_PRIV_GLOBAL_INPUT); - if (!KSUCCESS(privilegeStatus)) return privilegeStatus; - if (!eventOut) return NTLSTATUS_INVALID_PARAMETER; - - INPUT_MOUSE_EVENT event; - if (!KeInputMouseRead(&event)) return NTLSTATUS_WOULD_BLOCK; - return CopyToUser(eventOut, &event, sizeof(event)); -} - -// Implements SYS_GET_MOUSE_POS. -static uint64_t SyscallMouseGetPos(int* xOut, int* yOut) { - KSTATUS privilegeStatus = KiRequirePrivilege(SYS_PRIV_GLOBAL_INPUT); - if (!KSUCCESS(privilegeStatus)) return privilegeStatus; - if (!xOut || !yOut) return NTLSTATUS_INVALID_PARAMETER; - - int x = 0, y = 0; - KeInputMouseGetPosition(&x, &y); - KSTATUS s = KiCopyIntToUser(xOut, x); - if (!KSUCCESS(s)) return s; - return KiCopyIntToUser(yOut, y); -} - // Returns monotonic ticks since boot. static uint64_t SyscallGetTick(void) { return gHal ? gHal->Timer.QueryTicks() : 0; @@ -2919,19 +2678,19 @@ static uint64_t SyscallQueryTrapFrame(SYS_TRAP_FRAME_INFO* infoOut) { static KSTATUS KiBeginProcessBuildOperation(PROCESS* process) { if (!process) return NTLSTATUS_INVALID_PARAMETER; for (;;) { - if (_InterlockedCompareExchange(&process->BuildState, 0, 0) != KE_PROCESS_BUILD_CONSTRUCTING) + if (NtlAtomicLoad32(&process->BuildState) != KE_PROCESS_BUILD_CONSTRUCTING) return NTLSTATUS_ACCESS_DENIED; - _InterlockedIncrement(&process->BuildOperations); - if (_InterlockedCompareExchange(&process->BuildState, 0, 0) == KE_PROCESS_BUILD_CONSTRUCTING) + NtlAtomicIncrement32(&process->BuildOperations); + if (NtlAtomicLoad32(&process->BuildState) == KE_PROCESS_BUILD_CONSTRUCTING) return NTLSTATUS_SUCCESS; - _InterlockedDecrement(&process->BuildOperations); + NtlAtomicDecrement32(&process->BuildOperations); } } // Ends one process-construction memory operation. static void KiEndProcessBuildOperation(PROCESS* process) { if (!process) return; - long remaining = _InterlockedDecrement(&process->BuildOperations); + int32_t remaining = NtlAtomicDecrement32(&process->BuildOperations); if (remaining == 0 && KeIsProcessTerminating(process)) (void)KeTerminateProcess(process, process->ExitCode); } @@ -3094,20 +2853,20 @@ static uint64_t SyscallStartProcess(const SYS_START_PROCESS_REQUEST* request) { int buildActive = 0; status = KiBeginProcessBuildOperation(child); if (!KSUCCESS(status)) { - int terminating = KeIsProcessTerminating(child); + bool terminating = KeIsProcessTerminating(child); ObDereferenceObject(&child->Header); return terminating ? NTLSTATUS_OBJECT_DEAD : status; } buildActive = 1; THREAD* thread = (THREAD*)0; - if (_InterlockedCompareExchange(&child->BuildState, KE_PROCESS_BUILD_STARTING, + if (NtlAtomicCompareExchange32(&child->BuildState, KE_PROCESS_BUILD_STARTING, KE_PROCESS_BUILD_CONSTRUCTING) != KE_PROCESS_BUILD_CONSTRUCTING) { status = KeIsProcessTerminating(child) ? NTLSTATUS_OBJECT_DEAD : NTLSTATUS_ACCESS_DENIED; goto done; } - if (_InterlockedCompareExchange(&child->BuildOperations, 0, 0) != 1) { - (void)_InterlockedCompareExchange(&child->BuildState, KE_PROCESS_BUILD_CONSTRUCTING, + if (NtlAtomicLoad32(&child->BuildOperations) != 1) { + (void)NtlAtomicCompareExchange32(&child->BuildState, KE_PROCESS_BUILD_CONSTRUCTING, KE_PROCESS_BUILD_STARTING); status = NTLSTATUS_WOULD_BLOCK; goto done; @@ -3169,7 +2928,7 @@ static uint64_t SyscallStartProcess(const SYS_START_PROCESS_REQUEST* request) { parameters.Flags = KE_THREAD_CREATE_FLAG_SUSPENDED; status = KeCreateThread(child, req.EntryPoint, 0, ¶meters, &thread); if (KSUCCESS(status)) { - if (_InterlockedCompareExchange(&child->BuildState, KE_PROCESS_BUILD_STARTED, + if (NtlAtomicCompareExchange32(&child->BuildState, KE_PROCESS_BUILD_STARTED, KE_PROCESS_BUILD_STARTING) != KE_PROCESS_BUILD_STARTING) { (void)KeCancelSuspendedThread(thread); status = NTLSTATUS_OBJECT_DEAD; @@ -3184,7 +2943,7 @@ static uint64_t SyscallStartProcess(const SYS_START_PROCESS_REQUEST* request) { child->ImageSize = 0; (void)KeSetProcessStartupCapabilities(child, (const SYS_STARTUP_CAPABILITY*)0, 0); (void)KeSetProcessLoaderData(child, (const void*)0); - (void)_InterlockedCompareExchange(&child->BuildState, KE_PROCESS_BUILD_CONSTRUCTING, + (void)NtlAtomicCompareExchange32(&child->BuildState, KE_PROCESS_BUILD_CONSTRUCTING, KE_PROCESS_BUILD_STARTING); goto done; } @@ -3841,221 +3600,6 @@ static uint64_t SyscallWaitVolumeChange(SYS_HANDLE managerHandle, return IoWaitForVolumeChange(observedGeneration, timeoutMilliseconds); } -// Named Queue Registry - -// A flat, fixed-size table of { name -> QUEUE* } pairs protected by a -// spinlock. The registry holds one reference to each published queue for -// the lifetime of the entry, so the queue object cannot be destroyed while -// it is registered even if every process closes its own handle to it. - -// Limits: NAMED_QUEUE_MAX names, each up to NAMED_QUEUE_NAME_MAX bytes. -// These are generous for the intended usage (a handful of system servers). - -#define NAMED_QUEUE_MAX 16 -#define NAMED_QUEUE_NAME_MAX 64 - -typedef struct { - char Name[NAMED_QUEUE_NAME_MAX]; - size_t NameLength; - QUEUE* Queue; // NULL = slot free; non-NULL = occupied - uint32_t PublisherPid; // ProcessId of whoever called SysPublishQueue - SYS_HANDLE PublisherHandle; // Publisher-owned handle that keeps the name live -} NAMED_QUEUE_ENTRY; - -static NAMED_QUEUE_ENTRY gNamedQueues[NAMED_QUEUE_MAX]; -static SPINLOCK gNamedQueueLock; - -static int KiNamedQueueNameEquals(const NAMED_QUEUE_ENTRY* entry, - SYS_STRING_VIEW name) { - if (!entry || !entry->Queue) return 0; - return SysSvEqual(SysSvFromParts(entry->Name, entry->NameLength), name); -} - -static int KiNamedQueueOwnedByHandle(const NAMED_QUEUE_ENTRY* entry, - const PROCESS* process, - SYS_HANDLE handle, - const OBJECT* object) { - if (!entry || !entry->Queue || !process || !object) - return 0; - return entry->PublisherPid == process->ProcessId && - entry->PublisherHandle == handle && - &entry->Queue->Header == object; -} - -// Clears a named queue slot and returns the registry reference for later release. -static QUEUE* KiNamedQueueClearSlotLocked(NAMED_QUEUE_ENTRY* entry) { - if (!entry || !entry->Queue) - return (QUEUE*)0; - - QUEUE* queue = entry->Queue; - memset(entry, 0, sizeof(*entry)); - return queue; -} - -// Removes one published queue name owned by the current process. -static KSTATUS KiNamedQueueUnpublishByName(SYS_STRING_VIEW nameView) { - PROCESS* process = KeGetCurrentProcess(); - if (!process) - return NTLSTATUS_FAILURE; - - QUEUE* queue = (QUEUE*)0; - uintptr_t flags = KeAcquireSpinlock(&gNamedQueueLock); - for (int i = 0; i < NAMED_QUEUE_MAX; i++) { - if (KiNamedQueueNameEquals(&gNamedQueues[i], nameView)) { - if (gNamedQueues[i].PublisherPid != process->ProcessId) { - KeReleaseSpinlock(&gNamedQueueLock, flags); - return NTLSTATUS_ACCESS_DENIED; - } - queue = KiNamedQueueClearSlotLocked(&gNamedQueues[i]); - break; - } - } - KeReleaseSpinlock(&gNamedQueueLock, flags); - - if (!queue) - return NTLSTATUS_NOT_FOUND; - ObDereferenceObject(&queue->Header); - return NTLSTATUS_SUCCESS; -} - -// Removes published names whose owner handle is being closed. -void KeNamedQueueUnpublishHandle(PROCESS* process, SYS_HANDLE handle, OBJECT* object) { - if (!process || !object || object->Type != ObjectTypeQueue) - return; - - while (true) { - QUEUE* queue = (QUEUE*)0; - uintptr_t flags = KeAcquireSpinlock(&gNamedQueueLock); - for (int i = 0; i < NAMED_QUEUE_MAX; i++) { - if (KiNamedQueueOwnedByHandle(&gNamedQueues[i], process, handle, object)) { - queue = KiNamedQueueClearSlotLocked(&gNamedQueues[i]); - break; - } - } - KeReleaseSpinlock(&gNamedQueueLock, flags); - - if (!queue) - break; - ObDereferenceObject(&queue->Header); - } -} - -// Implements SYS_PUBLISH_QUEUE. - -// Registers the queue identified by QueueHandle under Name. -// The published name is owned by the publishing process and handle. Closing that -// handle or exiting the process withdraws every name published through it. -static uint64_t SyscallPublishQueue(const SYS_PUBLISH_QUEUE_REQUEST* request) { - KSTATUS privilegeStatus = KiRequirePrivilege(SYS_PRIV_NAMED_QUEUE_PUBLISH); - if (!KSUCCESS(privilegeStatus)) return privilegeStatus; - SYS_PUBLISH_QUEUE_REQUEST req; - KSTATUS s = KiCopySizedUserRequest(request, &req, sizeof(req)); - if (!KSUCCESS(s)) return s; - - char name[NAMED_QUEUE_NAME_MAX]; - SYS_STRING_VIEW nameView; - s = KiImportUserTextView(req.Name, name, sizeof(name), &nameView, false); - if (!KSUCCESS(s)) return s; - if (nameView.Length == 0) return NTLSTATUS_INVALID_PARAMETER; - - KSTATUS handleStatus = NTLSTATUS_SUCCESS; - QUEUE* q = (QUEUE*)KiHandleToObject(req.QueueHandle, ObjectTypeQueue, SYS_QUEUE_RIGHT_PUBLISH, &handleStatus); - if (!q) return handleStatus; - - uintptr_t flags = KeAcquireSpinlock(&gNamedQueueLock); - - for (int i = 0; i < NAMED_QUEUE_MAX; i++) { - if (KiNamedQueueNameEquals(&gNamedQueues[i], nameView)) { - KeReleaseSpinlock(&gNamedQueueLock, flags); - ObDereferenceObject(&q->Header); - return NTLSTATUS_ALREADY_EXISTS; - } - } - - for (int i = 0; i < NAMED_QUEUE_MAX; i++) { - if (!gNamedQueues[i].Queue) { - memcpy(gNamedQueues[i].Name, name, nameView.Length); - gNamedQueues[i].NameLength = nameView.Length; - gNamedQueues[i].Queue = q; - gNamedQueues[i].PublisherPid = KeGetCurrentProcess()->ProcessId; - gNamedQueues[i].PublisherHandle = req.QueueHandle; - KeReleaseSpinlock(&gNamedQueueLock, flags); - return NTLSTATUS_SUCCESS; - } - } - - KeReleaseSpinlock(&gNamedQueueLock, flags); - ObDereferenceObject(&q->Header); - return NTLSTATUS_NO_MEMORY; -} - -// Implements SYS_UNPUBLISH_QUEUE. -// Withdraws a published queue name owned by the calling process. -static uint64_t SyscallUnpublishQueue(const SYS_UNPUBLISH_QUEUE_REQUEST* request) { - KSTATUS privilegeStatus = KiRequirePrivilege(SYS_PRIV_NAMED_QUEUE_PUBLISH); - if (!KSUCCESS(privilegeStatus)) return privilegeStatus; - SYS_UNPUBLISH_QUEUE_REQUEST req; - KSTATUS s = KiCopySizedUserRequest(request, &req, sizeof(req)); - if (!KSUCCESS(s)) return s; - - char name[NAMED_QUEUE_NAME_MAX]; - SYS_STRING_VIEW nameView; - s = KiImportUserTextView(req.Name, name, sizeof(name), &nameView, false); - if (!KSUCCESS(s)) return s; - if (nameView.Length == 0) return NTLSTATUS_INVALID_PARAMETER; - - return KiNamedQueueUnpublishByName(nameView); -} - -// Implements SYS_OPEN_NAMED_QUEUE. - -// Looks up 'name' in the registry and allocates a handle to the queue in the -// calling process's handle table. Returns NTLSTATUS_NOT_FOUND if no match. -// Callers that race against the publisher should retry on NOT_FOUND. -static uint64_t SyscallOpenNamedQueue(const SYS_OPEN_NAMED_QUEUE_REQUEST* request) { - SYS_OPEN_NAMED_QUEUE_REQUEST req; - KSTATUS s = KiCopySizedUserRequest(request, &req, sizeof(req)); - if (!KSUCCESS(s)) return s; - if (!req.HandleOut) return NTLSTATUS_INVALID_PARAMETER; - - char name[NAMED_QUEUE_NAME_MAX]; - SYS_STRING_VIEW nameView; - s = KiImportUserTextView(req.Name, name, sizeof(name), &nameView, false); - if (!KSUCCESS(s)) return s; - if (nameView.Length == 0) return NTLSTATUS_INVALID_PARAMETER; - - s = MmProbeUserWrite(req.HandleOut, sizeof(SYS_HANDLE)); - if (!KSUCCESS(s)) return s; - - uintptr_t flags = KeAcquireSpinlock(&gNamedQueueLock); - - QUEUE* q = NULL; - for (int i = 0; i < NAMED_QUEUE_MAX; i++) { - if (KiNamedQueueNameEquals(&gNamedQueues[i], nameView)) { - q = gNamedQueues[i].Queue; - if (q && !ObTryReferenceObject(&q->Header)) - q = (QUEUE*)0; - break; - } - } - - KeReleaseSpinlock(&gNamedQueueLock, flags); - - if (!q) return NTLSTATUS_NOT_FOUND; - - SYS_HANDLE handle = SYS_HANDLE_INVALID; - s = KiAllocHandleForCurrentWithRights(&q->Header, KeGetValidHandleRights(ObjectTypeQueue), &handle); - if (!KSUCCESS(s)) return s; - - s = KiCopyHandleToUser(req.HandleOut, handle); - if (!KSUCCESS(s)) { - PROCESS* proc = KeGetCurrentProcess(); - if (proc) KeCloseHandle(proc, handle); - return s; - } - return NTLSTATUS_SUCCESS; -} - // Implements SYS_DUPLICATE_HANDLE. // Duplicates a handle from the caller into the target process table. @@ -4240,65 +3784,6 @@ static uint64_t SyscallOpenProcessSecurityContext(SYS_HANDLE processHandle, return status; } -// Implements SYS_OPEN_NAMED_QUEUE_OWNER_PROCESS. - -// Opens a process handle for the process that published a named queue. -static uint64_t SyscallOpenNamedQueueOwnerProcess(const SYS_NAMED_QUEUE_OWNER_REQUEST* request) { - SYS_NAMED_QUEUE_OWNER_REQUEST req; - KSTATUS s = KiCopySizedUserRequest(request, &req, sizeof(req)); - if (!KSUCCESS(s)) return s; - if (!req.ProcessHandleOut || req.DesiredAccess == 0) return NTLSTATUS_INVALID_PARAMETER; - SYS_RIGHTS validAccess = KeGetValidHandleRights(ObjectTypeProcess); - if ((req.DesiredAccess & ~validAccess) != 0) return NTLSTATUS_INVALID_PARAMETER; - - KSTATUS privilegeStatus = KiRequirePrivilege(SYS_PRIV_PROCESS_OPEN_ANY); - if (!KSUCCESS(privilegeStatus)) return privilegeStatus; - - char name[NAMED_QUEUE_NAME_MAX]; - SYS_STRING_VIEW nameView; - s = KiImportUserTextView(req.Name, name, sizeof(name), &nameView, false); - if (!KSUCCESS(s)) return s; - if (nameView.Length == 0) return NTLSTATUS_INVALID_PARAMETER; - - s = MmProbeUserWrite(req.ProcessHandleOut, sizeof(SYS_HANDLE)); - if (!KSUCCESS(s)) return s; - - uint32_t pid = 0; - uintptr_t flags = KeAcquireSpinlock(&gNamedQueueLock); - for (int i = 0; i < NAMED_QUEUE_MAX; i++) { - if (KiNamedQueueNameEquals(&gNamedQueues[i], nameView)) { - pid = gNamedQueues[i].PublisherPid; - break; - } - } - KeReleaseSpinlock(&gNamedQueueLock, flags); - if (!pid) return NTLSTATUS_NOT_FOUND; - - PROCESS* owner = KeGetProcessById(pid); - if (!owner) return NTLSTATUS_NOT_FOUND; - - PROCESS* caller = KeGetCurrentProcess(); - if (!caller) { - ObDereferenceObject(&owner->Header); - return NTLSTATUS_FAILURE; - } - - SYS_HANDLE handle = SYS_HANDLE_INVALID; - s = KeCreateHandle(caller, - &owner->Header, - req.DesiredAccess, - &handle); - ObDereferenceObject(&owner->Header); - if (!KSUCCESS(s)) return s; - - s = KiCopyHandleToUser(req.ProcessHandleOut, handle); - if (!KSUCCESS(s)) { - KeCloseHandle(caller, handle); - return s; - } - return NTLSTATUS_SUCCESS; -} - // Terminates the process referenced by one process handle. static uint64_t SyscallKillProcess(SYS_HANDLE processHandle, int32_t exitCode) { KSTATUS handleStatus = NTLSTATUS_SUCCESS; @@ -5108,13 +4593,13 @@ static uint64_t SyscallGetSocketOption(SYS_HANDLE socket, const SYS_SOCKET_OPTIO } // Polls deadline Expired. -static int KiPollDeadlineExpired(uint64_t start, uint32_t timeoutMs) { +static bool KiPollDeadlineExpired(uint64_t start, uint32_t timeoutMs) { if (timeoutMs == 0) - return 1; + return true; if (timeoutMs == SYS_POLL_TIMEOUT_INFINITE) - return 0; + return false; if (!gHal) - return 1; + return true; return (gHal->Timer.QueryTicks() - start) >= ((uint64_t)timeoutMs * 1000u); } @@ -5788,7 +5273,7 @@ static uint64_t SyscallQueryDisplayManagerConsoleInfo(SYS_HANDLE managerHandle, } // Updates the console cursor through an explicitly delegated display-manager capability. -static uint64_t SyscallSetDisplayManagerConsoleCursor(SYS_HANDLE managerHandle, int x, int y) { +static uint64_t SyscallSetDisplayManagerConsoleCursor(SYS_HANDLE managerHandle, int32_t x, int32_t y) { KSTATUS handleStatus = NTLSTATUS_SUCCESS; KE_DISPLAY_MANAGER* manager = KiReferenceDisplayManagerConsole(managerHandle, &handleStatus); if (!manager) return handleStatus; @@ -6659,7 +6144,8 @@ static uint64_t SyscallAudioSetVolume(const SYS_AUDIO_SET_VOLUME_REQUEST* userRe KSTATUS status = KiCopySizedUserRequest(userRequest, &request, sizeof(request)); if (!KSUCCESS(status)) return status; if (request.Flags != 0 || !request.StreamHandle || - request.Muted > 1u || request.VolumeQ16 > SYS_AUDIO_GAIN_Q16_UNITY || + ((const uint8_t*)&request)[offsetof(SYS_AUDIO_SET_VOLUME_REQUEST, Muted)] > 1u || + request.VolumeQ16 > SYS_AUDIO_GAIN_Q16_UNITY || request.PanQ16 < SYS_AUDIO_PAN_Q16_LEFT || request.PanQ16 > SYS_AUDIO_PAN_Q16_RIGHT) return NTLSTATUS_INVALID_PARAMETER; @@ -6682,7 +6168,8 @@ static uint64_t SyscallAudioSetMasterVolume(const SYS_AUDIO_SET_MASTER_VOLUME_RE KSTATUS status = KiCopySizedUserRequest(userRequest, &request, sizeof(request)); if (!KSUCCESS(status)) return status; if (request.Flags != 0 || !request.EndpointId || - request.Muted > 1u || request.VolumeQ16 > SYS_AUDIO_GAIN_Q16_UNITY) + ((const uint8_t*)&request)[offsetof(SYS_AUDIO_SET_MASTER_VOLUME_REQUEST, Muted)] > 1u || + request.VolumeQ16 > SYS_AUDIO_GAIN_Q16_UNITY) return NTLSTATUS_INVALID_PARAMETER; return KeAudioSetMasterVolume(request.EndpointId, @@ -6774,10 +6261,6 @@ static const SYSCALL_ENTRY gSyscallTable[SYS_MAX] = { [SYS_CREATE_QUEUE] = SYSCALL_ENTRY1(SyscallQueueCreate), [SYS_SEND_QUEUE] = SYSCALL_ENTRY1(SyscallQueueSend), [SYS_RECV_QUEUE] = SYSCALL_ENTRY1(SyscallQueueRecv), - [SYS_PUBLISH_QUEUE] = SYSCALL_ENTRY1(SyscallPublishQueue), - [SYS_UNPUBLISH_QUEUE] = SYSCALL_ENTRY1(SyscallUnpublishQueue), - [SYS_OPEN_NAMED_QUEUE] = SYSCALL_ENTRY1(SyscallOpenNamedQueue), - [SYS_OPEN_NAMED_QUEUE_OWNER_PROCESS] = SYSCALL_ENTRY1(SyscallOpenNamedQueueOwnerProcess), // LPC-like Ports and well-known system services. [SYS_CREATE_PORT] = SYSCALL_ENTRY1(SyscallCreatePort), @@ -6869,18 +6352,6 @@ static const SYSCALL_ENTRY gSyscallTable[SYS_MAX] = { // Input. [SYS_READ_INPUT_MANAGER_EVENT] = SYSCALL_ENTRY1(SyscallReadInputManagerEvent), - [SYS_POLL_KEYBOARD] = SYSCALL_ENTRY1(SyscallKbPoll), - [SYS_IS_KEY_DOWN] = SYSCALL_ENTRY2(SyscallKbIsKeyDown), - [SYS_READ_KEYBOARD_CHAR] = SYSCALL_ENTRY1(SyscallKbReadChar), - [SYS_READ_KEY] = SYSCALL_ENTRY1(SyscallKbReadKey), - [SYS_GET_KEY_MODIFIERS] = SYSCALL_ENTRY0(SyscallKbGetModifiers), - [SYS_QUERY_GLOBAL_INPUT_STATE] = SYSCALL_ENTRY1(SyscallQueryGlobalInputState), - [SYS_OPEN_INPUT_QUEUE] = SYSCALL_ENTRY2(SyscallOpenInputQueue), - [SYS_READ_INPUT_EVENT] = SYSCALL_ENTRY1(SyscallKbReadInputEvent), - [SYS_PEEK_INPUT_EVENT] = SYSCALL_ENTRY1(SyscallKbPeekInputEvent), - [SYS_PEEK_MOUSE] = SYSCALL_ENTRY1(SyscallMousePeek), - [SYS_READ_MOUSE] = SYSCALL_ENTRY1(SyscallMouseRead), - [SYS_GET_MOUSE_POS] = SYSCALL_ENTRY2(SyscallMouseGetPos), // Time, debug, and system information. [SYS_CREATE_TIMER] = SYSCALL_ENTRY1(SyscallCreateTimer), diff --git a/code/kernel/ke_thread.c b/code/kernel/ke_thread.c index 5f3ed78..d22e065 100644 --- a/code/kernel/ke_thread.c +++ b/code/kernel/ke_thread.c @@ -1,5 +1,8 @@ // Thread manager. This file owns thread allocation, kernel/user startup contexts, and lifecycle transitions. +#include +#include +#include #include #include #include @@ -127,7 +130,7 @@ void KeFinalizeThreadExit(THREAD* thread) { if (remaining == 0) { // ThreadCount represents live process membership, not retained thread // handles. Dead THREAD objects may remain waitable after process exit. - _InterlockedExchange(&owner->Terminating, 1); + NtlAtomicExchange32(&owner->Terminating, 1); KeStopProcessConstruction(owner); KeCompleteProcessExitIfLastThread(owner); } @@ -215,6 +218,8 @@ static KSTATUS KiValidateThreadName(const char* name, size_t* lengthOut) { if (length >= NTL_THREAD_NAME_MAX) return NTLSTATUS_INVALID_PARAMETER; + if (!SysSvIsUtf8Text(SysSvFromParts(name, length))) + return NTLSTATUS_INVALID_PARAMETER; *lengthOut = length; return NTLSTATUS_SUCCESS; } @@ -413,7 +418,7 @@ void KeEnterTerminationDeferral(void) { THREAD* thread = KeGetCurrentThread(); if (!thread || thread->IsIdleThread) return; - _InterlockedIncrement(&thread->TerminationDeferralCount); + NtlAtomicIncrement32(&thread->TerminationDeferralCount); } // Leaves a process-termination deferral region. @@ -423,7 +428,7 @@ void KeLeaveTerminationDeferral(void) { if (!thread || thread->IsIdleThread) return; - long remaining = _InterlockedDecrement(&thread->TerminationDeferralCount); + int32_t remaining = NtlAtomicDecrement32(&thread->TerminationDeferralCount); if (remaining < 0) { KePanic(KE_PANIC_CODE_SCHEDULER, "thread termination deferral underflow: thread=0x%016llX tid=%u\n", @@ -436,21 +441,17 @@ void KeLeaveTerminationDeferral(void) { } // Returns whether one thread still owns termination-sensitive kernel work. -int KeThreadTerminationDeferred(const THREAD* thread) { - return thread && _InterlockedCompareExchange((volatile long*)&thread->TerminationDeferralCount, - 0, - 0) > 0; +bool KeThreadTerminationDeferred(const THREAD* thread) { + return thread && NtlAtomicLoad32(&thread->TerminationDeferralCount) > 0; } // Returns whether terminal teardown owns the current thread stack. -int KeThreadExitInProgress(const THREAD* thread) { - return thread && _InterlockedCompareExchange((volatile long*)&thread->ExitInProgress, - 0, - 0) != 0; +bool KeThreadExitInProgress(const THREAD* thread) { + return thread && NtlAtomicLoad32(&thread->ExitInProgress) != 0; } // Implements the exit Thread operation. -__declspec(dllexport) void KeExitThread(THREAD* thread, int code) { +__declspec(dllexport) void KeExitThread(THREAD* thread, int32_t code) { (void)code; if (!thread) return; @@ -458,7 +459,7 @@ __declspec(dllexport) void KeExitThread(THREAD* thread, int code) { // stack until thread/process completion is fully published. Hardware // interrupts remain enabled, but interrupt-return scheduling observes // ExitInProgress and leaves the thread on-CPU until the final KeYield(). - if (_InterlockedCompareExchange(&thread->ExitInProgress, 1, 0) != 0) + if (NtlAtomicCompareExchange32(&thread->ExitInProgress, 1, 0) != 0) KePanic(KE_PANIC_CODE_SCHEDULER, "recursive thread exit: thread=0x%016llX tid=%u\n", (uint64_t)thread, @@ -634,7 +635,8 @@ __declspec(dllexport) KSTATUS KeCreateKernelThread(uintptr_t fn, uintptr_t arg, // Replaces one thread's diagnostic description. KSTATUS KeSetThreadName(THREAD* thread, const char* name, size_t length) { - if (!thread || (length && !name) || length >= NTL_THREAD_NAME_MAX) + if (!thread || length >= NTL_THREAD_NAME_MAX || + !SysSvIsUtf8Text(SysSvFromParts(name, length))) return NTLSTATUS_INVALID_PARAMETER; uintptr_t flags = KeAcquireSpinlock(&thread->NameLock); @@ -777,28 +779,28 @@ void KeUnblockThreadWithStatus(THREAD* thread, KSTATUS status) { } // Returns whether the scheduler link still belongs to an object wait list. -static int KiThreadWaitLinkIsLinked(const THREAD* thread) { - if (!thread) return 0; +static bool KiThreadWaitLinkIsLinked(const THREAD* thread) { + if (!thread) return false; return thread->SchedulerLink.Fwd != &thread->SchedulerLink || thread->SchedulerLink.Bwd != &thread->SchedulerLink; } // Removes a thread from one wait list while that wait object's lock is held. -static int KiRemoveThreadFromObjectWaitListLocked(THREAD* thread, LIST_ENTRY* waitListHead) { - if (!thread || !waitListHead) return 0; +static bool KiRemoveThreadFromObjectWaitListLocked(THREAD* thread, LIST_ENTRY* waitListHead) { + if (!thread || !waitListHead) return false; if (thread->State != ThreadWaiting || thread->WaitListHead != waitListHead) - return 0; + return false; // The signaling side pops the wait-list entry before it wakes the thread. // In that handoff window the scheduler link is already self-linked; abort // must not remove it again or turn a successful mutex/semaphore handoff // into an aborted wait for a thread that may already own the object. if (!KiThreadWaitLinkIsLinked(thread)) - return 0; + return false; KeListRemoveChecked(&thread->SchedulerLink); thread->WaitListHead = (LIST_ENTRY*)0; - return 1; + return true; } // Removes a thread from an object-owned wait queue using that object's lock. @@ -837,9 +839,9 @@ static int KiAbortObjectWait(THREAD* thread) { // unsignaled after the foreground thread has faulted. Object wait-list removal // must use the object's own lock; otherwise signal/abort races can leave stale // scheduler links in an event, mutex, or semaphore wait queue. -int KeAbortThreadWait(THREAD* thread, KSTATUS status) { - if (!thread) return 0; - if (thread->State != ThreadWaiting) return 0; +bool KeAbortThreadWait(THREAD* thread, KSTATUS status) { + if (!thread) return false; + if (thread->State != ThreadWaiting) return false; if (thread->WaitRundownRoutine) { KeRundownThreadWait(thread, status); @@ -848,18 +850,18 @@ int KeAbortThreadWait(THREAD* thread, KSTATUS status) { if (KiAbortObjectWait(thread)) { KeUnblockThreadWithStatus(thread, status); - return 1; + return true; } // A signaler that already dequeued an object waiter owns completion. Let // that handoff finish rather than returning timeout after ownership or a // semaphore count has already transferred to this thread. if (thread->WaitObject) - return 0; + return false; // Delays and auxiliary scheduler waits have no object-owned queue. KeUnblockThreadFromWaitListWithStatus(thread, status); - return 1; + return true; } // Implements the unblock Thread operation. diff --git a/code/kernel/ke_timer.c b/code/kernel/ke_timer.c index 429a7e8..c3d6222 100644 --- a/code/kernel/ke_timer.c +++ b/code/kernel/ke_timer.c @@ -1,6 +1,7 @@ // Kernel waitable timer objects. This file owns timer deadlines and timer wait signaling. #include +#include #include #include #include @@ -11,7 +12,7 @@ static LIST_ENTRY gTimerList; static SPINLOCK gTimerLock; -static uint8_t gTimerInitialized; +static bool gTimerInitialized; // Initializes the global timer list on first use. static void KiTimerEnsureInitialized(void) { diff --git a/code/kernel/ke_trap.c b/code/kernel/ke_trap.c index ca3e202..ef93b16 100644 --- a/code/kernel/ke_trap.c +++ b/code/kernel/ke_trap.c @@ -1,5 +1,6 @@ // Trap-frame tracking. This file owns current-thread trap snapshots and validation for user diagnostics. +#include #include #include #include @@ -45,8 +46,8 @@ STATIC_ASSERT_OFFSET(KTRAP_FRAME, PreviousFrame, 224); _Static_assert(sizeof(KTRAP_FRAME) == 240, "KTRAP_FRAME size must match ke_syscall.asm"); // Implements the trap Frame From User operation. -int KeTrapFrameFromUser(const KTRAP_FRAME* frame) { - if (!frame || frame->Magic != KTRAP_FRAME_MAGIC) return 0; +bool KeTrapFrameFromUser(const KTRAP_FRAME* frame) { + if (!frame || frame->Magic != KTRAP_FRAME_MAGIC) return false; return (frame->Cs & 3u) == 3u; } diff --git a/code/kernel/ke_work.c b/code/kernel/ke_work.c index 2643968..6a7ed3f 100644 --- a/code/kernel/ke_work.c +++ b/code/kernel/ke_work.c @@ -1,6 +1,7 @@ // Kernel passive-level work queue. This file owns the generic mechanism used // to move ISR/DPC-originated work to a sleepable system thread context. +#include #include #include #include @@ -19,8 +20,8 @@ static SPINLOCK gWorkQueueLock = SPINLOCK_INIT; static LIST_ENTRY gWorkQueueList; static EVENT* gWorkQueueEvent; static KDPC gWorkQueueWakeDpc; -static volatile long gWorkQueueListReady; -static volatile long gWorkQueueStartState; // 0=not started, 1=starting, 2=started. +static volatile int32_t gWorkQueueListReady; +static volatile int32_t gWorkQueueStartState; // 0=not started, 1=starting, 2=started. // Signals the passive worker after an enqueue from hardware interrupt context. static void KiWorkQueueWakeDpc(KDPC* dpc, void* context, void* systemArgument1, void* systemArgument2) { @@ -57,7 +58,7 @@ static void KiWorkQueueWorker(void* arg) { // Allow the item to be queued again while it is running. This keeps // level-triggered users such as cursor updates naturally coalesced // but does not lose an update that arrives during the callback. - _InterlockedExchange(&item->Queued, 0); + NtlAtomicExchange32(&item->Queued, 0); if (item->Routine) item->Routine(item->Context); } @@ -74,18 +75,18 @@ void KeInitializeWorkItem(KWORK_ITEM* item, KWORK_ROUTINE routine, void* context } bool KeIsWorkQueueStarted(void) { - return _InterlockedCompareExchange(&gWorkQueueStartState, 2, 2) == 2; + return NtlAtomicCompareExchange32(&gWorkQueueStartState, 2, 2) == 2; } bool KeQueueWorkItem(KWORK_ITEM* item) { if (!item || !item->Routine) return false; - if (_InterlockedCompareExchange(&item->Queued, 1, 0) != 0) + if (NtlAtomicCompareExchange32(&item->Queued, 1, 0) != 0) return false; - if (!_InterlockedCompareExchange(&gWorkQueueListReady, 1, 1)) { - _InterlockedExchange(&item->Queued, 0); + if (!NtlAtomicCompareExchange32(&gWorkQueueListReady, 1, 1)) { + NtlAtomicExchange32(&item->Queued, 0); return false; } @@ -106,18 +107,18 @@ bool KeQueueWorkItem(KWORK_ITEM* item) { } KSTATUS KeStartWorkQueue(void) { - long state = _InterlockedCompareExchange(&gWorkQueueStartState, 1, 0); + int32_t state = NtlAtomicCompareExchange32(&gWorkQueueStartState, 1, 0); if (state == 2) return NTLSTATUS_SUCCESS; if (state == 1) { - while (_InterlockedCompareExchange(&gWorkQueueStartState, 2, 2) == 1) + while (NtlAtomicCompareExchange32(&gWorkQueueStartState, 2, 2) == 1) KeYield(); return KeIsWorkQueueStarted() ? NTLSTATUS_SUCCESS : NTLSTATUS_FAILURE; } LIST_INIT(&gWorkQueueList); KeInitializeDpc(&gWorkQueueWakeDpc, KiWorkQueueWakeDpc, (void*)0); - _InterlockedExchange(&gWorkQueueListReady, 1); + NtlAtomicExchange32(&gWorkQueueListReady, 1); KSTATUS s = KeEventCreate(EventAutoReset, 0, &gWorkQueueEvent); if (KSUCCESS(s)) { @@ -133,10 +134,10 @@ KSTATUS KeStartWorkQueue(void) { } if (KSUCCESS(s)) { - _InterlockedExchange(&gWorkQueueStartState, 2); + NtlAtomicExchange32(&gWorkQueueStartState, 2); KeSignalEvent(gWorkQueueEvent); } else { - _InterlockedExchange(&gWorkQueueStartState, 0); + NtlAtomicExchange32(&gWorkQueueStartState, 0); } return s; } diff --git a/code/kernel/kmain.c b/code/kernel/kmain.c index 26866e8..d5441d7 100644 --- a/code/kernel/kmain.c +++ b/code/kernel/kmain.c @@ -1,5 +1,6 @@ // Kernel entry point. This file connects boot information to core subsystem initialization and scheduler start. +#include #include #include #include @@ -128,7 +129,7 @@ typedef KSTATUS (*DRIVER_ENTRY_FN)(const NTL_DDK* ddk, NTL_DRIVER_INIT* init); static const HAL_API* gKmainHal; // Implements the page In Bootloader Region operation. -static int KiPageInBootloaderRegion(const BOOT_INFO* bootInfo, PHYSICAL_ADDRESS page) { +static bool KiPageInBootloaderRegion(const BOOT_INFO* bootInfo, PHYSICAL_ADDRESS page) { for (uint32_t i = 0; i < bootInfo->MemoryMap.RegionCount; i++) { const MEMORY_REGION* r = &bootInfo->MemoryMap.Regions[i]; if (r->Type != MemoryBootloader) continue; @@ -136,10 +137,10 @@ static int KiPageInBootloaderRegion(const BOOT_INFO* bootInfo, PHYSICAL_ADDRESS uintptr_t start = r->Base & PAGE_MASK; uintptr_t end = (r->Base + r->Length + PAGE_SIZE - 1) & PAGE_MASK; if ((uintptr_t)page >= start && (uintptr_t)page < end) { - return 1; + return true; } } - return 0; + return false; } // Reclaims one exact page-backed blob after its boot consumer has copied or materialized it. @@ -170,7 +171,7 @@ static void KiReclaimBootloaderMemory(const BOOT_INFO* bootInfo) { } // Returns whether one drivers.ini line character is horizontal whitespace. -static int KiDriverConfigSpace(char c) { +static bool KiDriverConfigSpace(char c) { return c == ' ' || c == '\t' || c == '\r'; } @@ -183,10 +184,10 @@ static void KiDriverConfigTrim(const char** begin, const char** end) { } // Returns whether one immutable drivers.ini span exactly matches an ASCII string. -static int KiDriverConfigSpanEquals(const char* begin, const char* end, const char* text) { +static bool KiDriverConfigSpanEquals(const char* begin, const char* end, const char* text) { while (begin < end && *text) { if (*begin++ != *text++) - return 0; + return false; } return begin == end && *text == '\0'; } @@ -605,55 +606,55 @@ void SystemInitThread(void* arg) { } // Returns whether one page-backed boot allocation is aligned, nonempty, and does not overflow. -static int KiValidateBootPageRange(uintptr_t physicalBase, uint32_t pageCount) { +static bool KiValidateBootPageRange(uintptr_t physicalBase, uint32_t pageCount) { if (!physicalBase || (physicalBase & (PAGE_SIZE - 1u)) != 0 || pageCount == 0) - return 0; + return false; uint64_t byteCount = (uint64_t)pageCount * PAGE_SIZE; return byteCount != 0 && physicalBase <= UINTPTR_MAX - byteCount; } // Returns whether one page-backed raw boot file is fully contained in its recorded allocation. -static int KiValidateBootFile(const BOOT_FILE* file) { +static bool KiValidateBootFile(const BOOT_FILE* file) { if (!file || !KiValidateBootPageRange(file->PhysicalBase, file->PageCount) || file->Length == 0) - return 0; + return false; uint64_t capacity = (uint64_t)file->PageCount * PAGE_SIZE; if ((uint64_t)file->Length + 1u > capacity || file->Name[0] == '\0') - return 0; + return false; return memchr(file->Name, '\0', sizeof(file->Name)) != NULL; } // Returns whether one fixed core-image descriptor fits inside its page-backed physical allocation. -static int KiValidateBootImage(const BOOT_IMAGE* image, int requireEntry) { +static bool KiValidateBootImage(const BOOT_IMAGE* image, int requireEntry) { if (!image || !image->VirtualBase || (image->VirtualBase & (PAGE_SIZE - 1u)) != 0 || !KiValidateBootPageRange(image->PhysicalBase, image->PageCount) || !image->ImageSize) { - return 0; + return false; } uint64_t capacity = (uint64_t)image->PageCount * PAGE_SIZE; if (image->ImageSize > capacity || image->VirtualBase > UINTPTR_MAX - image->ImageSize) - return 0; + return false; if (requireEntry && image->EntryRva >= image->ImageSize) - return 0; - return 1; + return false; + return true; } // Validates the exact bootloader/kernel handoff contract before dereferencing variable physical blobs. -static int KiValidateBootInfo(const BOOT_INFO* bootInfo) { +static bool KiValidateBootInfo(const BOOT_INFO* bootInfo) { if (!bootInfo || bootInfo->Magic != BOOT_INFO_MAGIC || bootInfo->Version != BOOT_INFO_VERSION || bootInfo->Size != sizeof(BOOT_INFO) || bootInfo->MemoryMap.Magic != MEMORY_MAP_MAGIC || bootInfo->MemoryMap.RegionCount > MEMORY_MAX_REGIONS || bootInfo->PhysicalMapBase == 0) { - return 0; + return false; } uint64_t bootIniCapacity = (uint64_t)bootInfo->BootIni.PageCount * PAGE_SIZE; if (!KiValidateBootPageRange(bootInfo->BootIni.PhysicalBase, bootInfo->BootIni.PageCount) || bootInfo->BootIni.Length == 0 || (uint64_t)bootInfo->BootIni.Length + 1u > bootIniCapacity || bootInfo->BootIni.PhysicalBase > UINTPTR_MAX - bootInfo->PhysicalMapBase) { - return 0; + return false; } uint64_t driverListCapacity = (uint64_t)bootInfo->BootDrivers.PageCount * PAGE_SIZE; @@ -661,7 +662,7 @@ static int KiValidateBootInfo(const BOOT_INFO* bootInfo) { if (!KiValidateBootPageRange(bootInfo->BootDrivers.FilesPhysical, bootInfo->BootDrivers.PageCount) || bootInfo->BootDrivers.Count == 0 || driverListBytes > driverListCapacity || bootInfo->BootDrivers.FilesPhysical > UINTPTR_MAX - bootInfo->PhysicalMapBase) { - return 0; + return false; } const BOOT_FILE* bootFiles = @@ -669,16 +670,16 @@ static int KiValidateBootInfo(const BOOT_INFO* bootInfo) { for (uint32_t index = 0; index < bootInfo->BootDrivers.Count; ++index) { if (!KiValidateBootFile(&bootFiles[index]) || bootFiles[index].PhysicalBase > UINTPTR_MAX - bootInfo->PhysicalMapBase) { - return 0; + return false; } } if (!KiValidateBootImage(&bootInfo->KernelImage, 1) || !KiValidateBootImage(&bootInfo->HalImage, 0)) { - return 0; + return false; } - return 1; + return true; } // Main Kernel Entry. diff --git a/code/kernel/ldr_image.c b/code/kernel/ldr_image.c index 6675743..e4da3eb 100644 --- a/code/kernel/ldr_image.c +++ b/code/kernel/ldr_image.c @@ -188,7 +188,8 @@ static KSTATUS KiRegisterGlobalModule(const char* name, uintptr_t base, size_t s size_t nameLength = 0; while (nameLength < LDR_KERNEL_MODULE_NAME_MAX && name[nameLength]) nameLength++; - if (nameLength == 0 || nameLength >= LDR_KERNEL_MODULE_NAME_MAX) + if (nameLength == 0 || nameLength >= LDR_KERNEL_MODULE_NAME_MAX || + !SysSvIsUtf8Text(SysSvFromParts(name, nameLength))) return NTLSTATUS_INVALID_PARAMETER; MODULE_ENTRY* entry = (MODULE_ENTRY*)MmAllocatePool(sizeof(MODULE_ENTRY)); diff --git a/code/kernel/mm_file_cache.c b/code/kernel/mm_file_cache.c index dc78f8e..0c3a0e6 100644 --- a/code/kernel/mm_file_cache.c +++ b/code/kernel/mm_file_cache.c @@ -5,6 +5,7 @@ // truth. Dirty pages are flushed explicitly, by close, or by the background // writeback worker under pressure. +#include #include #include #include @@ -36,7 +37,7 @@ typedef struct MM_FILE_CACHE_ENTRY { uint8_t Referenced; uint8_t Standby; uint8_t Loading; - uint8_t Invalidated; + bool Invalidated; uint8_t Dirty; uint8_t Writeback; uint64_t DirtySequence; @@ -53,7 +54,7 @@ static uint64_t gFileCacheMaxResidentPages; static MM_FILE_CACHE_STATS gFileCacheStats; -static int KiFileCacheUnlinkEntryLocked(MM_FILE_CACHE_ENTRY* entry); +static bool KiFileCacheUnlinkEntryLocked(MM_FILE_CACHE_ENTRY* entry); static SPINLOCK gWritebackLock = SPINLOCK_INIT; static SEMAPHORE* gWritebackSemaphore; @@ -149,12 +150,12 @@ static void KiFileCacheRemoveEntryLocked(uint32_t bucket, MM_FILE_CACHE_ENTRY* p KiFileCacheSetDirtyLocked(e, 0); } -static int KiFileCacheIsReclaimCandidateLocked(MM_FILE_CACHE_ENTRY* e, FILE_OBJECT* skipFile, uint64_t skipOffset) { - if (!e) return 0; +static bool KiFileCacheIsReclaimCandidateLocked(MM_FILE_CACHE_ENTRY* e, FILE_OBJECT* skipFile, uint64_t skipOffset) { + if (!e) return false; if (skipFile && e->File == skipFile && e->Offset == skipOffset) - return 0; + return false; if (e->Loading || e->Invalidated || !e->Frame || e->Dirty || e->Writeback) - return 0; + return false; MM_PFN* pfn = MmPfnGet(e->Frame); return pfn && pfn->PinCount == 0 && pfn->MappedCount == 0 && pfn->RefCount == 1; @@ -491,19 +492,19 @@ static void KiFileCacheFreeLoadingEntry(MM_FILE_CACHE_ENTRY* entry) { KiFileCacheFreeEntry(entry); } -static int KiFileCacheUnlinkEntryLocked(MM_FILE_CACHE_ENTRY* entry) { +static bool KiFileCacheUnlinkEntryLocked(MM_FILE_CACHE_ENTRY* entry) { if (!entry) - return 0; + return false; uint32_t bucket = KiFileCacheHash(entry->File, entry->Offset); MM_FILE_CACHE_ENTRY* prev = (MM_FILE_CACHE_ENTRY*)0; for (MM_FILE_CACHE_ENTRY* e = gFileCacheBuckets[bucket]; e; prev = e, e = e->Next) { if (e == entry) { KiFileCacheRemoveEntryLocked(bucket, prev, e); - return 1; + return true; } } - return 0; + return false; } static KSTATUS KiFileCacheReserveLoadingPage(FILE_OBJECT* file, uint64_t aligned, MM_FILE_CACHE_ENTRY** outEntry) { @@ -536,7 +537,7 @@ static void KiFileCacheCancelLoadingPage(MM_FILE_CACHE_ENTRY* entry) { return; uintptr_t flags = KeAcquireSpinlock(&gFileCacheLock); - int unlinked = KiFileCacheUnlinkEntryLocked(entry); + bool unlinked = KiFileCacheUnlinkEntryLocked(entry); KeReleaseSpinlock(&gFileCacheLock, flags); if (unlinked) @@ -655,7 +656,7 @@ static KSTATUS KiFileCacheLoadPage(FILE_OBJECT* file, static KSTATUS KiFileCacheGetWritablePage(FILE_OBJECT* file, uint64_t aligned, uint64_t oldFileSize, - int needExistingBytes, + bool needExistingBytes, MM_FILE_CACHE_PAGE* outPage) { if (!file || !outPage) return NTLSTATUS_INVALID_PARAMETER; @@ -728,7 +729,7 @@ static void KiFileCacheMarkPageDirty(FILE_OBJECT* file, uint64_t aligned, uint32 } KSTATUS MmFileCacheGetWritablePage(FILE_OBJECT* file, uint64_t offset, - uint64_t fileSizeBefore, int needExistingBytes, + uint64_t fileSizeBefore, bool needExistingBytes, MM_FILE_CACHE_PAGE* outPage) { if (!outPage) return NTLSTATUS_INVALID_PARAMETER; diff --git a/code/kernel/mm_phys.c b/code/kernel/mm_phys.c index 0a636fc..49ab8a0 100644 --- a/code/kernel/mm_phys.c +++ b/code/kernel/mm_phys.c @@ -4,6 +4,7 @@ // but callers now get typed, reference-counted frames instead of anonymous // bitmap bits. +#include #include #include #include @@ -61,12 +62,12 @@ static inline PHYSICAL_ADDRESS KiAlignDown(PHYSICAL_ADDRESS value) { } // Returns whether a boot memory type describes RAM that the PFN database should track. -static int KiMemoryTypeManaged(MEMORY_TYPE type) { +static bool KiMemoryTypeManaged(MEMORY_TYPE type) { return type == MemoryUsable || type == MemoryBootloader || type == MemoryKernel; } // Returns whether an allocation may consume the emergency kernel reserve. -static int KiPfnUsageMayUseReserve(MM_PFN_USAGE usage) { +static bool KiPfnUsageMayUseReserve(MM_PFN_USAGE usage) { return usage == MmPfnUseKernel || usage == MmPfnUsePageTable || usage == MmPfnUseDma || @@ -142,11 +143,11 @@ static void KiRegisterManagedRange(PHYSICAL_ADDRESS base, } // Returns whether the future page-replacement code may consider this frame a candidate. -static int KiPfnEvictableLocked(const MM_PFN* entry) { +static bool KiPfnEvictableLocked(const MM_PFN* entry) { if (!entry || entry->PinCount) - return 0; + return false; if (entry->State != MmPfnStateActive && entry->State != MmPfnStateStandby && entry->State != MmPfnStateModified) - return 0; + return false; return entry->Usage == MmPfnUseUser || entry->Usage == MmPfnUseCleanFileCache || entry->Usage == MmPfnUseImage || @@ -156,18 +157,18 @@ static int KiPfnEvictableLocked(const MM_PFN* entry) { } // Returns whether a standby frame can be counted as immediately reclaimable. -static int KiPfnReclaimableLocked(const MM_PFN* entry) { +static bool KiPfnReclaimableLocked(const MM_PFN* entry) { if (!entry || entry->PinCount || entry->MappedCount) - return 0; + return false; if (entry->State != MmPfnStateStandby || entry->RefCount != 1) - return 0; + return false; return entry->Usage == MmPfnUseCleanFileCache || entry->Usage == MmPfnUseImage; } // Returns true when a PFN can satisfy a new allocation. -static int KiPfnAvailableLocked(size_t pfn) { +static bool KiPfnAvailableLocked(size_t pfn) { if (pfn >= gMaxPfn) - return 0; + return false; MM_PFN* entry = &gPfnDatabase[pfn]; return entry->State == MmPfnStateFree || entry->State == MmPfnStateZeroed; } diff --git a/code/kernel/mm_pool.c b/code/kernel/mm_pool.c index dc8e3dd..404dfc5 100644 --- a/code/kernel/mm_pool.c +++ b/code/kernel/mm_pool.c @@ -1,5 +1,6 @@ // Kernel pool allocator and corruption diagnostics. +#include #include #include #include @@ -163,9 +164,9 @@ static _Noreturn void KiPoolPanic(const char* reason, HEAP* h, BLOCK_HDR* blk, c } // Tests whether an allocator header pointer is inside the committed heap. -static int KiPoolHeaderInCommittedHeap(const HEAP* h, const BLOCK_HDR* blk) { +static bool KiPoolHeaderInCommittedHeap(const HEAP* h, const BLOCK_HDR* blk) { if (!h || !blk) - return 0; + return false; uintptr_t addr = (uintptr_t)blk; return addr >= h->Start && addr < h->Brk; } @@ -250,9 +251,9 @@ static void KiFreeListInsertHeadLocked(HEAP* h, BLOCK_HDR* blk, uintptr_t caller static void KiFreeListRemoveLocked(HEAP* h, BLOCK_HDR* blk, uintptr_t caller); // Extend heap by at least 'need' bytes (rounded up to page boundary). -static int KiHeapGrow(HEAP* h, size_t need, uintptr_t caller) { +static bool KiHeapGrow(HEAP* h, size_t need, uintptr_t caller) { size_t grow = (need + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1); - if (h->Brk + grow > h->Limit) return 0; + if (h->Brk + grow > h->Limit) return false; uintptr_t start = h->Brk; uintptr_t mappedEnd = start; @@ -260,12 +261,12 @@ static int KiHeapGrow(HEAP* h, size_t need, uintptr_t caller) { PHYSICAL_ADDRESS frame = MmPfnAllocate(MmPfnUseKernel); if (!frame) { KiHeapRollbackGrow(start, mappedEnd); - return 0; + return false; } if (!KSUCCESS(MmPteMapPage(va, frame, h->MapFlags, MmPteMapFailIfPresent))) { MmPfnFree(frame); KiHeapRollbackGrow(start, mappedEnd); - return 0; + return false; } mappedEnd = va + PAGE_SIZE; memset((void*)va, 0, PAGE_SIZE); @@ -294,7 +295,7 @@ static int KiHeapGrow(HEAP* h, size_t need, uintptr_t caller) { KiHdrToFtr(prev)->Magic = BLOCK_MAGIC; KiHdrToFtr(prev)->Free = BLOCK_STATE_FREE; KiHdrToFtr(prev)->Size = prev->Size; - return 1; + return true; } } } @@ -323,7 +324,7 @@ static int KiHeapGrow(HEAP* h, size_t need, uintptr_t caller) { KiHdrToFtr(blk)->Size = blk->Size; KiFreeListInsertHeadLocked(h, blk, caller); - return 1; + return true; } // Validates a free-list node before the allocator mutates list links. diff --git a/code/kernel/mm_pte.c b/code/kernel/mm_pte.c index 04c105e..b481a0c 100644 --- a/code/kernel/mm_pte.c +++ b/code/kernel/mm_pte.c @@ -1,5 +1,7 @@ // Kernel page-table manager. This file owns x86-64 PTE manipulation, TLB invalidation, and shared-CR3 activation. +#include +#include #include #include #include @@ -27,9 +29,9 @@ static inline uint64_t* KiPtePtr(PHYSICAL_ADDRESS tablePhys, unsigned idx) { static PHYSICAL_ADDRESS gPml4Phys = 0; static SPINLOCK gPageTableLock = SPINLOCK_INIT; -static volatile long gTlbShootdownLock = 0; -static volatile long gTlbShootdownActive = 0; -static volatile long gTlbShootdownAcks = 0; +static volatile int32_t gTlbShootdownLock = 0; +static volatile int32_t gTlbShootdownActive = 0; +static volatile int32_t gTlbShootdownAcks = 0; static uintptr_t gTlbShootdownVirt = 0; static MM_PTE_STATS gPteStats; @@ -113,7 +115,7 @@ static void KiAcquireTlbShootdownLock(void) { // A CPU spinning with IF=0 on the shootdown lock could not acknowledge a // concurrent fixed-vector TLB IPI, creating the exact deadlock this lock is // meant to avoid. - while (_InterlockedCompareExchange(&gTlbShootdownLock, 1, 0) != 0) + while (NtlAtomicCompareExchange32(&gTlbShootdownLock, 1, 0) != 0) _mm_pause(); _ReadWriteBarrier(); } @@ -121,7 +123,7 @@ static void KiAcquireTlbShootdownLock(void) { // Releases the shootdown state lock after all shared fields are stable. static void KiReleaseTlbShootdownLock(void) { _ReadWriteBarrier(); - _InterlockedExchange(&gTlbShootdownLock, 0); + NtlAtomicExchange32(&gTlbShootdownLock, 0); } // Flushes one kernel mapping on every online CPU that shares this PML4. @@ -139,11 +141,11 @@ void MmPteFlushKernelAddress(uintptr_t virt) { KiAcquireTlbShootdownLock(); - long targets = 0; + int32_t targets = 0; gTlbShootdownVirt = virt; - _InterlockedExchange(&gTlbShootdownAcks, 0); + NtlAtomicExchange32(&gTlbShootdownAcks, 0); _ReadWriteBarrier(); - _InterlockedExchange(&gTlbShootdownActive, 1); + NtlAtomicExchange32(&gTlbShootdownActive, 1); _ReadWriteBarrier(); for (uint32_t i = 0; i < gCpuCount; i++) { @@ -159,7 +161,7 @@ void MmPteFlushKernelAddress(uintptr_t virt) { if (!targets) { KiPteStatInc(&gPteStats.ShootdownSkippedNoTargets); - _InterlockedExchange(&gTlbShootdownActive, 0); + NtlAtomicExchange32(&gTlbShootdownActive, 0); _ReadWriteBarrier(); KiReleaseTlbShootdownLock(); return; @@ -168,13 +170,13 @@ void MmPteFlushKernelAddress(uintptr_t virt) { KiPteStatInc(&gPteStats.ShootdownRequests); for (uint32_t spin = 0; targets > 0 && spin < TLB_SHOOTDOWN_TIMEOUT_SPINS; spin++) { - if (_InterlockedCompareExchange(&gTlbShootdownAcks, 0, 0) >= targets) + if (NtlAtomicLoad32(&gTlbShootdownAcks) >= targets) break; _mm_pause(); } - long acks = _InterlockedCompareExchange(&gTlbShootdownAcks, 0, 0); - _InterlockedExchange(&gTlbShootdownActive, 0); + int32_t acks = NtlAtomicLoad32(&gTlbShootdownAcks); + NtlAtomicExchange32(&gTlbShootdownActive, 0); _ReadWriteBarrier(); KiReleaseTlbShootdownLock(); @@ -188,20 +190,20 @@ void MmPteFlushKernelAddress(uintptr_t virt) { } // Handles an incoming shootdown request on the current CPU. -static int KiHandleTlbShootdown(void) { - if (_InterlockedCompareExchange(&gTlbShootdownActive, 0, 0) == 0) - return 0; +static bool KiHandleTlbShootdown(void) { + if (NtlAtomicLoad32(&gTlbShootdownActive) == 0) + return false; uintptr_t va = gTlbShootdownVirt; _ReadWriteBarrier(); MmPteFlushLocal(va); _ReadWriteBarrier(); - _InterlockedIncrement(&gTlbShootdownAcks); - return 1; + NtlAtomicIncrement32(&gTlbShootdownAcks); + return true; } // Handles the NMI vector reserved for TLB shootdowns. -int MmPteHandleShootdownNmi(void) { +bool MmPteHandleShootdownNmi(void) { return KiHandleTlbShootdown(); } @@ -363,9 +365,9 @@ __declspec(dllexport) KSTATUS MmPteProtectRange(uintptr_t virt, size_t size, uin // used by the pressure trimmer as a second-chance test for clean file-backed // pages. If the page was accessed recently, reclaim skips it once and flushes // the TLB entry so future accesses can set PAGE_ACCESSED again. -__declspec(dllexport) int MmPteTestAndClearAccessed(uintptr_t virt) { +__declspec(dllexport) bool MmPteTestAndClearAccessed(uintptr_t virt) { if (!gPml4Phys) - return 0; + return false; uintptr_t va = virt & PAGE_MASK; int wasAccessed = 0; @@ -399,9 +401,9 @@ out: } // Tests and clears the hardware dirty bit on a mapped 4 KiB leaf. -__declspec(dllexport) int MmPteTestAndClearDirty(uintptr_t virt) { +__declspec(dllexport) bool MmPteTestAndClearDirty(uintptr_t virt) { if (!gPml4Phys) - return 0; + return false; uintptr_t va = virt & PAGE_MASK; int wasDirty = 0; diff --git a/code/kernel/mm_swap.c b/code/kernel/mm_swap.c index 130a060..779a707 100644 --- a/code/kernel/mm_swap.c +++ b/code/kernel/mm_swap.c @@ -3,6 +3,7 @@ // Kernel memory and filesystem code remain non-pageable, so this layer is safe // to use before the VMM has direct block-extents for pageout. +#include #include #include #include @@ -21,14 +22,14 @@ typedef struct MM_SWAP_STATE { uint32_t SlotCount; uint32_t UsedSlots; uint32_t HighWaterSlots; - int Enabled; + bool Enabled; MM_SWAP_STATS Stats; } MM_SWAP_STATE; static MM_SWAP_STATE gSwap = { .Lock = SPINLOCK_INIT }; // Returns whether a swap slot is marked in use. -static int KiSwapTestSlot(uint32_t slot) { +static bool KiSwapTestSlot(uint32_t slot) { return (gSwap.Bitmap[slot >> 3] & (uint8_t)(1u << (slot & 7))) != 0; } @@ -133,7 +134,7 @@ KSTATUS MmSwapInitialize(const char* relativePath, uint64_t sizeBytes) { gSwap.HighWaterSlots = 0; gSwap.Enabled = 1; memset(&gSwap.Stats, 0, sizeof(gSwap.Stats)); - gSwap.Stats.Enabled = 1; + gSwap.Stats.Enabled = true; gSwap.Stats.TotalSlots = slotCount; gSwap.Stats.FreeSlots = slotCount; KeReleaseSpinlock(&gSwap.Lock, flags); @@ -146,7 +147,7 @@ KSTATUS MmSwapInitialize(const char* relativePath, uint64_t sizeBytes) { } // Returns whether swap is available for pageout. -int MmSwapIsEnabled(void) { +bool MmSwapIsEnabled(void) { return gSwap.Enabled; } @@ -193,7 +194,7 @@ KSTATUS MmSwapPageIn(uint32_t slot, PHYSICAL_ADDRESS frame) { return NTLSTATUS_NOT_SUPPORTED; uintptr_t flags = KeAcquireSpinlock(&gSwap.Lock); - int valid = slot < gSwap.SlotCount && KiSwapTestSlot(slot); + bool valid = slot < gSwap.SlotCount && KiSwapTestSlot(slot); KeReleaseSpinlock(&gSwap.Lock, flags); if (!valid) { gSwap.Stats.PageInFailures++; @@ -234,7 +235,7 @@ void MmSwapQueryStats(MM_SWAP_STATS* out) { if (!out) return; uintptr_t flags = KeAcquireSpinlock(&gSwap.Lock); MM_SWAP_STATS stats = gSwap.Stats; - stats.Enabled = gSwap.Enabled ? 1 : 0; + stats.Enabled = gSwap.Enabled; stats.TotalSlots = gSwap.SlotCount; stats.UsedSlots = gSwap.UsedSlots; stats.FreeSlots = gSwap.SlotCount >= gSwap.UsedSlots ? gSwap.SlotCount - gSwap.UsedSlots : 0; diff --git a/code/kernel/mm_virt.c b/code/kernel/mm_virt.c index 0a40266..8ee98aa 100644 --- a/code/kernel/mm_virt.c +++ b/code/kernel/mm_virt.c @@ -16,6 +16,8 @@ // User mappings: // VADs separate reservation from commitment. Committed pages become resident // when a user fault or kernel prefault path materializes them. +#include +#include #include #include #include @@ -42,12 +44,12 @@ static PHYSICAL_ADDRESS gVadZeroPageFrame; // User/kernel copy boundary helpers. // Implements the user Range Valid operation. -static int KiUserRangeValid(uintptr_t addr, size_t size) { - if (size == 0) return 1; - if (addr < USER_BASE) return 0; - if (addr >= USER_LIMIT) return 0; - if (size > (size_t)(USER_LIMIT - addr)) return 0; - return 1; +static bool KiUserRangeValid(uintptr_t addr, size_t size) { + if (size == 0) return true; + if (addr < USER_BASE) return false; + if (addr >= USER_LIMIT) return false; + if (size > (size_t)(USER_LIMIT - addr)) return false; + return true; } // Probes user Range and materializes demand-zero pages before the kernel copies through it. @@ -300,7 +302,7 @@ static inline size_t KiAlignUpSize(size_t v, size_t align) { } // Returns whether two half-open address ranges intersect. -static int KiRangesOverlap(uintptr_t aBase, size_t aSize, uintptr_t bBase, size_t bSize) { +static bool KiRangesOverlap(uintptr_t aBase, size_t aSize, uintptr_t bBase, size_t bSize) { uintptr_t aEnd = aBase + aSize; uintptr_t bEnd = bBase + bSize; return aBase < bEnd && bBase < aEnd; @@ -376,19 +378,19 @@ static MM_VAD* KiFindReservedVadContainingLocked(PROCESS* process, uintptr_t bas } // Returns whether every page in the aligned range is committed in the VAD metadata. -static int KiVadRangeCommitted(const MM_VAD* vad, uintptr_t alignedStart, uintptr_t alignedEnd) { +static bool KiVadRangeCommitted(const MM_VAD* vad, uintptr_t alignedStart, uintptr_t alignedEnd) { if (!vad || alignedStart < vad->Base || alignedEnd > vad->Base + vad->PayloadSize) - return 0; + return false; size_t first = KiVadPageIndex(vad, alignedStart); size_t count = KiVadRangePageCount(alignedStart, alignedEnd); if (first > vad->PageCount || count > vad->PageCount - first) - return 0; + return false; for (size_t i = 0; i < count; ++i) { if (vad->Pages[first + i].State != MmVadPageCommitted) - return 0; + return false; } - return 1; + return true; } static uint64_t KiVadHardwarePageFlags(const MM_VAD_PAGE* page); @@ -447,7 +449,7 @@ static void KiVadSyncSharedFilePageBeforeRelease(MM_VAD* vad, size_t index) { return; int wasDirty = MmPteTestAndClearDirty(va); - int wasWriteEnabled = (page->Flags & MM_VAD_PAGE_FILE_DIRTY_TRACK) == 0; + bool wasWriteEnabled = (page->Flags & MM_VAD_PAGE_FILE_DIRTY_TRACK) == 0; if (wasDirty || wasWriteEnabled) (void)KiVadMarkSharedFilePageDirty(vad, index, 0); } @@ -544,7 +546,7 @@ static KSTATUS KiVadCheckPageAccess(const MM_VAD_PAGE* page, int wantWrite, int // concurrent faults from treating half-transitioned metadata as either resident or // fully swapped. static void KiVadWaitForPagingComplete(MM_VAD_PAGE* page) { - while (page && (_InterlockedCompareExchange((volatile long*)&page->Flags, 0, 0) & + while (page && (NtlAtomicLoadU32(&page->Flags) & MM_VAD_PAGE_PAGING)) KeYield(); } @@ -555,18 +557,18 @@ static void KiVadWaitForPagingComplete(MM_VAD_PAGE* page) { // swapped/demand-zero page can both allocate/read/map it; one map fails and rolls // metadata back to SWAPPED while the other has already published a valid PTE. That // leaves stale or freed swap slots behind and corrupts user data under pressure. -static int KiVadTryBeginPaging(MM_VAD_PAGE* page) { - if (!page) return 0; +static bool KiVadTryBeginPaging(MM_VAD_PAGE* page) { + if (!page) return false; while (true) { - uint32_t oldFlags = (uint32_t)_InterlockedCompareExchange((volatile long*)&page->Flags, 0, 0); + uint32_t oldFlags = (uint32_t)NtlAtomicLoadU32(&page->Flags); if (oldFlags & MM_VAD_PAGE_PAGING) - return 0; + return false; uint32_t newFlags = oldFlags | MM_VAD_PAGE_PAGING; - if ((uint32_t)_InterlockedCompareExchange((volatile long*)&page->Flags, - (long)newFlags, - (long)oldFlags) == oldFlags) - return 1; + if ((uint32_t)NtlAtomicCompareExchangeU32(&page->Flags, + newFlags, + oldFlags) == oldFlags) + return true; _mm_pause(); } } @@ -575,11 +577,11 @@ static void KiVadEndPaging(MM_VAD_PAGE* page) { if (!page) return; while (true) { - uint32_t oldFlags = (uint32_t)_InterlockedCompareExchange((volatile long*)&page->Flags, 0, 0); + uint32_t oldFlags = (uint32_t)NtlAtomicLoadU32(&page->Flags); uint32_t newFlags = oldFlags & ~MM_VAD_PAGE_PAGING; - if ((uint32_t)_InterlockedCompareExchange((volatile long*)&page->Flags, - (long)newFlags, - (long)oldFlags) == oldFlags) + if ((uint32_t)NtlAtomicCompareExchangeU32(&page->Flags, + newFlags, + oldFlags) == oldFlags) return; _mm_pause(); } @@ -1201,7 +1203,7 @@ static void KiVadRemoveUserBufferPageLock(PROCESS* process, MM_VAD* vad) { __declspec(dllexport) KSTATUS MmLockUserBuffer(PROCESS* process, void* userAddress, size_t size, - int writeAccess, + bool writeAccess, MM_LOCKED_USER_BUFFER** out) { if (!out) return NTLSTATUS_INVALID_PARAMETER; @@ -1939,73 +1941,73 @@ KSTATUS MmVadCreateSharedViewFromFrames(PROCESS* targetProcess, // read-only mappings cannot have been modified, and the backing file can // repopulate the page on the next fault. Writable/private file mappings still // fall back to the swap path because this VMM does not yet track dirty bits. -static int KiVadPageCanDiscardCleanFile(const MM_VAD* vad, const MM_VAD_PAGE* page) { +static bool KiVadPageCanDiscardCleanFile(const MM_VAD* vad, const MM_VAD_PAGE* page) { if (!vad || !page || vad->Type != MmVadMappedFile) - return 0; + return false; if (!vad->FileObject || page->State != MmVadPageCommitted) - return 0; + return false; if (!page->Resident || !page->Frame || !page->PageFlags) - return 0; + return false; if ((page->Flags & MM_VAD_PAGE_FILE_BACKED) == 0) - return 0; + return false; if (page->PageFlags & PAGE_WRITE) - return 0; + return false; if (page->Flags & (MM_VAD_PAGE_COW | MM_VAD_PAGE_ZERO_PAGE | MM_VAD_PAGE_SHARED_FRAME | MM_VAD_PAGE_SWAPPED | MM_VAD_PAGE_PAGING)) - return 0; - return 1; + return false; + return true; } -static int KiVadPageCanDiscardCleanFileAfterPagingClaim(const MM_VAD* vad, const MM_VAD_PAGE* page) { +static bool KiVadPageCanDiscardCleanFileAfterPagingClaim(const MM_VAD* vad, const MM_VAD_PAGE* page) { if (!vad || !page || vad->Type != MmVadMappedFile) - return 0; + return false; if (!vad->FileObject || page->State != MmVadPageCommitted) - return 0; + return false; if (!page->Resident || !page->Frame || !page->PageFlags) - return 0; + return false; if ((page->Flags & MM_VAD_PAGE_FILE_BACKED) == 0) - return 0; + return false; if (page->PageFlags & PAGE_WRITE) - return 0; + return false; if (page->Flags & (MM_VAD_PAGE_COW | MM_VAD_PAGE_ZERO_PAGE | MM_VAD_PAGE_SHARED_FRAME | MM_VAD_PAGE_SWAPPED)) - return 0; + return false; return (page->Flags & MM_VAD_PAGE_PAGING) != 0; } // Returns whether a resident VAD page is safe to move to swap. Clean read-only // file-backed pages are intentionally excluded; the pressure trimmer discards // them and reloads them from the file instead of wasting swap slots. -static int KiVadPageCanSwap(const MM_VAD_PAGE* page) { +static bool KiVadPageCanSwap(const MM_VAD_PAGE* page) { if (!page || page->State != MmVadPageCommitted) - return 0; + return false; if (!page->Resident || !page->Frame || !page->PageFlags) - return 0; + return false; if ((page->Flags & MM_VAD_PAGE_FILE_BACKED) && ((page->PageFlags & PAGE_WRITE) == 0)) - return 0; + return false; if (page->Flags & MM_VAD_PAGE_FILE_SHARED_WRITE) - return 0; + return false; if (page->Flags & (MM_VAD_PAGE_COW | MM_VAD_PAGE_ZERO_PAGE | MM_VAD_PAGE_SHARED_FRAME | MM_VAD_PAGE_SWAPPED | MM_VAD_PAGE_PAGING)) - return 0; - return 1; + return false; + return true; } -static int KiVadPageCanSwapAfterPagingClaim(const MM_VAD_PAGE* page) { +static bool KiVadPageCanSwapAfterPagingClaim(const MM_VAD_PAGE* page) { if (!page || page->State != MmVadPageCommitted) - return 0; + return false; if (!page->Resident || !page->Frame || !page->PageFlags) - return 0; + return false; if ((page->Flags & MM_VAD_PAGE_FILE_BACKED) && ((page->PageFlags & PAGE_WRITE) == 0)) - return 0; + return false; if (page->Flags & MM_VAD_PAGE_FILE_SHARED_WRITE) - return 0; + return false; if (page->Flags & (MM_VAD_PAGE_COW | MM_VAD_PAGE_ZERO_PAGE | MM_VAD_PAGE_SHARED_FRAME | MM_VAD_PAGE_SWAPPED)) - return 0; + return false; if ((page->Flags & MM_VAD_PAGE_PAGING) == 0) - return 0; - return 1; + return false; + return true; } // Discards one resident clean file-backed page. No swap slot is allocated; the @@ -2215,7 +2217,7 @@ KSTATUS MmVadSyncFileMappings(FILE_OBJECT* file) { uintptr_t va = vad->Base + i * PAGE_SIZE; int wasDirty = MmPteTestAndClearDirty(va); - int wasWriteEnabled = (page->Flags & MM_VAD_PAGE_FILE_DIRTY_TRACK) == 0; + bool wasWriteEnabled = (page->Flags & MM_VAD_PAGE_FILE_DIRTY_TRACK) == 0; if (wasDirty || wasWriteEnabled) (void)KiVadMarkSharedFilePageDirty(vad, i, 0); diff --git a/code/kernel/ob_object.c b/code/kernel/ob_object.c index f6b609d..468509b 100644 --- a/code/kernel/ob_object.c +++ b/code/kernel/ob_object.c @@ -1,5 +1,6 @@ // Object manager. This file owns reference-counted kernel objects and their terminal lifetime transitions. +#include #include #include #include @@ -26,7 +27,6 @@ static uint32_t KiObjectPoolTag(OBJECT_TYPE type) { case ObjectTypeUsbPipe: return MM_POOL_TAG('u', 's', 'b', 'p'); case ObjectTypeFileMapping: return MM_POOL_TAG('m', 'a', 'p', 'f'); case ObjectTypeSurface: return MM_POOL_TAG('s', 'u', 'r', 'f'); - case ObjectTypeInputQueue: return MM_POOL_TAG('i', 'n', 'p', 'q'); case ObjectTypeImageSection: return MM_POOL_TAG('i', 'm', 's', 'e'); case ObjectTypeTimer: return MM_POOL_TAG('t', 'm', 'r', ' '); case ObjectTypeDisplayClock: return MM_POOL_TAG('d', 'c', 'l', 'k'); @@ -73,16 +73,16 @@ __declspec(dllexport) bool ObTryReferenceObject(OBJECT* object) { return false; while (true) { - if (_InterlockedCompareExchange(&object->LifetimeState, 0, 0) != ObjectAlive) + if (NtlAtomicLoad32(&object->LifetimeState) != ObjectAlive) return false; - if ((_InterlockedCompareExchange(&object->Flags, 0, 0) & OBJECT_FLAG_INSERTED) == 0) + if ((NtlAtomicLoadU32(&object->Flags) & OBJECT_FLAG_INSERTED) == 0) return false; - long refs = _InterlockedCompareExchange(&object->RefCount, 0, 0); + int32_t refs = NtlAtomicLoad32(&object->RefCount); if (refs <= 0 || refs == 0x7fffffffL) return false; - if (_InterlockedCompareExchange(&object->RefCount, refs + 1, refs) == refs) + if (NtlAtomicCompareExchange32(&object->RefCount, refs + 1, refs) == refs) return true; } } @@ -108,7 +108,7 @@ __declspec(dllexport) void ObDereferenceObject(OBJECT* object) { if (!object) return; - long refs = _InterlockedDecrement(&object->RefCount); + int32_t refs = NtlAtomicDecrement32(&object->RefCount); if (refs > 0) return; if (refs < 0) { @@ -121,7 +121,7 @@ __declspec(dllexport) void ObDereferenceObject(OBJECT* object) { (unsigned)object->Flags); } - if (_InterlockedCompareExchange(&object->LifetimeState, ObjectDying, ObjectAlive) != ObjectAlive) { + if (NtlAtomicCompareExchange32(&object->LifetimeState, ObjectDying, ObjectAlive) != ObjectAlive) { KePanic(KE_PANIC_CODE_GENERIC, "object: duplicate terminal transition object=0x%016llX type=%u state=%ld flags=0x%08x\n", (uint64_t)(uintptr_t)object, @@ -130,10 +130,10 @@ __declspec(dllexport) void ObDereferenceObject(OBJECT* object) { (unsigned)object->Flags); } - _InterlockedExchange(&object->Flags, OBJECT_FLAG_UNLINKED); + NtlAtomicExchangeU32(&object->Flags, OBJECT_FLAG_UNLINKED); if (object->Destroy) object->Destroy(object); - _InterlockedExchange(&object->LifetimeState, ObjectDead); + NtlAtomicExchange32(&object->LifetimeState, ObjectDead); MmFreePool(object); } diff --git a/code/public/freestanding/intrin.h b/code/public/freestanding/intrin.h index d71e1de..4bd61cf 100644 --- a/code/public/freestanding/intrin.h +++ b/code/public/freestanding/intrin.h @@ -2,20 +2,37 @@ #pragma once +#include + unsigned __int64 __readgsqword(unsigned long offset); long _InterlockedCompareExchange(long volatile* destination, long exchange, long comparand); long _InterlockedExchange(long volatile* target, long value); long _InterlockedIncrement(long volatile* addend); long _InterlockedDecrement(long volatile* addend); +__int64 _InterlockedCompareExchange64(__int64 volatile* destination, __int64 exchange, __int64 comparand); +__int64 _InterlockedIncrement64(__int64 volatile* addend); +__int64 _InterlockedDecrement64(__int64 volatile* addend); +__int64 _InterlockedExchangeAdd64(__int64 volatile* addend, __int64 value); +void* _InterlockedCompareExchangePointer(void* volatile* destination, void* exchange, void* comparand); +void* _InterlockedExchangePointer(void* volatile* target, void* value); void _ReadWriteBarrier(void); void _mm_pause(void); +_Static_assert(sizeof(long) == sizeof(int32_t), "MSVC long must be 32-bit"); +_Static_assert(sizeof(__int64) == sizeof(int64_t), "MSVC __int64 must be 64-bit"); + #ifdef _MSC_VER #pragma intrinsic(__readgsqword) #pragma intrinsic(_InterlockedCompareExchange) #pragma intrinsic(_InterlockedExchange) #pragma intrinsic(_InterlockedIncrement) #pragma intrinsic(_InterlockedDecrement) +#pragma intrinsic(_InterlockedCompareExchange64) +#pragma intrinsic(_InterlockedIncrement64) +#pragma intrinsic(_InterlockedDecrement64) +#pragma intrinsic(_InterlockedExchangeAdd64) +#pragma intrinsic(_InterlockedCompareExchangePointer) +#pragma intrinsic(_InterlockedExchangePointer) #pragma intrinsic(_ReadWriteBarrier) #pragma intrinsic(_mm_pause) #endif diff --git a/code/public/freestanding/stddef.h b/code/public/freestanding/stddef.h index e432246..0e7ab27 100644 --- a/code/public/freestanding/stddef.h +++ b/code/public/freestanding/stddef.h @@ -10,5 +10,9 @@ typedef __int64 ptrdiff_t; #endif #ifndef offsetof +#if defined(__clang__) || defined(__GNUC__) +#define offsetof(type, member) __builtin_offsetof(type, member) +#else #define offsetof(type, member) ((size_t)&(((type*)0)->member)) #endif +#endif diff --git a/code/public/ntlite/atomic.h b/code/public/ntlite/atomic.h new file mode 100644 index 0000000..65a3fe6 --- /dev/null +++ b/code/public/ntlite/atomic.h @@ -0,0 +1,98 @@ +// Fixed-width atomic operations for nt-lite-owned storage. + +#pragma once + +#include +#include + +_Static_assert(sizeof(long) == sizeof(int32_t), "MSVC long must be 32-bit"); +_Static_assert(sizeof(__int64) == sizeof(int64_t), "MSVC __int64 must be 64-bit"); + +// Atomically compares and exchanges a signed 32-bit value. +static __forceinline int32_t NtlAtomicCompareExchange32(volatile int32_t* destination, + int32_t exchange, + int32_t comparand) { + return (int32_t)_InterlockedCompareExchange((volatile long*)destination, + (long)exchange, + (long)comparand); +} + +// Atomically loads a signed 32-bit value. +static __forceinline int32_t NtlAtomicLoad32(const volatile int32_t* source) { + return (int32_t)_InterlockedCompareExchange((volatile long*)source, 0, 0); +} + +// Atomically compares and exchanges an unsigned 32-bit value. +static __forceinline uint32_t NtlAtomicCompareExchangeU32(volatile uint32_t* destination, + uint32_t exchange, + uint32_t comparand) { + return (uint32_t)_InterlockedCompareExchange((volatile long*)destination, + (long)exchange, + (long)comparand); +} + +// Atomically loads an unsigned 32-bit value. +static __forceinline uint32_t NtlAtomicLoadU32(const volatile uint32_t* source) { + return (uint32_t)_InterlockedCompareExchange((volatile long*)source, 0, 0); +} + +// Atomically exchanges an unsigned 32-bit value. +static __forceinline uint32_t NtlAtomicExchangeU32(volatile uint32_t* destination, uint32_t value) { + return (uint32_t)_InterlockedExchange((volatile long*)destination, (long)value); +} + +// Atomically exchanges a signed 32-bit value. +static __forceinline int32_t NtlAtomicExchange32(volatile int32_t* destination, int32_t value) { + return (int32_t)_InterlockedExchange((volatile long*)destination, (long)value); +} + +// Atomically increments a signed 32-bit value. +static __forceinline int32_t NtlAtomicIncrement32(volatile int32_t* destination) { + return (int32_t)_InterlockedIncrement((volatile long*)destination); +} + +// Atomically decrements a signed 32-bit value. +static __forceinline int32_t NtlAtomicDecrement32(volatile int32_t* destination) { + return (int32_t)_InterlockedDecrement((volatile long*)destination); +} + +// Atomically compares and exchanges a signed 64-bit value. +static __forceinline int64_t NtlAtomicCompareExchange64(volatile int64_t* destination, + int64_t exchange, + int64_t comparand) { + return (int64_t)_InterlockedCompareExchange64((volatile __int64*)destination, + (__int64)exchange, + (__int64)comparand); +} + +// Atomically loads a signed 64-bit value. +static __forceinline int64_t NtlAtomicLoad64(const volatile int64_t* source) { + return (int64_t)_InterlockedCompareExchange64((volatile __int64*)source, 0, 0); +} + +// Atomically increments a signed 64-bit value. +static __forceinline int64_t NtlAtomicIncrement64(volatile int64_t* destination) { + return (int64_t)_InterlockedIncrement64((volatile __int64*)destination); +} + +// Atomically decrements a signed 64-bit value. +static __forceinline int64_t NtlAtomicDecrement64(volatile int64_t* destination) { + return (int64_t)_InterlockedDecrement64((volatile __int64*)destination); +} + +// Atomically adds to a signed 64-bit value and returns the previous value. +static __forceinline int64_t NtlAtomicExchangeAdd64(volatile int64_t* destination, int64_t value) { + return (int64_t)_InterlockedExchangeAdd64((volatile __int64*)destination, (__int64)value); +} + +// Atomically compares and exchanges a pointer value. +static __forceinline void* NtlAtomicCompareExchangePointer(void* volatile* destination, + void* exchange, + void* comparand) { + return _InterlockedCompareExchangePointer((void* volatile*)destination, exchange, comparand); +} + +// Atomically exchanges a pointer value. +static __forceinline void* NtlAtomicExchangePointer(void* volatile* destination, void* value) { + return _InterlockedExchangePointer((void* volatile*)destination, value); +} diff --git a/code/public/ntlite/audio.h b/code/public/ntlite/audio.h index a7f5b0f..6d4fb3c 100644 --- a/code/public/ntlite/audio.h +++ b/code/public/ntlite/audio.h @@ -3,10 +3,11 @@ #pragma once #include +#include #include #include -#define SYS_AUDIO_ABI_VERSION 4u +#define SYS_AUDIO_ABI_VERSION 5u #define SYS_AUDIO_MAX_ENDPOINTS 32u #define SYS_AUDIO_STREAM_CAPACITY_FRAMES 4096u @@ -64,7 +65,7 @@ typedef struct SYS_AUDIO_ENDPOINT_INFO { uint32_t PeriodCount; uint32_t FramesPerPeriod; uint32_t MasterVolumeQ16; - uint32_t MasterMuted; + bool MasterMuted; KSTATUS FailureStatus; uint32_t Reserved; } SYS_AUDIO_ENDPOINT_INFO; @@ -157,7 +158,7 @@ typedef struct SYS_AUDIO_STREAM_INFO { uint64_t DiscontinuityGeneration; uint32_t VolumeQ16; int32_t PanQ16; - uint8_t Muted; + bool Muted; uint8_t ReservedBytes[7]; } SYS_AUDIO_STREAM_INFO; @@ -167,7 +168,7 @@ typedef struct SYS_AUDIO_SET_VOLUME_REQUEST { uint32_t Size; uint32_t Flags; SYS_HANDLE StreamHandle; - uint32_t Muted; + bool Muted; uint32_t VolumeQ16; int32_t PanQ16; } SYS_AUDIO_SET_VOLUME_REQUEST; @@ -177,7 +178,7 @@ typedef struct SYS_AUDIO_SET_MASTER_VOLUME_REQUEST { uint32_t Size; uint32_t Flags; uint32_t EndpointId; - uint32_t Muted; + bool Muted; uint32_t VolumeQ16; } SYS_AUDIO_SET_MASTER_VOLUME_REQUEST; diff --git a/code/public/ntlite/boot_info.h b/code/public/ntlite/boot_info.h index b5c598f..5f077fa 100644 --- a/code/public/ntlite/boot_info.h +++ b/code/public/ntlite/boot_info.h @@ -2,6 +2,7 @@ #pragma once +#include #include #include @@ -97,3 +98,16 @@ typedef struct BOOT_INFO { // Firmware wall-clock snapshot taken before ExitBootServices. BOOT_TIME BootTime; } BOOT_INFO; + +// BOOT_INFO is an x64 firmware-to-kernel binary handoff, not a private C structure. +_Static_assert(sizeof(uintptr_t) == 8u, "BOOT_INFO requires x64 pointers"); +_Static_assert(sizeof(BOOT_TIME) == 20u, "BOOT_TIME ABI size changed"); +_Static_assert(sizeof(BOOT_BLOB) == 16u, "BOOT_BLOB ABI size changed"); +_Static_assert(sizeof(BOOT_FILE) == 80u, "BOOT_FILE ABI size changed"); +_Static_assert(sizeof(BOOT_FILE_LIST) == 16u, "BOOT_FILE_LIST ABI size changed"); +_Static_assert(sizeof(BOOT_IMAGE) == 32u, "BOOT_IMAGE ABI size changed"); +_Static_assert(offsetof(BOOT_INFO, MemoryMap) == 16u, "BOOT_INFO MemoryMap offset changed"); +_Static_assert(offsetof(BOOT_INFO, KernelImage) == 6200u, "BOOT_INFO KernelImage offset changed"); +_Static_assert(offsetof(BOOT_INFO, BootDrivers) == 6280u, "BOOT_INFO BootDrivers offset changed"); +_Static_assert(offsetof(BOOT_INFO, BootTime) == 6320u, "BOOT_INFO BootTime offset changed"); +_Static_assert(sizeof(BOOT_INFO) == 6344u, "BOOT_INFO ABI size changed"); diff --git a/code/public/ntlite/console_service.h b/code/public/ntlite/console_service.h index b675393..1f57e85 100644 --- a/code/public/ntlite/console_service.h +++ b/code/public/ntlite/console_service.h @@ -55,7 +55,7 @@ typedef struct NTL_CONSOLE_REQUEST { int32_t Y; } Cursor; struct { - char Character; + char Character; // One legacy ASCII display cell, never one UTF-8 code point. uint8_t Reserved[3]; int32_t X; int32_t Y; @@ -69,7 +69,7 @@ typedef struct NTL_CONSOLE_REQUEST { uint32_t Length; } FillAttribute; struct { - char Character; + char Character; // One legacy ASCII display cell, never one UTF-8 code point. uint8_t Reserved[3]; int32_t X; int32_t Y; @@ -93,6 +93,14 @@ typedef struct NTL_CONSOLE_REPLY { NTL_CONSOLE_INFO Info; } NTL_CONSOLE_REPLY; +// RSSRV console IPC layout must agree with every native console client. +_Static_assert(sizeof(NTL_CONSOLE_INFO) == 48u, "NTL_CONSOLE_INFO ABI size changed"); +_Static_assert(offsetof(NTL_CONSOLE_INFO, Generation) == 40u, "NTL_CONSOLE_INFO generation offset changed"); +_Static_assert(sizeof(NTL_CONSOLE_REQUEST) == 544u, "NTL_CONSOLE_REQUEST ABI size changed"); +_Static_assert(offsetof(NTL_CONSOLE_REQUEST, Data) == 24u, "NTL_CONSOLE_REQUEST payload offset changed"); +_Static_assert(sizeof(NTL_CONSOLE_REPLY) == 80u, "NTL_CONSOLE_REPLY ABI size changed"); +_Static_assert(offsetof(NTL_CONSOLE_REPLY, Status) == 16u, "NTL_CONSOLE_REPLY status offset changed"); + #if defined(NTL_SYSCALL_STATIC) #define NTL_CONSOLE_API #elif defined(NTL_SYSCALL_BUILD) @@ -105,12 +113,14 @@ NTL_CONSOLE_API KSTATUS NtlConsoleConnect(SYS_HANDLE* sessionOut); NTL_CONSOLE_API KSTATUS NtlConsoleClose(SYS_HANDLE session); NTL_CONSOLE_API KSTATUS NtlConsoleWrite(SYS_HANDLE session, const void* data, size_t length, size_t* writtenOut); NTL_CONSOLE_API KSTATUS NtlConsoleQueryInfo(SYS_HANDLE session, NTL_CONSOLE_INFO* infoOut); -NTL_CONSOLE_API KSTATUS NtlConsoleSetCursor(SYS_HANDLE session, int x, int y); -NTL_CONSOLE_API KSTATUS NtlConsoleFillCharacter(SYS_HANDLE session, char character, int x, int y, +NTL_CONSOLE_API KSTATUS NtlConsoleSetCursor(SYS_HANDLE session, int32_t x, int32_t y); +// Legacy cell operations accept ASCII glyph codes only. Unicode text uses +// NtlConsoleWrite's byte stream until the RSSRV text renderer is upgraded. +NTL_CONSOLE_API KSTATUS NtlConsoleFillCharacter(SYS_HANDLE session, char character, int32_t x, int32_t y, size_t length, size_t* writtenOut); -NTL_CONSOLE_API KSTATUS NtlConsoleFillAttribute(SYS_HANDLE session, uint16_t attribute, int x, int y, +NTL_CONSOLE_API KSTATUS NtlConsoleFillAttribute(SYS_HANDLE session, uint16_t attribute, int32_t x, int32_t y, size_t length, size_t* writtenOut); -NTL_CONSOLE_API KSTATUS NtlConsolePutCharacter(SYS_HANDLE session, char character, int x, int y, +NTL_CONSOLE_API KSTATUS NtlConsolePutCharacter(SYS_HANDLE session, char character, int32_t x, int32_t y, uint32_t foreground, uint32_t background); NTL_CONSOLE_API KSTATUS NtlConsoleClear(SYS_HANDLE session, uint32_t color); NTL_CONSOLE_API KSTATUS NtlConsoleReadInput(SYS_HANDLE session, uint64_t timeoutMilliseconds, @@ -118,4 +128,5 @@ NTL_CONSOLE_API KSTATUS NtlConsoleReadInput(SYS_HANDLE session, uint64_t timeout NTL_CONSOLE_API KSTATUS NtlConsolePeekInput(SYS_HANDLE session, uint64_t timeoutMilliseconds, INPUT_KEY_RECORD* recordOut); NTL_CONSOLE_API KSTATUS NtlConsoleFlushInput(SYS_HANDLE session); +// ASCII-only legacy key convenience function; use NtlConsoleReadInput for key events. NTL_CONSOLE_API KSTATUS NtlConsoleReadCharacter(SYS_HANDLE session, char* characterOut); diff --git a/code/public/ntlite/ddi/display.h b/code/public/ntlite/ddi/display.h index 4eb5508..ce25ab7 100644 --- a/code/public/ntlite/ddi/display.h +++ b/code/public/ntlite/ddi/display.h @@ -24,26 +24,26 @@ typedef struct DISPLAY_DEVICE_OPS { KSTATUS (*GetMode)(DISPLAY_DEVICE* device, DISPLAY_MODE* outMode); KSTATUS (*Blit)(DISPLAY_DEVICE* device, - int x, int y, int w, int h, + int32_t x, int32_t y, int32_t w, int32_t h, const uint32_t* pixels, uint32_t pitchBytes); KSTATUS (*PresentPitched)(DISPLAY_DEVICE* device, - int x, int y, int w, int h, + int32_t x, int32_t y, int32_t w, int32_t h, const uint32_t* pixels, uint32_t pitchBytes); KSTATUS (*FillRect)(DISPLAY_DEVICE* device, - int x, int y, int w, int h, + int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color); KSTATUS (*CopyRect)(DISPLAY_DEVICE* device, - int dstX, int dstY, - int srcX, int srcY, - int w, int h); + int32_t dstX, int32_t dstY, + int32_t srcX, int32_t srcY, + int32_t w, int32_t h); KSTATUS (*ReadRect)(DISPLAY_DEVICE* device, - int x, int y, int w, int h, + int32_t x, int32_t y, int32_t w, int32_t h, uint32_t* outPixels, uint32_t outStride); KSTATUS (*Present)(DISPLAY_DEVICE* device, @@ -53,11 +53,11 @@ typedef struct DISPLAY_DEVICE_OPS { KSTATUS (*Sync)(DISPLAY_DEVICE* device); KSTATUS (*SetCursorImage)(DISPLAY_DEVICE* device, - int width, int height, - int hotX, int hotY, + int32_t width, int32_t height, + int32_t hotX, int32_t hotY, const uint32_t* argbPixels); - KSTATUS (*SetCursorPosition)(DISPLAY_DEVICE* device, int x, int y); + KSTATUS (*SetCursorPosition)(DISPLAY_DEVICE* device, int32_t x, int32_t y); KSTATUS (*ShowCursor)(DISPLAY_DEVICE* device, bool show); KSTATUS (*QueryStats)(DISPLAY_DEVICE* device, DISPLAY_STATS* outStats); diff --git a/code/public/ntlite/ddi/input_device.h b/code/public/ntlite/ddi/input_device.h index 8e73df8..bdaea40 100644 --- a/code/public/ntlite/ddi/input_device.h +++ b/code/public/ntlite/ddi/input_device.h @@ -10,4 +10,4 @@ __declspec(dllexport) KSTATUS KeInputRegisterDevice(const char* name, uint32_t f __declspec(dllexport) void KeInputSubmitKey(const INPUT_KEY_EVENT* event); __declspec(dllexport) void KeInputSubmitMouse(const INPUT_MOUSE_EVENT* event); __declspec(dllexport) void KeInputSubmitMouseAbsolute(const INPUT_MOUSE_ABSOLUTE_EVENT* event); -__declspec(dllexport) void KeInputSubmitMouseAbsoluteScaled(uint32_t rawX, uint32_t rawY, uint32_t maxX, uint32_t maxY, int wheel, uint8_t buttons); +__declspec(dllexport) void KeInputSubmitMouseAbsoluteScaled(uint32_t rawX, uint32_t rawY, uint32_t maxX, uint32_t maxY, int32_t wheel, uint8_t buttons); diff --git a/code/public/ntlite/ddi/io_driver.h b/code/public/ntlite/ddi/io_driver.h index 175d783..1a711f1 100644 --- a/code/public/ntlite/ddi/io_driver.h +++ b/code/public/ntlite/ddi/io_driver.h @@ -97,7 +97,7 @@ struct DEVICE_OBJECT { uint32_t AlignmentRequirement; SPINLOCK Lock; - volatile long OutstandingIrps; + volatile int32_t OutstandingIrps; bool DeletePending; SYS_STRING_VIEW Name; diff --git a/code/public/ntlite/ddk/async.h b/code/public/ntlite/ddk/async.h index b927a1d..97f3d22 100644 --- a/code/public/ntlite/ddk/async.h +++ b/code/public/ntlite/ddk/async.h @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -36,7 +37,7 @@ struct IO_ASYNC_REQUEST_OBJECT { OBJECT Header; LIST_ENTRY Link; IO_ASYNC_REQUEST_STATE State; - uint8_t CancelRequested; + bool CancelRequested; struct EVENT* WaitEvent; struct EVENT* CompletionEvent; struct KAPC* CompletionApc; diff --git a/code/public/ntlite/ddk/audio.h b/code/public/ntlite/ddk/audio.h index ecabab2..e4b6c67 100644 --- a/code/public/ntlite/ddk/audio.h +++ b/code/public/ntlite/ddk/audio.h @@ -4,6 +4,7 @@ #include #include +#include #include struct DEVICE_OBJECT; @@ -52,7 +53,7 @@ typedef struct IO_AUDIO_PROVIDER_OPERATIONS { // Stops DMA before unregister permits provider storage to be released. void (*Stop)(void* providerContext); // Applies endpoint gain and mute only with IO_AUDIO_ENDPOINT_CAP_HARDWARE_GAIN. - KSTATUS (*SetGain)(void* providerContext, int32_t gainMilliDb, uint8_t muted); + KSTATUS (*SetGain)(void* providerContext, int32_t gainMilliDb, bool muted); } IO_AUDIO_PROVIDER_OPERATIONS; // Registers one fixed-format render ring owned by a hardware provider. @@ -61,7 +62,7 @@ typedef struct IO_AUDIO_ENDPOINT_REGISTRATION { struct DEVICE_OBJECT* Device; IO_AUDIO_ENDPOINT_KIND Kind; uint32_t Capabilities; - uint8_t InitiallyAvailable; + bool InitiallyAvailable; void* ProviderContext; IO_AUDIO_PROVIDER_OPERATIONS Operations; void* PeriodBuffer; @@ -96,7 +97,7 @@ typedef struct NTL_DDK_AUDIO { uint64_t timestampTicks); // Updates jack-derived endpoint availability without changing endpoint identity. - void (*SetEndpointAvailable)(IO_AUDIO_ENDPOINT* endpoint, uint8_t available); + void (*SetEndpointAvailable)(IO_AUDIO_ENDPOINT* endpoint, bool available); // Fails an endpoint and wakes every stream owner. void (*ReportEndpointFailure)(IO_AUDIO_ENDPOINT* endpoint, diff --git a/code/public/ntlite/ddk/debug.h b/code/public/ntlite/ddk/debug.h index 1a87988..c779be2 100644 --- a/code/public/ntlite/ddk/debug.h +++ b/code/public/ntlite/ddk/debug.h @@ -43,4 +43,4 @@ typedef enum KE_PANIC_CODE { __declspec(dllexport) void DbgPrint(const char* fmt, ...); // Stops the system with a formatted diagnostic message. -__declspec(dllexport) _Noreturn void KePanic(int code, const char* fmt, ...); +__declspec(dllexport) _Noreturn void KePanic(int32_t code, const char* fmt, ...); diff --git a/code/public/ntlite/ddk/display.h b/code/public/ntlite/ddk/display.h index 0c7c96a..cb55097 100644 --- a/code/public/ntlite/ddk/display.h +++ b/code/public/ntlite/ddk/display.h @@ -40,24 +40,24 @@ DISPLAY_API void KeDisplaySuspendConsole(bool suspend); DISPLAY_API KSTATUS KeDisplayGetMode(DISPLAY_MODE* outMode); // Queries the current display size. -DISPLAY_API KSTATUS KeDisplayGetSize(int* outWidth, int* outHeight); +DISPLAY_API KSTATUS KeDisplayGetSize(int32_t* outWidth, int32_t* outHeight); -DISPLAY_API KSTATUS KeDisplayBlit(int x, int y, int w, int h, const uint32_t* pixels); -DISPLAY_API KSTATUS KeDisplayBlitPitched(int x, int y, int w, int h, const uint32_t* pixels, uint32_t pitchBytes); -DISPLAY_API KSTATUS KeDisplayPresentPitched(int x, int y, int w, int h, const uint32_t* pixels, uint32_t pitchBytes); -DISPLAY_API KSTATUS KeDisplayFillRect(int x, int y, int w, int h, uint32_t color); +DISPLAY_API KSTATUS KeDisplayBlit(int32_t x, int32_t y, int32_t w, int32_t h, const uint32_t* pixels); +DISPLAY_API KSTATUS KeDisplayBlitPitched(int32_t x, int32_t y, int32_t w, int32_t h, const uint32_t* pixels, uint32_t pitchBytes); +DISPLAY_API KSTATUS KeDisplayPresentPitched(int32_t x, int32_t y, int32_t w, int32_t h, const uint32_t* pixels, uint32_t pitchBytes); +DISPLAY_API KSTATUS KeDisplayFillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color); DISPLAY_API KSTATUS KeDisplayClear(uint32_t color); -DISPLAY_API KSTATUS KeDisplayCopyRect(int dstX, int dstY, int srcX, int srcY, int w, int h); -DISPLAY_API KSTATUS KeDisplayScroll(int lines); -DISPLAY_API KSTATUS KeDisplayPutChar(char c, int x, int y, uint32_t fg, uint32_t bg); +DISPLAY_API KSTATUS KeDisplayCopyRect(int32_t dstX, int32_t dstY, int32_t srcX, int32_t srcY, int32_t w, int32_t h); +DISPLAY_API KSTATUS KeDisplayScroll(int32_t lines); +DISPLAY_API KSTATUS KeDisplayPutChar(char c, int32_t x, int32_t y, uint32_t fg, uint32_t bg); DISPLAY_API KSTATUS KeDisplayPrintChar(char c, uint32_t fg, uint32_t bg); DISPLAY_API KSTATUS KeDisplayGetConsoleInfo(KE_CONSOLE_INFO* outInfo); -DISPLAY_API KSTATUS KeDisplaySetConsoleCursor(int col, int row); -DISPLAY_API KSTATUS KeDisplayFillConsoleChars(char c, int col, int row, size_t length, size_t* writtenOut); -DISPLAY_API KSTATUS KeDisplayFillConsoleAttrs(uint16_t attr, int col, int row, size_t length, size_t* writtenOut); +DISPLAY_API KSTATUS KeDisplaySetConsoleCursor(int32_t col, int32_t row); +DISPLAY_API KSTATUS KeDisplayFillConsoleChars(char c, int32_t col, int32_t row, size_t length, size_t* writtenOut); +DISPLAY_API KSTATUS KeDisplayFillConsoleAttrs(uint16_t attr, int32_t col, int32_t row, size_t length, size_t* writtenOut); DISPLAY_API KSTATUS KeDisplayPresent(const DISPLAY_RECT* rects, size_t rectCount); DISPLAY_API KSTATUS KeDisplaySync(void); DISPLAY_API KSTATUS KeDisplayQueryInfo(KE_DISPLAY_INFO* outInfo); -DISPLAY_API KSTATUS KeDisplaySetCursorImage(int width, int height, int hotX, int hotY, const uint32_t* argbPixels); -DISPLAY_API KSTATUS KeDisplaySetCursorPosition(int x, int y); +DISPLAY_API KSTATUS KeDisplaySetCursorImage(int32_t width, int32_t height, int32_t hotX, int32_t hotY, const uint32_t* argbPixels); +DISPLAY_API KSTATUS KeDisplaySetCursorPosition(int32_t x, int32_t y); DISPLAY_API KSTATUS KeDisplayShowCursor(bool show); diff --git a/code/public/ntlite/ddk/dpc.h b/code/public/ntlite/ddk/dpc.h index a5648c0..b6303e6 100644 --- a/code/public/ntlite/ddk/dpc.h +++ b/code/public/ntlite/ddk/dpc.h @@ -21,7 +21,7 @@ struct KDPC { void* Context; void* SystemArgument1; void* SystemArgument2; - volatile long Inserted; + volatile int32_t Inserted; }; // Initializes a DPC object. diff --git a/code/public/ntlite/ddk/event.h b/code/public/ntlite/ddk/event.h index be58f8b..408647a 100644 --- a/code/public/ntlite/ddk/event.h +++ b/code/public/ntlite/ddk/event.h @@ -3,6 +3,7 @@ #pragma once #include +#include #include #include #include @@ -17,13 +18,13 @@ typedef struct EVENT { OBJECT Header; SPINLOCK Lock; EVENT_TYPE Type; - uint32_t Signaled; + bool Signaled; LIST_ENTRY WaitList; LIST_ENTRY MultiWaitList; } EVENT; // Creates an event object. -__declspec(dllexport) KSTATUS KeEventCreate(EVENT_TYPE type, uint32_t initialState, EVENT** out); +__declspec(dllexport) KSTATUS KeEventCreate(EVENT_TYPE type, bool initialState, EVENT** out); // Signals an event. __declspec(dllexport) void KeSignalEvent(EVENT* event); @@ -32,7 +33,7 @@ __declspec(dllexport) void KeSignalEvent(EVENT* event); __declspec(dllexport) void KeClearEvent(EVENT* event); // Waits for an event. -__declspec(dllexport) KSTATUS KeWaitForEvent(EVENT* event, uint8_t alertable); +__declspec(dllexport) KSTATUS KeWaitForEvent(EVENT* event, bool alertable); // Attempts to wait for an event without blocking. __declspec(dllexport) KSTATUS KeTryWaitEvent(EVENT* event); @@ -40,6 +41,6 @@ __declspec(dllexport) KSTATUS KeTryWaitEvent(EVENT* event); // Waits for one of several events. __declspec(dllexport) KSTATUS KeWaitForMultipleEvents(EVENT** events, uint32_t count, - uint8_t alertable, + bool alertable, uint64_t timeoutMs, uint32_t* signaledIndexOut); diff --git a/code/public/ntlite/ddk/irp.h b/code/public/ntlite/ddk/irp.h index ece9216..c0405f1 100644 --- a/code/public/ntlite/ddk/irp.h +++ b/code/public/ntlite/ddk/irp.h @@ -121,7 +121,7 @@ typedef struct IRP { // Current remove-lock layout. typedef struct IO_REMOVE_LOCK { SPINLOCK Lock; - volatile long IoCount; + volatile int32_t IoCount; bool Removing; struct EVENT* RemovedEvent; } IO_REMOVE_LOCK; diff --git a/code/public/ntlite/ddk/irql.h b/code/public/ntlite/ddk/irql.h index 17386d8..16ee0d3 100644 --- a/code/public/ntlite/ddk/irql.h +++ b/code/public/ntlite/ddk/irql.h @@ -3,6 +3,7 @@ #pragma once #include +#include // Interrupt request level value used by DDK synchronization and IRP state. typedef uint8_t KIRQL; @@ -35,7 +36,7 @@ __declspec(dllexport) void KeAssertIrql(KIRQL expected); __declspec(dllexport) void KeAssertIrqlAtOrBelow(KIRQL maxIrql); // Returns nonzero when the current context may block. -__declspec(dllexport) int KeCanWait(void); +__declspec(dllexport) bool KeCanWait(void); // Returns a diagnostic name for an IRQL. __declspec(dllexport) const char* KeIrqlName(KIRQL irql); diff --git a/code/public/ntlite/ddk/mutex.h b/code/public/ntlite/ddk/mutex.h index b00628f..f87c6d2 100644 --- a/code/public/ntlite/ddk/mutex.h +++ b/code/public/ntlite/ddk/mutex.h @@ -2,6 +2,7 @@ #pragma once +#include #include #include #include @@ -16,8 +17,8 @@ typedef struct MUTEX { LIST_ENTRY OwnerLink; KE_SCHEDULING_CLASS DonationClass; uint32_t DonationPriority; - uint8_t HasDonation; - uint8_t Abandoned; + bool HasDonation; + bool Abandoned; } MUTEX; // Tests whether a mutex acquisition transferred ownership to the caller. diff --git a/code/public/ntlite/ddk/object.h b/code/public/ntlite/ddk/object.h index f4c8b44..311f3f1 100644 --- a/code/public/ntlite/ddk/object.h +++ b/code/public/ntlite/ddk/object.h @@ -24,7 +24,6 @@ typedef enum OBJECT_TYPE { ObjectTypeUsbPipe = 14, ObjectTypeFileMapping = 15, ObjectTypeSurface = 16, - ObjectTypeInputQueue = 17, ObjectTypeImageSection = 18, ObjectTypeTimer = 19, ObjectTypeDisplayClock = 20, @@ -51,9 +50,9 @@ typedef enum OBJECT_LIFETIME_STATE { // Object header embedded by kernel and driver-owned reference-counted objects. typedef struct OBJECT { OBJECT_TYPE Type; - volatile long RefCount; - volatile long LifetimeState; - volatile long Flags; + volatile int32_t RefCount; + volatile int32_t LifetimeState; + volatile uint32_t Flags; void (*Destroy)(struct OBJECT* self); } OBJECT; diff --git a/code/public/ntlite/ddk/sync.h b/code/public/ntlite/ddk/sync.h index b558fe1..fe4cf7a 100644 --- a/code/public/ntlite/ddk/sync.h +++ b/code/public/ntlite/ddk/sync.h @@ -2,13 +2,15 @@ #pragma once +#include #include +#include #include #include // Legacy spinlock storage used by current driver-visible objects. typedef struct SPINLOCK { - volatile long Locked; + volatile int32_t Locked; } SPINLOCK; #define SPINLOCK_INIT { 0 } @@ -43,7 +45,7 @@ static __forceinline uintptr_t KeAcquireSpinlock(SPINLOCK* sl) { if (oldIrql < DISPATCH_LEVEL) KeRaiseIrql(DISPATCH_LEVEL); - while (_InterlockedCompareExchange(&sl->Locked, 1, 0) != 0) + while (NtlAtomicCompareExchange32(&sl->Locked, 1, 0) != 0) _mm_pause(); return KiPackSpinlockCookie(flags, oldIrql); @@ -54,7 +56,7 @@ static __forceinline void KeReleaseSpinlock(SPINLOCK* sl, uintptr_t cookie) { uintptr_t flags = KiSpinlockCookieFlags(cookie); KIRQL oldIrql = KiSpinlockCookieIrql(cookie); - _InterlockedExchange(&sl->Locked, 0); + NtlAtomicExchange32(&sl->Locked, 0); // Restore the caller's hardware interrupt state before dropping to an // IRQL that can dispatch APCs. PASSIVE_LEVEL with IF still clear is not a @@ -64,23 +66,23 @@ static __forceinline void KeReleaseSpinlock(SPINLOCK* sl, uintptr_t cookie) { } // Attempts to acquire a spinlock without blocking. -static __forceinline int KeTryAcquireSpinlock(SPINLOCK* sl, uintptr_t* outCookie) { +static __forceinline bool KeTryAcquireSpinlock(SPINLOCK* sl, uintptr_t* outCookie) { uintptr_t flags = KeDisableInterrupts(); KIRQL oldIrql = KeGetCurrentIrql(); if (oldIrql < DISPATCH_LEVEL) KeRaiseIrql(DISPATCH_LEVEL); - if (_InterlockedCompareExchange(&sl->Locked, 1, 0) == 0) { + if (NtlAtomicCompareExchange32(&sl->Locked, 1, 0) == 0) { *outCookie = KiPackSpinlockCookie(flags, oldIrql); - return 1; + return true; } // Keep the failed-acquire unwind consistent with normal release: restore // IF before a PASSIVE transition can dispatch pending APCs. KeRestoreInterrupts(flags); KeLowerIrql(oldIrql); - return 0; + return false; } // Acquires a spinlock for DPC-level code. diff --git a/code/public/ntlite/desktop.h b/code/public/ntlite/desktop.h index bd343a3..411df51 100644 --- a/code/public/ntlite/desktop.h +++ b/code/public/ntlite/desktop.h @@ -2,13 +2,15 @@ #pragma once +#include #include +#include #include #include #include #include -#define NTL_DESKTOP_PROTOCOL_VERSION 1u +#define NTL_DESKTOP_PROTOCOL_VERSION 2u #define NTL_DESKTOP_SERVICE_PATH "/Sessions/0/Desktop" #define NTL_DESKTOP_FLAG_NONE 0x00000000u @@ -58,10 +60,10 @@ typedef struct NTL_DESKTOP_REPLY { uint32_t Reserved; NTL_WINDOW_ID WindowId; DISPLAY_RECT Bounds; - uint32_t Visible; - uint32_t Focused; - uint32_t Captured; - uint32_t HasSurface; + bool Visible; + bool Focused; + bool Captured; + bool HasSurface; uint32_t SurfaceWidth; uint32_t SurfaceHeight; } NTL_DESKTOP_REPLY; @@ -79,6 +81,16 @@ typedef struct NTL_DESKTOP_EVENT { INPUT_MOUSE_EVENT Mouse; } NTL_DESKTOP_EVENT; +// DWM and clients exchange these versioned fixed-layout protocol messages. +_Static_assert(sizeof(NTL_DESKTOP_REQUEST) == 48u, "NTL_DESKTOP_REQUEST ABI size changed"); +_Static_assert(offsetof(NTL_DESKTOP_REQUEST, WindowId) == 16u, "NTL_DESKTOP_REQUEST window offset changed"); +_Static_assert(offsetof(NTL_DESKTOP_REPLY, Visible) == 48u, "NTL_DESKTOP_REPLY Visible offset changed"); +_Static_assert(offsetof(NTL_DESKTOP_REPLY, SurfaceWidth) == 52u, "NTL_DESKTOP_REPLY SurfaceWidth offset changed"); +_Static_assert(sizeof(NTL_DESKTOP_REPLY) == 64u, "NTL_DESKTOP_REPLY ABI size changed"); +_Static_assert(offsetof(NTL_DESKTOP_REPLY, Status) == 16u, "NTL_DESKTOP_REPLY status offset changed"); +_Static_assert(sizeof(NTL_DESKTOP_EVENT) == 56u, "NTL_DESKTOP_EVENT ABI size changed"); +_Static_assert(offsetof(NTL_DESKTOP_EVENT, Key) == 32u, "NTL_DESKTOP_EVENT key offset changed"); + #if defined(NTL_SYSCALL_STATIC) #define NTL_DESKTOP_API #elif defined(NTL_SYSCALL_BUILD) @@ -100,7 +112,7 @@ NTL_DESKTOP_API KSTATUS NtlDesktopDestroyWindow(SYS_HANDLE session, NTL_WINDOW_I NTL_DESKTOP_API KSTATUS NtlDesktopSetBounds(SYS_HANDLE session, NTL_WINDOW_ID window, const DISPLAY_RECT* bounds); // Shows or hides one window without changing its z-order. -NTL_DESKTOP_API KSTATUS NtlDesktopSetVisible(SYS_HANDLE session, NTL_WINDOW_ID window, uint32_t visible); +NTL_DESKTOP_API KSTATUS NtlDesktopSetVisible(SYS_HANDLE session, NTL_WINDOW_ID window, bool visible); // Moves one window to the top of the z-order. NTL_DESKTOP_API KSTATUS NtlDesktopRaiseWindow(SYS_HANDLE session, NTL_WINDOW_ID window); diff --git a/code/public/ntlite/display.h b/code/public/ntlite/display.h index 79b8af2..0de5620 100644 --- a/code/public/ntlite/display.h +++ b/code/public/ntlite/display.h @@ -7,7 +7,9 @@ // data types and capability bits. Kernel/driver provider interfaces live in // . +#include #include +#include #ifdef __cplusplus extern "C" { @@ -55,7 +57,7 @@ typedef struct SYS_SURFACE_DESC { uint64_t CommitClippedRectCount; uint64_t CommitEmptyClipCount; DISPLAY_RECT LastDamageBounds; - uint32_t LastDamageValid; + bool LastDamageValid; uint32_t Reserved; } SYS_SURFACE_DESC; @@ -119,6 +121,12 @@ typedef struct SYS_DISPLAY_INFO { DISPLAY_STATS DeviceStats; } SYS_DISPLAY_INFO; +// Surfaces and damage rectangles are shared between DWM and native clients. +_Static_assert(sizeof(DISPLAY_PIXEL_FORMAT) == 4u, "DISPLAY_PIXEL_FORMAT ABI width changed"); +_Static_assert(sizeof(DISPLAY_RECT) == 16u, "DISPLAY_RECT ABI size changed"); +_Static_assert(offsetof(SYS_SURFACE_DESC, LastDamageBounds) == 80u, "SYS_SURFACE_DESC damage offset changed"); +_Static_assert(sizeof(SYS_SURFACE_DESC) == 104u, "SYS_SURFACE_DESC ABI size changed"); + #ifdef __cplusplus } #endif diff --git a/code/public/ntlite/exception.h b/code/public/ntlite/exception.h index 0c318b6..d5146cd 100644 --- a/code/public/ntlite/exception.h +++ b/code/public/ntlite/exception.h @@ -3,6 +3,7 @@ #pragma once #include +#include #include #if defined(NTL_EXCEPTION_EXPORTS) @@ -11,14 +12,14 @@ #define NTL_EXCEPTION_API #endif -#define NTL_EXCEPTION_CONTINUE_EXECUTION ((int)SYS_EXCEPTION_DISPOSITION_CONTINUE_EXECUTION) -#define NTL_EXCEPTION_CONTINUE_SEARCH ((int)SYS_EXCEPTION_DISPOSITION_CONTINUE_SEARCH) -#define NTL_EXCEPTION_TERMINATE_PROCESS ((int)SYS_EXCEPTION_DISPOSITION_TERMINATE_PROCESS) +#define NTL_EXCEPTION_CONTINUE_EXECUTION ((uint32_t)SYS_EXCEPTION_DISPOSITION_CONTINUE_EXECUTION) +#define NTL_EXCEPTION_CONTINUE_SEARCH ((uint32_t)SYS_EXCEPTION_DISPOSITION_CONTINUE_SEARCH) +#define NTL_EXCEPTION_TERMINATE_PROCESS ((uint32_t)SYS_EXCEPTION_DISPOSITION_TERMINATE_PROCESS) -typedef int (*NTL_EXCEPTION_HANDLER)(SYS_EXCEPTION_RECORD* record, +typedef uint32_t (*NTL_EXCEPTION_HANDLER)(SYS_EXCEPTION_RECORD* record, SYS_USER_CONTEXT* context, void* userContext); -NTL_EXCEPTION_API uint32_t NtlInitializeExceptionRuntime(void); -NTL_EXCEPTION_API void* NtlAddExceptionHandler(int first, NTL_EXCEPTION_HANDLER handler, void* context); -NTL_EXCEPTION_API uint32_t NtlRemoveExceptionHandler(void* handle); +NTL_EXCEPTION_API KSTATUS NtlInitializeExceptionRuntime(void); +NTL_EXCEPTION_API void* NtlAddExceptionHandler(bool first, NTL_EXCEPTION_HANDLER handler, void* context); +NTL_EXCEPTION_API KSTATUS NtlRemoveExceptionHandler(void* handle); diff --git a/code/public/ntlite/file_stat.h b/code/public/ntlite/file_stat.h index 8a82d1f..7a79672 100644 --- a/code/public/ntlite/file_stat.h +++ b/code/public/ntlite/file_stat.h @@ -1,6 +1,7 @@ // Public NTLite file metadata ABI declarations shared between kernel, drivers, and user mode. #pragma once +#include #include #include @@ -29,3 +30,8 @@ typedef struct SYS_FILE_STAT { SYS_SYSTEM_TIME LastWriteTime; SYS_SYSTEM_TIME ChangeTime; } SYS_FILE_STAT; + +// Native file metadata is copied between independently built modules. +_Static_assert(offsetof(SYS_FILE_STAT, FileId) == 32u, "SYS_FILE_STAT FileId offset changed"); +_Static_assert(offsetof(SYS_FILE_STAT, CreationTime) == 40u, "SYS_FILE_STAT CreationTime offset changed"); +_Static_assert(sizeof(SYS_FILE_STAT) == 200u, "SYS_FILE_STAT ABI size changed"); diff --git a/code/public/ntlite/handle.h b/code/public/ntlite/handle.h index f359fd1..e265af9 100644 --- a/code/public/ntlite/handle.h +++ b/code/public/ntlite/handle.h @@ -43,7 +43,6 @@ typedef uint32_t SYS_RIGHTS; // Queue-specific rights. #define SYS_QUEUE_RIGHT_SEND 0x00000100u #define SYS_QUEUE_RIGHT_RECEIVE 0x00000200u -#define SYS_QUEUE_RIGHT_PUBLISH 0x00000400u // File-specific rights. #define SYS_FILE_RIGHT_READ_DATA 0x00000100u @@ -104,9 +103,6 @@ typedef uint32_t SYS_RIGHTS; #define SYS_SURFACE_RIGHT_MAP_WRITE 0x00000200u #define SYS_SURFACE_RIGHT_COMMIT 0x00000400u -// Input-queue rights. -#define SYS_INPUT_QUEUE_RIGHT_READ 0x00000100u - // Timer rights. #define SYS_TIMER_RIGHT_SET 0x00000100u @@ -118,3 +114,7 @@ typedef uint32_t SYS_RIGHTS; // Rights normally retained by a newly created root capability. #define SYS_RIGHT_MANAGE (SYS_RIGHT_DUPLICATE | SYS_RIGHT_DERIVE | SYS_RIGHT_REVOKE | SYS_RIGHT_TRANSFER) + +// Native capability identifiers and access masks are 32-bit values. +_Static_assert(sizeof(SYS_HANDLE) == 4u, "SYS_HANDLE ABI width changed"); +_Static_assert(sizeof(SYS_RIGHTS) == 4u, "SYS_RIGHTS ABI width changed"); diff --git a/code/public/ntlite/input.h b/code/public/ntlite/input.h index fc9a1a2..d842c34 100644 --- a/code/public/ntlite/input.h +++ b/code/public/ntlite/input.h @@ -138,20 +138,25 @@ typedef struct INPUT_KEY_EVENT { typedef struct INPUT_KEY_RECORD { INPUT_KEY Key; - char Char; + char Char; // Legacy ASCII key translation, not a UTF-8 character. uint32_t Modifiers; } INPUT_KEY_RECORD; typedef struct INPUT_MOUSE_EVENT { - int dx; - int dy; - int wheel; + int32_t dx; + int32_t dy; + int32_t wheel; uint8_t buttons; // bit0=left, bit1=right, bit2=middle } INPUT_MOUSE_EVENT; typedef struct INPUT_MOUSE_ABSOLUTE_EVENT { - int x; - int y; - int wheel; + int32_t x; + int32_t y; + int32_t wheel; uint8_t buttons; // bit0=left, bit1=right, bit2=middle } INPUT_MOUSE_ABSOLUTE_EVENT; + +_Static_assert(sizeof(INPUT_KEY_EVENT) == 4u, "INPUT_KEY_EVENT ABI size changed"); +_Static_assert(sizeof(INPUT_KEY_RECORD) == 8u, "INPUT_KEY_RECORD ABI size changed"); +_Static_assert(sizeof(INPUT_MOUSE_EVENT) == 16u, "INPUT_MOUSE_EVENT ABI size changed"); +_Static_assert(sizeof(INPUT_MOUSE_ABSOLUTE_EVENT) == 16u, "INPUT_MOUSE_ABSOLUTE_EVENT ABI size changed"); diff --git a/code/public/ntlite/input_service.h b/code/public/ntlite/input_service.h index afd37eb..2239a7f 100644 --- a/code/public/ntlite/input_service.h +++ b/code/public/ntlite/input_service.h @@ -87,6 +87,6 @@ NTL_INPUT_API KSTATUS NtlInputReadKeyboardChar(char* charOut); NTL_INPUT_API KSTATUS NtlInputReadKey(INPUT_KEY* keyOut); NTL_INPUT_API uint32_t NtlInputGetKeyModifiers(void); NTL_INPUT_API KSTATUS NtlInputQueryDefaultState(NTL_INPUT_STATE* stateOut); -NTL_INPUT_API KSTATUS NtlInputGetMousePos(int* xOut, int* yOut); +NTL_INPUT_API KSTATUS NtlInputGetMousePos(int32_t* xOut, int32_t* yOut); #undef NTL_INPUT_API diff --git a/code/public/ntlite/memory_map.h b/code/public/ntlite/memory_map.h index 27e6aa5..37b16e7 100644 --- a/code/public/ntlite/memory_map.h +++ b/code/public/ntlite/memory_map.h @@ -3,6 +3,7 @@ #pragma once // memory_map.h - shared between bootx64.efi and kernel.exe // Describes physical memory layout passed from bootloader to kernel. +#include #include #define MEMORY_MAP_MAGIC 0x4D454D4D41500000ULL // 'MEMMAP\0\0' @@ -30,3 +31,10 @@ typedef struct MEMORY_MAP { uint32_t _pad; MEMORY_REGION Regions[MEMORY_MAX_REGIONS]; } MEMORY_MAP; + +// The bootloader and kernel must agree on physical memory handoff layout. +_Static_assert(sizeof(MEMORY_TYPE) == 4u, "MEMORY_TYPE ABI width changed"); +_Static_assert(sizeof(MEMORY_REGION) == 24u, "MEMORY_REGION ABI size changed"); +_Static_assert(offsetof(MEMORY_REGION, Type) == 16u, "MEMORY_REGION Type offset changed"); +_Static_assert(offsetof(MEMORY_MAP, Regions) == 16u, "MEMORY_MAP Regions offset changed"); +_Static_assert(sizeof(MEMORY_MAP) == 6160u, "MEMORY_MAP ABI size changed"); diff --git a/code/public/ntlite/port.h b/code/public/ntlite/port.h index 26d6788..88206d5 100644 --- a/code/public/ntlite/port.h +++ b/code/public/ntlite/port.h @@ -151,3 +151,10 @@ typedef struct SYS_REPLY_PORT_REQUEST { const SYS_PORT_HANDLE_TRANSFER* Handles; uint32_t HandleCount; } SYS_REPLY_PORT_REQUEST; + +// Port metadata and transfer descriptors cross the kernel/userspace boundary. +_Static_assert(sizeof(SYS_PORT_HANDLE_TRANSFER) == 12u, "SYS_PORT_HANDLE_TRANSFER ABI size changed"); +_Static_assert(sizeof(SYS_PORT_MESSAGE_INFO) == 36u, "SYS_PORT_MESSAGE_INFO ABI size changed"); +_Static_assert(offsetof(SYS_PORT_MESSAGE_INFO, SenderSecurityId) == 32u, + "SYS_PORT_MESSAGE_INFO sender identity offset changed"); +_Static_assert(sizeof(SYS_PORT_CONNECTION_INFO) == 20u, "SYS_PORT_CONNECTION_INFO ABI size changed"); diff --git a/code/public/ntlite/posix_service.h b/code/public/ntlite/posix_service.h index 9307379..038798d 100644 --- a/code/public/ntlite/posix_service.h +++ b/code/public/ntlite/posix_service.h @@ -2,6 +2,7 @@ #pragma once +#include #include #include #include @@ -155,13 +156,20 @@ typedef struct NTL_POSIX_SHARED_OPEN_DESCRIPTION { uint32_t Type; uint32_t Reserved; uint64_t DescriptionId; - volatile long StatusFlags; + volatile uint32_t StatusFlags; uint32_t Reserved2; // Optional server-owned userspace data-plane pointer. Pipe endpoint // descriptions use this for SYS_PIPE_SHARED_STATE; other types leave it zero. uint64_t SharedObjectData; } NTL_POSIX_SHARED_OPEN_DESCRIPTION; +_Static_assert(offsetof(NTL_POSIX_SHARED_OPEN_DESCRIPTION, StatusFlags) == 24u, + "NTL_POSIX_SHARED_OPEN_DESCRIPTION status layout changed"); +_Static_assert(offsetof(NTL_POSIX_SHARED_OPEN_DESCRIPTION, SharedObjectData) == 32u, + "NTL_POSIX_SHARED_OPEN_DESCRIPTION data layout changed"); +_Static_assert(sizeof(NTL_POSIX_SHARED_OPEN_DESCRIPTION) == 40u, + "NTL_POSIX_SHARED_OPEN_DESCRIPTION ABI size changed"); + // Server-owned spawn/exec builders place this opaque personality payload // in the generic userspace process-loader startup data. Zero token/PID/generation // means an ordinary process is self-registering for the first time. diff --git a/code/public/ntlite/posix_signal.h b/code/public/ntlite/posix_signal.h index 9c26083..35641e6 100644 --- a/code/public/ntlite/posix_signal.h +++ b/code/public/ntlite/posix_signal.h @@ -3,6 +3,7 @@ #pragma once #include +#include #define NTL_POSIX_SIGNAL_HUP 1 #define NTL_POSIX_SIGNAL_INT 2 @@ -24,6 +25,6 @@ #define NTL_POSIX_SIGNAL_VALID_MASK ((1ull << NTL_POSIX_SIGNAL_MAX) - 1ull) #define NTL_POSIX_SIGNAL_UNBLOCKABLE_MASK NTL_POSIX_SIGNAL_BIT(NTL_POSIX_SIGNAL_KILL) -static inline int NtlPosixSignalValid(int signalNumber) { +static inline bool NtlPosixSignalValid(int signalNumber) { return signalNumber >= NTL_POSIX_SIGNAL_HUP && signalNumber <= NTL_POSIX_SIGNAL_MAX; } diff --git a/code/public/ntlite/process_loader.h b/code/public/ntlite/process_loader.h index b3c2050..51e78a2 100644 --- a/code/public/ntlite/process_loader.h +++ b/code/public/ntlite/process_loader.h @@ -13,7 +13,7 @@ #define NTL_PROCESS_LOAD_FLAG_NONE 0x00000000u #define NTL_PROCESS_STARTUP_DATA_MAX 1024u -// Generic userspace process vectors. The builder copies every view into the +// Generic userspace process vectors. The loader copies every view into the // child loader block; callers retain ownership of the source arrays/strings. typedef struct NTL_PROCESS_STRING_VECTOR { const SYS_STRING_VIEW* Entries; @@ -28,16 +28,13 @@ typedef struct NTL_PROCESS_STARTUP_CAPABILITY { uint32_t DuplicateFlags; } NTL_PROCESS_STARTUP_CAPABILITY; -// Reusable inert-process builder used by policy services such as posixsrv. -// The storage is opaque to callers; initialize it to zero before first use. -#define NTL_PROCESS_BUILDER_OPAQUE_WORDS 160u -typedef struct NTL_PROCESS_BUILDER { - uintptr_t Opaque[NTL_PROCESS_BUILDER_OPAQUE_WORDS]; -} NTL_PROCESS_BUILDER; +// Opaque preparation owned by the userspace process loader. A successful +// prepare returns one allocation consumed by Start or Abort; never copy it. +typedef struct NTL_PROCESS_PREPARATION NTL_PROCESS_PREPARATION; // Describes construction of an inert child. Unlike NTL_CREATE_PROCESS_AT_REQUEST, // preparation does not start the process and therefore has no output handle field. -typedef struct NTL_PROCESS_BUILDER_REQUEST { +typedef struct NTL_PREPARE_PROCESS_REQUEST { uint32_t Size; uint32_t Flags; SYS_HANDLE RootHandle; @@ -55,7 +52,7 @@ typedef struct NTL_PROCESS_BUILDER_REQUEST { NTL_PROCESS_STRING_VECTOR Environment; const void* StartupData; size_t StartupDataSize; -} NTL_PROCESS_BUILDER_REQUEST; +} NTL_PREPARE_PROCESS_REQUEST; // Creates and starts one PE32+ process entirely through the userspace image loader. typedef struct NTL_CREATE_PROCESS_AT_REQUEST { @@ -113,25 +110,28 @@ typedef struct NTL_CREATE_SYSTEM_PROCESS_REQUEST { #define NTL_PROCESS_LOADER_API #endif -// Prepares an inert child and materializes its image/dependencies/bootstrap data. -// imageFile may be SYS_HANDLE_INVALID to open request->Path relative to RootHandle. -NTL_PROCESS_LOADER_API KSTATUS NtlProcessBuilderPrepare(NTL_PROCESS_BUILDER* builder, - const NTL_PROCESS_BUILDER_REQUEST* request, - SYS_HANDLE imageFile); -// Returns the prepared process capability without transferring ownership from the builder. -// The borrowed handle remains valid only until Start or Abort consumes the builder. -NTL_PROCESS_LOADER_API KSTATUS NtlProcessBuilderGetProcessHandle(NTL_PROCESS_BUILDER* builder, - SYS_HANDLE* processHandleOut); -// Starts a prepared child and returns the retained process capability to the caller. -// On success the builder is consumed and reset to the empty state. -NTL_PROCESS_LOADER_API KSTATUS NtlProcessBuilderStart(NTL_PROCESS_BUILDER* builder, - SYS_HANDLE* processHandleOut); -// Aborts a prepared child, terminating the inert native process and releasing loader state. -NTL_PROCESS_LOADER_API void NtlProcessBuilderAbort(NTL_PROCESS_BUILDER* builder); +// Allocates and prepares an inert child, including image, dependencies, inherited +// capabilities, and bootstrap data. On failure *preparationOut is NULL and no +// prepared process or child handle is retained. imageFile may be invalid to +// open request->Path relative to RootHandle. +NTL_PROCESS_LOADER_API KSTATUS NtlPrepareProcess(const NTL_PREPARE_PROCESS_REQUEST* request, + SYS_HANDLE imageFile, + NTL_PROCESS_PREPARATION** preparationOut); +// Borrows the native process capability without transferring ownership. This +// handle must not be closed by the caller and is valid only until Start/Abort. +NTL_PROCESS_LOADER_API KSTATUS NtlPreparedProcessGetHandle( + const NTL_PROCESS_PREPARATION* preparation, SYS_HANDLE* processHandleOut); +// Consumes one valid preparation on both success and ordinary start failure. +// On success returns the caller-owned native process capability. On failure +// the inert child is discarded and *processHandleOut is invalid. +NTL_PROCESS_LOADER_API KSTATUS NtlStartPreparedProcess( + NTL_PROCESS_PREPARATION* preparation, SYS_HANDLE* processHandleOut); +// Consumes one prepared child without starting it. NULL is accepted. +NTL_PROCESS_LOADER_API void NtlAbortPreparedProcess(NTL_PROCESS_PREPARATION* preparation); NTL_PROCESS_LOADER_API KSTATUS NtlCreateProcessAt(const NTL_CREATE_PROCESS_AT_REQUEST* request); NTL_PROCESS_LOADER_API KSTATUS NtlCreateSystemProcess(const NTL_CREATE_SYSTEM_PROCESS_REQUEST* request); -// Copies the optional opaque startup data supplied by the userspace process builder. +// Copies the optional opaque startup data supplied at process construction. NTL_PROCESS_LOADER_API KSTATUS NtlGetProcessStartupData(void* buffer, size_t bufferSize, size_t* sizeOut); diff --git a/code/public/ntlite/security.h b/code/public/ntlite/security.h index 5a1e730..96645b6 100644 --- a/code/public/ntlite/security.h +++ b/code/public/ntlite/security.h @@ -14,7 +14,6 @@ typedef uint32_t SYS_SERVICE_CLAIMS; #define SYS_PRIV_PROCESS_ENUMERATE 0x00000002u #define SYS_PRIV_THREAD_ENUMERATE_ANY 0x00000004u #define SYS_PRIV_KERNEL_DIAGNOSTICS 0x00000008u -#define SYS_PRIV_GLOBAL_INPUT 0x00000010u #define SYS_PRIV_AUDIO_ACCESS 0x00000020u #define SYS_PRIV_AUDIO_MASTER_CONTROL 0x00000040u #define SYS_PRIV_CONSOLE_ACCESS 0x00000080u @@ -24,9 +23,7 @@ typedef uint32_t SYS_SERVICE_CLAIMS; #define SYS_PRIV_RAW_STORAGE 0x00000800u #define SYS_PRIV_FILESYSTEM_PROVIDER 0x00001000u #define SYS_PRIV_NETWORK_ACCESS 0x00002000u -// Allows publication into the legacy global named-queue registry; opening an explicitly published queue stays public. -#define SYS_PRIV_NAMED_QUEUE_PUBLISH 0x00004000u -#define SYS_PRIV_ALL 0x00007FFFu +#define SYS_PRIV_ALL 0x00003FEFu // Service claims constrain which well-known service identities a privileged context may register. #define SYS_SERVICE_CLAIM_RESOURCE 0x00000001u diff --git a/code/public/ntlite/status.h b/code/public/ntlite/status.h index c51825a..8f1f567 100644 --- a/code/public/ntlite/status.h +++ b/code/public/ntlite/status.h @@ -92,3 +92,6 @@ typedef uint32_t KSTATUS; #define KSUCCESS(status) (((KSTATUS)(status)) == NTLSTATUS_SUCCESS) #define KFAILED(status) KSTATUS_IS_ERROR(status) + +// Keep status codes as a fixed-width native ABI value. +_Static_assert(sizeof(KSTATUS) == 4u, "KSTATUS ABI width changed"); diff --git a/code/public/ntlite/string_view.h b/code/public/ntlite/string_view.h index 1e89ebd..406ad15 100644 --- a/code/public/ntlite/string_view.h +++ b/code/public/ntlite/string_view.h @@ -2,11 +2,14 @@ #pragma once // sys_string.h - shared native string-view ABI. -// A SYS_STRING_VIEW is a borrowed byte span. It is not NUL-terminated by -// contract and it does not own the pointed-to storage. +// A SYS_STRING_VIEW is a borrowed byte span. Length counts bytes, never Unicode +// scalar values or characters. The view is not NUL-terminated and owns no storage. +// Native names/text must contain strict UTF-8 (no normalization or case folding); +// generic byte payloads may use the same representation without becoming text. #include #include +#include #include #ifndef SYS_STRING_VIEW_DEFINED @@ -37,20 +40,114 @@ static inline SYS_STRING_VIEW SysSvFromCstr(const char* s) { } // Implements the sv Is Empty operation. -static inline int SysSvIsEmpty(SYS_STRING_VIEW s) { +static inline bool SysSvIsEmpty(SYS_STRING_VIEW s) { return s.Length == 0; } // Implements the sv Is Valid operation. -static inline int SysSvIsValid(SYS_STRING_VIEW s) { +static inline bool SysSvIsValid(SYS_STRING_VIEW s) { return s.Data != (const char*)0 || s.Length == 0; } +// Validates well-formed UTF-8 bytes (RFC 3629). No normalization is performed. +// Rejects overlong encodings, surrogates, truncated sequences and > U+10FFFF. +static inline bool SysSvIsUtf8(SYS_STRING_VIEW view) { + if (!SysSvIsValid(view)) return false; + const uint8_t* data = (const uint8_t*)view.Data; + size_t i = 0; + while (i < view.Length) { + uint8_t lead = data[i++]; + if (lead <= 0x7Fu) continue; + uint32_t scalar; + size_t continuation; + if (lead >= 0xC2u && lead <= 0xDFu) { + scalar = lead & 0x1Fu; + continuation = 1; + } else if (lead >= 0xE0u && lead <= 0xEFu) { + scalar = lead & 0x0Fu; + continuation = 2; + } else if (lead >= 0xF0u && lead <= 0xF4u) { + scalar = lead & 0x07u; + continuation = 3; + } else { + return false; + } + if (continuation > view.Length - i) return false; + for (size_t j = 0; j < continuation; ++j) { + uint8_t byte = data[i++]; + if ((byte & 0xC0u) != 0x80u) return false; + scalar = (scalar << 6) | (uint32_t)(byte & 0x3Fu); + } + if ((continuation == 1 && scalar < 0x80u) || + (continuation == 2 && scalar < 0x800u) || + (continuation == 3 && scalar < 0x10000u) || + (scalar >= 0xD800u && scalar <= 0xDFFFu) || scalar > 0x10FFFFu) + return false; + } + return true; +} + +// Names and other retained native text forbid embedded NUL as well as bad UTF-8. +// Empty views, including the canonical null view, are valid text. +static inline bool SysSvIsUtf8Text(SYS_STRING_VIEW view) { + if (!SysSvIsUtf8(view)) return false; + for (size_t i = 0; i < view.Length; ++i) + if (view.Data[i] == '\0') return false; + return true; +} + +// Copies external filename bytes into display-only UTF-8, replacing each invalid +// byte (including embedded NUL) with U+FFFD. Never splits a valid scalar or a +// replacement at the output capacity; always NUL-terminates when capacity > 0. +// This is a lossy display projection, not a native namespace identity mapping. +static inline size_t SysSvCopyUtf8WithReplacement(SYS_STRING_VIEW source, + char* destination, + size_t capacity) { + if (!destination || capacity == 0) return 0; + size_t read = 0; + size_t written = 0; + if (!SysSvIsValid(source)) { + destination[0] = '\0'; + return 0; + } + while (read < source.Length) { + uint8_t first = (uint8_t)source.Data[read]; + size_t sequenceLength = 0; + if (first >= 1u && first <= 0x7Fu) { + sequenceLength = 1; + } else if (first >= 0xC2u && first <= 0xDFu) { + sequenceLength = 2; + } else if (first >= 0xE0u && first <= 0xEFu) { + sequenceLength = 3; + } else if (first >= 0xF0u && first <= 0xF4u) { + sequenceLength = 4; + } + + bool valid = sequenceLength && sequenceLength <= source.Length - read && + SysSvIsUtf8(SysSvFromParts(source.Data + read, sequenceLength)); + size_t appendLength = valid ? sequenceLength : 3u; + if (appendLength > capacity - written - 1u) break; + + if (valid) { + memcpy(destination + written, source.Data + read, sequenceLength); + read += sequenceLength; + } else { + destination[written] = (char)0xEF; + destination[written + 1u] = (char)0xBF; + destination[written + 2u] = (char)0xBD; + ++read; + } + written += appendLength; + } + destination[written] = '\0'; + return written; +} + // Implements the sv Equal operation. -static inline int SysSvEqual(SYS_STRING_VIEW a, SYS_STRING_VIEW b) { - if (a.Length != b.Length) return 0; - if (a.Length == 0) return 1; - if (!a.Data || !b.Data) return 0; +static inline bool SysSvEqual(SYS_STRING_VIEW a, SYS_STRING_VIEW b) { + if (a.Length != b.Length) return false; + if (a.Length == 0) return true; + if (!a.Data || !b.Data) return false; return memcmp(a.Data, b.Data, a.Length) == 0; } @@ -60,33 +157,33 @@ static inline char SysAsciiLower(char c) { } // Implements the sv Equal Ascii I operation. -static inline int SysSvEqualAsciiI(SYS_STRING_VIEW a, SYS_STRING_VIEW b) { - if (a.Length != b.Length) return 0; - if (a.Length == 0) return 1; - if (!a.Data || !b.Data) return 0; +static inline bool SysSvEqualAsciiI(SYS_STRING_VIEW a, SYS_STRING_VIEW b) { + if (a.Length != b.Length) return false; + if (a.Length == 0) return true; + if (!a.Data || !b.Data) return false; for (size_t i = 0; i < a.Length; i++) { - if (SysAsciiLower(a.Data[i]) != SysAsciiLower(b.Data[i])) return 0; + if (SysAsciiLower(a.Data[i]) != SysAsciiLower(b.Data[i])) return false; } - return 1; + return true; } // Implements the sv Equal Lit operation. -static inline int SysSvEqualLit(SYS_STRING_VIEW a, const char* lit) { +static inline bool SysSvEqualLit(SYS_STRING_VIEW a, const char* lit) { return SysSvEqual(a, SysSvFromCstr(lit)); } // Implements the sv Equal Lit Ascii I operation. -static inline int SysSvEqualLitAsciiI(SYS_STRING_VIEW a, const char* lit) { +static inline bool SysSvEqualLitAsciiI(SYS_STRING_VIEW a, const char* lit) { return SysSvEqualAsciiI(a, SysSvFromCstr(lit)); } // Implements the sv Contains Byte operation. -static inline int SysSvContainsByte(SYS_STRING_VIEW s, char ch) { - if (!s.Data && s.Length) return 0; +static inline bool SysSvContainsByte(SYS_STRING_VIEW s, char ch) { + if (!s.Data && s.Length) return false; for (size_t i = 0; i < s.Length; i++) { - if (s.Data[i] == ch) return 1; + if (s.Data[i] == ch) return true; } - return 0; + return false; } // Implements the sv Slice operation. @@ -98,6 +195,7 @@ static inline SYS_STRING_VIEW SysSvSlice(SYS_STRING_VIEW s, size_t off, size_t l // Implements the sv Trim Ascii operation. static inline SYS_STRING_VIEW SysSvTrimAscii(SYS_STRING_VIEW s) { + if (!SysSvIsValid(s)) return SYS_SV_NULL; size_t begin = 0; size_t end = s.Length; while (begin < end) { @@ -114,14 +212,14 @@ static inline SYS_STRING_VIEW SysSvTrimAscii(SYS_STRING_VIEW s) { } // Implements the sv Copy Z operation. -static inline int SysSvCopyZ(char* dst, size_t dstSize, SYS_STRING_VIEW src) { - if (!dst || dstSize == 0) return 0; - if (src.Length && !src.Data) return 0; +static inline bool SysSvCopyZ(char* dst, size_t dstSize, SYS_STRING_VIEW src) { + if (!dst || dstSize == 0) return false; + if (src.Length && !src.Data) return false; if (src.Length + 1 > dstSize) { dst[0] = '\0'; - return 0; + return false; } if (src.Length) memcpy(dst, src.Data, src.Length); dst[src.Length] = '\0'; - return 1; + return true; } diff --git a/code/public/ntlite/syscall.h b/code/public/ntlite/syscall.h index 6634a5e..8de8d94 100644 --- a/code/public/ntlite/syscall.h +++ b/code/public/ntlite/syscall.h @@ -75,6 +75,15 @@ typedef struct SYS_TEB { void* SubsystemData; } SYS_TEB; +// User bootstrap is consumed by the kernel, loader and personalities. +_Static_assert(sizeof(SYS_STARTUP_CAPABILITY) == 8u, "SYS_STARTUP_CAPABILITY ABI size changed"); +_Static_assert(offsetof(SYS_PEB, ImagePath) == 16u, "SYS_PEB ImagePath offset changed"); +_Static_assert(offsetof(SYS_PEB, LoaderData) == 64u, "SYS_PEB LoaderData offset changed"); +_Static_assert(sizeof(SYS_PEB) == 72u, "SYS_PEB ABI size changed"); +_Static_assert(offsetof(SYS_TEB, Peb) == 0u, "SYS_TEB GS:[0] PEB pointer changed"); +_Static_assert(offsetof(SYS_TEB, SubsystemData) == 40u, "SYS_TEB personality slot offset changed"); +_Static_assert(sizeof(SYS_TEB) == 48u, "SYS_TEB ABI size changed"); + #endif // SYS_USER_ENV_TYPES_DEFINED // Returns the current thread's process environment block. @@ -104,9 +113,13 @@ static inline SYS_TEB* SysGetTeb(void) { // APC and async I/O ABI. typedef struct SYS_IO_STATUS_BLOCK { - volatile uint32_t Status; + volatile KSTATUS Status; volatile uint64_t Information; } SYS_IO_STATUS_BLOCK; +// A completion status is a native KSTATUS even when written asynchronously. +_Static_assert(sizeof(SYS_IO_STATUS_BLOCK) == 16u, "SYS_IO_STATUS_BLOCK size changed"); +_Static_assert(offsetof(SYS_IO_STATUS_BLOCK, Information) == 8u, "SYS_IO_STATUS_BLOCK Information offset changed"); + typedef void (*SYS_USER_APC_ROUTINE)(void* normalContext, SYS_IO_STATUS_BLOCK* iosb, @@ -549,20 +562,27 @@ typedef struct SYS_PIPE_SHARED_STATE { uint32_t Version; uint32_t Capacity; uint32_t HeaderBytes; - volatile long ReaderOpen; - volatile long WriterOpen; + volatile int32_t ReaderOpen; + volatile int32_t WriterOpen; uint8_t HeaderReserved[SYS_PIPE_SHARED_HEADER_BYTES - 24u]; volatile uint64_t ReadPosition; volatile uint64_t WritePosition; - volatile long ReadLock; - volatile long WriteLock; - volatile long ReadWaiters; - volatile long WriteWaiters; + volatile int32_t ReadLock; + volatile int32_t WriteLock; + volatile int32_t ReadWaiters; + volatile int32_t WriteWaiters; uint8_t RingReserved[16]; uint8_t Buffer[SYS_PIPE_BUFFER_CAPACITY]; } SYS_PIPE_SHARED_STATE; +_Static_assert(offsetof(SYS_PIPE_SHARED_STATE, ReadPosition) == SYS_PIPE_SHARED_HEADER_BYTES, + "SYS_PIPE_SHARED_STATE control page layout changed"); +_Static_assert(offsetof(SYS_PIPE_SHARED_STATE, Buffer) == SYS_PIPE_SHARED_HEADER_BYTES + 48u, + "SYS_PIPE_SHARED_STATE ring layout changed"); +_Static_assert(sizeof(SYS_PIPE_SHARED_STATE) == SYS_PIPE_SHARED_HEADER_BYTES + 48u + SYS_PIPE_BUFFER_CAPACITY, + "SYS_PIPE_SHARED_STATE ABI size changed"); + typedef struct SYS_CREATE_PIPE_REQUEST { uint32_t Size; uint32_t Flags; @@ -581,32 +601,6 @@ typedef struct SYS_PRINT_DEBUG_REQUEST { SYS_STRING_VIEW Text; } SYS_PRINT_DEBUG_REQUEST; -// Published queue names are owned by the publishing process and QueueHandle. -// SysUnpublishQueue withdraws a name explicitly. Closing the publishing handle -// or exiting the process withdraws every name published through that handle. -typedef struct SYS_PUBLISH_QUEUE_REQUEST { - uint32_t Size; - SYS_STRING_VIEW Name; - SYS_HANDLE QueueHandle; -} SYS_PUBLISH_QUEUE_REQUEST; - -typedef struct SYS_UNPUBLISH_QUEUE_REQUEST { - uint32_t Size; - SYS_STRING_VIEW Name; -} SYS_UNPUBLISH_QUEUE_REQUEST; - -typedef struct SYS_OPEN_NAMED_QUEUE_REQUEST { - uint32_t Size; - SYS_STRING_VIEW Name; - SYS_HANDLE* HandleOut; -} SYS_OPEN_NAMED_QUEUE_REQUEST; - -typedef struct SYS_NAMED_QUEUE_OWNER_REQUEST { - uint32_t Size; - SYS_STRING_VIEW Name; - SYS_RIGHTS DesiredAccess; - SYS_HANDLE* ProcessHandleOut; -} SYS_NAMED_QUEUE_OWNER_REQUEST; #define SYS_QUEUE_FLAG_NONE 0x00000000u #define SYS_QUEUE_MESSAGE_SIZE 56u @@ -695,12 +689,6 @@ KSTATUS SysProtectProcessVm(SYS_HANDLE processHandle, void* base, size_t pageCou KSTATUS SysStartProcess(const SYS_START_PROCESS_REQUEST* request); KSTATUS SysWaitSingle(SYS_HANDLE handle, uint32_t flags, uint64_t timeoutMilliseconds); KSTATUS SysWaitMany(const SYS_WAIT_MANY_REQUEST* request); -// SysOpenInputQueue returns a readable and waitable handle to the default -// keyboard input queue. The handle does not create a second event stream; -// SysReadInputEvent, SysPeekInputEvent, SysReadKey, and SysReadKeyboardChar -// consume the same queue. SysWaitSingle succeeds for this handle when a -// keyboard input event can be read. -KSTATUS SysOpenInputQueue(uint32_t flags, SYS_HANDLE* handleOut); static inline KSTATUS SysWait(SYS_HANDLE handle, uint32_t flags) { return SysWaitSingle(handle, flags, SYS_WAIT_TIMEOUT_INFINITE); @@ -997,10 +985,6 @@ KSTATUS SysClearEvent(SYS_HANDLE handle); KSTATUS SysCreateQueue(SYS_HANDLE* handleOut); KSTATUS SysSendQueue(const SYS_QUEUE_SEND_REQUEST* request); KSTATUS SysRecvQueue(const SYS_QUEUE_RECV_REQUEST* request); -KSTATUS SysPublishQueue(const SYS_PUBLISH_QUEUE_REQUEST* request); -KSTATUS SysUnpublishQueue(const SYS_UNPUBLISH_QUEUE_REQUEST* request); -KSTATUS SysOpenNamedQueue(const SYS_OPEN_NAMED_QUEUE_REQUEST* request); -KSTATUS SysOpenNamedQueueOwnerProcess(const SYS_NAMED_QUEUE_OWNER_REQUEST* request); KSTATUS SysCreatePort(const SYS_CREATE_PORT_REQUEST* request); KSTATUS SysConnectPort(const SYS_CONNECT_PORT_REQUEST* request); @@ -1058,7 +1042,7 @@ typedef struct SYS_CONSOLE_INFO { KSTATUS SysWriteConsole(const SYS_WRITE_CONSOLE_REQUEST* request); KSTATUS SysGetConsoleInfo(SYS_CONSOLE_INFO* infoOut); -KSTATUS SysSetConsoleCursor(int x, int y); +KSTATUS SysSetConsoleCursor(int32_t x, int32_t y); typedef struct SYS_FILL_CONSOLE_CHARACTER_REQUEST { uint32_t Size; @@ -1109,7 +1093,7 @@ KSTATUS SysUnmapSurface(void* base); KSTATUS SysQuerySurface(SYS_HANDLE surfaceHandle, SYS_SURFACE_DESC* descOut); KSTATUS SysCommitSurface(SYS_HANDLE surfaceHandle, const DISPLAY_RECT* rects, uint32_t rectCount, uint64_t* generationOut); KSTATUS SysClearScreen(uint32_t color); -KSTATUS SysGetScreenSize(int* widthOut, int* heightOut); +KSTATUS SysGetScreenSize(int32_t* widthOut, int32_t* heightOut); KSTATUS SysQueryDisplayInfo(SYS_DISPLAY_INFO* infoOut); #define SYS_DISPLAY_MANAGER_CURSOR_FLAG_NONE 0x00000000u @@ -1131,7 +1115,7 @@ KSTATUS SysPresentDisplayManagerSurface(SYS_HANDLE managerHandle, SYS_HANDLE sur const DISPLAY_RECT* rects, uint32_t rectCount); KSTATUS SysWriteDisplayManagerConsole(SYS_HANDLE managerHandle, const SYS_WRITE_CONSOLE_REQUEST* request); KSTATUS SysQueryDisplayManagerConsoleInfo(SYS_HANDLE managerHandle, SYS_CONSOLE_INFO* infoOut); -KSTATUS SysSetDisplayManagerConsoleCursor(SYS_HANDLE managerHandle, int x, int y); +KSTATUS SysSetDisplayManagerConsoleCursor(SYS_HANDLE managerHandle, int32_t x, int32_t y); KSTATUS SysFillDisplayManagerConsoleCharacter(SYS_HANDLE managerHandle, const SYS_FILL_CONSOLE_CHARACTER_REQUEST* request); KSTATUS SysFillDisplayManagerConsoleAttribute(SYS_HANDLE managerHandle, const SYS_FILL_CONSOLE_ATTRIBUTE_REQUEST* request); KSTATUS SysPutDisplayManagerConsoleCharacter(SYS_HANDLE managerHandle, const SYS_PUT_SCREEN_CHAR_REQUEST* request); @@ -1178,50 +1162,6 @@ typedef struct SYS_INPUT_MANAGER_READ_REQUEST { // Reads the next normalized physical input event through an input-manager capability. KSTATUS SysReadInputManagerEvent(const SYS_INPUT_MANAGER_READ_REQUEST* request); -#define SYS_INPUT_QUEUE_FLAG_NONE 0x00000000u -#define SYS_INPUT_EVENT_FLAG_NONE 0x00000000u -#define SYS_GLOBAL_INPUT_STATE_FLAG_NONE 0x00000000u -#define SYS_GLOBAL_INPUT_KEY_BYTES 32u - -// Legacy kernel-global input snapshot retained for compatibility. Normal interactive -// clients should consume RSSRV logical input through . -typedef struct SYS_GLOBAL_INPUT_STATE { - uint32_t Size; - uint32_t Flags; - uint64_t Generation; - uint32_t Modifiers; - uint8_t KeyDown[SYS_GLOBAL_INPUT_KEY_BYTES]; - int32_t MouseX; - int32_t MouseY; - int32_t MouseWheel; - uint32_t MouseButtons; -} SYS_GLOBAL_INPUT_STATE; - -// A zero QueueHandle means the default keyboard input queue. A -// nonzero QueueHandle must be a handle returned by SysOpenInputQueue. A zero -// timeout is an immediate try and returns NTLSTATUS_WOULD_BLOCK when the queue is -// empty. A finite timeout returns NTLSTATUS_TIMEOUT only when its deadline expires. -typedef struct SYS_INPUT_EVENT_REQUEST { - uint32_t Size; - SYS_HANDLE QueueHandle; - uint32_t Flags; - uint64_t TimeoutMilliseconds; - INPUT_KEY_RECORD* RecordOut; -} SYS_INPUT_EVENT_REQUEST; - -KSTATUS SysPollKeyboard(INPUT_KEY* keyOut); -KSTATUS SysIsKeyDown(INPUT_KEY key, bool* downOut); -KSTATUS SysReadKeyboardChar(char* charOut); -KSTATUS SysReadKey(INPUT_KEY* keyOut); -uint32_t SysGetKeyModifiers(void); -KSTATUS SysQueryGlobalInputState(SYS_GLOBAL_INPUT_STATE* stateOut); -KSTATUS SysReadInputEvent(const SYS_INPUT_EVENT_REQUEST* request); -KSTATUS SysPeekInputEvent(const SYS_INPUT_EVENT_REQUEST* request); - -KSTATUS SysPeekMouse(INPUT_MOUSE_EVENT* eventOut); -KSTATUS SysReadMouse(INPUT_MOUSE_EVENT* eventOut); -KSTATUS SysGetMousePos(int* xOut, int* yOut); - // Native sockets. #define SYS_AF_INET 2u #define SYS_AF_INET6 10u @@ -1438,7 +1378,7 @@ typedef struct SYS_MEMORY_STATUS { // Public stats payloads start with Size and Version. Size is the byte count // returned for that payload, including any trailing records. Version describes // the field layout for fixed fields in the named payload. -#define SYS_STATS_VERSION 3u +#define SYS_STATS_VERSION 4u typedef struct SYS_EXCEPTION_STATS { @@ -1748,7 +1688,7 @@ typedef struct SYS_NET_IRP_STATS { typedef struct SYS_SWAP_STATS { uint32_t Size; uint32_t Version; - uint64_t Enabled; + bool Enabled; uint64_t TotalSlots; uint64_t UsedSlots; uint64_t FreeSlots; diff --git a/code/public/ntlite/time.h b/code/public/ntlite/time.h index bc8de2b..52d3097 100644 --- a/code/public/ntlite/time.h +++ b/code/public/ntlite/time.h @@ -1,6 +1,7 @@ // Public NTLite time ABI declarations shared between kernel, drivers, and user mode. #pragma once +#include #include #define SYS_TIME_FLAG_VALID 0x0001u @@ -31,3 +32,7 @@ typedef struct SYS_SYSTEM_TIME { uint64_t EpochSeconds; uint64_t TickMicroseconds; } SYS_SYSTEM_TIME; + +// File metadata and time syscalls share this fixed wall-clock representation. +_Static_assert(offsetof(SYS_SYSTEM_TIME, EpochSeconds) == 24u, "SYS_SYSTEM_TIME EpochSeconds offset changed"); +_Static_assert(sizeof(SYS_SYSTEM_TIME) == 40u, "SYS_SYSTEM_TIME ABI size changed"); diff --git a/code/user/apps/doom/doom.c b/code/user/apps/doom/doom.c index c2af0f4..87e1bcc 100644 --- a/code/user/apps/doom/doom.c +++ b/code/user/apps/doom/doom.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -20,12 +21,17 @@ int _fltused; -// --------------------------------------------------------------------------- -// doom_print_fn -// --------------------------------------------------------------------------- +// PureDOOM's progress messages must not touch the physical console while DWM is presenting. static void doom_print_wrapper(const char* msg) +{ + (void)msg; +} + +// Reports an unrecoverable Doom platform error before exiting. +static void doom_fatal(const char* msg) { if (msg) (void)NtlConsoleWrite(SYS_HANDLE_INVALID, msg, strlen(msg), NULL); + SysExitProcess(1); } // --------------------------------------------------------------------------- @@ -148,7 +154,7 @@ static int doom_seek_wrapper(void* handle, int offset, doom_seek_t origin) int64_t target = base + (int64_t)offset; if (target < 0) target = 0; - uint32_t s = SysSeekFile(f->Handle, (uint64_t)target); + KSTATUS s = SysSeekFile(f->Handle, (uint64_t)target); if (s != 0) return -1; f->Pos = (uint64_t)target; return 0; @@ -200,10 +206,8 @@ static char* doom_getenv_wrapper(const char* name) { if (strcmp(name, "HOME") == 0) { static char home[] = "."; - doom_print_wrapper("getenv: Returning HOME="); doom_print_wrapper(home); doom_print_wrapper("\n"); return home; } - doom_print_wrapper("getenv: Unknown variable "); doom_print_wrapper(name); doom_print_wrapper("\n"); return NULL; } @@ -316,6 +320,8 @@ static uint32_t g_surface_handle; static uint32_t g_display_clock_handle; static uint32_t* g_surface_pixels; static uint32_t g_surface_pitch_pixels; +static SYS_HANDLE g_desktop_handle = SYS_HANDLE_INVALID; +static NTL_WINDOW_ID g_window; static void init_surface(void) { @@ -327,10 +333,9 @@ static void init_surface(void) req.Format = SYS_SURFACE_FORMAT_XRGB8888; req.Flags = SYS_SURFACE_FLAG_NONE; req.HandleOut = &g_surface_handle; - uint32_t s = SysCreateSurface(&req); + KSTATUS s = SysCreateSurface(&req); if (s != NTLSTATUS_SUCCESS || !g_surface_handle) { - doom_print_wrapper("doom: SysCreateSurface failed\n"); - SysExitProcess(1); + doom_fatal("doom: SysCreateSurface failed\n"); } void* base = 0; @@ -338,14 +343,51 @@ static void init_surface(void) s = SysMapSurface(g_surface_handle, SYS_SURFACE_MAP_READ | SYS_SURFACE_MAP_WRITE, &base, &pitchBytes); if (s != NTLSTATUS_SUCCESS || !base || pitchBytes < DISP_W * sizeof(uint32_t)) { - doom_print_wrapper("doom: SysMapSurface failed\n"); - SysExitProcess(1); + doom_fatal("doom: SysMapSurface failed\n"); } g_surface_pixels = (uint32_t*)base; g_surface_pitch_pixels = pitchBytes / sizeof(uint32_t); } +// Registers the Doom surface with DWM so committed frames are composited. +static void init_window(void) +{ + const char* failedOperation = "NtlDesktopConnect"; + KSTATUS status = NtlDesktopConnect(&g_desktop_handle); + if (!KSUCCESS(status) || g_desktop_handle == SYS_HANDLE_INVALID) + goto failed; + + DISPLAY_RECT bounds = { 0, 0, DISP_W, DISP_H }; + failedOperation = "NtlDesktopCreateWindow"; + status = NtlDesktopCreateWindow(g_desktop_handle, &bounds, &g_window); + if (!KSUCCESS(status) || !g_window) + goto failed; + + failedOperation = "NtlDesktopAttachSurface"; + status = NtlDesktopAttachSurface(g_desktop_handle, g_window, g_surface_handle); + if (!KSUCCESS(status)) + goto failed; + + failedOperation = "NtlDesktopSetVisible"; + status = NtlDesktopSetVisible(g_desktop_handle, g_window, 1u); + if (!KSUCCESS(status)) + goto failed; + + (void)NtlDesktopRaiseWindow(g_desktop_handle, g_window); + return; + +failed: + (void)NtlConsoleWrite(SYS_HANDLE_INVALID, "doom: ", sizeof("doom: ") - 1u, NULL); + (void)NtlConsoleWrite(SYS_HANDLE_INVALID, failedOperation, strlen(failedOperation), NULL); + (void)NtlConsoleWrite(SYS_HANDLE_INVALID, " failed\n", sizeof(" failed\n") - 1u, NULL); + if (g_window) + (void)NtlDesktopDestroyWindow(g_desktop_handle, g_window); + if (g_desktop_handle != SYS_HANDLE_INVALID) + (void)NtlDesktopClose(g_desktop_handle); + SysExitProcess(1); +} + static void init_display_clock(void) { SYS_OPEN_DISPLAY_CLOCK_REQUEST req; @@ -355,10 +397,9 @@ static void init_display_clock(void) req.PeriodMilliseconds = 1; req.HandleOut = &g_display_clock_handle; - uint32_t s = SysOpenDisplayClock(&req); + KSTATUS s = SysOpenDisplayClock(&req); if (s != NTLSTATUS_SUCCESS) { g_display_clock_handle = 0; - doom_print_wrapper("doom: display clock unavailable, using sleep pacing\n"); } } @@ -369,8 +410,7 @@ static void render_frame(const uint8_t* rgba) uint32_t dstPitch = g_surface_pitch_pixels; if (!dstBase || dstPitch < DISP_W) { - doom_print_wrapper("doom: surface not initialized\n"); - SysExitProcess(1); + doom_fatal("doom: surface not initialized\n"); } for (int y = 0; y < DOOM_H; y++) { @@ -392,7 +432,7 @@ static void render_frame(const uint8_t* rgba) } DISPLAY_RECT rect = { 0, 0, DISP_W, DISP_H }; - uint32_t code = SysCommitSurface(g_surface_handle, &rect, 1, 0); + KSTATUS code = SysCommitSurface(g_surface_handle, &rect, 1, 0); if (code != NTLSTATUS_SUCCESS) SysExitProcess(code); } @@ -452,7 +492,11 @@ static int wait_for_next_doom_tic(int lastTic) return now; if (g_display_clock_handle) { - (void)SysWaitSingle(g_display_clock_handle, SYS_WAIT_FLAG_NONE, SYS_WAIT_TIMEOUT_INFINITE); + KSTATUS status = SysWaitSingle(g_display_clock_handle, SYS_WAIT_FLAG_NONE, 100u); + if (status != NTLSTATUS_SUCCESS && status != NTLSTATUS_TIMEOUT) { + (void)SysCloseHandle(g_display_clock_handle); + g_display_clock_handle = 0; + } } else { SysSleep(1); } @@ -496,20 +540,11 @@ int main(void) char argvStorage[512]; const char* argv[32]; int argc = build_argv_from_command_line(cmdline, argvStorage, sizeof(argvStorage), argv, 32); - if (argc == 1) - doom_print_wrapper("doom: parsed argc == 1\n"); - else if (argc > 1) - doom_print_wrapper("doom: parsed argc > 1\n"); - else - doom_print_wrapper("doom: parsed argc <= 0\n"); init_surface(); init_display_clock(); - (void)NtlConsoleWrite(SYS_HANDLE_INVALID, "doom: starting doom\n", sizeof("doom: starting doom\n") - 1u, NULL); doom_init(argc, argv, 0); - (void)NtlConsoleWrite(SYS_HANDLE_INVALID, "doom: starting audio\n", sizeof("doom: starting audio\n") - 1u, NULL); DoomAudioStart(); - - (void)NtlConsoleWrite(SYS_HANDLE_INVALID, "doom: entering game loop\n", sizeof("doom: entering game loop\n") - 1u, NULL); + init_window(); // Core execution loop. Doom advances at TICRATE, so rendering duplicate // frames as fast as possible only amplifies scheduler and input jitter. diff --git a/code/user/apps/doom/doom_audio.c b/code/user/apps/doom/doom_audio.c index 1ad1356..2f90ff2 100644 --- a/code/user/apps/doom/doom_audio.c +++ b/code/user/apps/doom/doom_audio.c @@ -1,5 +1,7 @@ // Doom audio mixes PureDOOM SFX with optional SoundFont MIDI into one native render stream. +#include +#include #include #include #include @@ -206,15 +208,15 @@ typedef struct DOOM_AUDIO_STATE { uint32_t WorkerId; uint32_t SynthLoaderHandle; uint32_t SynthLoaderId; - volatile long StopRequested; + volatile int32_t StopRequested; tsf* volatile Synth; } DOOM_AUDIO_STATE; static DOOM_AUDIO_STATE g_doomAudio; -// Writes one complete diagnostic line to the process console. +// Suppresses audio diagnostics until there is a display-independent user-mode log sink. static void DoomAudioPrint(const char* text) { - if (text) (void)NtlConsoleWrite(SYS_HANDLE_INVALID, text, strlen(text), NULL); + (void)text; } // Opens the default available native render endpoint. @@ -262,8 +264,8 @@ static KSTATUS DoomAudioFindEndpoint(uint32_t* endpointIdOut) { // Returns the currently published synthesizer without taking the PureDOOM lock. static tsf* DoomAudioGetSynth(void) { - return (tsf*)_InterlockedCompareExchangePointer( - (void* volatile*)&g_doomAudio.Synth, NULL, NULL); + return (tsf*)NtlAtomicCompareExchangePointer( + &g_doomAudio.Synth, NULL, NULL); } // Maps the optional SoundFont so TinySoundFont can parse it without a separate input copy. @@ -353,7 +355,7 @@ static void DoomAudioSynthLoader(uintptr_t argument) { SysExitThread(0); } - if (_InterlockedCompareExchange(&g_doomAudio.StopRequested, 0, 0)) { + if (NtlAtomicLoad32(&g_doomAudio.StopRequested)) { (void)SysFreeVm(soundFontBase); SysExitThread(0); } @@ -367,16 +369,15 @@ static void DoomAudioSynthLoader(uintptr_t argument) { status = DoomAudioConfigureSynth(synth); if (status != NTLSTATUS_SUCCESS || - _InterlockedCompareExchange(&g_doomAudio.StopRequested, 0, 0)) { + NtlAtomicLoad32(&g_doomAudio.StopRequested)) { if (status != NTLSTATUS_SUCCESS) DoomAudioPrint("doom: MIDI synthesizer initialization failed; continuing with sound effects only\n"); tsf_close(synth); SysExitThread(0); } - (void)_InterlockedExchangePointer( - (void* volatile*)&g_doomAudio.Synth, synth); - DoomAudioPrint("doom: MIDI synthesizer ready\n"); + (void)NtlAtomicExchangePointer( + &g_doomAudio.Synth, synth); SysExitThread(0); } @@ -620,7 +621,7 @@ static void DoomAudioWorker(uintptr_t argument) { uint32_t requiredFrames; KSTATUS status; - if (_InterlockedCompareExchange(&g_doomAudio.StopRequested, 0, 0)) + if (NtlAtomicLoad32(&g_doomAudio.StopRequested)) break; status = DoomAudioQueryStream(&info); @@ -686,8 +687,8 @@ static void DoomAudioReleaseResources(void) { (void)SysCloseHandle(g_doomAudio.StreamHandle); g_doomAudio.StreamHandle = 0; } - tsf* synth = (tsf*)_InterlockedExchangePointer( - (void* volatile*)&g_doomAudio.Synth, NULL); + tsf* synth = (tsf*)NtlAtomicExchangePointer( + &g_doomAudio.Synth, NULL); if (synth) tsf_close(synth); if (g_doomAudio.CoreMutex) { @@ -767,7 +768,7 @@ void DoomAudioStart(void) { // Stops playback immediately without waiting for the producer thread. void DoomAudioAbort(void) { - _InterlockedExchange(&g_doomAudio.StopRequested, 1); + NtlAtomicExchange32(&g_doomAudio.StopRequested, 1); if (g_doomAudio.StreamHandle) (void)SysAudioStop(g_doomAudio.StreamHandle); } diff --git a/code/user/apps/edit/edit.c b/code/user/apps/edit/edit.c index 271b459..335891b 100644 --- a/code/user/apps/edit/edit.c +++ b/code/user/apps/edit/edit.c @@ -374,7 +374,7 @@ static void move_down(void) { // Updates file. static void save_file(void) { uint32_t h = 0; - uint32_t st = NtlOpenFile(SysSvFromCstr(gFilename), + KSTATUS st = NtlOpenFile(SysSvFromCstr(gFilename), SYS_FILE_ACCESS_WRITE | SYS_FILE_ACCESS_QUERY, SYS_FILE_CREATE_ALWAYS, SYS_FILE_FLAG_NONE, &h); if (st != 0) { msg_set("save failed: cannot open file"); return; } diff --git a/code/user/apps/httpget/httpget.c b/code/user/apps/httpget/httpget.c index 15d537c..e32e82d 100644 --- a/code/user/apps/httpget/httpget.c +++ b/code/user/apps/httpget/httpget.c @@ -71,12 +71,12 @@ static void format_ipv4(char* dst, size_t cap, const uint8_t ip[4]) { static void append_hex16(char* dst, size_t cap, size_t* off, uint16_t value) { static const char hex[] = "0123456789abcdef"; - int started = 0; + bool started = false; for (int shift = 12; shift >= 0; shift -= 4) { unsigned nibble = (value >> shift) & 0xfu; if (nibble || started || shift == 0) { append_char(dst, cap, off, hex[nibble]); - started = 1; + started = true; } } } @@ -153,7 +153,7 @@ static void usage(void) { } // Implements the resolve host operation. -static uint32_t resolve_host(const char* host, SYS_SOCKET_ADDRESS* address) { +static KSTATUS resolve_host(const char* host, SYS_SOCKET_ADDRESS* address) { uint8_t ip[4]; if (parse_ipv4(host, ip)) { memset(address, 0, sizeof(*address)); @@ -173,7 +173,7 @@ static uint32_t resolve_host(const char* host, SYS_SOCKET_ADDRESS* address) { req.AddressOut = address; req.AddressSizeInOut = &resolvedSize; - uint32_t st = SysResolveHost(&req); + KSTATUS st = SysResolveHost(&req); if (st == NTLSTATUS_SUCCESS) return NTLSTATUS_SUCCESS; @@ -224,7 +224,7 @@ int main(void) { } } - uint32_t st = resolve_host(host, &addr); + KSTATUS st = resolve_host(host, &addr); if (st != NTLSTATUS_SUCCESS) { print("httpget: resolve failed "); print_u64(st); print(" for "); print(host); print("\n"); usage(); diff --git a/code/user/apps/ping/ping.c b/code/user/apps/ping/ping.c index 5b47dd1..2c8eec4 100644 --- a/code/user/apps/ping/ping.c +++ b/code/user/apps/ping/ping.c @@ -131,7 +131,7 @@ static bool parse_u32(const char* s, uint32_t* out) { } // Implements the resolve host operation. -static uint32_t resolve_host(const char* host, uint8_t ip[4]) { +static KSTATUS resolve_host(const char* host, uint8_t ip[4]) { if (parse_ipv4(host, ip)) return NTLSTATUS_SUCCESS; @@ -146,7 +146,7 @@ static uint32_t resolve_host(const char* host, uint8_t ip[4]) { req.AddressOut = &resolved; req.AddressSizeInOut = &resolvedSize; - uint32_t st = SysResolveHost(&req); + KSTATUS st = SysResolveHost(&req); if (st != NTLSTATUS_SUCCESS) return st; if (resolved.Family != SYS_AF_INET) @@ -258,7 +258,7 @@ int main(void) { } uint8_t ip[4]; - uint32_t st = resolve_host(host, ip); + KSTATUS st = resolve_host(host, ip); if (st != NTLSTATUS_SUCCESS) { print("ping: resolve failed "); print_u64(st); print(" for "); print(host); print("\n"); SysExitProcess(1); diff --git a/code/user/apps/resolve/resolve.c b/code/user/apps/resolve/resolve.c index 25a4cc2..8967481 100644 --- a/code/user/apps/resolve/resolve.c +++ b/code/user/apps/resolve/resolve.c @@ -1,5 +1,6 @@ // User application entry points for resolve. +#include #include #include #include @@ -54,13 +55,13 @@ static void print_ipv6(const uint8_t ip[16]) { static const char hex[] = "0123456789abcdef"; for (int i = 0; i < 8; ++i) { uint16_t word = ((uint16_t)ip[i * 2] << 8) | ip[i * 2 + 1]; - int started = 0; + bool started = false; for (int shift = 12; shift >= 0; shift -= 4) { unsigned nibble = (word >> shift) & 0xfu; if (nibble || started || shift == 0) { char c = hex[nibble]; print_char(c); - started = 1; + started = true; } } if (i != 7) @@ -109,7 +110,7 @@ int main(void) { req.AddressOut = &addr; req.AddressSizeInOut = &addrSize; - uint32_t st = SysResolveHost(&req); + KSTATUS st = SysResolveHost(&req); if (st != NTLSTATUS_SUCCESS && family == 0) { req.Family = SYS_AF_INET; addrSize = sizeof(addr); diff --git a/code/user/apps/shell_native/shell_native.c b/code/user/apps/shell_native/shell_native.c index 904fdd8..4c85c84 100644 --- a/code/user/apps/shell_native/shell_native.c +++ b/code/user/apps/shell_native/shell_native.c @@ -32,30 +32,30 @@ static void printUInt(uint32_t n); static void printHex32(uint32_t v); // Opens file. -static uint32_t open_file(const char* path, uint32_t* handleOut) { +static KSTATUS open_file(const char* path, uint32_t* handleOut) { return NtlOpenFile(SysSvFromCstr(path), SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_QUERY, SYS_FILE_OPEN_EXISTING, SYS_FILE_FLAG_NONE, handleOut); } // Creates file. -static uint32_t create_file(const char* path, uint32_t* handleOut) { +static KSTATUS create_file(const char* path, uint32_t* handleOut) { return NtlOpenFile(SysSvFromCstr(path), SYS_FILE_ACCESS_WRITE | SYS_FILE_ACCESS_QUERY, SYS_FILE_CREATE_ALWAYS, SYS_FILE_FLAG_NONE, handleOut); } // Opens dir. -static uint32_t open_dir(const char* path, uint32_t* handleOut) { +static KSTATUS open_dir(const char* path, uint32_t* handleOut) { return NtlOpenDir(SysSvFromCstr(path), SYS_DIR_ACCESS_ENUMERATE | SYS_DIR_ACCESS_OPEN, SYS_DIR_FLAG_NONE, handleOut); } // Updates cwd. -static uint32_t set_cwd(const char* path) { +static KSTATUS set_cwd(const char* path) { return NtlSetCurrentDirectory(SysSvFromCstr(path)); } // Releases file. -static uint32_t delete_file(const char* path) { +static KSTATUS delete_file(const char* path) { return NtlDeleteFile(SysSvFromCstr(path)); } @@ -759,6 +759,13 @@ static bool readLine(char* buf, int len) { break; } + if (key == INPUT_KEY_ESCAPE) { + shellEditorReplaceLine(&editor, ""); + historyCursor = gHistoryCount; + historyDraft[0] = '\0'; + continue; + } + if (key == INPUT_KEY_BACKSPACE) { if (editor.Cursor > 0) (void)shellEditorReplaceRange(&editor, editor.Cursor - 1, editor.Cursor, ""); @@ -1007,7 +1014,7 @@ static void cmd_stats(void) { SYS_PTE_STATS pte; SYS_SURFACE_STATS surface; SYS_EXCEPTION_STATS exceptionStats; - uint32_t status; + KSTATUS status; status = SysQueryKernelStats(SYS_KERNEL_STATS_SCHEDULER, &sched, sizeof(sched), NULL); if (status != 0) { @@ -1410,7 +1417,7 @@ static void cmd_stats(void) { // Implements the cmd time operation. static void cmd_time(void) { SYS_SYSTEM_TIME t; - uint32_t status = SysGetSystemTime(&t); + KSTATUS status = SysGetSystemTime(&t); if (status != 0) { print("time: failed: "); printUInt(status); @@ -1470,7 +1477,7 @@ static void printDisplayStats(const char* title, const DISPLAY_STATS* st) { // Implements the cmd display operation. static void cmd_display(void) { SYS_DISPLAY_INFO info; - uint32_t status = SysQueryDisplayInfo(&info); + KSTATUS status = SysQueryDisplayInfo(&info); if (status != 0) { print("display: query failed: "); printUInt(status); @@ -1495,7 +1502,7 @@ typedef struct SHELL_FB_BENCH_RESULT { uint32_t Width; uint32_t Height; uint32_t Frames; - uint32_t Status; + KSTATUS Status; uint64_t ElapsedTicks; uint64_t Frequency; uint64_t CommitBytes; @@ -1548,7 +1555,7 @@ static void shellRunFramebufferBenchmark(uint32_t width, create.Flags = SYS_SURFACE_FLAG_NONE; create.HandleOut = &surface; - uint32_t status = SysCreateSurface(&create); + KSTATUS status = SysCreateSurface(&create); if (status != NTLSTATUS_SUCCESS || !surface) { result->Status = status ? status : NTLSTATUS_FAILURE; return; @@ -1619,7 +1626,7 @@ static void shellPrintFramebufferBenchmark(const char* name, // Benchmarks surface commit bookkeeping without implying physical presentation. static void cmd_surfbench(void) { SYS_DISPLAY_INFO info; - uint32_t status = SysQueryDisplayInfo(&info); + KSTATUS status = SysQueryDisplayInfo(&info); if (status != NTLSTATUS_SUCCESS) { print("surfbench: display query failed status=0x"); printHex32(status); print("\n"); return; @@ -1763,7 +1770,7 @@ static void cmd_ps(void) { SYS_PROCESS_INFO procs[MAX_PROCS]; size_t count = 0; - uint32_t status = SysGetProcessList(procs, MAX_PROCS, &count); + KSTATUS status = SysGetProcessList(procs, MAX_PROCS, &count); if (status != 0) { print("ps: failed to retrieve process list\n"); return; @@ -1826,7 +1833,7 @@ static void cmd_threads(const char* args) { SYS_THREAD_INFO threads[MAX_THREADS]; size_t count = 0; - uint32_t status = SysGetThreadList(pid, threads, MAX_THREADS, &count); + KSTATUS status = SysGetThreadList(pid, threads, MAX_THREADS, &count); if (status != 0) { print("threads: query failed status="); printUInt(status); @@ -1890,7 +1897,7 @@ static void cmd_kill(const char* args) { if (!parseUInt(args, &pid)) { print("kill: usage: kill \n"); return; } uint32_t hProc = 0; - uint32_t status = SysOpenProcess(pid, SYS_PROCESS_RIGHT_TERMINATE, &hProc); + KSTATUS status = SysOpenProcess(pid, SYS_PROCESS_RIGHT_TERMINATE, &hProc); if (status == 0) { status = SysKillProcess(hProc, (int32_t)NTLSTATUS_PROCESS_KILLED); SysCloseHandle(hProc); @@ -1957,7 +1964,7 @@ static int shellCompareDirEntries(const SYS_DIR_ENTRY* a, const SYS_DIR_ENTRY* b } // Allocates a directory-entry array from VM so large listings do not consume stack. -static uint32_t shellAllocDirEntries(uint32_t capacity, SYS_DIR_ENTRY** entriesOut) { +static KSTATUS shellAllocDirEntries(uint32_t capacity, SYS_DIR_ENTRY** entriesOut) { void* base = 0; size_t bytes = (size_t)capacity * sizeof(SYS_DIR_ENTRY); size_t pages = (bytes + 4095u) / 4096u; @@ -1971,7 +1978,7 @@ static uint32_t shellAllocDirEntries(uint32_t capacity, SYS_DIR_ENTRY** entriesO req.PageCount = pages; req.BaseOut = &base; - uint32_t status = SysAllocVm(&req); + KSTATUS status = SysAllocVm(&req); if (status != 0 || !base) { *entriesOut = 0; return status ? status : 1; @@ -1982,11 +1989,11 @@ static uint32_t shellAllocDirEntries(uint32_t capacity, SYS_DIR_ENTRY** entriesO } // Grows a directory-entry array while preserving already-read entries. -static uint32_t shellGrowDirEntries(SYS_DIR_ENTRY** entries, uint32_t* capacity, uint32_t count) { +static KSTATUS shellGrowDirEntries(SYS_DIR_ENTRY** entries, uint32_t* capacity, uint32_t count) { uint32_t oldCapacity = *capacity; uint32_t newCapacity = oldCapacity ? oldCapacity * 2u : SHELL_DIR_INITIAL_CAPACITY; SYS_DIR_ENTRY* next = 0; - uint32_t status = shellAllocDirEntries(newCapacity, &next); + KSTATUS status = shellAllocDirEntries(newCapacity, &next); if (status != 0) return status; @@ -2080,7 +2087,7 @@ static void cmd_dir(const char* path) { const char* target = (path && *path != '\0') ? path : "."; if (shellTryResourceDir(target)) return; - uint32_t st = open_dir(target, &hDir); + KSTATUS st = open_dir(target, &hDir); if (st != 0) { print("dir: failed to open directory, status=0x"); printHex32(st); @@ -2163,11 +2170,11 @@ static bool shellIsKeyDown(INPUT_KEY key) { } // Waits for a foreground process while polling Ctrl-C without consuming child input. -static uint32_t waitForegroundProcess(uint32_t hProc) { +static KSTATUS waitForegroundProcess(uint32_t hProc) { bool sentBreak = false; while (true) { - uint32_t status = SysWaitSingle(hProc, + KSTATUS status = SysWaitSingle(hProc, SYS_WAIT_FLAG_NONE, SHELL_FOREGROUND_POLL_MS); if (status == NTLSTATUS_SUCCESS) @@ -2179,7 +2186,7 @@ static uint32_t waitForegroundProcess(uint32_t hProc) { if (!sentBreak && (modifiers & INPUT_MOD_CTRL) && shellIsKeyDown(INPUT_KEY_C)) { print("^C\n"); - uint32_t killStatus = SysKillProcess( + KSTATUS killStatus = SysKillProcess( hProc, (int32_t)NTLSTATUS_PROCESS_KILLED); if (killStatus != NTLSTATUS_SUCCESS) { @@ -2194,65 +2201,65 @@ static uint32_t waitForegroundProcess(uint32_t hProc) { } // Run one command. -static int runCommand(const char* cmd) { - if (!cmd || *cmd == '\0') return 0; - if (strcmp(cmd, "exit") == 0) return 1; - if (strcmp(cmd, "help") == 0) { cmd_help(); return 0; } - if (strcmp(cmd, "clear") == 0) { (void)NtlConsoleClear(SYS_HANDLE_INVALID, 0x012456); return 0; } +static bool runCommand(const char* cmd) { + if (!cmd || *cmd == '\0') return false; + if (strcmp(cmd, "exit") == 0) return true; + if (strcmp(cmd, "help") == 0) { cmd_help(); return false; } + if (strcmp(cmd, "clear") == 0) { (void)NtlConsoleClear(SYS_HANDLE_INVALID, 0x012456); return false; } if (strncmp(cmd, "crash", 5) == 0) { // Check the sub-option trailing "crash " (offset 6). if (strcmp(cmd + 6, "void") == 0) { // Jumping into the void. void (*invalid_func)(void) = NULL; invalid_func(); - return 0; + return false; } else if (strcmp(cmd + 6, "noncanonical") == 0) { // Non-canonical pointer dereference. volatile int* ptr = (volatile int*)0x4242424242424242ULL; *ptr = 1337; - return 0; + return false; } else if (strcmp(cmd + 6, "ud2") == 0) { __ud2(); - return 0; + return false; } else if (strcmp(cmd + 6, "nx") == 0) { // NX / DEP Violation. unsigned char code[] = { 0xC3 }; void (*execute_stack)(void) = (void (*)(void))code; execute_stack(); - return 0; + return false; } else if (strcmp(cmd + 6, "divzero") == 0) { // Division by zero. volatile int zero = 0; volatile int result = 1 / zero; (void)result; - return 0; + return false; } else { // Default fallback if just "crash" or an unknown sub-option is passed. *(volatile int*)0 = 42; - return 0; + return false; } } - if (strcmp(cmd, "mem") == 0) { cmd_mem(); return 0; } - if (strcmp(cmd, "time") == 0 || strcmp(cmd, "date") == 0) { cmd_time(); return 0; } - if (strcmp(cmd, "display") == 0 || strcmp(cmd, "gpu") == 0) { cmd_display(); return 0; } - if (strcmp(cmd, "surfbench") == 0) { cmd_surfbench(); return 0; } - if (strcmp(cmd, "volume") == 0) { cmd_volume(""); return 0; } - if (strncmp(cmd, "volume ", 7) == 0) { cmd_volume(cmd + 7); return 0; } - if (strcmp(cmd, "stats") == 0 || strcmp(cmd, "telemetry") == 0) { cmd_stats(); return 0; } - if (strcmp(cmd, "ps") == 0) { cmd_ps(); return 0; } - if (strncmp(cmd, "threads ", 8) == 0) { cmd_threads(cmd + 8); return 0; } - if (strncmp(cmd, "kill ", 5) == 0) { cmd_kill(cmd + 5); return 0; } - if (strcmp(cmd, "dir") == 0) { cmd_dir("."); return 0; } - if (strncmp(cmd, "dir ", 4) == 0) { cmd_dir(cmd + 4); return 0; } - if (strncmp(cmd, "cd ", 3) == 0) { if (set_cwd(cmd + 3) != 0) print("cd: failed\n"); return 0; } - if (strncmp(cmd, "del ", 4) == 0) { delete_file(cmd + 4); return 0; } - if (strncmp(cmd, "type ", 5) == 0) { cmd_type(cmd + 5); return 0; } - if (strncmp(cmd, "write ", 6) == 0) { cmd_write(cmd + 6); return 0; } + if (strcmp(cmd, "mem") == 0) { cmd_mem(); return false; } + if (strcmp(cmd, "time") == 0 || strcmp(cmd, "date") == 0) { cmd_time(); return false; } + if (strcmp(cmd, "display") == 0 || strcmp(cmd, "gpu") == 0) { cmd_display(); return false; } + if (strcmp(cmd, "surfbench") == 0) { cmd_surfbench(); return false; } + if (strcmp(cmd, "volume") == 0) { cmd_volume(""); return false; } + if (strncmp(cmd, "volume ", 7) == 0) { cmd_volume(cmd + 7); return false; } + if (strcmp(cmd, "stats") == 0 || strcmp(cmd, "telemetry") == 0) { cmd_stats(); return false; } + if (strcmp(cmd, "ps") == 0) { cmd_ps(); return false; } + if (strncmp(cmd, "threads ", 8) == 0) { cmd_threads(cmd + 8); return false; } + if (strncmp(cmd, "kill ", 5) == 0) { cmd_kill(cmd + 5); return false; } + if (strcmp(cmd, "dir") == 0) { cmd_dir("."); return false; } + if (strncmp(cmd, "dir ", 4) == 0) { cmd_dir(cmd + 4); return false; } + if (strncmp(cmd, "cd ", 3) == 0) { if (set_cwd(cmd + 3) != 0) print("cd: failed\n"); return false; } + if (strncmp(cmd, "del ", 4) == 0) { delete_file(cmd + 4); return false; } + if (strncmp(cmd, "type ", 5) == 0) { cmd_type(cmd + 5); return false; } + if (strncmp(cmd, "write ", 6) == 0) { cmd_write(cmd + 6); return false; } // Make a local copy of the command line so we can mutate it safely if needed char modifiedCmd[MAX_CMD]; @@ -2275,7 +2282,7 @@ static int runCommand(const char* cmd) { } char program[MAX_CMD]; - if (!extractProgramPath(modifiedCmd, program, sizeof(program))) return 0; + if (!extractProgramPath(modifiedCmd, program, sizeof(program))) return false; uint32_t hProc; const char* launchFailureStage = NULL; @@ -2289,7 +2296,7 @@ static int runCommand(const char* cmd) { SysCloseHandle(hProc); } else { // Foreground mode: Wait for completion and flush standard keyboard stream. - uint32_t waitStatus = waitForegroundProcess(hProc); + KSTATUS waitStatus = waitForegroundProcess(hProc); if (waitStatus != NTLSTATUS_SUCCESS) { print("shell: wait failed: "); printUInt(waitStatus); @@ -2322,7 +2329,7 @@ static int runCommand(const char* cmd) { printStatus(launchStatus); print("\n"); } - return 0; + return false; } // Runs the requested operation. diff --git a/code/user/apps/shell_posix/shell_posix.c b/code/user/apps/shell_posix/shell_posix.c index a9da3fb..efbb10e 100644 --- a/code/user/apps/shell_posix/shell_posix.c +++ b/code/user/apps/shell_posix/shell_posix.c @@ -25,6 +25,10 @@ static void readLine(char* buf, int len) { while (true) { char c; if (read(hIn, &c, 1) <= 0) break; if (c == '\r' || c == '\n') { print("\n"); break; } + if (c == '\x1B') { + while (pos > 0) { --pos; print("\b \b"); } + continue; + } if ((c == '\b' || c == 127) && pos > 0) { pos--; print("\b \b"); continue; } if (c < 0x20 || c == 127) continue; if (pos < len - 1) { buf[pos++] = c; char echo[2] = {c, '\0'}; print(echo); } @@ -190,18 +194,18 @@ static void cmd_type(const char* filename) { } // Runs command. -static int runCommand(const char* cmd) { - if (!cmd || *cmd == '\0') return 0; - if (strcmp(cmd, "exit") == 0) return 1; - if (strcmp(cmd, "help") == 0) { cmd_help(); return 0; } - if (strcmp(cmd, "clear") == 0) { clearscreen(); return 0; } - if (strcmp(cmd, "crash") == 0) { *(volatile int*)0 = 42; return 0; } - if (strcmp(cmd, "dir") == 0) { cmd_dir("."); return 0; } - if (strncmp(cmd, "dir ", 4) == 0) { cmd_dir(cmd + 4); return 0; } - if (strncmp(cmd, "cd ", 3) == 0) { if (chdir(cmd + 3) != 0) print("cd: failed\n"); return 0; } - if (strncmp(cmd, "del ", 4) == 0) { unlink(cmd + 4); return 0; } - if (strncmp(cmd, "type ", 5) == 0) { cmd_type(cmd + 5); return 0; } - if (strncmp(cmd, "write ", 6) == 0) { cmd_write(cmd + 6); return 0; } +static bool runCommand(const char* cmd) { + if (!cmd || *cmd == '\0') return false; + if (strcmp(cmd, "exit") == 0) return true; + if (strcmp(cmd, "help") == 0) { cmd_help(); return false; } + if (strcmp(cmd, "clear") == 0) { clearscreen(); return false; } + if (strcmp(cmd, "crash") == 0) { *(volatile int*)0 = 42; return false; } + if (strcmp(cmd, "dir") == 0) { cmd_dir("."); return false; } + if (strncmp(cmd, "dir ", 4) == 0) { cmd_dir(cmd + 4); return false; } + if (strncmp(cmd, "cd ", 3) == 0) { if (chdir(cmd + 3) != 0) print("cd: failed\n"); return false; } + if (strncmp(cmd, "del ", 4) == 0) { unlink(cmd + 4); return false; } + if (strncmp(cmd, "type ", 5) == 0) { cmd_type(cmd + 5); return false; } + if (strncmp(cmd, "write ", 6) == 0) { cmd_write(cmd + 6); return false; } char cmdBuf[MAX_CMD]; strncpy(cmdBuf, cmd, MAX_CMD - 1); @@ -209,14 +213,14 @@ static int runCommand(const char* cmd) { char* argv[32]; int argc = splitCommandLine(cmdBuf, argv, 32); - if (argc == 0) return 0; + if (argc == 0) return false; int pid; if (posix_spawn(&pid, argv[0], NULL, NULL, argv, NULL) == 0) { int status; waitpid(pid, &status, 0); clearkeyboard(); } else { print("shell: launch failed\n"); } - return 0; + return false; } // Runs the requested operation. diff --git a/code/user/apps/shell_win32/shell_win32.c b/code/user/apps/shell_win32/shell_win32.c index 12dee7e..bef8876 100644 --- a/code/user/apps/shell_win32/shell_win32.c +++ b/code/user/apps/shell_win32/shell_win32.c @@ -42,6 +42,10 @@ static void readLine(char* buf, int len) { ReadConsoleA(hIn, &c, 1, &nRead, 0); if (!nRead) continue; if (c == '\r' || c == '\n') { print("\n"); break; } + if (c == '\x1B') { + while (pos > 0) { --pos; print("\b \b"); } + continue; + } if ((c == '\b' || c == 127) && pos > 0) { pos--; print("\b \b"); continue; } if (c < 0x20 || c == 127) continue; if (pos < len - 1) { @@ -205,18 +209,18 @@ static void cmd_type(const char* filename) { } // Runs command. -static int runCommand(const char* cmd) { - if (!cmd || *cmd == '\0') return 0; - if (strcmp(cmd, "exit") == 0) return 1; - if (strcmp(cmd, "help") == 0) { cmd_help(); return 0; } - if (strcmp(cmd, "clear") == 0) { ClearConsoleScreenBuffer(hOut); return 0; } - if (strcmp(cmd, "crash") == 0) { *(int*)0 = 42; return 0; } - if (strcmp(cmd, "dir") == 0) { cmd_dir("."); return 0; } - if (strncmp(cmd, "dir ", 4) == 0) { cmd_dir(cmd + 4); return 0; } - if (strncmp(cmd, "cd ", 3) == 0) { if (!SetCurrentDirectoryA(cmd + 3)) print("cd: failed\n"); return 0; } - if (strncmp(cmd, "del ", 4) == 0) { DeleteFileA(cmd + 4); return 0; } - if (strncmp(cmd, "type ", 5) == 0) { cmd_type(cmd + 5); return 0; } - if (strncmp(cmd, "write ", 6) == 0) { cmd_write(cmd + 6); return 0; } +static bool runCommand(const char* cmd) { + if (!cmd || *cmd == '\0') return false; + if (strcmp(cmd, "exit") == 0) return true; + if (strcmp(cmd, "help") == 0) { cmd_help(); return false; } + if (strcmp(cmd, "clear") == 0) { ClearConsoleScreenBuffer(hOut); return false; } + if (strcmp(cmd, "crash") == 0) { *(int*)0 = 42; return false; } + if (strcmp(cmd, "dir") == 0) { cmd_dir("."); return false; } + if (strncmp(cmd, "dir ", 4) == 0) { cmd_dir(cmd + 4); return false; } + if (strncmp(cmd, "cd ", 3) == 0) { if (!SetCurrentDirectoryA(cmd + 3)) print("cd: failed\n"); return false; } + if (strncmp(cmd, "del ", 4) == 0) { DeleteFileA(cmd + 4); return false; } + if (strncmp(cmd, "type ", 5) == 0) { cmd_type(cmd + 5); return false; } + if (strncmp(cmd, "write ", 6) == 0) { cmd_write(cmd + 6); return false; } PROCESS_INFORMATION pi; char cmdBuf[MAX_CMD]; @@ -232,7 +236,7 @@ static int runCommand(const char* cmd) { } else { print("shell: launch failed\n"); } - return 0; + return false; } // Runs the requested operation. diff --git a/code/user/apps/tcpserve/tcpserve.c b/code/user/apps/tcpserve/tcpserve.c index ef9b311..304061a 100644 --- a/code/user/apps/tcpserve/tcpserve.c +++ b/code/user/apps/tcpserve/tcpserve.c @@ -40,13 +40,13 @@ static void print_ipv6(const uint8_t ip[16]) { static const char hex[] = "0123456789abcdef"; for (int i = 0; i < 8; ++i) { uint16_t word = ((uint16_t)ip[i * 2] << 8) | ip[i * 2 + 1]; - int started = 0; + bool started = false; for (int shift = 12; shift >= 0; shift -= 4) { unsigned nibble = (word >> shift) & 0xfu; if (nibble || started || shift == 0) { char c = hex[nibble]; print_char(c); - started = 1; + started = true; } } if (i != 7) print(":"); @@ -130,7 +130,7 @@ int main(void) { } uint32_t listener = 0; - uint32_t st = SysCreateSocket(family, SYS_SOCK_STREAM, SYS_IPPROTO_TCP, &listener); + KSTATUS st = SysCreateSocket(family, SYS_SOCK_STREAM, SYS_IPPROTO_TCP, &listener); if (st != NTLSTATUS_SUCCESS) { print("tcpserve: SysCreateSocket failed "); print_u64(st); print("\n"); SysExitProcess(1); diff --git a/code/user/apps/udp/udp.c b/code/user/apps/udp/udp.c index c6f8b58..3ae893f 100644 --- a/code/user/apps/udp/udp.c +++ b/code/user/apps/udp/udp.c @@ -40,13 +40,13 @@ static void print_ipv6(const uint8_t ip[16]) { static const char hex[] = "0123456789abcdef"; for (int i = 0; i < 8; ++i) { uint16_t word = ((uint16_t)ip[i * 2] << 8) | ip[i * 2 + 1]; - int started = 0; + bool started = false; for (int shift = 12; shift >= 0; shift -= 4) { unsigned nibble = (word >> shift) & 0xfu; if (nibble || started || shift == 0) { char c = hex[nibble]; print_char(c); - started = 1; + started = true; } } if (i != 7) print(":"); @@ -141,7 +141,7 @@ static void usage(void) { } // Resolves an IPv4 or IPv6 destination. -static uint32_t resolve_address(const char* text, SYS_SOCKET_ADDRESS* out) { +static KSTATUS resolve_address(const char* text, SYS_SOCKET_ADDRESS* out) { uint8_t ip[4]; if (parse_ipv4(text, ip)) { memset(out, 0, sizeof(*out)); @@ -161,7 +161,7 @@ static uint32_t resolve_address(const char* text, SYS_SOCKET_ADDRESS* out) { req.AddressOut = out; req.AddressSizeInOut = &addressSize; - uint32_t status = SysResolveHost(&req); + KSTATUS status = SysResolveHost(&req); if (status == NTLSTATUS_SUCCESS) return NTLSTATUS_SUCCESS; @@ -179,7 +179,7 @@ static int run_send(const char* cmd) { cmd = next_token(cmd, tok, sizeof(tok)); if (!tok[0]) { usage(); return 1; } - uint32_t status = resolve_address(tok, &remote); + KSTATUS status = resolve_address(tok, &remote); if (status != NTLSTATUS_SUCCESS) { print("udp: resolve failed "); print_u64(status); print("\n"); return 1; } cmd = next_token(cmd, tok, sizeof(tok)); if (!tok[0] || !parse_port(tok, &port)) { usage(); return 1; } @@ -197,7 +197,7 @@ static int run_send(const char* cmd) { } uint32_t sock = 0; - uint32_t st = SysCreateSocket(remote.Family, SYS_SOCK_DGRAM, SYS_IPPROTO_UDP, &sock); + KSTATUS st = SysCreateSocket(remote.Family, SYS_SOCK_DGRAM, SYS_IPPROTO_UDP, &sock); if (st != NTLSTATUS_SUCCESS) { print("udp: SysCreateSocket failed "); print_u64(st); print("\n"); return 1; @@ -243,7 +243,7 @@ static int run_listen(const char* cmd) { if (!tok[0] || !parse_port(tok, &port)) { usage(); return 1; } uint32_t sock = 0; - uint32_t st = SysCreateSocket(family, SYS_SOCK_DGRAM, SYS_IPPROTO_UDP, &sock); + KSTATUS st = SysCreateSocket(family, SYS_SOCK_DGRAM, SYS_IPPROTO_UDP, &sock); if (st != NTLSTATUS_SUCCESS) { print("udp: SysCreateSocket failed "); print_u64(st); print("\n"); return 1; diff --git a/code/user/apps/video/video.c b/code/user/apps/video/video.c index bb3039a..2275a73 100644 --- a/code/user/apps/video/video.c +++ b/code/user/apps/video/video.c @@ -4,6 +4,7 @@ // include here; Win32 compatibility belongs in the Win32 personality // layer, not in native apps. +#include #include #include #include @@ -45,7 +46,7 @@ static void print_u64(uint64_t v) { } } -static void fail_status(const char* what, uint32_t status) { +static void fail_status(const char* what, KSTATUS status) { print("video: "); print(what); print(" failed status="); @@ -65,7 +66,7 @@ static SYS_STRING_VIEW command_line_view(void) { return peb->CommandLine; } -static int sv_is_space(char c) { +static bool sv_is_space(char c) { return c == ' ' || c == '\t'; } @@ -116,21 +117,21 @@ static SYS_STRING_VIEW command_line_first_argument(SYS_STRING_VIEW cmdline) { return SysSvFromParts(rest.Data + begin, end - begin); } -static int sv_equals_cstr(SYS_STRING_VIEW s, const char* cstr) { +static bool sv_equals_cstr(SYS_STRING_VIEW s, const char* cstr) { size_t len = 0; if (!s.Data || !cstr) - return 0; + return false; while (cstr[len]) len++; if (s.Length != len) - return 0; + return false; return memcmp(s.Data, cstr, len) == 0; } -static int video_argument_is_help(SYS_STRING_VIEW arg) { +static bool video_argument_is_help(SYS_STRING_VIEW arg) { return sv_equals_cstr(arg, "-h") || sv_equals_cstr(arg, "--help") || sv_equals_cstr(arg, "/?"); @@ -158,7 +159,7 @@ static const char* path_to_cstr(SYS_STRING_VIEW path, char* out, size_t outSize) return out; } -static uint32_t open_file_view(SYS_STRING_VIEW path, uint32_t* handleOut) { +static KSTATUS open_file_view(SYS_STRING_VIEW path, uint32_t* handleOut) { return NtlOpenFile(path, SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_QUERY, SYS_FILE_OPEN_EXISTING, SYS_FILE_FLAG_NONE, handleOut); } @@ -179,7 +180,7 @@ int main(void) { } uint32_t file = 0; - uint32_t status = open_file_view(pathView, &file); + KSTATUS status = open_file_view(pathView, &file); if (status != NTLSTATUS_SUCCESS) { char pathBuf[260]; print("video: failed to open input file: "); diff --git a/code/user/personalities/posix/posix_dir.c b/code/user/personalities/posix/posix_dir.c index 9ffba02..9a5a85b 100644 --- a/code/user/personalities/posix/posix_dir.c +++ b/code/user/personalities/posix/posix_dir.c @@ -114,7 +114,7 @@ __declspec(dllexport) char* realpath(const char* path, char* resolved_path) { return PosixCopyResolvedPath(path, resolved_path); size_t required = 0; - uint32_t status = NtlRealPath(SysSvFromCstr(translated), (char*)0, 0, &required); + KSTATUS status = NtlRealPath(SysSvFromCstr(translated), (char*)0, 0, &required); if (status != NTLSTATUS_SUCCESS) { errno = PosixStatusToErrno(status); return (char*)0; } if (required == 0 || required > SYS_PATH_MAX + 1u) { errno = ERANGE; return (char*)0; } @@ -132,7 +132,7 @@ __declspec(dllexport) DIR* opendir(const char* path) { char native[512]; PosixTranslatePath(path, native, sizeof(native)); uint32_t kh; - uint32_t s = NtlOpenDir(SysSvFromCstr(native), + KSTATUS s = NtlOpenDir(SysSvFromCstr(native), SYS_DIR_ACCESS_ENUMERATE | SYS_DIR_ACCESS_OPEN, SYS_DIR_FLAG_NONE, &kh); if (s != 0) { errno = ENOENT; return (DIR*)0; } @@ -149,7 +149,7 @@ __declspec(dllexport) DIR* opendir(const char* path) { __declspec(dllexport) struct dirent* readdir(DIR* d) { if (!d) { errno = EBADF; return (struct dirent*)0; } KDIR_ENTRY ke; - uint32_t s = SysReadDir(d->KHandle, &ke); + KSTATUS s = SysReadDir(d->KHandle, &ke); if (s != 0) return (struct dirent*)0; // end of directory or error size_t len = strlen(ke.Name); @@ -214,7 +214,7 @@ __declspec(dllexport) int stat(const char* path, struct stat* out) { return PosixFillSyntheticStat(S_IFCHR, out); SYS_FILE_STAT nativeStat; - uint32_t s = NtlStatPath(SysSvFromCstr(p), &nativeStat); + KSTATUS s = NtlStatPath(SysSvFromCstr(p), &nativeStat); if (s != 0) { errno = PosixStatusToErrno(s); return -1; } return PosixFillStat(&nativeStat, out); } @@ -243,7 +243,7 @@ __declspec(dllexport) int fstat(int fd, struct stat* out) { break; case PosixFdFile: { SYS_FILE_STAT nativeStat; - uint32_t status = SysGetFileStat(description->KHandle, &nativeStat); + KSTATUS status = SysGetFileStat(description->KHandle, &nativeStat); if (status != NTLSTATUS_SUCCESS) errno = PosixStatusToErrno(status); else diff --git a/code/user/personalities/posix/posix_dlfcn.c b/code/user/personalities/posix/posix_dlfcn.c index 48f92c8..55e1f9e 100644 --- a/code/user/personalities/posix/posix_dlfcn.c +++ b/code/user/personalities/posix/posix_dlfcn.c @@ -20,7 +20,7 @@ static void DlSetError(const char* message) { } // Converts one native loader status to errno and a dlerror message. -static void DlSetStatusError(const char* operation, uint32_t status) { +static void DlSetStatusError(const char* operation, KSTATUS status) { (void)status; errno = PosixStatusToErrno(status); DlSetError(operation); @@ -38,7 +38,7 @@ __declspec(dllexport) void* dlopen(const char* filename, int flags) { } char native[512]; - uint32_t status = PosixProcessPath(filename, native, sizeof(native)); + KSTATUS status = PosixProcessPath(filename, native, sizeof(native)); if (status != NTLSTATUS_SUCCESS) { errno = EINVAL; DlSetError("dlopen path is not a loadable module"); @@ -94,7 +94,7 @@ __declspec(dllexport) void* dlsym(void* handle, const char* symbol) { } uintptr_t address = 0; - uint32_t status = NtlGetModuleExport((NTL_MODULE_HANDLE)(uintptr_t)handle, + KSTATUS status = NtlGetModuleExport((NTL_MODULE_HANDLE)(uintptr_t)handle, SysSvFromCstr(symbol), &address); if (status != NTLSTATUS_SUCCESS || address == 0) { DlSetStatusError("dlsym failed", status); @@ -113,7 +113,7 @@ __declspec(dllexport) int dlclose(void* handle) { return -1; } - uint32_t status = NtlUnloadModule((NTL_MODULE_HANDLE)(uintptr_t)handle); + KSTATUS status = NtlUnloadModule((NTL_MODULE_HANDLE)(uintptr_t)handle); if (status != NTLSTATUS_SUCCESS) { DlSetStatusError("dlclose failed", status); return -1; diff --git a/code/user/personalities/posix/posix_fd.c b/code/user/personalities/posix/posix_fd.c index 37aad9f..c346905 100644 --- a/code/user/personalities/posix/posix_fd.c +++ b/code/user/personalities/posix/posix_fd.c @@ -1,5 +1,7 @@ // POSIX descriptor table and shared open-description implementation. +#include +#include #include #include #include @@ -9,7 +11,7 @@ #include "posix_fd.h" // Maps one native status to the POSIX personality errno space. -int PosixStatusToErrno(uint32_t s) { +int PosixStatusToErrno(KSTATUS s) { switch (s) { case NTLSTATUS_SUCCESS: return 0; case NTLSTATUS_TIMEOUT: return ETIMEDOUT; @@ -76,7 +78,7 @@ static int PosixFdLock(POSIX_LOGICAL_PROCESS_STATE* process) { errno = EIO; return -1; } - uint32_t status = SysWait(process->FdLock, SYS_WAIT_FLAG_NONE); + KSTATUS status = SysWait(process->FdLock, SYS_WAIT_FLAG_NONE); if (status != NTLSTATUS_SUCCESS) { errno = PosixStatusToErrno(status); return -1; @@ -86,13 +88,13 @@ static int PosixFdLock(POSIX_LOGICAL_PROCESS_STATE* process) { // Unlocks one logical process descriptor table. static void PosixFdUnlock(POSIX_LOGICAL_PROCESS_STATE* process) { - uint32_t status = SysReleaseMutex(process->FdLock); + KSTATUS status = SysReleaseMutex(process->FdLock); if (status != NTLSTATUS_SUCCESS) errno = PosixStatusToErrno(status); } // Returns whether one open-description type requires a retained native capability. -static int PosixOpenDescriptionNeedsHandle(POSIX_FD_TYPE type) { +static bool PosixOpenDescriptionNeedsHandle(POSIX_FD_TYPE type) { return type == PosixFdFile || type == PosixFdSocket || type == PosixFdPipeRead || type == PosixFdPipeWrite; } @@ -262,15 +264,14 @@ static POSIX_OPEN_DESCRIPTION* PosixOpenDescriptionCreate(POSIX_FD_TYPE type, // Adds one reference to an open description. static void PosixFdReference(POSIX_OPEN_DESCRIPTION* description) { if (description) - _InterlockedIncrement(&description->RefCount); + NtlAtomicIncrement32(&description->RefCount); } // Returns one atomic snapshot of server-owned shared open-description status flags. int PosixFdGetStatusFlags(const POSIX_OPEN_DESCRIPTION* description) { if (!description || !description->Shared) return 0; - return (int)_InterlockedCompareExchange( - (volatile long*)&description->Shared->StatusFlags, 0, 0); + return (int)NtlAtomicLoadU32(&description->Shared->StatusFlags); } // Applies mutable status flags through posixsrv so native backing and shared state stay serialized. @@ -306,7 +307,7 @@ int PosixFdSetStatusFlags(POSIX_OPEN_DESCRIPTION* description, int statusFlags) // Releases one stable local wrapper without changing server holder ownership. static void PosixFdReleaseLocal(POSIX_OPEN_DESCRIPTION* description) { if (!description) return; - if (_InterlockedDecrement(&description->RefCount) != 0) return; + if (NtlAtomicDecrement32(&description->RefCount) != 0) return; if (PosixOpenDescriptionNeedsHandle(description->Type) && description->KHandle != SYS_HANDLE_INVALID) (void)SysCloseHandle(description->KHandle); @@ -316,7 +317,7 @@ static void PosixFdReleaseLocal(POSIX_OPEN_DESCRIPTION* description) { // Releases one stable local wrapper and its server holder at the final reference. void PosixFdRelease(POSIX_OPEN_DESCRIPTION* description) { if (!description) return; - if (_InterlockedDecrement(&description->RefCount) != 0) return; + if (NtlAtomicDecrement32(&description->RefCount) != 0) return; if (PosixOpenDescriptionNeedsHandle(description->Type) && description->KHandle != SYS_HANDLE_INVALID) (void)SysCloseHandle(description->KHandle); @@ -375,8 +376,8 @@ static POSIX_OPEN_DESCRIPTION* PosixOpenDescriptionImport(POSIX_FD_TYPE type, uint32_t khandle, uint64_t descriptionId, NTL_POSIX_SHARED_OPEN_DESCRIPTION* shared) { - int needsHandle = PosixOpenDescriptionNeedsHandle(type); - int isPipe = type == PosixFdPipeRead || type == PosixFdPipeWrite; + bool needsHandle = PosixOpenDescriptionNeedsHandle(type); + bool isPipe = type == PosixFdPipeRead || type == PosixFdPipeWrite; if (descriptionId == 0 || !shared || (needsHandle && khandle == SYS_HANDLE_INVALID) || (!needsHandle && khandle != SYS_HANDLE_INVALID) || @@ -464,7 +465,7 @@ int PosixFdClose(int fd) { } // Installs one fixed standard descriptor during process initialization. -static uint32_t PosixFdInstallStandard(POSIX_LOGICAL_PROCESS_STATE* process, +static KSTATUS PosixFdInstallStandard(POSIX_LOGICAL_PROCESS_STATE* process, int fd, POSIX_FD_TYPE type, int statusFlags) { @@ -481,7 +482,7 @@ static uint32_t PosixFdInstallStandard(POSIX_LOGICAL_PROCESS_STATE* process, } // Reconstructs one exact descriptor table from a server-owned inheritance snapshot. -static uint32_t PosixFdImportInheritance(POSIX_LOGICAL_PROCESS_STATE* process, uint64_t token) { +static KSTATUS PosixFdImportInheritance(POSIX_LOGICAL_PROCESS_STATE* process, uint64_t token) { if (!process || token == 0) return NTLSTATUS_INVALID_PARAMETER; NTL_POSIX_FD_MANIFEST_ENTRY manifest[NTL_POSIX_FD_INHERIT_MAX]; @@ -494,7 +495,7 @@ static uint32_t PosixFdImportInheritance(POSIX_LOGICAL_PROCESS_STATE* process, u uint32_t descriptionCount = 0; uint32_t descriptionIndex = 0; uint32_t descriptionTotal = UINT32_MAX; - uint32_t result = NTLSTATUS_SUCCESS; + KSTATUS result = NTLSTATUS_SUCCESS; for (;;) { NTL_POSIX_REQUEST request; @@ -560,7 +561,7 @@ static uint32_t PosixFdImportInheritance(POSIX_LOGICAL_PROCESS_STATE* process, u } if (!KSUCCESS(result)) break; POSIX_FD_TYPE type = (POSIX_FD_TYPE)entry->OpenDescriptionType; - int needsHandle = PosixOpenDescriptionNeedsHandle(type); + bool needsHandle = PosixOpenDescriptionNeedsHandle(type); SYS_HANDLE handle = SYS_HANDLE_INVALID; if (needsHandle) { if (entry->HandleIndex >= handleCount || handleUsed[entry->HandleIndex]) { @@ -634,12 +635,12 @@ static uint32_t PosixFdImportInheritance(POSIX_LOGICAL_PROCESS_STATE* process, u } // Initializes one logical process descriptor table and standard descriptors. -uint32_t PosixFdInitializeLogicalProcess(POSIX_LOGICAL_PROCESS_STATE* process, uint64_t inheritanceToken) { +KSTATUS PosixFdInitializeLogicalProcess(POSIX_LOGICAL_PROCESS_STATE* process, uint64_t inheritanceToken) { if (!process) return NTLSTATUS_INVALID_PARAMETER; uint32_t lock = SYS_HANDLE_INVALID; - uint32_t status = SysCreateMutex(&lock); + KSTATUS status = SysCreateMutex(&lock); if (status != NTLSTATUS_SUCCESS) return status; process->FdLock = lock; @@ -757,7 +758,7 @@ static int PosixFlockRange(POSIX_OPEN_DESCRIPTION* description, } int64_t base = 0; - uint32_t status = NTLSTATUS_SUCCESS; + KSTATUS status = NTLSTATUS_SUCCESS; if (lock->l_whence == SEEK_CUR) { uint64_t current = 0; status = SysGetFilePos(description->KHandle, ¤t); @@ -828,7 +829,7 @@ static int PosixFdControlLock(POSIX_OPEN_DESCRIPTION* description, int command, request.Offset = offset; request.Length = length; request.InfoOut = &info; - uint32_t status = SysQueryFileLock(&request); + KSTATUS status = SysQueryFileLock(&request); if (status != NTLSTATUS_SUCCESS) { errno = PosixStatusToErrno(status); return -1; @@ -854,7 +855,7 @@ static int PosixFdControlLock(POSIX_OPEN_DESCRIPTION* description, int command, request.Flags = SYS_FILE_LOCK_FLAG_NONE; request.Offset = offset; request.Length = length; - uint32_t status = SysUnlockFile(&request); + KSTATUS status = SysUnlockFile(&request); if (status == NTLSTATUS_SUCCESS || status == NTLSTATUS_NOT_FOUND) return 0; errno = PosixStatusToErrno(status); @@ -889,7 +890,7 @@ static int PosixFdControlLock(POSIX_OPEN_DESCRIPTION* description, int command, request.Offset = offset; request.Length = length; request.TimeoutMilliseconds = command == F_SETLKW ? SYS_WAIT_TIMEOUT_INFINITE : 0; - uint32_t status = SysLockFile(&request); + KSTATUS status = SysLockFile(&request); if (status != NTLSTATUS_SUCCESS) { errno = PosixStatusToErrno(status); return -1; diff --git a/code/user/personalities/posix/posix_fd.h b/code/user/personalities/posix/posix_fd.h index 092b36a..b7b58ff 100644 --- a/code/user/personalities/posix/posix_fd.h +++ b/code/user/personalities/posix/posix_fd.h @@ -23,7 +23,7 @@ typedef enum POSIX_FD_TYPE { // One refcounted open description shared by one or more descriptors. typedef struct POSIX_OPEN_DESCRIPTION { - volatile long RefCount; + volatile int32_t RefCount; POSIX_FD_TYPE Type; uint32_t KHandle; uint64_t DescriptionId; @@ -39,7 +39,7 @@ typedef struct POSIX_FD { struct POSIX_LOGICAL_PROCESS_STATE; // Initializes one logical process descriptor table from inherited state or fresh standard descriptors. -uint32_t PosixFdInitializeLogicalProcess(struct POSIX_LOGICAL_PROCESS_STATE* process, uint64_t inheritanceToken); +KSTATUS PosixFdInitializeLogicalProcess(struct POSIX_LOGICAL_PROCESS_STATE* process, uint64_t inheritanceToken); // Captures the current descriptor table in one server-owned inheritance snapshot. KSTATUS PosixFdCreateInheritanceSnapshot(uint32_t flags, uint64_t* tokenOut); @@ -78,4 +78,4 @@ void PosixFdRelease(POSIX_OPEN_DESCRIPTION* description); // Removes one descriptor and releases its table-owned open-description reference. int PosixFdClose(int fd); -int PosixStatusToErrno(uint32_t status); +int PosixStatusToErrno(KSTATUS status); diff --git a/code/user/personalities/posix/posix_internal.h b/code/user/personalities/posix/posix_internal.h index a26ef50..e186ca3 100644 --- a/code/user/personalities/posix/posix_internal.h +++ b/code/user/personalities/posix/posix_internal.h @@ -105,7 +105,7 @@ typedef struct POSIX_THREAD_STATE { } POSIX_THREAD_STATE; // Initializes process-local POSIX state and attaches the initial thread. -uint32_t PosixRuntimeInitializeProcess(void); +KSTATUS PosixRuntimeInitializeProcess(void); // Returns the POSIX state for the current process after startup. POSIX_PROCESS_STATE* PosixRuntimeProcessState(void); @@ -117,10 +117,10 @@ POSIX_THREAD_STATE* PosixRuntimeThreadState(void); POSIX_LOGICAL_PROCESS_STATE* PosixRuntimeLogicalProcessState(void); // Attaches POSIX state to the current native TEB. -uint32_t PosixRuntimeAttachThread(void); +KSTATUS PosixRuntimeAttachThread(void); // Attaches a thread while seeding its inherited persistent signal mask. -uint32_t PosixRuntimeAttachThreadWithSignalMask(uint64_t signalMask); +KSTATUS PosixRuntimeAttachThreadWithSignalMask(uint64_t signalMask); // Allocates one borrowed logical child and pins its inherited descriptor snapshot. KSTATUS PosixVforkBeginBorrowedChild(POSIX_VFORK_CHILD_STATE** childOut); diff --git a/code/user/personalities/posix/posix_io.c b/code/user/personalities/posix/posix_io.c index 641293b..14ea79d 100644 --- a/code/user/personalities/posix/posix_io.c +++ b/code/user/personalities/posix/posix_io.c @@ -1,5 +1,7 @@ // POSIX compatibility support for byte-stream file I/O. +#include +#include #include #include #include @@ -38,16 +40,16 @@ static SYS_PIPE_SHARED_STATE* PosixPipeShared(POSIX_OPEN_DESCRIPTION* descriptio } // Acquires one shared pipe-side serialization lock without entering the kernel. -static void PosixPipeLock(volatile long* lock) { - while (_InterlockedCompareExchange(lock, 1, 0) != 0) +static void PosixPipeLock(volatile int32_t* lock) { + while (NtlAtomicCompareExchange32(lock, 1, 0) != 0) _mm_pause(); _ReadWriteBarrier(); } // Releases one shared pipe-side serialization lock. -static void PosixPipeUnlock(volatile long* lock) { +static void PosixPipeUnlock(volatile int32_t* lock) { _ReadWriteBarrier(); - _InterlockedExchange(lock, 0); + NtlAtomicExchange32(lock, 0); } // Returns buffered bytes, rejecting corrupted shared counters. @@ -105,7 +107,7 @@ static ssize_t PosixPipeRead(POSIX_OPEN_DESCRIPTION* description, void* buffer, _ReadWriteBarrier(); shared->ReadPosition = readPosition + transfer; _ReadWriteBarrier(); - long writeWaiters = shared->WriteWaiters; + int32_t writeWaiters = shared->WriteWaiters; PosixPipeUnlock(&shared->ReadLock); if (writeWaiters != 0) (void)SysNotifyPipe(description->KHandle); @@ -124,7 +126,7 @@ static ssize_t PosixPipeRead(POSIX_OPEN_DESCRIPTION* description, void* buffer, POSIX_THREAD_STATE* thread = PosixRuntimeThreadState(); uint64_t deliveryCount = thread ? thread->SignalDeliveryCount : 0; - uint32_t status = SysWait(description->KHandle, SYS_WAIT_FLAG_ALERTABLE); + KSTATUS status = SysWait(description->KHandle, SYS_WAIT_FLAG_ALERTABLE); if (status == NTLSTATUS_USER_APC || status == NTLSTATUS_KERNEL_APC || status == NTLSTATUS_ALERTED) { if (!thread || thread->SignalDeliveryCount == deliveryCount) @@ -173,7 +175,7 @@ static ssize_t PosixPipeWrite(POSIX_OPEN_DESCRIPTION* description, const void* b _ReadWriteBarrier(); shared->WritePosition = writePosition + transfer; _ReadWriteBarrier(); - long readWaiters = shared->ReadWaiters; + int32_t readWaiters = shared->ReadWaiters; PosixPipeUnlock(&shared->WriteLock); if (readWaiters != 0) (void)SysNotifyPipe(description->KHandle); @@ -189,7 +191,7 @@ static ssize_t PosixPipeWrite(POSIX_OPEN_DESCRIPTION* description, const void* b POSIX_THREAD_STATE* thread = PosixRuntimeThreadState(); uint64_t deliveryCount = thread ? thread->SignalDeliveryCount : 0; - uint32_t status = SysWait(description->KHandle, SYS_WAIT_FLAG_ALERTABLE); + KSTATUS status = SysWait(description->KHandle, SYS_WAIT_FLAG_ALERTABLE); if (status == NTLSTATUS_USER_APC || status == NTLSTATUS_KERNEL_APC || status == NTLSTATUS_ALERTED) { if (!thread || thread->SignalDeliveryCount == deliveryCount) @@ -251,7 +253,7 @@ __declspec(dllexport) int open(const char* path, int flags, ...) { else if (flags & O_TRUNC) disposition = SYS_FILE_TRUNCATE_EXISTING; - uint32_t status = NtlOpenFile(SysSvFromCstr(p), access, disposition, + KSTATUS status = NtlOpenFile(SysSvFromCstr(p), access, disposition, SYS_FILE_FLAG_NONE, &kh); if (status != NTLSTATUS_SUCCESS) { errno = PosixStatusToErrno(status); @@ -288,7 +290,7 @@ __declspec(dllexport) ssize_t read(int fd, void* buf, size_t count) { char* dst = (char*)buf; result = (ssize_t)count; for (size_t i = 0; i < count; i++) { - uint32_t status = NtlConsoleReadCharacter(SYS_HANDLE_INVALID, &dst[i]); + KSTATUS status = NtlConsoleReadCharacter(SYS_HANDLE_INVALID, &dst[i]); if (status != NTLSTATUS_SUCCESS) { errno = PosixStatusToErrno(status); result = -1; @@ -312,7 +314,7 @@ __declspec(dllexport) ssize_t read(int fd, void* buf, size_t count) { size_t got = 0; SYS_FILE_IO_REQUEST request = SysMakeFileIoRequest(description->KHandle, buf, count, &got); - uint32_t status = SysReadFile(&request); + KSTATUS status = SysReadFile(&request); if (status != NTLSTATUS_SUCCESS && got == 0) errno = PosixStatusToErrno(status); else @@ -328,7 +330,7 @@ __declspec(dllexport) ssize_t read(int fd, void* buf, size_t count) { sizeof(SYS_SOCKET_RECV_REQUEST), description->KHandle, 0, buf, count, &got }; - uint32_t status = SysRecvSocket(&request); + KSTATUS status = SysRecvSocket(&request); if (status != NTLSTATUS_SUCCESS) errno = PosixStatusToErrno(status); else @@ -366,7 +368,7 @@ __declspec(dllexport) ssize_t write(int fd, const void* buf, size_t count) { switch (description->Type) { case PosixFdConsoleWrite: { size_t written = 0; - uint32_t status = NtlConsoleWrite(SYS_HANDLE_INVALID, buf, count, &written); + KSTATUS status = NtlConsoleWrite(SYS_HANDLE_INVALID, buf, count, &written); if (status != NTLSTATUS_SUCCESS && written == 0) errno = PosixStatusToErrno(status); else @@ -381,7 +383,7 @@ __declspec(dllexport) ssize_t write(int fd, const void* buf, size_t count) { size_t written = 0; SYS_FILE_IO_REQUEST request = SysMakeFileIoRequest(description->KHandle, (void*)buf, count, &written); - uint32_t status = SysWriteFile(&request); + KSTATUS status = SysWriteFile(&request); if (status != NTLSTATUS_SUCCESS && written == 0) errno = PosixStatusToErrno(status); else @@ -397,7 +399,7 @@ __declspec(dllexport) ssize_t write(int fd, const void* buf, size_t count) { sizeof(SYS_SOCKET_SEND_REQUEST), description->KHandle, 0, buf, count, &sent }; - uint32_t status = SysSendSocket(&request); + KSTATUS status = SysSendSocket(&request); if (status != NTLSTATUS_SUCCESS) errno = PosixStatusToErrno(status); else @@ -452,7 +454,7 @@ __declspec(dllexport) int pipe(int pipefd[2]) { SYS_PIPE_SHARED_STATE* pipeShared = readShared ? (SYS_PIPE_SHARED_STATE*)(uintptr_t)readShared->SharedObjectData : (SYS_PIPE_SHARED_STATE*)0; - int valid = handles[0] != SYS_HANDLE_INVALID && handles[1] != SYS_HANDLE_INVALID && + bool valid = handles[0] != SYS_HANDLE_INVALID && handles[1] != SYS_HANDLE_INVALID && reply.OpenDescriptionId != 0 && reply.OpenDescriptionId2 != 0 && reply.OpenDescriptionId != reply.OpenDescriptionId2 && reply.OpenDescriptionType == NTL_POSIX_OPEN_TYPE_PIPE_READ && @@ -518,7 +520,7 @@ __declspec(dllexport) int unlink(const char* path) { return -1; } - uint32_t status = NtlDeleteFile(SysSvFromCstr(p)); + KSTATUS status = NtlDeleteFile(SysSvFromCstr(p)); if (status != NTLSTATUS_SUCCESS) { errno = PosixStatusToErrno(status); return -1; @@ -538,7 +540,7 @@ __declspec(dllexport) off_t lseek(int fd, off_t offset, int whence) { } int64_t target = 0; - uint32_t status = NTLSTATUS_SUCCESS; + KSTATUS status = NTLSTATUS_SUCCESS; if (whence == SEEK_SET) { target = (int64_t)offset; } else if (whence == SEEK_CUR) { diff --git a/code/user/personalities/posix/posix_mman.c b/code/user/personalities/posix/posix_mman.c index 973be7f..874a918 100644 --- a/code/user/personalities/posix/posix_mman.c +++ b/code/user/personalities/posix/posix_mman.c @@ -55,7 +55,7 @@ __declspec(dllexport) void* mmap(void* addr, size_t length, int prot, sizeof(SYS_CREATE_FILE_MAPPING_REQUEST), description->KHandle, protect, SYS_MAP_FLAG_NONE, maxSize, &mapping }; - uint32_t s = SysCreateFileMapping(&createMapping); + KSTATUS s = SysCreateFileMapping(&createMapping); PosixFdRelease(description); if (s != NTLSTATUS_SUCCESS) { errno = PosixStatusToErrno(s); @@ -80,7 +80,7 @@ __declspec(dllexport) void* mmap(void* addr, size_t length, int prot, __declspec(dllexport) int munmap(void* addr, size_t length) { (void)length; if (!addr) { errno = EINVAL; return -1; } - uint32_t s = SysFreeVm(addr); + KSTATUS s = SysFreeVm(addr); if (s != NTLSTATUS_SUCCESS) { errno = PosixStatusToErrno(s); return -1; } return 0; } diff --git a/code/user/personalities/posix/posix_path.c b/code/user/personalities/posix/posix_path.c index 60ef292..8a5fd18 100644 --- a/code/user/personalities/posix/posix_path.c +++ b/code/user/personalities/posix/posix_path.c @@ -1,5 +1,6 @@ // POSIX compatibility support for path translation. +#include #include #include #include @@ -63,17 +64,17 @@ KSTATUS PosixProcessPath(const char* in, char* out, size_t outSize) { return NTLSTATUS_SUCCESS; } -static int PosixViewEqualsI(SYS_STRING_VIEW view, const char* text) { +static bool PosixViewEqualsI(SYS_STRING_VIEW view, const char* text) { size_t length = strlen(text); - if (view.Length != length) return 0; + if (view.Length != length) return false; for (size_t i = 0; i < length; ++i) { char a = view.Data[i]; char b = text[i]; if (a >= 'A' && a <= 'Z') a = (char)(a - 'A' + 'a'); if (b >= 'A' && b <= 'Z') b = (char)(b - 'A' + 'a'); - if (a != b) return 0; + if (a != b) return false; } - return 1; + return true; } // POSIX owns the msvcrt compatibility alias; all other import names are native. diff --git a/code/user/personalities/posix/posix_runtime.c b/code/user/personalities/posix/posix_runtime.c index 8f933d0..73a5cb5 100644 --- a/code/user/personalities/posix/posix_runtime.c +++ b/code/user/personalities/posix/posix_runtime.c @@ -463,7 +463,7 @@ static KSTATUS PosixRuntimeRegisterProcess(POSIX_PROCESS_STATE* process, } // Initializes process-local POSIX state, registers stable identity, and attaches the initial thread. -uint32_t PosixRuntimeInitializeProcess(void) { +KSTATUS PosixRuntimeInitializeProcess(void) { if (gPosixProcessState) return PosixRuntimeAttachThread(); @@ -482,7 +482,7 @@ uint32_t PosixRuntimeInitializeProcess(void) { gPosixProcessState = process; NTL_POSIX_BOOTSTRAP_DATA bootstrap; - uint32_t status = PosixRuntimeReadBootstrap(&bootstrap); + KSTATUS status = PosixRuntimeReadBootstrap(&bootstrap); if (status != NTLSTATUS_SUCCESS) { gPosixProcessState = (POSIX_PROCESS_STATE*)0; free(process); @@ -608,7 +608,7 @@ static void PosixRuntimeUnregisterThread(POSIX_THREAD_STATE* thread) { } // Attaches the native thread with one inherited persistent POSIX signal mask. -uint32_t PosixRuntimeAttachThreadWithSignalMask(uint64_t signalMask) { +KSTATUS PosixRuntimeAttachThreadWithSignalMask(uint64_t signalMask) { SYS_TEB* teb = SysGetTeb(); POSIX_PROCESS_STATE* process = PosixRuntimeProcessState(); if (!teb || teb->Magic != SYS_TEB_MAGIC || !process) @@ -649,7 +649,7 @@ uint32_t PosixRuntimeAttachThreadWithSignalMask(uint64_t signalMask) { } // Attaches a native thread with the default empty signal mask. -uint32_t PosixRuntimeAttachThread(void) { +KSTATUS PosixRuntimeAttachThread(void) { return PosixRuntimeAttachThreadWithSignalMask(0); } @@ -763,7 +763,7 @@ static KSTATUS PosixRuntimeReportBootstrapReady(POSIX_PROCESS_STATE* process) { // Initializes all POSIX process state required before application main. NTL_POSIX_API uint32_t __ntl_posix_process_start(void) { - uint32_t status = PosixRuntimeInitializeProcess(); + KSTATUS status = PosixRuntimeInitializeProcess(); if (status != NTLSTATUS_SUCCESS) return status; diff --git a/code/user/personalities/posix/posix_signal.c b/code/user/personalities/posix/posix_signal.c index 983afb0..6333449 100644 --- a/code/user/personalities/posix/posix_signal.c +++ b/code/user/personalities/posix/posix_signal.c @@ -1,5 +1,6 @@ // POSIX signal policy and user-mode dispatch over generic thread APC delivery. +#include #include #include #include @@ -33,13 +34,13 @@ static KSTATUS PosixSignalCallServer(NTL_POSIX_REQUEST* request, NTL_POSIX_REPLY } // Applies one caught/default/ignored signal on the current POSIX thread. -static int PosixSignalDeliver(POSIX_THREAD_STATE* thread, int signalNumber) { - if (!thread || !thread->NativeProcess || !NtlPosixSignalValid(signalNumber)) return 0; +static bool PosixSignalDeliver(POSIX_THREAD_STATE* thread, int signalNumber) { + if (!thread || !thread->NativeProcess || !NtlPosixSignalValid(signalNumber)) return false; uint64_t bit = PosixSignalBit(signalNumber); uint64_t effectiveMask = thread->SignalBlocked | thread->SignalHandlerBlocked; if (signalNumber != SIGKILL && (effectiveMask & bit) != 0) { thread->SignalPending |= bit; - return 0; + return false; } thread->SignalPending &= ~bit; @@ -49,10 +50,10 @@ static int PosixSignalDeliver(POSIX_THREAD_STATE* thread, int signalNumber) { if (handlerValue == (uintptr_t)SIG_IGN || (handlerValue == (uintptr_t)SIG_DFL && signalNumber == SIGCHLD)) - return 0; + return false; if (handlerValue == (uintptr_t)SIG_DFL) { _exit(128 + signalNumber); - return 1; + return true; } uint64_t previousHandlerMask = thread->SignalHandlerBlocked; @@ -65,7 +66,7 @@ static int PosixSignalDeliver(POSIX_THREAD_STATE* thread, int signalNumber) { ((sighandler_t)handlerValue)(signalNumber); thread->SignalHandlerBlocked = previousHandlerMask; PosixSignalDispatchLocalPending(thread); - return 1; + return true; } // Maps one personality-neutral native exception class to a POSIX signal. @@ -95,7 +96,7 @@ static int PosixSignalForNativeException(uint32_t code) { } // Translates selected native faults at the POSIX personality boundary. -static int PosixSignalNativeException(SYS_EXCEPTION_RECORD* record, +static uint32_t PosixSignalNativeException(SYS_EXCEPTION_RECORD* record, SYS_USER_CONTEXT* context, void* userContext) { (void)context; diff --git a/code/user/personalities/posix/posix_socket.c b/code/user/personalities/posix/posix_socket.c index c8ecef8..a3ca7da 100644 --- a/code/user/personalities/posix/posix_socket.c +++ b/code/user/personalities/posix/posix_socket.c @@ -1,5 +1,6 @@ // POSIX compatibility support for posix socket. +#include #include #include #include @@ -31,27 +32,27 @@ __declspec(dllexport) uint32_t htonl(uint32_t x) { return bswap32(x); } __declspec(dllexport) uint32_t ntohl(uint32_t x) { return bswap32(x); } // Parses byte. -static int ParseByte(const char** p, uint8_t* out) { +static bool ParseByte(const char** p, uint8_t* out) { const char* s = *p; - if (*s < '0' || *s > '9') return 0; + if (*s < '0' || *s > '9') return false; unsigned v = 0; while (*s >= '0' && *s <= '9') { v = v * 10 + (unsigned)(*s - '0'); - if (v > 255) return 0; + if (v > 255) return false; s++; } *out = (uint8_t)v; *p = s; - return 1; + return true; } // Parses ipv4 Bytes. -static int ParseIpv4Bytes(const char* text, uint8_t out[4]) { +static bool ParseIpv4Bytes(const char* text, uint8_t out[4]) { const char* p = text; for (int i = 0; i < 4; i++) { - if (!ParseByte(&p, &out[i])) return 0; + if (!ParseByte(&p, &out[i])) return false; if (i != 3) { - if (*p != '.') return 0; + if (*p != '.') return false; p++; } } @@ -65,14 +66,14 @@ static int ParseHex(char c) { return -1; } -static int ParseIpv6Bytes(const char* text, uint8_t out[16]) { +static bool ParseIpv6Bytes(const char* text, uint8_t out[16]) { uint16_t words[8]; int count = 0; int compress = -1; const char* p = text; if (!text || !*text || !out) - return 0; + return false; memset(words, 0, sizeof(words)); if (p[0] == ':' && p[1] == ':') { @@ -84,7 +85,7 @@ static int ParseIpv6Bytes(const char* text, uint8_t out[16]) { while (*p) { if (count >= 8) - return 0; + return false; unsigned value = 0; int digits = 0; @@ -97,17 +98,17 @@ static int ParseIpv6Bytes(const char* text, uint8_t out[16]) { digits++; } if (digits == 0) - return 0; + return false; words[count++] = (uint16_t)value; if (*p == '\0') break; if (*p != ':') - return 0; + return false; p++; if (*p == ':') { if (compress >= 0) - return 0; + return false; compress = count; p++; if (*p == '\0') @@ -118,30 +119,30 @@ static int ParseIpv6Bytes(const char* text, uint8_t out[16]) { if (compress >= 0) { int zeros = 8 - count; if (zeros <= 0) - return 0; + return false; for (int i = count - 1; i >= compress; --i) words[i + zeros] = words[i]; for (int i = 0; i < zeros; ++i) words[compress + i] = 0; } else if (count != 8) { - return 0; + return false; } for (int i = 0; i < 8; ++i) { out[i * 2 + 0] = (uint8_t)(words[i] >> 8); out[i * 2 + 1] = (uint8_t)words[i]; } - return 1; + return true; } static char* AppendHex16(char* out, uint16_t value) { static const char hex[] = "0123456789abcdef"; - int started = 0; + bool started = false; for (int shift = 12; shift >= 0; shift -= 4) { unsigned nibble = (value >> shift) & 0xfu; if (nibble || started || shift == 0) { *out++ = hex[nibble]; - started = 1; + started = true; } } return out; @@ -300,7 +301,7 @@ static POSIX_OPEN_DESCRIPTION* SocketFdAcquire(int fd) { // Creates a socket and installs it as a process-local descriptor. __declspec(dllexport) int socket(int domain, int type, int protocol) { uint32_t kh = SYS_HANDLE_INVALID; - uint32_t status = SysCreateSocket((uint32_t)domain, (uint32_t)type, (uint32_t)protocol, &kh); + KSTATUS status = SysCreateSocket((uint32_t)domain, (uint32_t)type, (uint32_t)protocol, &kh); if (status != NTLSTATUS_SUCCESS) { errno = PosixStatusToErrno(status); return -1; @@ -321,7 +322,7 @@ __declspec(dllexport) int bind(int fd, const struct sockaddr* addr, socklen_t ad PosixFdRelease(description); return -1; } - uint32_t status = SysBindSocket(description->KHandle, &address, sizeof(address)); + KSTATUS status = SysBindSocket(description->KHandle, &address, sizeof(address)); PosixFdRelease(description); if (status != NTLSTATUS_SUCCESS) { errno = PosixStatusToErrno(status); @@ -334,7 +335,7 @@ __declspec(dllexport) int bind(int fd, const struct sockaddr* addr, socklen_t ad __declspec(dllexport) int listen(int fd, int backlog) { POSIX_OPEN_DESCRIPTION* description = SocketFdAcquire(fd); if (!description) return -1; - uint32_t status = SysListenSocket(description->KHandle, backlog < 0 ? 0u : (uint32_t)backlog); + KSTATUS status = SysListenSocket(description->KHandle, backlog < 0 ? 0u : (uint32_t)backlog); PosixFdRelease(description); if (status != NTLSTATUS_SUCCESS) { errno = PosixStatusToErrno(status); @@ -351,7 +352,7 @@ __declspec(dllexport) int accept(int fd, struct sockaddr* addr, socklen_t* addrl SYS_SOCKET_ADDRESS remote; size_t remoteSize = sizeof(remote); uint32_t kh = SYS_HANDLE_INVALID; - uint32_t status = SysAcceptSocket(description->KHandle, + KSTATUS status = SysAcceptSocket(description->KHandle, addr ? &remote : (SYS_SOCKET_ADDRESS*)0, addr ? &remoteSize : (size_t*)0, &kh); @@ -379,7 +380,7 @@ __declspec(dllexport) int connect(int fd, const struct sockaddr* addr, socklen_t PosixFdRelease(description); return -1; } - uint32_t status = SysConnectSocket(description->KHandle, &address, sizeof(address)); + KSTATUS status = SysConnectSocket(description->KHandle, &address, sizeof(address)); PosixFdRelease(description); if (status != NTLSTATUS_SUCCESS) { errno = PosixStatusToErrno(status); @@ -397,7 +398,7 @@ __declspec(dllexport) ssize_t send(int fd, const void* buf, size_t len, int flag sizeof(SYS_SOCKET_SEND_REQUEST), description->KHandle, (uint32_t)flags, buf, len, &sent }; - uint32_t status = SysSendSocket(&request); + KSTATUS status = SysSendSocket(&request); PosixFdRelease(description); if (status != NTLSTATUS_SUCCESS) { errno = PosixStatusToErrno(status); @@ -415,7 +416,7 @@ __declspec(dllexport) ssize_t recv(int fd, void* buf, size_t len, int flags) { sizeof(SYS_SOCKET_RECV_REQUEST), description->KHandle, (uint32_t)flags, buf, len, &got }; - uint32_t status = SysRecvSocket(&request); + KSTATUS status = SysRecvSocket(&request); PosixFdRelease(description); if (status != NTLSTATUS_SUCCESS) { errno = PosixStatusToErrno(status); @@ -446,7 +447,7 @@ __declspec(dllexport) ssize_t sendto(int fd, const void* buf, size_t len, int fl request.RemoteAddressSize = sizeof(remote); request.SentOut = &sent; - uint32_t status = SysSendToSocket(description->KHandle, &request); + KSTATUS status = SysSendToSocket(description->KHandle, &request); PosixFdRelease(description); if (status != NTLSTATUS_SUCCESS) { errno = PosixStatusToErrno(status); @@ -474,7 +475,7 @@ __declspec(dllexport) ssize_t recvfrom(int fd, void* buf, size_t len, int flags, request.RemoteAddressSizeInOut = src ? &remoteSize : (size_t*)0; request.ReceivedOut = &got; - uint32_t status = SysRecvFromSocket(description->KHandle, &request); + KSTATUS status = SysRecvFromSocket(description->KHandle, &request); PosixFdRelease(description); if (status != NTLSTATUS_SUCCESS) { errno = PosixStatusToErrno(status); @@ -488,7 +489,7 @@ __declspec(dllexport) ssize_t recvfrom(int fd, void* buf, size_t len, int flags, __declspec(dllexport) int shutdown(int fd, int how) { POSIX_OPEN_DESCRIPTION* description = SocketFdAcquire(fd); if (!description) return -1; - uint32_t status = SysShutdownSocket(description->KHandle, (uint32_t)how); + KSTATUS status = SysShutdownSocket(description->KHandle, (uint32_t)how); PosixFdRelease(description); if (status != NTLSTATUS_SUCCESS) { errno = PosixStatusToErrno(status); @@ -550,7 +551,7 @@ __declspec(dllexport) int setsockopt(int fd, int level, int optname, const void* request.Option = option; request.Value = &value; request.ValueSize = sizeof(value); - uint32_t status = SysSetSocketOption(description->KHandle, &request); + KSTATUS status = SysSetSocketOption(description->KHandle, &request); PosixFdRelease(description); if (status != NTLSTATUS_SUCCESS) { errno = PosixStatusToErrno(status); @@ -584,7 +585,7 @@ __declspec(dllexport) int getsockopt(int fd, int level, int optname, void* optva request.Value = &value; request.ValueSize = sizeof(value); request.ActualSizeOut = &actual; - uint32_t status = SysGetSocketOption(description->KHandle, &request); + KSTATUS status = SysGetSocketOption(description->KHandle, &request); PosixFdRelease(description); if (status != NTLSTATUS_SUCCESS) { errno = PosixStatusToErrno(status); @@ -723,7 +724,7 @@ static int ResolveNameFamily(const char* name, int family, SYS_SOCKET_ADDRESS* o req.Family = family == AF_INET6 ? SYS_AF_INET6 : SYS_AF_INET; req.AddressOut = out; req.AddressSizeInOut = &size; - uint32_t s = SysResolveHost(&req); + KSTATUS s = SysResolveHost(&req); if (s != 0) { errno = PosixStatusToErrno(s); return -1; } return 0; } diff --git a/code/user/personalities/posix/posix_thread.c b/code/user/personalities/posix/posix_thread.c index 7104d87..6074307 100644 --- a/code/user/personalities/posix/posix_thread.c +++ b/code/user/personalities/posix/posix_thread.c @@ -94,7 +94,7 @@ __declspec(dllexport) int pthread_create(pthread_t* t, const pthread_attr_t* att &handle, &tid }; - uint32_t s = SysCreateThread(&req); + KSTATUS s = SysCreateThread(&req); if (s != 0) { free(tr); return EAGAIN; @@ -112,7 +112,7 @@ __declspec(dllexport) int pthread_create(pthread_t* t, const pthread_attr_t* att // Implements the pthread join operation. __declspec(dllexport) int pthread_join(pthread_t t, void** retval) { - uint32_t s = SysWait(t.Handle, SYS_WAIT_FLAG_NONE); + KSTATUS s = SysWait(t.Handle, SYS_WAIT_FLAG_NONE); if (s != 0) return ESRCH; POSIX_PTHREAD_SLOT* slot = PthreadSlotFind(t.Tid); @@ -136,7 +136,7 @@ __declspec(dllexport) void pthread_exit(void* retval) { __declspec(dllexport) int pthread_mutex_init(pthread_mutex_t* m, const pthread_mutexattr_t* attr) { (void)attr; uint32_t h; - uint32_t s = SysCreateMutex(&h); + KSTATUS s = SysCreateMutex(&h); if (s != 0) return ENOMEM; m->Handle = h; return 0; @@ -151,18 +151,18 @@ __declspec(dllexport) int pthread_mutex_destroy(pthread_mutex_t* m) { // Implements the pthread mutex lock operation. __declspec(dllexport) int pthread_mutex_lock(pthread_mutex_t* m) { - uint32_t s = SysWait(m->Handle, SYS_WAIT_FLAG_NONE); + KSTATUS s = SysWait(m->Handle, SYS_WAIT_FLAG_NONE); return s == 0 ? 0 : EINVAL; } // Implements the pthread mutex trylock operation. __declspec(dllexport) int pthread_mutex_trylock(pthread_mutex_t* m) { - uint32_t s = SysTryAcquireMutex(m->Handle); + KSTATUS s = SysTryAcquireMutex(m->Handle); return s == 0 ? 0 : EAGAIN; } // Implements the pthread mutex unlock operation. __declspec(dllexport) int pthread_mutex_unlock(pthread_mutex_t* m) { - uint32_t s = SysReleaseMutex(m->Handle); + KSTATUS s = SysReleaseMutex(m->Handle); return s == 0 ? 0 : EINVAL; } diff --git a/code/user/personalities/win32/kernel32.c b/code/user/personalities/win32/kernel32.c index 8a6555b..f73193b 100644 --- a/code/user/personalities/win32/kernel32.c +++ b/code/user/personalities/win32/kernel32.c @@ -16,6 +16,7 @@ // - File I/O resolves Win32 paths in userspace, then uses capability-relative // opens plus the common read/write/seek/query/close syscalls. +#include #include #include #include @@ -45,7 +46,15 @@ typedef long LONG; typedef struct { LONG x; LONG y; } POINT; typedef struct { SHORT X; SHORT Y; } COORD; typedef struct { SHORT Left; SHORT Top; SHORT Right; SHORT Bottom; } SMALL_RECT; -typedef struct { uintptr_t _opaque[5]; } CRITICAL_SECTION; +typedef struct { + int32_t Lock; + uint32_t LockPadding; + uintptr_t Owner; + uintptr_t Recursion; + uintptr_t Reserved[2]; +} CRITICAL_SECTION; +_Static_assert(sizeof(CRITICAL_SECTION) == sizeof(uintptr_t) * 5u, + "Win64 CRITICAL_SECTION compatibility storage must remain five pointers"); typedef struct { void* _ptr; } CONDITION_VARIABLE; // Forward declarations used by the process-local GetProcAddress surface. @@ -271,7 +280,7 @@ static void TranslatePath(const char* input, char* output) { } // Implements the k32 Open File Native operation. -static uint32_t K32OpenFileNative(const char* path, +static KSTATUS K32OpenFileNative(const char* path, uint32_t access, uint32_t disposition, uint32_t* handleOut) { @@ -279,25 +288,25 @@ static uint32_t K32OpenFileNative(const char* path, } // Implements the k32 Delete File Native operation. -static uint32_t K32DeleteFileNative(const char* path) { +static KSTATUS K32DeleteFileNative(const char* path) { return NtlDeleteFile(SysSvFromCstr(path)); } // Implements the k32 Open Dir Native operation. -static uint32_t K32OpenDirNative(const char* path, uint32_t* handleOut) { +static KSTATUS K32OpenDirNative(const char* path, uint32_t* handleOut) { return NtlOpenDir(SysSvFromCstr(path), SYS_DIR_ACCESS_ENUMERATE | SYS_DIR_ACCESS_OPEN, SYS_DIR_FLAG_NONE, handleOut); } // Implements the k32 Set Cwd Native operation. -static uint32_t K32SetCwdNative(const char* path) { +static KSTATUS K32SetCwdNative(const char* path) { return NtlSetCurrentDirectory(SysSvFromCstr(path)); } // Implements the k32 Get Cwd Native operation. -static uint32_t K32GetCwdNative(char* buffer, size_t capacity, size_t* lengthOut) { +static KSTATUS K32GetCwdNative(char* buffer, size_t capacity, size_t* lengthOut) { size_t required = 0; - uint32_t status = NtlGetCurrentDirectory(buffer, capacity, &required); + KSTATUS status = NtlGetCurrentDirectory(buffer, capacity, &required); if (lengthOut) *lengthOut = required ? required - 1u : 0; return status; } @@ -308,7 +317,7 @@ static KSTATUS K32ResolveModuleName(void* context, SYS_STRING_VIEW requested, SY } // Translates Win32 process syntax into native resource/CWD syntax. -static uint32_t K32ProcessPath(const char* input, char* output, size_t outputSize) { +static KSTATUS K32ProcessPath(const char* input, char* output, size_t outputSize) { if (!input || !output || outputSize == 0) return NTLSTATUS_INVALID_PARAMETER; char translated[MAX_PATH]; TranslatePath(input, translated); @@ -320,9 +329,9 @@ static uint32_t K32ProcessPath(const char* input, char* output, size_t outputSiz } // Creates one Win32 child through RSSRV lookup plus the userspace PE loader. -static uint32_t K32CreateProcessNative(const char* path, uint32_t imageFile, uint32_t* handleOut, const char* commandLine, const char* currentDirectory) { +static KSTATUS K32CreateProcessNative(const char* path, uint32_t imageFile, uint32_t* handleOut, const char* commandLine, const char* currentDirectory) { char relative[MAX_PATH]; - uint32_t status = K32ProcessPath(path, relative, sizeof(relative)); + KSTATUS status = K32ProcessPath(path, relative, sizeof(relative)); if (status != NTLSTATUS_SUCCESS) return status; NTL_CREATE_SYSTEM_PROCESS_REQUEST req; memset(&req, 0, sizeof(req)); @@ -619,7 +628,7 @@ __declspec(dllexport) void* VirtualAlloc(void* lpAddress, size_t dwSize, __declspec(dllexport) BOOL VirtualFree(void* lpAddress, size_t dwSize, DWORD dwFreeType) { (void)dwSize; (void)dwFreeType; if (!lpAddress) return TRUE; - uint32_t s = SysFreeVm(lpAddress); + KSTATUS s = SysFreeVm(lpAddress); if (s != NTLSTATUS_SUCCESS) { gLastError = 87; return FALSE; } return TRUE; } @@ -629,7 +638,7 @@ __declspec(dllexport) BOOL VirtualProtect(void* lpAddress, size_t dwSize, if (!lpAddress || !dwSize) { gLastError = 87; return FALSE; } size_t pages = (dwSize + 4095u) / 4096u; uint32_t oldSysProtect = 0; - uint32_t s = SysProtectVm(lpAddress, pages, WinProtectToSysVm(flNewProtect), + KSTATUS s = SysProtectVm(lpAddress, pages, WinProtectToSysVm(flNewProtect), lpflOldProtect ? &oldSysProtect : NULL); if (s != NTLSTATUS_SUCCESS) { gLastError = 87; return FALSE; } if (lpflOldProtect) *lpflOldProtect = SysProtectVmToWin(oldSysProtect); @@ -645,7 +654,7 @@ __declspec(dllexport) size_t VirtualQuery(const void* lpAddress, } SYS_VM_REGION_INFO info; - uint32_t s = SysQueryVm((void*)lpAddress, &info); + KSTATUS s = SysQueryVm((void*)lpAddress, &info); if (s != NTLSTATUS_SUCCESS) { gLastError = 87; return 0; @@ -682,7 +691,7 @@ __declspec(dllexport) HANDLE CreateFileMappingA(HANDLE hFile, WinFileMappingProtectToSysVm(flProtect), SYS_MAP_FLAG_NONE, maximumSize, &kh }; - uint32_t s = SysCreateFileMapping(&createMapping); + KSTATUS s = SysCreateFileMapping(&createMapping); if (s != NTLSTATUS_SUCCESS) { gLastError = (s == NTLSTATUS_NOT_SUPPORTED) ? 120 : ((s == NTLSTATUS_NO_MEMORY) ? 8 : 87); return NULL; @@ -718,7 +727,7 @@ __declspec(dllexport) void* MapViewOfFile(HANDLE hFileMappingObject, WinFileMapAccessToSys(dwDesiredAccess), SYS_MAP_FLAG_NONE, offset, dwNumberOfBytesToMap, &base }; - uint32_t s = SysMapFile(&mapFile); + KSTATUS s = SysMapFile(&mapFile); if (s != NTLSTATUS_SUCCESS) { gLastError = (s == NTLSTATUS_NOT_SUPPORTED) ? 120 : ((s == NTLSTATUS_NO_MEMORY) ? 8 : 87); return NULL; @@ -728,7 +737,7 @@ __declspec(dllexport) void* MapViewOfFile(HANDLE hFileMappingObject, __declspec(dllexport) BOOL UnmapViewOfFile(const void* lpBaseAddress) { if (!lpBaseAddress) { gLastError = 87; return FALSE; } - uint32_t s = SysFreeVm((void*)lpBaseAddress); + KSTATUS s = SysFreeVm((void*)lpBaseAddress); if (s != NTLSTATUS_SUCCESS) { gLastError = 87; return FALSE; } return TRUE; } @@ -909,7 +918,7 @@ __declspec(dllexport) HANDLE CreateFileA(const char* name, } uint32_t kh = 0; - uint32_t s = K32OpenFileNative(translatedName, sysAccess, sysDisposition, &kh); + KSTATUS s = K32OpenFileNative(translatedName, sysAccess, sysDisposition, &kh); if (s != 0) { gLastError = (s == NTLSTATUS_OBJECT_NAME_COLLISION) ? 80 : (s == NTLSTATUS_NOT_FOUND) ? 2 : 5; @@ -924,7 +933,7 @@ __declspec(dllexport) HANDLE CreateFileA(const char* name, __declspec(dllexport) DWORD GetFileSize(HANDLE hFile, DWORD* fileSizeHigh) { if (!hFile || hFile == INVALID_HANDLE_VALUE) { gLastError = 6; return 0xFFFFFFFFUL; } uint64_t sz = 0; - uint32_t s = SysGetFileSize(HANDLE_TO_KH(hFile), &sz); + KSTATUS s = SysGetFileSize(HANDLE_TO_KH(hFile), &sz); if (s != 0) { gLastError = 6; return 0xFFFFFFFFUL; } if (fileSizeHigh) *fileSizeHigh = (DWORD)(sz >> 32); return (DWORD)(sz & 0xFFFFFFFFULL); @@ -942,7 +951,7 @@ __declspec(dllexport) BOOL ReadFile(HANDLE hFile, void* buf, DWORD toRead, size_t got = 0; SYS_FILE_IO_REQUEST req = SysMakeFileIoRequest(HANDLE_TO_KH(hFile), buf, (size_t)toRead, &got); - uint32_t s = SysReadFile(&req); + KSTATUS s = SysReadFile(&req); if (bytesRead) *bytesRead = (DWORD)got; if (s != 0) { @@ -967,7 +976,7 @@ __declspec(dllexport) BOOL WriteFile(HANDLE hFile, const void* buf, DWORD toWrit if (!hFile || hFile == INVALID_HANDLE_VALUE || !buf) { gLastError = 6; return FALSE; } size_t written = 0; SYS_FILE_IO_REQUEST req = SysMakeFileIoRequest(HANDLE_TO_KH(hFile), buf, (size_t)toWrite, &written); - uint32_t s = SysWriteFile(&req); + KSTATUS s = SysWriteFile(&req); if (bytesWritten) *bytesWritten = (DWORD)written; if (s != 0) { gLastError = 29; return FALSE; } // ERROR_WRITE_FAULT return TRUE; @@ -996,12 +1005,12 @@ __declspec(dllexport) DWORD SetFilePointer(HANDLE hFile, long dist, target = delta; } else if (method == FILE_CURRENT) { uint64_t cur = 0; - uint32_t s = SysGetFilePos(kh, &cur); + KSTATUS s = SysGetFilePos(kh, &cur); if (s != 0 || cur > 0x7FFFFFFFFFFFFFFFULL) { gLastError = 6; return 0xFFFFFFFFUL; } target = (int64_t)cur + delta; } else if (method == FILE_END) { uint64_t sz = 0; - uint32_t s = SysGetFileSize(kh, &sz); + KSTATUS s = SysGetFileSize(kh, &sz); if (s != 0 || sz > 0x7FFFFFFFFFFFFFFFULL) { gLastError = 6; return 0xFFFFFFFFUL; } target = (int64_t)sz + delta; } else { @@ -1014,7 +1023,7 @@ __declspec(dllexport) DWORD SetFilePointer(HANDLE hFile, long dist, } uint64_t offset = (uint64_t)target; - uint32_t s = SysSeekFile(kh, offset); + KSTATUS s = SysSeekFile(kh, offset); if (s != 0) { gLastError = 1; return 0xFFFFFFFFUL; } if (distHigh) *distHigh = (long)(int32_t)(offset >> 32); return (DWORD)(offset & 0xFFFFFFFFULL); @@ -1025,7 +1034,7 @@ __declspec(dllexport) BOOL SetEndOfFile(HANDLE hFile) { if (!hFile || hFile == INVALID_HANDLE_VALUE) { gLastError = 6; return FALSE; } uint32_t kh = HANDLE_TO_KH(hFile); uint64_t pos = 0; - uint32_t s = SysGetFilePos(kh, &pos); + KSTATUS s = SysGetFilePos(kh, &pos); if (s != 0) { gLastError = 6; return FALSE; } s = SysTruncateFile(kh, pos); if (s != 0) { gLastError = 1; return FALSE; } @@ -1049,38 +1058,38 @@ __declspec(dllexport) BOOL DeleteFileA(const char* lpFileName) { typedef struct K32_MUTEX_HANDLE { struct K32_MUTEX_HANDLE* Next; uint32_t Magic; - volatile long StateLock; - volatile long ApiUsers; + int32_t StateLock; + int32_t ApiUsers; uint32_t NativeHandle; DWORD OwnerTid; uint32_t Recursion; uint32_t Waiters; - volatile long Closing; + int32_t Closing; } K32_MUTEX_HANDLE; static K32_MUTEX_HANDLE* gK32MutexRegistry; -static volatile long gK32MutexRegistryLock; +static int32_t gK32MutexRegistryLock; // Acquires one short process-local mutex registry lock. static void K32MutexRegistryLock(void) { - while (_InterlockedCompareExchange(&gK32MutexRegistryLock, 1, 0) != 0) + while (NtlAtomicCompareExchange32(&gK32MutexRegistryLock, 1, 0) != 0) (void)SysYield(); } // Releases the process-local mutex registry lock. static void K32MutexRegistryUnlock(void) { - _InterlockedExchange(&gK32MutexRegistryLock, 0); + NtlAtomicExchange32(&gK32MutexRegistryLock, 0); } // Acquires one short mutex-wrapper state lock. static void K32MutexStateLock(K32_MUTEX_HANDLE* mutex) { - while (_InterlockedCompareExchange(&mutex->StateLock, 1, 0) != 0) + while (NtlAtomicCompareExchange32(&mutex->StateLock, 1, 0) != 0) (void)SysYield(); } // Releases one mutex-wrapper state lock. static void K32MutexStateUnlock(K32_MUTEX_HANDLE* mutex) { - _InterlockedExchange(&mutex->StateLock, 0); + NtlAtomicExchange32(&mutex->StateLock, 0); } // Registers one process-local Win32 mutex wrapper. @@ -1100,8 +1109,8 @@ static K32_MUTEX_HANDLE* K32ReferenceMutex(HANDLE handle) { current = current->Next) { if ((HANDLE)current == handle && current->Magic == K32_MUTEX_MAGIC && - _InterlockedCompareExchange(¤t->Closing, 0, 0) == 0) { - _InterlockedIncrement(¤t->ApiUsers); + NtlAtomicLoad32(¤t->Closing) == 0) { + NtlAtomicIncrement32(¤t->ApiUsers); found = current; break; } @@ -1113,7 +1122,7 @@ static K32_MUTEX_HANDLE* K32ReferenceMutex(HANDLE handle) { // Releases one mutex-wrapper API reference. static void K32DereferenceMutex(K32_MUTEX_HANDLE* mutex) { if (mutex) - _InterlockedDecrement(&mutex->ApiUsers); + NtlAtomicDecrement32(&mutex->ApiUsers); } // Removes one mutex wrapper so no new API references can be acquired. @@ -1138,7 +1147,7 @@ static BOOL K32RemoveMutex(K32_MUTEX_HANDLE* mutex) { static DWORD K32WaitMutex(K32_MUTEX_HANDLE* mutex, DWORD milliseconds) { DWORD tid = K32CurrentTidOrOne(); K32MutexStateLock(mutex); - if (_InterlockedCompareExchange(&mutex->Closing, 0, 0) != 0) { + if (NtlAtomicLoad32(&mutex->Closing) != 0) { K32MutexStateUnlock(mutex); gLastError = 6; return 0xFFFFFFFFUL; @@ -1159,7 +1168,7 @@ static DWORD K32WaitMutex(K32_MUTEX_HANDLE* mutex, DWORD milliseconds) { uint64_t timeout = milliseconds == 0xFFFFFFFFUL ? SYS_WAIT_TIMEOUT_INFINITE : (uint64_t)milliseconds; - uint32_t status = SysWaitSingle(mutex->NativeHandle, SYS_WAIT_FLAG_NONE, timeout); + KSTATUS status = SysWaitSingle(mutex->NativeHandle, SYS_WAIT_FLAG_NONE, timeout); K32MutexStateLock(mutex); if (mutex->Waiters) @@ -1184,7 +1193,7 @@ static DWORD K32WaitMutex(K32_MUTEX_HANDLE* mutex, DWORD milliseconds) { static BOOL K32CloseMutex(K32_MUTEX_HANDLE* mutex) { K32MutexStateLock(mutex); BOOL busy = mutex->OwnerTid != 0 || mutex->Waiters != 0; - if (!busy && _InterlockedCompareExchange(&mutex->Closing, 1, 0) != 0) + if (!busy && NtlAtomicCompareExchange32(&mutex->Closing, 1, 0) != 0) busy = TRUE; K32MutexStateUnlock(mutex); if (busy) { @@ -1198,9 +1207,9 @@ static BOOL K32CloseMutex(K32_MUTEX_HANDLE* mutex) { return FALSE; } - while (mutex->ApiUsers > 1) + while (NtlAtomicLoad32(&mutex->ApiUsers) > 1) (void)SysYield(); - uint32_t status = SysCloseHandle(mutex->NativeHandle); + KSTATUS status = SysCloseHandle(mutex->NativeHandle); mutex->Magic = 0; K32DereferenceMutex(mutex); free(mutex); @@ -1222,7 +1231,7 @@ __declspec(dllexport) BOOL CloseHandle(HANDLE hObject) { if (mutex) return K32CloseMutex(mutex); - uint32_t status = SysCloseHandle(HANDLE_TO_KH(hObject)); + KSTATUS status = SysCloseHandle(HANDLE_TO_KH(hObject)); if (status != NTLSTATUS_SUCCESS) { gLastError = 6; return FALSE; @@ -1387,7 +1396,7 @@ __declspec(dllexport) HANDLE LoadLibraryA(const char* lpLibFileName) { if (!lpLibFileName || !lpLibFileName[0]) { gLastError = 87; return NULL; } SYS_HANDLE root = SYS_HANDLE_INVALID; - uint32_t status = NtlResourceOpenSystemRoot(SYS_RIGHT_QUERY | SYS_VOLUME_RIGHT_OPEN, &root); + KSTATUS status = NtlResourceOpenSystemRoot(SYS_RIGHT_QUERY | SYS_VOLUME_RIGHT_OPEN, &root); if (status != NTLSTATUS_SUCCESS) { gLastError = 126; return NULL; } char relative[MAX_PATH]; @@ -1454,7 +1463,7 @@ __declspec(dllexport) void* GetProcAddress(HANDLE hModule, const char* lpProcNam if (hModule != WIN32_HMODULE_WIN32) { uintptr_t address = 0; - uint32_t status = NtlGetModuleExport((NTL_MODULE_HANDLE)(uintptr_t)hModule, + KSTATUS status = NtlGetModuleExport((NTL_MODULE_HANDLE)(uintptr_t)hModule, SysSvFromCstr(lpProcName), &address); if (status != NTLSTATUS_SUCCESS || !address) { gLastError = 127; return NULL; } return (void*)address; @@ -1497,7 +1506,7 @@ __declspec(dllexport) void* GetProcAddress(HANDLE hModule, const char* lpProcNam __declspec(dllexport) BOOL FreeLibrary(HANDLE hLibModule) { if (!hLibModule || hLibModule == WIN32_HMODULE_WIN32 || hLibModule == WIN32_HMODULE_EXE) return TRUE; - uint32_t status = NtlUnloadModule((NTL_MODULE_HANDLE)(uintptr_t)hLibModule); + KSTATUS status = NtlUnloadModule((NTL_MODULE_HANDLE)(uintptr_t)hLibModule); if (status != NTLSTATUS_SUCCESS) { gLastError = 126; return FALSE; @@ -1535,7 +1544,7 @@ __declspec(dllexport) BOOL TerminateProcess(HANDLE hProcess, unsigned int uExitC return FALSE; } - uint32_t s = SysKillProcess(HANDLE_TO_KH(hProcess), (int32_t)uExitCode); + KSTATUS s = SysKillProcess(HANDLE_TO_KH(hProcess), (int32_t)uExitCode); if (s != 0) { gLastError = (s == NTLSTATUS_INVALID_HANDLE) ? 6 : 5; return FALSE; @@ -1636,7 +1645,7 @@ __declspec(dllexport) HANDLE CreateThread(void* lpThreadAttributes, &kh, &tid }; - uint32_t status = SysCreateThread(&req); + KSTATUS status = SysCreateThread(&req); if (status != NTLSTATUS_SUCCESS) { free(context); gLastError = status == NTLSTATUS_NO_MEMORY ? 8 : 11; @@ -1676,7 +1685,7 @@ __declspec(dllexport) DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMillisec uint64_t timeout = dwMilliseconds == INFINITE ? SYS_WAIT_TIMEOUT_INFINITE : (uint64_t)dwMilliseconds; - uint32_t s = SysWaitSingle(HANDLE_TO_KH(hHandle), SYS_WAIT_FLAG_NONE, timeout); + KSTATUS s = SysWaitSingle(HANDLE_TO_KH(hHandle), SYS_WAIT_FLAG_NONE, timeout); if (s == NTLSTATUS_SUCCESS) return WAIT_OBJECT_0; if (s == NTLSTATUS_ABANDONED) return 0x00000080UL; if (s == NTLSTATUS_TIMEOUT) return WAIT_TIMEOUT; @@ -1697,7 +1706,7 @@ __declspec(dllexport) HANDLE CreateEventA(void* lpEventAttributes, } uint32_t handle = 0; - uint32_t status = SysCreateEvent(bManualReset ? 1u : 0u, + KSTATUS status = SysCreateEvent(bManualReset ? 1u : 0u, bInitialState ? 1u : 0u, &handle); if (status != NTLSTATUS_SUCCESS) { @@ -1722,7 +1731,7 @@ __declspec(dllexport) HANDLE CreateEventW(void* lpEventAttributes, // Signals one native event. __declspec(dllexport) BOOL SetEvent(HANDLE hEvent) { if (!IS_VALID_KERNEL_HANDLE(hEvent)) { gLastError = 6; return FALSE; } - uint32_t status = SysSignalEvent(HANDLE_TO_KH(hEvent)); + KSTATUS status = SysSignalEvent(HANDLE_TO_KH(hEvent)); if (status != NTLSTATUS_SUCCESS) { gLastError = 6; return FALSE; } return TRUE; } @@ -1730,7 +1739,7 @@ __declspec(dllexport) BOOL SetEvent(HANDLE hEvent) { // Clears one native event. __declspec(dllexport) BOOL ResetEvent(HANDLE hEvent) { if (!IS_VALID_KERNEL_HANDLE(hEvent)) { gLastError = 6; return FALSE; } - uint32_t status = SysClearEvent(HANDLE_TO_KH(hEvent)); + KSTATUS status = SysClearEvent(HANDLE_TO_KH(hEvent)); if (status != NTLSTATUS_SUCCESS) { gLastError = 6; return FALSE; } return TRUE; } @@ -1807,7 +1816,7 @@ __declspec(dllexport) BOOL CreateProcessA( (targetPath[2] == '\\' || targetPath[2] == '/')); if (relativeImage) { TranslatePath(targetPath, translatedPath); - uint32_t openStatus = K32OpenFileNative(translatedPath, + KSTATUS openStatus = K32OpenFileNative(translatedPath, SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_QUERY, SYS_FILE_OPEN_EXISTING, &imageFile); @@ -1853,7 +1862,7 @@ __declspec(dllexport) BOOL GetExitCodeProcess(HANDLE hProcess, DWORD* lpExitCode uint32_t kh = HANDLE_TO_KH(hProcess); if (!kh) { gLastError = 6; return FALSE; } int32_t code = 0; - uint32_t s = SysGetProcessExitCode(kh, &code); + KSTATUS s = SysGetProcessExitCode(kh, &code); if (s != 0) { gLastError = 6; return FALSE; } *lpExitCode = (DWORD)code; return TRUE; @@ -1867,7 +1876,7 @@ __declspec(dllexport) DWORD ResumeThread(HANDLE hThread) { } uint32_t previousSuspendCount = 0; - uint32_t status = SysResumeThread(HANDLE_TO_KH(hThread), + KSTATUS status = SysResumeThread(HANDLE_TO_KH(hThread), &previousSuspendCount); if (status != NTLSTATUS_SUCCESS) { gLastError = status == NTLSTATUS_ACCESS_DENIED ? 5 : 6; @@ -1922,7 +1931,7 @@ static BOOL K32IsConsoleOutputHandle(HANDLE h) { } // Implements the k32 Set Console Last Error operation. -static void K32SetConsoleLastError(uint32_t status) { +static void K32SetConsoleLastError(KSTATUS status) { switch (status) { case NTLSTATUS_SUCCESS: gLastError = 0; @@ -2011,7 +2020,7 @@ __declspec(dllexport) BOOL GetConsoleScreenBufferInfo(HANDLE hConsoleOutput, } NTL_CONSOLE_INFO info; - uint32_t s = NtlConsoleQueryInfo(SYS_HANDLE_INVALID, &info); + KSTATUS s = NtlConsoleQueryInfo(SYS_HANDLE_INVALID, &info); if (s != NTLSTATUS_SUCCESS) { K32SetConsoleLastError(s); return FALSE; @@ -2042,7 +2051,7 @@ __declspec(dllexport) BOOL SetConsoleCursorPosition(HANDLE hConsoleOutput, return FALSE; } - uint32_t s = NtlConsoleSetCursor(SYS_HANDLE_INVALID, (int)dwCursorPosition.X, (int)dwCursorPosition.Y); + KSTATUS s = NtlConsoleSetCursor(SYS_HANDLE_INVALID, (int)dwCursorPosition.X, (int)dwCursorPosition.Y); if (s != NTLSTATUS_SUCCESS) { K32SetConsoleLastError(s); return FALSE; @@ -2068,7 +2077,7 @@ __declspec(dllexport) BOOL FillConsoleOutputCharacterA(HANDLE hConsoleOutput, return TRUE; size_t written = 0; - uint32_t s = NtlConsoleFillCharacter(SYS_HANDLE_INVALID, cCharacter, + KSTATUS s = NtlConsoleFillCharacter(SYS_HANDLE_INVALID, cCharacter, (int)dwWriteCoord.X, (int)dwWriteCoord.Y, (size_t)nLength, &written); if (lpNumberOfCharsWritten) *lpNumberOfCharsWritten = (DWORD)written; @@ -2097,7 +2106,7 @@ __declspec(dllexport) BOOL FillConsoleOutputAttribute(HANDLE hConsoleOutput, return TRUE; size_t written = 0; - uint32_t s = NtlConsoleFillAttribute(SYS_HANDLE_INVALID, wAttribute, + KSTATUS s = NtlConsoleFillAttribute(SYS_HANDLE_INVALID, wAttribute, (int)dwWriteCoord.X, (int)dwWriteCoord.Y, (size_t)nLength, &written); if (lpNumberOfAttrsWritten) *lpNumberOfAttrsWritten = (DWORD)written; @@ -2123,22 +2132,22 @@ typedef struct { // Minimal wildcard match: supports '*' (any sequence) and '?' (any char). // Case-insensitive to match FAT32/Windows behaviour. -static int WildMatch(const char* pat, const char* name) { +static bool WildMatch(const char* pat, const char* name) { while (*pat && *name) { if (*pat == '*') { // Skip consecutive stars. while (*pat == '*') pat++; - if (!*pat) return 1; // trailing * matches everything + if (!*pat) return true; // trailing * matches everything // Try matching the rest of the pattern at every position. while (*name) { - if (WildMatch(pat, name)) return 1; + if (WildMatch(pat, name)) return true; name++; } - return 0; + return false; } char p = (*pat >= 'A' && *pat <= 'Z') ? *pat + 32 : *pat; char n = (*name >= 'A' && *name <= 'Z') ? *name + 32 : *name; - if (*pat != '?' && p != n) return 0; + if (*pat != '?' && p != n) return false; pat++; name++; } // Consume trailing stars in the pattern. @@ -2198,7 +2207,7 @@ static void SplitFindPath(const char* searchPath, #define FILE_ATTRIBUTE_NORMAL 0x00000080UL // Implements the k32 Dir Is Leap Year operation. -static int K32DirIsLeapYear(unsigned int year) { +static bool K32DirIsLeapYear(unsigned int year) { return ((year % 4u) == 0u && (year % 100u) != 0u) || ((year % 400u) == 0u); } @@ -2299,7 +2308,7 @@ __declspec(dllexport) HANDLE FindFirstFileA(const char* lpFileName, // Read the first entry that matches the pattern. KDIR_ENTRY ke; while (true) { - uint32_t rs = SysReadDir(kh, &ke); + KSTATUS rs = SysReadDir(kh, &ke); if (rs != 0) { // Directory exhausted or error. SysCloseHandle(kh); @@ -2325,7 +2334,7 @@ __declspec(dllexport) BOOL FindNextFileA(HANDLE hFindFile, KDIR_ENTRY ke; while (true) { - uint32_t rs = SysReadDir(fh->kh, &ke); + KSTATUS rs = SysReadDir(fh->kh, &ke); if (rs != 0) { gLastError = 18; // ERROR_NO_MORE_FILES return FALSE; @@ -2416,7 +2425,7 @@ __declspec(dllexport) HANDLE CreateMutexA(void* lpMutexAttributes, BOOL bInitial gLastError = 8; return NULL; } - uint32_t status = SysCreateMutex(&mutex->NativeHandle); + KSTATUS status = SysCreateMutex(&mutex->NativeHandle); if (status != NTLSTATUS_SUCCESS || !mutex->NativeHandle) { free(mutex); gLastError = status == NTLSTATUS_NO_MEMORY ? 8 : 1; @@ -2473,7 +2482,7 @@ __declspec(dllexport) BOOL ReleaseMutex(HANDLE hMutex) { mutex->OwnerTid = 0; K32MutexStateUnlock(mutex); - uint32_t status = SysReleaseMutex(mutex->NativeHandle); + KSTATUS status = SysReleaseMutex(mutex->NativeHandle); if (status != NTLSTATUS_SUCCESS) { K32MutexStateLock(mutex); mutex->OwnerTid = tid; @@ -2492,7 +2501,7 @@ __declspec(dllexport) HANDLE CreateSemaphoreA(void* lpSemaphoreAttributes, const char* lpName) { (void)lpSemaphoreAttributes; (void)lpName; uint32_t kh = 0; - uint32_t s = SysCreateSem((int32_t)lInitialCount, (int32_t)lMaximumCount, &kh); + KSTATUS s = SysCreateSem((int32_t)lInitialCount, (int32_t)lMaximumCount, &kh); if (s != 0 || !kh) { gLastError = 8; return NULL; } return KH_TO_HANDLE(kh); } @@ -2502,7 +2511,7 @@ __declspec(dllexport) BOOL ReleaseSemaphore(HANDLE hSemaphore, long lReleaseCoun if (!IS_VALID_KERNEL_HANDLE(hSemaphore) || lReleaseCount < 0) { gLastError = 6; return FALSE; } if (lpPreviousCount) *lpPreviousCount = 0; for (long i = 0; i < lReleaseCount; ++i) { - uint32_t s = SysSignalSem(HANDLE_TO_KH(hSemaphore)); + KSTATUS s = SysSignalSem(HANDLE_TO_KH(hSemaphore)); if (s != 0) { gLastError = 6; return FALSE; } } return TRUE; @@ -2515,7 +2524,7 @@ __declspec(dllexport) BOOL ReleaseSemaphore(HANDLE hSemaphore, long lReleaseCoun // races as soon as CreateThread started working. typedef struct K32_CONDITION_WAITER K32_CONDITION_WAITER; typedef struct K32_CONDITION_STATE { - volatile long Lock; + int32_t Lock; K32_CONDITION_WAITER* Head; K32_CONDITION_WAITER* Tail; } K32_CONDITION_STATE; @@ -2527,14 +2536,6 @@ struct K32_CONDITION_WAITER { BOOL Queued; }; -#define K32_CS_LOCK 0 -#define K32_CS_OWNER 1 -#define K32_CS_RECURSION 2 - -static volatile long* K32CsLockWord(CRITICAL_SECTION* cs) { - return (volatile long*)&cs->_opaque[K32_CS_LOCK]; -} - static DWORD K32CurrentTidOrOne(void) { DWORD tid = (DWORD)SysGetCurrentTid(); return tid ? tid : 1; @@ -2557,51 +2558,51 @@ __declspec(dllexport) void DeleteCriticalSection(CRITICAL_SECTION* lpCS) { (voi __declspec(dllexport) void EnterCriticalSection(CRITICAL_SECTION* lpCS) { if (!lpCS) return; DWORD tid = K32CurrentTidOrOne(); - if ((DWORD)lpCS->_opaque[K32_CS_OWNER] == tid) { - lpCS->_opaque[K32_CS_RECURSION]++; + if ((DWORD)lpCS->Owner == tid) { + lpCS->Recursion++; return; } uint32_t spins = 0; - while (_InterlockedCompareExchange(K32CsLockWord(lpCS), 1, 0) != 0) { + while (NtlAtomicCompareExchange32(&lpCS->Lock, 1, 0) != 0) { _mm_pause(); if ((++spins & 0xFFFu) == 0) (void)SysYield(); } - lpCS->_opaque[K32_CS_OWNER] = tid; - lpCS->_opaque[K32_CS_RECURSION] = 1; + lpCS->Owner = tid; + lpCS->Recursion = 1; } // Implements the leave Critical Section operation. __declspec(dllexport) void LeaveCriticalSection(CRITICAL_SECTION* lpCS) { if (!lpCS) return; DWORD tid = K32CurrentTidOrOne(); - if ((DWORD)lpCS->_opaque[K32_CS_OWNER] != tid || lpCS->_opaque[K32_CS_RECURSION] == 0) + if ((DWORD)lpCS->Owner != tid || lpCS->Recursion == 0) return; - lpCS->_opaque[K32_CS_RECURSION]--; - if (lpCS->_opaque[K32_CS_RECURSION] == 0) { - lpCS->_opaque[K32_CS_OWNER] = 0; + lpCS->Recursion--; + if (lpCS->Recursion == 0) { + lpCS->Owner = 0; _ReadWriteBarrier(); - _InterlockedExchange(K32CsLockWord(lpCS), 0); + NtlAtomicExchange32(&lpCS->Lock, 0); } } // Implements the try Enter Critical Section operation. __declspec(dllexport) BOOL TryEnterCriticalSection(CRITICAL_SECTION* lpCS) { if (!lpCS) return FALSE; DWORD tid = K32CurrentTidOrOne(); - if ((DWORD)lpCS->_opaque[K32_CS_OWNER] == tid) { - lpCS->_opaque[K32_CS_RECURSION]++; + if ((DWORD)lpCS->Owner == tid) { + lpCS->Recursion++; return TRUE; } - if (_InterlockedCompareExchange(K32CsLockWord(lpCS), 1, 0) != 0) + if (NtlAtomicCompareExchange32(&lpCS->Lock, 1, 0) != 0) return FALSE; - lpCS->_opaque[K32_CS_OWNER] = tid; - lpCS->_opaque[K32_CS_RECURSION] = 1; + lpCS->Owner = tid; + lpCS->Recursion = 1; return TRUE; } // Acquires one condition-variable queue lock. static void K32ConditionLock(K32_CONDITION_STATE* state) { uint32_t spins = 0; - while (_InterlockedCompareExchange(&state->Lock, 1, 0) != 0) { + while (NtlAtomicCompareExchange32(&state->Lock, 1, 0) != 0) { _mm_pause(); if ((++spins & 0xFFFu) == 0) (void)SysYield(); @@ -2611,7 +2612,7 @@ static void K32ConditionLock(K32_CONDITION_STATE* state) { // Releases one condition-variable queue lock. static void K32ConditionUnlock(K32_CONDITION_STATE* state) { _ReadWriteBarrier(); - _InterlockedExchange(&state->Lock, 0); + NtlAtomicExchange32(&state->Lock, 0); } // Returns the lazily allocated process-local condition-variable state. @@ -2627,7 +2628,7 @@ static K32_CONDITION_STATE* K32ConditionState(CONDITION_VARIABLE* cv, BOOL creat if (!allocated) return NULL; - void* previous = _InterlockedCompareExchangePointer((void* volatile*)&cv->_ptr, + void* previous = NtlAtomicCompareExchangePointer(&cv->_ptr, allocated, NULL); if (previous) { @@ -2676,8 +2677,8 @@ __declspec(dllexport) BOOL SleepConditionVariableCS(CONDITION_VARIABLE* cv, } DWORD tid = K32CurrentTidOrOne(); - uintptr_t recursion = cs->_opaque[K32_CS_RECURSION]; - if ((DWORD)cs->_opaque[K32_CS_OWNER] != tid || recursion == 0) { + uintptr_t recursion = cs->Recursion; + if ((DWORD)cs->Owner != tid || recursion == 0) { gLastError = 87; return FALSE; } @@ -2717,10 +2718,10 @@ __declspec(dllexport) BOOL SleepConditionVariableCS(CONDITION_VARIABLE* cv, // The waiter is visible before the lock is released, so a wake that races // this transition is retained by the private auto-reset event. - cs->_opaque[K32_CS_RECURSION] = 0; - cs->_opaque[K32_CS_OWNER] = 0; + cs->Recursion = 0; + cs->Owner = 0; _ReadWriteBarrier(); - _InterlockedExchange(K32CsLockWord(cs), 0); + NtlAtomicExchange32(&cs->Lock, 0); uint64_t timeout = dwMilliseconds == INFINITE ? SYS_WAIT_TIMEOUT_INFINITE @@ -2733,7 +2734,7 @@ __declspec(dllexport) BOOL SleepConditionVariableCS(CONDITION_VARIABLE* cv, K32ConditionUnlock(state); EnterCriticalSection(cs); - cs->_opaque[K32_CS_RECURSION] = recursion; + cs->Recursion = recursion; (void)SysCloseHandle(event); free(waiter); @@ -2783,7 +2784,7 @@ typedef union { long long QuadPart; } LARGE_INTEGER; -static volatile long long gTscFreq = 0; +static int64_t gTscFreq; // Attempt to read TSC frequency from CPUID leaf 0x15 (Modern Intel/AMD). static long long TscFreqFromCpuid(void) { @@ -2797,7 +2798,7 @@ static long long TscFreqFromCpuid(void) { unsigned crystal_hz = (unsigned)info[2]; if (!denom || !numer || !crystal_hz) return 0; - return (long long)crystal_hz * numer / denom; + return (int64_t)crystal_hz * numer / denom; } // Calibrates TSC against the native monotonic tick source. @@ -2822,18 +2823,21 @@ static long long TscFreqCalibrate(void) { uint64_t delta_us = SysTicksToMicroseconds(t1 - t0, frequency); if (!delta_us) return 0; - return (long long)(delta_tsc * 1000000ULL / delta_us); + return (int64_t)(delta_tsc * 1000000ULL / delta_us); } // Implements the tsc Freq Get operation. static long long TscFreqGet(void) { - if (gTscFreq) return gTscFreq; + int64_t cached = NtlAtomicLoad64(&gTscFreq); + if (cached) + return cached; - long long f = TscFreqFromCpuid(); - if (!f) f = TscFreqCalibrate(); + int64_t frequency = TscFreqFromCpuid(); + if (!frequency) + frequency = TscFreqCalibrate(); - gTscFreq = f; - return f; + int64_t previous = NtlAtomicCompareExchange64(&gTscFreq, frequency, 0); + return previous ? previous : frequency; } // Returns performance Frequency. @@ -2847,7 +2851,7 @@ __declspec(dllexport) BOOL QueryPerformanceCounter(LARGE_INTEGER* lpPerformanceC // Ensure calibration happens before the first read. TscFreqGet(); if (lpPerformanceCount) - lpPerformanceCount->QuadPart = (long long)__rdtsc(); + lpPerformanceCount->QuadPart = (int64_t)__rdtsc(); return TRUE; } @@ -3185,8 +3189,8 @@ typedef struct _RTL_EXCEPTION_FRAME { } RTL_EXCEPTION_FRAME; typedef struct WIN_VECTORED_HANDLER_ENTRY { - uint32_t Active; - uint32_t IsContinue; + bool Active; + bool IsContinue; uint32_t Previous; uint32_t Next; PVECTORED_EXCEPTION_HANDLER Handler; @@ -3201,7 +3205,7 @@ typedef struct WIN_SEH_FRAME_SNAPSHOT { void* UserContext; } WIN_SEH_FRAME_SNAPSHOT; -static volatile long gWinVectoredLock; +static int32_t gWinVectoredLock; static uint32_t gWinVectoredExceptionHead = UINT32_MAX; static uint32_t gWinVectoredExceptionTail = UINT32_MAX; static uint32_t gWinVectoredContinueHead = UINT32_MAX; @@ -3211,7 +3215,7 @@ static void* gWinExceptionNativeHandle; // Acquires the Win32 vectored-handler list lock. static void WinpVectoredLock(void) { - while (_InterlockedCompareExchange(&gWinVectoredLock, 1, 0) != 0) + while (NtlAtomicCompareExchange32(&gWinVectoredLock, 1, 0) != 0) _mm_pause(); _ReadWriteBarrier(); } @@ -3219,7 +3223,7 @@ static void WinpVectoredLock(void) { // Releases the Win32 vectored-handler list lock. static void WinpVectoredUnlock(void) { _ReadWriteBarrier(); - _InterlockedExchange(&gWinVectoredLock, 0); + NtlAtomicExchange32(&gWinVectoredLock, 0); } // Returns the list head slot for one vectored-handler kind. @@ -3440,7 +3444,7 @@ static int WinpRunSehFrames(EXCEPTION_POINTERS* pointers, SYS_USER_CONTEXT* nati } // Bridges the native exception runtime into Win32 exception policy. -static int WinpExceptionNativeDispatch(SYS_EXCEPTION_RECORD* record, SYS_USER_CONTEXT* context, void* userContext) { +static uint32_t WinpExceptionNativeDispatch(SYS_EXCEPTION_RECORD* record, SYS_USER_CONTEXT* context, void* userContext) { (void)userContext; EXCEPTION_RECORD winRecord; @@ -3654,7 +3658,7 @@ __declspec(dllexport) void RaiseException(DWORD dwExceptionCode, DWORD dwExcepti request.ParameterCount = count; request.Parameters = count ? arguments : NULL; - uint32_t status = SysRaiseException(&request); + KSTATUS status = SysRaiseException(&request); if (status != NTLSTATUS_SUCCESS) SysExitProcess((int32_t)status); } diff --git a/code/user/personalities/win32/winmm_waveout.c b/code/user/personalities/win32/winmm_waveout.c index ecc07ee..66912c8 100644 --- a/code/user/personalities/win32/winmm_waveout.c +++ b/code/user/personalities/win32/winmm_waveout.c @@ -1,5 +1,7 @@ // WinMM waveform output over the native render-stream syscall ABI. +#include +#include #include #include #include @@ -158,15 +160,15 @@ typedef struct WINMM_WAVE_BUFFER { uint32_t FrameCount; uint32_t SubmittedFrames; uint64_t PresentedTarget; - uint8_t FullySubmitted; + bool FullySubmitted; uint64_t Sequence; } WINMM_WAVE_BUFFER; typedef struct WINMM_WAVEOUT { struct WINMM_WAVEOUT* RegistryNext; uint32_t Magic; - volatile long Lock; - volatile long ApiUsers; + int32_t Lock; + int32_t ApiUsers; uint32_t NativeStream; uint32_t WakeEvent; uint32_t CommandEvent; @@ -180,10 +182,10 @@ typedef struct WINMM_WAVEOUT { uint32_t CallbackMode; uint32_t DeviceId; uint32_t Volume; - uint32_t Closing; - uint32_t UserPaused; - uint32_t AutoPaused; - uint32_t Started; + bool Closing; + bool UserPaused; + bool AutoPaused; + bool Started; uint32_t TerminalError; WINMM_WAVE_COMMAND Command; MMRESULT CommandResult; @@ -198,7 +200,7 @@ typedef struct WINMM_WAVEOUT { } WINMM_WAVEOUT; static WINMM_WAVEOUT* gWaveOutRegistry; -static volatile long gWaveOutRegistryLock; +static int32_t gWaveOutRegistryLock; typedef struct WINMM_DEBUG_LINE { @@ -230,12 +232,12 @@ static void WinmmDebugU64(WINMM_DEBUG_LINE* line, uint64_t value) { static void WinmmDebugHex(WINMM_DEBUG_LINE* line, uint64_t value) { static const char digits[] = "0123456789ABCDEF"; WinmmDebugText(line, "0x"); - int started = 0; + bool started = false; for (int shift = 60; shift >= 0; shift -= 4) { uint8_t digit = (uint8_t)((value >> shift) & 0xFu); if (!started && digit == 0 && shift) continue; - started = 1; + started = true; if (line->Length + 1 < sizeof(line->Buffer)) line->Buffer[line->Length++] = digits[digit]; } @@ -295,14 +297,14 @@ static void WinmmDebugResult(const char* operation, } // Acquires one short user-mode spin lock without entering the kernel. -static void WinmmLock(volatile long* lock) { - while (_InterlockedCompareExchange(lock, 1, 0) != 0) +static void WinmmLock(int32_t* lock) { + while (NtlAtomicCompareExchange32(lock, 1, 0) != 0) (void)SysYield(); } // Releases one short user-mode spin lock. -static void WinmmUnlock(volatile long* lock) { - _InterlockedExchange(lock, 0); +static void WinmmUnlock(int32_t* lock) { + NtlAtomicExchange32(lock, 0); } // Maps one native status to the closest waveform-open result. @@ -321,7 +323,7 @@ static MMRESULT WinmmMapNativeStatus(KSTATUS status) { } // Returns whether one source rate is accepted by the native stream ABI. -static int WinmmRateIsSupported(uint32_t sampleRate) { +static bool WinmmRateIsSupported(uint32_t sampleRate) { return sampleRate == 11025u || sampleRate == 22050u || sampleRate == 44100u || sampleRate == 48000u; } @@ -439,7 +441,7 @@ static WINMM_WAVEOUT* WinmmReferenceWaveOut(HWAVEOUT handle) { current; current = current->RegistryNext) { if ((HWAVEOUT)current == handle && current->Magic == WINMM_WAVEOUT_MAGIC) { - _InterlockedIncrement(¤t->ApiUsers); + NtlAtomicIncrement32(¤t->ApiUsers); found = current; break; } @@ -451,19 +453,19 @@ static WINMM_WAVEOUT* WinmmReferenceWaveOut(HWAVEOUT handle) { // Releases one API-use reference acquired through the registry. static void WinmmReleaseWaveOut(WINMM_WAVEOUT* waveOut) { if (waveOut) - _InterlockedDecrement(&waveOut->ApiUsers); + NtlAtomicDecrement32(&waveOut->ApiUsers); } // Removes one waveform handle so no new API references can be acquired. -static int WinmmRemoveWaveOut(WINMM_WAVEOUT* waveOut) { - int removed = 0; +static bool WinmmRemoveWaveOut(WINMM_WAVEOUT* waveOut) { + bool removed = false; WinmmLock(&gWaveOutRegistryLock); WINMM_WAVEOUT** link = &gWaveOutRegistry; while (*link) { if (*link == waveOut) { *link = waveOut->RegistryNext; waveOut->RegistryNext = NULL; - removed = 1; + removed = true; break; } link = &(*link)->RegistryNext; @@ -713,7 +715,7 @@ static KSTATUS WinmmFeedNativeStream(WINMM_WAVEOUT* waveOut, } // Determines whether any queued buffer still needs native-ring submission. -static int WinmmHasUnsubmittedBuffer(WINMM_WAVEOUT* waveOut) { +static bool WinmmHasUnsubmittedBuffer(WINMM_WAVEOUT* waveOut) { return WinmmFindSubmitNode(waveOut) != NULL; } @@ -751,7 +753,7 @@ static void WinmmStopEmptyStream(WINMM_WAVEOUT* waveOut) { // Arms native progress conditions and waits for either stream progress or new work. static KSTATUS WinmmWaitForWork(WINMM_WAVEOUT* waveOut, const SYS_AUDIO_STREAM_INFO* info, - int hasUnsubmitted) { + bool hasUnsubmitted) { SYS_AUDIO_ARM_WAIT_REQUEST arm; memset(&arm, 0, sizeof(arm)); arm.Size = sizeof(arm); @@ -877,7 +879,7 @@ static void WinmmWaveWorker(uintptr_t argument) { WinmmFailStream(waveOut, WinmmMapNativeStatus(status)); continue; } - int hasUnsubmitted = WinmmHasUnsubmittedBuffer(waveOut); + bool hasUnsubmitted = WinmmHasUnsubmittedBuffer(waveOut); if (!hasUnsubmitted && waveOut->Started && !waveOut->UserPaused && !waveOut->AutoPaused && info.FramesConsumed >= waveOut->NativeSubmitted) { status = SysAudioPause(waveOut->NativeStream); @@ -1088,7 +1090,7 @@ __declspec(dllexport) MMRESULT waveOutClose(HWAVEOUT handle) { } WinmmLock(&waveOut->Lock); - int busy = waveOut->QueueHead != NULL || waveOut->Command != WinmmWaveCommandNone; + bool busy = waveOut->QueueHead != NULL || waveOut->Command != WinmmWaveCommandNone; WinmmUnlock(&waveOut->Lock); if (busy) { WinmmDebugResult("close still playing", waveOut, waveOut->HeadersQueued - waveOut->HeadersCompleted); @@ -1103,7 +1105,7 @@ __declspec(dllexport) MMRESULT waveOutClose(HWAVEOUT handle) { waveOut->Closing = 1; (void)SysSignalEvent(waveOut->WakeEvent); KSTATUS status = SysWait(waveOut->WorkerHandle, SYS_WAIT_FLAG_NONE); - while (waveOut->ApiUsers > 1) + while (NtlAtomicLoad32(&waveOut->ApiUsers) > 1) (void)SysYield(); waveOut->Magic = 0; diff --git a/code/user/runtime/heap.c b/code/user/runtime/heap.c index 7af9dd0..8b0ab7c 100644 --- a/code/user/runtime/heap.c +++ b/code/user/runtime/heap.c @@ -1,5 +1,7 @@ // User-mode page-backed heap. This file owns segment allocation, block splitting, coalescing, and exported allocation primitives. +#include +#include #include #include #include @@ -31,12 +33,12 @@ typedef struct HEAP_SEGMENT { HEAP_BLOCK* First; } HEAP_SEGMENT; -static volatile long gHeapLock; +static volatile int32_t gHeapLock; static HEAP_SEGMENT* gSegments; // Acquires the process-local heap lock. static void HeapLock(void) { - while (_InterlockedCompareExchange(&gHeapLock, 1, 0) != 0) + while (NtlAtomicCompareExchange32(&gHeapLock, 1, 0) != 0) _mm_pause(); _ReadWriteBarrier(); } @@ -44,7 +46,7 @@ static void HeapLock(void) { // Releases the process-local heap lock. static void HeapUnlock(void) { _ReadWriteBarrier(); - _InterlockedExchange(&gHeapLock, 0); + NtlAtomicExchange32(&gHeapLock, 0); } // Rounds requested allocation sizes up to the heap alignment while preserving non-zero allocation semantics. diff --git a/code/user/runtime/native_exception.c b/code/user/runtime/native_exception.c index b9b3d0f..6404425 100644 --- a/code/user/runtime/native_exception.c +++ b/code/user/runtime/native_exception.c @@ -1,5 +1,7 @@ // Native user-mode exception runtime. This file owns process-local native handler dispatch. +#include +#include #include #include #include @@ -10,7 +12,7 @@ #define NTL_EXCEPTION_MAX_HANDLERS 16u typedef struct NTL_EXCEPTION_HANDLER_ENTRY { - uint32_t Active; + bool Active; uint32_t Previous; uint32_t Next; NTL_EXCEPTION_HANDLER Handler; @@ -22,14 +24,14 @@ typedef struct NTL_EXCEPTION_HANDLER_SNAPSHOT { void* Context; } NTL_EXCEPTION_HANDLER_SNAPSHOT; -static volatile long gNtlExceptionLock; +static volatile int32_t gNtlExceptionLock; static uint32_t gNtlExceptionListHead = UINT32_MAX; static uint32_t gNtlExceptionListTail = UINT32_MAX; static NTL_EXCEPTION_HANDLER_ENTRY gNtlExceptionHandlers[NTL_EXCEPTION_MAX_HANDLERS]; // Acquires the process-local exception handler lock. static void NtlpExceptionLock(void) { - while (_InterlockedCompareExchange(&gNtlExceptionLock, 1, 0) != 0) + while (NtlAtomicCompareExchange32(&gNtlExceptionLock, 1, 0) != 0) _mm_pause(); _ReadWriteBarrier(); } @@ -37,11 +39,11 @@ static void NtlpExceptionLock(void) { // Releases the process-local exception handler lock. static void NtlpExceptionUnlock(void) { _ReadWriteBarrier(); - _InterlockedExchange(&gNtlExceptionLock, 0); + NtlAtomicExchange32(&gNtlExceptionLock, 0); } // Returns a generic status after a failed exception-continue syscall. -static uint32_t NtlpStatusOrFailure(uint32_t status) { +static KSTATUS NtlpStatusOrFailure(KSTATUS status) { return status == NTLSTATUS_SUCCESS ? NTLSTATUS_FAILURE : status; } @@ -52,7 +54,7 @@ static void NtlpTerminateExceptionDispatch(void) { request.Size = sizeof(request); request.Disposition = SYS_EXCEPTION_DISPOSITION_TERMINATE_PROCESS; - uint32_t status = SysContinueException(&request); + KSTATUS status = SysContinueException(&request); while (true) SysExitProcess((int32_t)NtlpStatusOrFailure(status)); } @@ -63,7 +65,7 @@ static void NtlpContinueExceptionSearch(void) { request.Size = sizeof(request); request.Disposition = SYS_EXCEPTION_DISPOSITION_CONTINUE_SEARCH; - uint32_t status = SysContinueException(&request); + KSTATUS status = SysContinueException(&request); while (true) SysExitProcess((int32_t)NtlpStatusOrFailure(status)); } @@ -75,23 +77,23 @@ static void NtlpContinueExecution(SYS_USER_CONTEXT* context) { request.Disposition = SYS_EXCEPTION_DISPOSITION_CONTINUE_EXECUTION; request.Context = context; - uint32_t status = SysContinueException(&request); + KSTATUS status = SysContinueException(&request); while (true) SysExitProcess((int32_t)NtlpStatusOrFailure(status)); } // Returns whether one kernel-provided exception frame has the expected ABI shape. -static int NtlpFrameIsValid(SYS_EXCEPTION_FRAME* frame) { +static bool NtlpFrameIsValid(SYS_EXCEPTION_FRAME* frame) { if (!frame) - return 0; + return false; if (frame->Size != sizeof(*frame) || frame->Version != SYS_EXCEPTION_ABI_VERSION) - return 0; + return false; if (frame->ExceptionRecord.Size != sizeof(frame->ExceptionRecord) || frame->ExceptionRecord.Version != SYS_EXCEPTION_ABI_VERSION) - return 0; + return false; if (frame->ContextRecord.Size != sizeof(frame->ContextRecord) || frame->ContextRecord.Version != SYS_EXCEPTION_ABI_VERSION) - return 0; - return 1; + return false; + return true; } // Copies the current handler list so callbacks can run without holding the list lock. @@ -139,13 +141,13 @@ static void NtlpUserExceptionDispatcher(SYS_EXCEPTION_FRAME* frame) { } // Registers the native dispatcher for the current process when no other dispatcher owns the process. -NTL_EXCEPTION_API uint32_t NtlInitializeExceptionRuntime(void) { +NTL_EXCEPTION_API KSTATUS NtlInitializeExceptionRuntime(void) { NtlpExceptionLock(); SYS_EXCEPTION_DISPATCHER_REQUEST current; memset(¤t, 0, sizeof(current)); current.Size = sizeof(current); - uint32_t status = SysGetExceptionDispatcher(¤t); + KSTATUS status = SysGetExceptionDispatcher(¤t); if (status != NTLSTATUS_SUCCESS) { NtlpExceptionUnlock(); return status; @@ -171,7 +173,7 @@ NTL_EXCEPTION_API uint32_t NtlInitializeExceptionRuntime(void) { } // Adds one process-local native exception handler. -NTL_EXCEPTION_API void* NtlAddExceptionHandler(int first, NTL_EXCEPTION_HANDLER handler, void* context) { +NTL_EXCEPTION_API void* NtlAddExceptionHandler(bool first, NTL_EXCEPTION_HANDLER handler, void* context) { if (!handler) return 0; if (NtlInitializeExceptionRuntime() != NTLSTATUS_SUCCESS) @@ -217,7 +219,7 @@ NTL_EXCEPTION_API void* NtlAddExceptionHandler(int first, NTL_EXCEPTION_HANDLER } // Removes one process-local native exception handler. -NTL_EXCEPTION_API uint32_t NtlRemoveExceptionHandler(void* handle) { +NTL_EXCEPTION_API KSTATUS NtlRemoveExceptionHandler(void* handle) { if (!handle) return NTLSTATUS_INVALID_PARAMETER; diff --git a/code/user/runtime/native_start.c b/code/user/runtime/native_start.c index 765862f..066ef2e 100644 --- a/code/user/runtime/native_start.c +++ b/code/user/runtime/native_start.c @@ -1,5 +1,6 @@ // Native process startup. This file owns process-local native runtime initialization before main runs. +#include #include #include #include @@ -8,7 +9,7 @@ extern int main(void); // Initializes native process state and exits with main's return value. void NtlNativeStart(void) { - uint32_t status = NtlInitializeExceptionRuntime(); + KSTATUS status = NtlInitializeExceptionRuntime(); if (status != NTLSTATUS_SUCCESS) { while (true) SysExitProcess((int32_t)status); } diff --git a/code/user/runtime/posix_start.c b/code/user/runtime/posix_start.c index 6ba067e..6433440 100644 --- a/code/user/runtime/posix_start.c +++ b/code/user/runtime/posix_start.c @@ -1,5 +1,6 @@ // POSIX process startup. This file owns process-local POSIX personality initialization before main runs. +#include #include #include #include @@ -13,7 +14,7 @@ __declspec(dllimport) uint32_t __ntl_posix_process_vectors(int* argcOut, // Initializes POSIX process state and exits with main's return value. void PosixStart(void) { - uint32_t status = NtlInitializeExceptionRuntime(); + KSTATUS status = NtlInitializeExceptionRuntime(); if (status != NTLSTATUS_SUCCESS) { while (true) SysExitProcess((int32_t)status); } diff --git a/code/user/syscall/console_service.c b/code/user/syscall/console_service.c index fa4d1ca..538035f 100644 --- a/code/user/syscall/console_service.c +++ b/code/user/syscall/console_service.c @@ -1,5 +1,6 @@ // Native console client. RSSRV owns the logical console; this file speaks its Port protocol. +#include #include #include #include @@ -7,7 +8,7 @@ #include #include -static volatile long gDefaultConsoleSession; +static volatile uint32_t gDefaultConsoleSession; static KSTATUS NtlConsoleGetDefaultSession(SYS_HANDLE* sessionOut); // Connects to RSSRV's native console service. @@ -40,9 +41,9 @@ KSTATUS NtlConsoleClose(SYS_HANDLE session) { // Returns one process-local cached console session. static KSTATUS NtlConsoleGetDefaultSession(SYS_HANDLE* sessionOut) { if (!sessionOut) return NTLSTATUS_INVALID_PARAMETER; - long current = gDefaultConsoleSession; + uint32_t current = gDefaultConsoleSession; if (current != 0) { - *sessionOut = (SYS_HANDLE)(uint32_t)current; + *sessionOut = (SYS_HANDLE)current; return NTLSTATUS_SUCCESS; } @@ -50,10 +51,10 @@ static KSTATUS NtlConsoleGetDefaultSession(SYS_HANDLE* sessionOut) { KSTATUS status = NtlConsoleConnect(&created); if (!KSUCCESS(status)) return status; - long previous = _InterlockedCompareExchange(&gDefaultConsoleSession, (long)created, 0); + uint32_t previous = NtlAtomicCompareExchangeU32(&gDefaultConsoleSession, created, 0); if (previous != 0) { (void)NtlConsoleClose(created); - created = (SYS_HANDLE)(uint32_t)previous; + created = (SYS_HANDLE)previous; } *sessionOut = created; return NTLSTATUS_SUCCESS; @@ -152,7 +153,7 @@ static KSTATUS NtlConsoleSimpleCall(SYS_HANDLE session, NTL_CONSOLE_REQUEST* req } // Updates the logical console cursor. -KSTATUS NtlConsoleSetCursor(SYS_HANDLE session, int x, int y) { +KSTATUS NtlConsoleSetCursor(SYS_HANDLE session, int32_t x, int32_t y) { NTL_CONSOLE_REQUEST request; memset(&request, 0, sizeof(request)); request.Size = sizeof(request); @@ -164,7 +165,7 @@ KSTATUS NtlConsoleSetCursor(SYS_HANDLE session, int x, int y) { } // Fills console characters over one linear cell range. -KSTATUS NtlConsoleFillCharacter(SYS_HANDLE session, char character, int x, int y, +KSTATUS NtlConsoleFillCharacter(SYS_HANDLE session, char character, int32_t x, int32_t y, size_t length, size_t* writtenOut) { if (writtenOut) *writtenOut = 0; if (length > UINT32_MAX) return NTLSTATUS_INVALID_PARAMETER; @@ -184,7 +185,7 @@ KSTATUS NtlConsoleFillCharacter(SYS_HANDLE session, char character, int x, int y } // Fills console attributes over one linear cell range. -KSTATUS NtlConsoleFillAttribute(SYS_HANDLE session, uint16_t attribute, int x, int y, +KSTATUS NtlConsoleFillAttribute(SYS_HANDLE session, uint16_t attribute, int32_t x, int32_t y, size_t length, size_t* writtenOut) { if (writtenOut) *writtenOut = 0; if (length > UINT32_MAX) return NTLSTATUS_INVALID_PARAMETER; @@ -204,7 +205,7 @@ KSTATUS NtlConsoleFillAttribute(SYS_HANDLE session, uint16_t attribute, int x, i } // Draws one glyph at explicit pixel coordinates without moving console state. -KSTATUS NtlConsolePutCharacter(SYS_HANDLE session, char character, int x, int y, +KSTATUS NtlConsolePutCharacter(SYS_HANDLE session, char character, int32_t x, int32_t y, uint32_t foreground, uint32_t background) { NTL_CONSOLE_REQUEST request; memset(&request, 0, sizeof(request)); diff --git a/code/user/syscall/desktop.c b/code/user/syscall/desktop.c index d5ea5af..feb4685 100644 --- a/code/user/syscall/desktop.c +++ b/code/user/syscall/desktop.c @@ -111,7 +111,7 @@ KSTATUS NtlDesktopSetBounds(SYS_HANDLE session, NTL_WINDOW_ID window, const DISP } // Shows or hides one window without changing its z-order. -KSTATUS NtlDesktopSetVisible(SYS_HANDLE session, NTL_WINDOW_ID window, uint32_t visible) { +KSTATUS NtlDesktopSetVisible(SYS_HANDLE session, NTL_WINDOW_ID window, bool visible) { NTL_DESKTOP_REQUEST request; NtlDesktopInitRequest(&request, NTL_DESKTOP_OP_SET_VISIBLE, window); request.Value = visible ? 1u : 0u; diff --git a/code/user/syscall/input_service.c b/code/user/syscall/input_service.c index 93000a6..d0f7e2c 100644 --- a/code/user/syscall/input_service.c +++ b/code/user/syscall/input_service.c @@ -1,5 +1,7 @@ // Native logical-input client. RSSRV owns keyboard interpretation and pointer policy; this file speaks its Port protocol. +#include +#include #include #include #include @@ -7,7 +9,7 @@ #include #include -static volatile long gDefaultInputSession; +static volatile uint32_t gDefaultInputSession; static KSTATUS NtlInputGetDefaultSession(SYS_HANDLE* sessionOut); // Connects to the RSSRV listener published at /Services/Input. @@ -152,9 +154,9 @@ KSTATUS NtlInputQueryState(SYS_HANDLE session, NTL_INPUT_STATE* stateOut) { // Returns one process-local cached input session, connecting it on first use. static KSTATUS NtlInputGetDefaultSession(SYS_HANDLE* sessionOut) { if (!sessionOut) return NTLSTATUS_INVALID_PARAMETER; - long current = gDefaultInputSession; + uint32_t current = gDefaultInputSession; if (current != 0) { - *sessionOut = (SYS_HANDLE)(uint32_t)current; + *sessionOut = (SYS_HANDLE)current; return NTLSTATUS_SUCCESS; } @@ -162,10 +164,10 @@ static KSTATUS NtlInputGetDefaultSession(SYS_HANDLE* sessionOut) { KSTATUS status = NtlInputConnect(&created); if (!KSUCCESS(status)) return status; - long previous = _InterlockedCompareExchange(&gDefaultInputSession, (long)created, 0); + uint32_t previous = NtlAtomicCompareExchangeU32(&gDefaultInputSession, created, 0); if (previous != 0) { (void)NtlInputClose(created); - created = (SYS_HANDLE)(uint32_t)previous; + created = (SYS_HANDLE)previous; } *sessionOut = created; return NTLSTATUS_SUCCESS; @@ -239,7 +241,7 @@ KSTATUS NtlInputQueryDefaultState(NTL_INPUT_STATE* stateOut) { } // Returns the current RSSRV-owned logical pointer position. -KSTATUS NtlInputGetMousePos(int* xOut, int* yOut) { +KSTATUS NtlInputGetMousePos(int32_t* xOut, int32_t* yOut) { if (!xOut || !yOut) return NTLSTATUS_INVALID_PARAMETER; NTL_INPUT_STATE state; KSTATUS status = NtlInputQueryDefaultState(&state); diff --git a/code/user/syscall/module_loader.c b/code/user/syscall/module_loader.c index 45856e2..3a997e5 100644 --- a/code/user/syscall/module_loader.c +++ b/code/user/syscall/module_loader.c @@ -1,5 +1,7 @@ // Userspace PE module registry and dependency loader. Kernel process objects know only VADs. +#include +#include #include #include #include @@ -71,8 +73,8 @@ struct NTL_MODULE_CONTEXT { }; static NTL_MODULE_CONTEXT gCurrentContext; -static volatile long gCurrentContextState; -static volatile long gCurrentContextBusy; +static volatile int32_t gCurrentContextState; +static volatile int32_t gCurrentContextBusy; // Converts one ASCII character to lowercase for module-name comparisons. static char NtlpLower(char c) { @@ -80,14 +82,14 @@ static char NtlpLower(char c) { } // Compares one string view with a module name without case sensitivity. -static int NtlpViewEqualsNameI(SYS_STRING_VIEW view, const char* name) { - if (!name) return 0; +static bool NtlpViewEqualsNameI(SYS_STRING_VIEW view, const char* name) { + if (!name) return false; size_t n = strlen(name); - if (view.Length != n) return 0; + if (view.Length != n) return false; for (size_t i = 0; i < n; ++i) { - if (NtlpLower(view.Data[i]) != NtlpLower(name[i])) return 0; + if (NtlpLower(view.Data[i]) != NtlpLower(name[i])) return false; } - return 1; + return true; } // Allocates page-backed userspace metadata and clears the complete allocation. @@ -387,7 +389,7 @@ KSTATUS NtlpCreateModuleContext(SYS_HANDLE processHandle, SYS_STRING_VIEW fallbackModuleDirectory, NTL_MODULE_NAME_RESOLVER resolver, void* resolverContext, - int seedCurrentProcess, + bool seedCurrentProcess, NTL_MODULE_CONTEXT** contextOut) { if (!contextOut || primaryModuleRootHandle == SYS_HANDLE_INVALID || !SysSvIsValid(primaryModuleDirectory) || SysSvContainsByte(primaryModuleDirectory, '\0') || @@ -830,14 +832,14 @@ static KSTATUS NtlpEnsureCurrentContext(SYS_HANDLE moduleRoot, void* resolverContext, NTL_MODULE_CONTEXT** contextOut) { if (!contextOut) return NTLSTATUS_INVALID_PARAMETER; - if (_InterlockedCompareExchange(&gCurrentContextState, 1, 0) == 0) { + if (NtlAtomicCompareExchange32(&gCurrentContextState, 1, 0) == 0) { NTL_MODULE_CONTEXT* temporary = 0; KSTATUS status = NtlpCreateModuleContext(SYS_HANDLE_INVALID, moduleRoot, moduleDirectory, SYS_HANDLE_INVALID, SysSvFromParts((const char*)0, 0), SYS_HANDLE_INVALID, SysSvFromParts((const char*)0, 0), resolver, resolverContext, 1, &temporary); if (!KSUCCESS(status)) { - _InterlockedExchange(&gCurrentContextState, 0); + NtlAtomicExchange32(&gCurrentContextState, 0); return status; } gCurrentContext = *temporary; @@ -845,10 +847,10 @@ static KSTATUS NtlpEnsureCurrentContext(SYS_HANDLE moduleRoot, temporary->Head = 0; temporary->Magic = 0; NtlpFree(temporary); - _InterlockedExchange(&gCurrentContextState, 2); + NtlAtomicExchange32(&gCurrentContextState, 2); } else { - while (_InterlockedCompareExchange(&gCurrentContextState, 2, 2) == 1) SysYield(); - if (_InterlockedCompareExchange(&gCurrentContextState, 2, 2) != 2) + while (NtlAtomicCompareExchange32(&gCurrentContextState, 2, 2) == 1) SysYield(); + if (NtlAtomicCompareExchange32(&gCurrentContextState, 2, 2) != 2) return NTLSTATUS_FAILURE; gCurrentContext.PrimaryModuleRootHandle = moduleRoot; gCurrentContext.PrimaryModuleDirectory = moduleDirectory; @@ -865,13 +867,13 @@ static KSTATUS NtlpEnsureCurrentContext(SYS_HANDLE moduleRoot, // Serializes current-process module mutations because loader metadata is process-global. static KSTATUS NtlpEnterCurrent(void) { - while (_InterlockedCompareExchange(&gCurrentContextBusy, 1, 0) != 0) SysYield(); + while (NtlAtomicCompareExchange32(&gCurrentContextBusy, 1, 0) != 0) SysYield(); return NTLSTATUS_SUCCESS; } // Releases the current-process loader serialization guard. static void NtlpLeaveCurrent(void) { - _InterlockedExchange(&gCurrentContextBusy, 0); + NtlAtomicExchange32(&gCurrentContextBusy, 0); } // Loads or references one DLL-like PE in the current process through capability-relative I/O. @@ -961,7 +963,7 @@ KSTATUS NtlLoadModuleAt(const NTL_LOAD_MODULE_AT_REQUEST* request) { KSTATUS NtlUnloadModule(NTL_MODULE_HANDLE module) { KSTATUS status = NtlpEnterCurrent(); if (!KSUCCESS(status)) return status; - if (_InterlockedCompareExchange(&gCurrentContextState, 2, 2) != 2) { + if (NtlAtomicCompareExchange32(&gCurrentContextState, 2, 2) != 2) { NtlpLeaveCurrent(); return NTLSTATUS_INVALID_HANDLE; } @@ -985,7 +987,7 @@ KSTATUS NtlGetModuleInfo(NTL_MODULE_HANDLE module, NTL_MODULE_INFO* infoOut) { if (!infoOut) return NTLSTATUS_INVALID_PARAMETER; KSTATUS status = NtlpEnterCurrent(); if (!KSUCCESS(status)) return status; - if (_InterlockedCompareExchange(&gCurrentContextState, 2, 2) != 2) { + if (NtlAtomicCompareExchange32(&gCurrentContextState, 2, 2) != 2) { NtlpLeaveCurrent(); return NTLSTATUS_INVALID_HANDLE; } @@ -1001,7 +1003,7 @@ KSTATUS NtlGetModuleExport(NTL_MODULE_HANDLE module, SYS_STRING_VIEW name, uintp if (!addressOut) return NTLSTATUS_INVALID_PARAMETER; KSTATUS status = NtlpEnterCurrent(); if (!KSUCCESS(status)) return status; - if (_InterlockedCompareExchange(&gCurrentContextState, 2, 2) != 2) { + if (NtlAtomicCompareExchange32(&gCurrentContextState, 2, 2) != 2) { NtlpLeaveCurrent(); return NTLSTATUS_INVALID_HANDLE; } @@ -1017,7 +1019,7 @@ KSTATUS NtlGetModuleList(NTL_MODULE_INFO* modulesOut, size_t maxCount, size_t* c if (!countOut) return NTLSTATUS_INVALID_PARAMETER; KSTATUS status = NtlpEnterCurrent(); if (!KSUCCESS(status)) return status; - if (_InterlockedCompareExchange(&gCurrentContextState, 2, 2) != 2) { + if (NtlAtomicCompareExchange32(&gCurrentContextState, 2, 2) != 2) { *countOut = 0; NtlpLeaveCurrent(); return NTLSTATUS_SUCCESS; diff --git a/code/user/syscall/module_loader_internal.h b/code/user/syscall/module_loader_internal.h index 3f4d5b3..d25d954 100644 --- a/code/user/syscall/module_loader_internal.h +++ b/code/user/syscall/module_loader_internal.h @@ -1,6 +1,7 @@ // Internal shared state for userspace PE dependency loading. #pragma once +#include #include #include @@ -19,7 +20,7 @@ KSTATUS NtlpCreateModuleContext(SYS_HANDLE processHandle, SYS_STRING_VIEW fallbackModuleDirectory, NTL_MODULE_NAME_RESOLVER resolver, void* resolverContext, - int seedCurrentProcess, + bool seedCurrentProcess, NTL_MODULE_CONTEXT** contextOut); // Releases loader metadata without releasing image mappings owned elsewhere. void NtlpDestroyModuleContext(NTL_MODULE_CONTEXT* context); diff --git a/code/user/syscall/path.c b/code/user/syscall/path.c index 1815d77..8bd9d07 100644 --- a/code/user/syscall/path.c +++ b/code/user/syscall/path.c @@ -1,5 +1,7 @@ // Native userspace path policy. RSSRV resolves global resource prefixes; kernel I/O sees only capabilities and relative paths. +#include +#include #include #include #include @@ -14,49 +16,49 @@ typedef struct NTL_RESOLVED_ROOT { size_t ResolvedLength; } NTL_RESOLVED_ROOT; -static volatile long gPathState; -static volatile long gPathLock; +static volatile int32_t gPathState; +static volatile int32_t gPathLock; static char gCurrentDirectory[SYS_PATH_MAX + 1u]; static size_t gCurrentDirectoryLength; static size_t gCurrentDirectoryRootLength; // Acquires the tiny userspace CWD lock without introducing a kernel object. static void NtlpLockPathState(void) { - while (_InterlockedCompareExchange(&gPathLock, 1, 0) != 0) + while (NtlAtomicCompareExchange32(&gPathLock, 1, 0) != 0) (void)SysYield(); } // Releases the userspace CWD lock. static void NtlpUnlockPathState(void) { - _InterlockedExchange(&gPathLock, 0); + NtlAtomicExchange32(&gPathLock, 0); } // Initializes the process-local CWD from opaque loader data, or /SystemRoot for bootstrap init. static KSTATUS NtlpEnsurePathState(void) { - if (_InterlockedCompareExchange(&gPathState, 2, 2) == 2) + if (NtlAtomicCompareExchange32(&gPathState, 2, 2) == 2) return NTLSTATUS_SUCCESS; - if (_InterlockedCompareExchange(&gPathState, 1, 0) == 0) { + if (NtlAtomicCompareExchange32(&gPathState, 1, 0) == 0) { SYS_STRING_VIEW inherited; KSTATUS status = NtlpGetLoaderCurrentDirectory(&inherited); if (!KSUCCESS(status)) inherited = SYS_SV_LITERAL("/SystemRoot"); if (!SysSvIsValid(inherited) || inherited.Length == 0 || inherited.Length > SYS_PATH_MAX || inherited.Data[0] != '/' || SysSvContainsByte(inherited, '\\') || SysSvContainsByte(inherited, '\0')) { - _InterlockedExchange(&gPathState, 0); + NtlAtomicExchange32(&gPathState, 0); return NTLSTATUS_FAILURE; } memcpy(gCurrentDirectory, inherited.Data, inherited.Length); gCurrentDirectory[inherited.Length] = '\0'; gCurrentDirectoryLength = inherited.Length; gCurrentDirectoryRootLength = 0; - _InterlockedExchange(&gPathState, 2); + NtlAtomicExchange32(&gPathState, 2); return NTLSTATUS_SUCCESS; } - while (_InterlockedCompareExchange(&gPathState, 2, 2) == 1) + while (NtlAtomicCompareExchange32(&gPathState, 2, 2) == 1) (void)SysYield(); - return _InterlockedCompareExchange(&gPathState, 2, 2) == 2 + return NtlAtomicCompareExchange32(&gPathState, 2, 2) == 2 ? NTLSTATUS_SUCCESS : NTLSTATUS_FAILURE; } diff --git a/code/user/syscall/process_loader.c b/code/user/syscall/process_loader.c index 3c44dd4..da97464 100644 --- a/code/user/syscall/process_loader.c +++ b/code/user/syscall/process_loader.c @@ -1,5 +1,6 @@ // Native userspace process construction built on capability-relative file open and PE materialization. +#include #include #include #include @@ -15,26 +16,42 @@ static void NtlDiscardChild(SYS_HANDLE processHandle) { (void)SysCloseHandle(processHandle); } -#define NTL_PROCESS_BUILDER_MAGIC 0x3142504Eu +#define NTL_PREPARATION_PAGE_SIZE 4096u -typedef struct NTL_PROCESS_BUILDER_INTERNAL { - uint32_t Magic; +// The preparation is allocated by the loader; its private layout is not ABI. +struct NTL_PROCESS_PREPARATION { uint32_t StartupCapabilityCount; SYS_HANDLE ProcessHandle; NTL_MODULE_CONTEXT* Modules; NTL_PE_IMAGE Image; void* LoaderData; SYS_STARTUP_CAPABILITY ChildCapabilities[SYS_STARTUP_CAPABILITY_MAX]; -} NTL_PROCESS_BUILDER_INTERNAL; +}; -typedef char NTL_PROCESS_BUILDER_SIZE_CHECK[ - sizeof(NTL_PROCESS_BUILDER_INTERNAL) <= sizeof(NTL_PROCESS_BUILDER) ? 1 : -1]; - -static NTL_PROCESS_BUILDER_INTERNAL* NtlBuilderInternal(NTL_PROCESS_BUILDER* builder) { - return (NTL_PROCESS_BUILDER_INTERNAL*)(void*)builder; +// Allocates zero-initialized page-backed preparation state in user space. +static KSTATUS NtlAllocatePreparation(NTL_PROCESS_PREPARATION** preparationOut) { + if (!preparationOut) return NTLSTATUS_INVALID_PARAMETER; + *preparationOut = 0; + if (sizeof(NTL_PROCESS_PREPARATION) > SIZE_MAX - (NTL_PREPARATION_PAGE_SIZE - 1u)) + return NTLSTATUS_NO_MEMORY; + size_t pages = (sizeof(NTL_PROCESS_PREPARATION) + NTL_PREPARATION_PAGE_SIZE - 1u) / + NTL_PREPARATION_PAGE_SIZE; + SYS_ALLOC_VM_REQUEST request; + memset(&request, 0, sizeof(request)); + request.Size = sizeof(request); + request.Protect = SYS_VM_READWRITE; + request.Flags = SYS_VM_FLAG_NONE; + request.PageCount = pages; + request.BaseOut = (void**)preparationOut; + KSTATUS status = SysAllocVm(&request); + if (KSUCCESS(status)) { + memset(*preparationOut, 0, pages * NTL_PREPARATION_PAGE_SIZE); + (*preparationOut)->ProcessHandle = SYS_HANDLE_INVALID; + } + return status; } -static int NtlBuilderRequestValid(const NTL_PROCESS_BUILDER_REQUEST* request) { +static bool NtlPreparationRequestValid(const NTL_PREPARE_PROCESS_REQUEST* request) { return request && request->Size == sizeof(*request) && request->Flags == NTL_PROCESS_LOAD_FLAG_NONE && request->RootHandle != SYS_HANDLE_INVALID && @@ -49,25 +66,22 @@ static int NtlBuilderRequestValid(const NTL_PROCESS_BUILDER_REQUEST* request) { (!request->StartupDataSize || request->StartupData); } -static void NtlBuilderReset(NTL_PROCESS_BUILDER* builder) { - if (builder) memset(builder, 0, sizeof(*builder)); +// Disposes private preparation state and optionally discards the inert child. +static void NtlDestroyPreparation(NTL_PROCESS_PREPARATION* preparation, bool discardChild) { + if (!preparation) return; + if (preparation->Modules) NtlpDestroyModuleContext(preparation->Modules); + if (discardChild && preparation->ProcessHandle != SYS_HANDLE_INVALID) + NtlDiscardChild(preparation->ProcessHandle); + (void)SysFreeVm(preparation); } -// Aborts one prepared inert child. This is deliberately safe on an empty builder. -void NtlProcessBuilderAbort(NTL_PROCESS_BUILDER* builder) { - if (!builder) return; - NTL_PROCESS_BUILDER_INTERNAL* state = NtlBuilderInternal(builder); - if (state->Magic != NTL_PROCESS_BUILDER_MAGIC) { - NtlBuilderReset(builder); - return; - } - if (state->Modules) NtlpDestroyModuleContext(state->Modules); - if (state->ProcessHandle != SYS_HANDLE_INVALID) NtlDiscardChild(state->ProcessHandle); - NtlBuilderReset(builder); +// Aborts and consumes an inert child; NULL requires no action. +void NtlAbortPreparedProcess(NTL_PROCESS_PREPARATION* preparation) { + NtlDestroyPreparation(preparation, true); } // Opens the executable's containing directory as the private dependency-search root. -static KSTATUS NtlOpenImageDirectory(const NTL_PROCESS_BUILDER_REQUEST* request, +static KSTATUS NtlOpenImageDirectory(const NTL_PREPARE_PROCESS_REQUEST* request, SYS_HANDLE* directoryOut, int* borrowedRootOut) { if (!request || !directoryOut || !borrowedRootOut || !SysSvIsValid(request->Path) || request->Path.Length == 0 || SysSvContainsByte(request->Path, '\0')) @@ -101,13 +115,15 @@ static KSTATUS NtlOpenImageDirectory(const NTL_PROCESS_BUILDER_REQUEST* request, // Prepares an inert child: process object, image/dependencies, inherited startup // capabilities, CWD, and loader/bootstrap block are all complete before return. -KSTATUS NtlProcessBuilderPrepare(NTL_PROCESS_BUILDER* builder, - const NTL_PROCESS_BUILDER_REQUEST* request, - SYS_HANDLE imageFile) { - if (!builder || !NtlBuilderRequestValid(request)) return NTLSTATUS_INVALID_PARAMETER; - NtlBuilderReset(builder); - NTL_PROCESS_BUILDER_INTERNAL* state = NtlBuilderInternal(builder); - state->ProcessHandle = SYS_HANDLE_INVALID; +KSTATUS NtlPrepareProcess(const NTL_PREPARE_PROCESS_REQUEST* request, + SYS_HANDLE imageFile, + NTL_PROCESS_PREPARATION** preparationOut) { + if (!preparationOut) return NTLSTATUS_INVALID_PARAMETER; + *preparationOut = 0; + if (!NtlPreparationRequestValid(request)) return NTLSTATUS_INVALID_PARAMETER; + NTL_PROCESS_PREPARATION* state = 0; + KSTATUS status = NtlAllocatePreparation(&state); + if (!KSUCCESS(status)) return status; SYS_HANDLE openedImage = SYS_HANDLE_INVALID; if (imageFile == SYS_HANDLE_INVALID) { @@ -121,7 +137,10 @@ KSTATUS NtlProcessBuilderPrepare(NTL_PROCESS_BUILDER* builder, open.Flags = SYS_FILE_FLAG_NONE; open.HandleOut = &openedImage; KSTATUS openStatus = SysOpenFileAt(&open); - if (!KSUCCESS(openStatus)) return openStatus; + if (!KSUCCESS(openStatus)) { + NtlDestroyPreparation(state, true); + return openStatus; + } imageFile = openedImage; } @@ -133,8 +152,8 @@ KSTATUS NtlProcessBuilderPrepare(NTL_PROCESS_BUILDER* builder, create.CommandLine = request->CommandLine; create.SecurityContext = request->SecurityContext; create.ProcessHandleOut = &state->ProcessHandle; - KSTATUS status = SysCreateProcessObject(&create); - if (!KSUCCESS(status)) goto done; + status = SysCreateProcessObject(&create); + if (!KSUCCESS(status)) goto fail; char currentDirectoryBuffer[SYS_PATH_MAX + 1u]; SYS_STRING_VIEW currentDirectory; @@ -231,99 +250,91 @@ KSTATUS NtlProcessBuilderPrepare(NTL_PROCESS_BUILDER* builder, request->StartupDataSize, &state->LoaderData); if (!KSUCCESS(status)) goto fail; - state->Magic = NTL_PROCESS_BUILDER_MAGIC; + *preparationOut = state; status = NTLSTATUS_SUCCESS; goto done; fail: - if (state->Modules) NtlpDestroyModuleContext(state->Modules); - if (state->ProcessHandle != SYS_HANDLE_INVALID) NtlDiscardChild(state->ProcessHandle); - NtlBuilderReset(builder); + NtlDestroyPreparation(state, true); done: if (openedImage != SYS_HANDLE_INVALID) (void)SysCloseHandle(openedImage); return status; } -// Returns the process capability owned by one prepared builder without transferring it. -KSTATUS NtlProcessBuilderGetProcessHandle(NTL_PROCESS_BUILDER* builder, - SYS_HANDLE* processHandleOut) { - if (!builder || !processHandleOut) return NTLSTATUS_INVALID_PARAMETER; +// Returns the borrowed process capability of a prepared child. +KSTATUS NtlPreparedProcessGetHandle(const NTL_PROCESS_PREPARATION* preparation, + SYS_HANDLE* processHandleOut) { + if (!processHandleOut) return NTLSTATUS_INVALID_PARAMETER; *processHandleOut = SYS_HANDLE_INVALID; - NTL_PROCESS_BUILDER_INTERNAL* state = NtlBuilderInternal(builder); - if (state->Magic != NTL_PROCESS_BUILDER_MAGIC || - state->ProcessHandle == SYS_HANDLE_INVALID || !state->Modules || !state->LoaderData) + if (!preparation || preparation->ProcessHandle == SYS_HANDLE_INVALID || + !preparation->Modules || !preparation->LoaderData) return NTLSTATUS_INVALID_PARAMETER; - *processHandleOut = state->ProcessHandle; + *processHandleOut = preparation->ProcessHandle; return NTLSTATUS_SUCCESS; } -// Starts one prepared child. On success ownership of the retained native process -// capability moves to the caller; on failure the child is destroyed. -KSTATUS NtlProcessBuilderStart(NTL_PROCESS_BUILDER* builder, SYS_HANDLE* processHandleOut) { - if (!builder || !processHandleOut) return NTLSTATUS_INVALID_PARAMETER; +// Starts and consumes one prepared child, including on ordinary start failure. +KSTATUS NtlStartPreparedProcess(NTL_PROCESS_PREPARATION* preparation, + SYS_HANDLE* processHandleOut) { + if (!preparation || !processHandleOut) return NTLSTATUS_INVALID_PARAMETER; *processHandleOut = SYS_HANDLE_INVALID; - NTL_PROCESS_BUILDER_INTERNAL* state = NtlBuilderInternal(builder); - if (state->Magic != NTL_PROCESS_BUILDER_MAGIC || - state->ProcessHandle == SYS_HANDLE_INVALID || !state->Modules || !state->LoaderData) + if (preparation->ProcessHandle == SYS_HANDLE_INVALID || + !preparation->Modules || !preparation->LoaderData) return NTLSTATUS_INVALID_PARAMETER; SYS_START_PROCESS_REQUEST startRequest; memset(&startRequest, 0, sizeof(startRequest)); startRequest.Size = sizeof(startRequest); - startRequest.ProcessHandle = state->ProcessHandle; + startRequest.ProcessHandle = preparation->ProcessHandle; startRequest.Flags = SYS_START_PROCESS_FLAG_NONE; - startRequest.MainImageBase = state->Image.Base; - startRequest.MainImageSize = state->Image.Size; - startRequest.EntryPoint = state->Image.EntryPoint; - startRequest.StartupCapabilities = state->ChildCapabilities; - startRequest.StartupCapabilityCount = state->StartupCapabilityCount; - startRequest.LoaderData = state->LoaderData; + startRequest.MainImageBase = preparation->Image.Base; + startRequest.MainImageSize = preparation->Image.Size; + startRequest.EntryPoint = preparation->Image.EntryPoint; + startRequest.StartupCapabilities = preparation->ChildCapabilities; + startRequest.StartupCapabilityCount = preparation->StartupCapabilityCount; + startRequest.LoaderData = preparation->LoaderData; KSTATUS status = SysStartProcess(&startRequest); - NtlpDestroyModuleContext(state->Modules); - state->Modules = 0; if (!KSUCCESS(status)) { - NtlDiscardChild(state->ProcessHandle); - NtlBuilderReset(builder); + NtlDestroyPreparation(preparation, true); return status; } - *processHandleOut = state->ProcessHandle; - NtlBuilderReset(builder); + // Transfer the native handle before releasing loader-owned metadata. + *processHandleOut = preparation->ProcessHandle; + preparation->ProcessHandle = SYS_HANDLE_INVALID; + NtlDestroyPreparation(preparation, false); return NTLSTATUS_SUCCESS; } -static void NtlBuilderRequestFromCreate(const NTL_CREATE_PROCESS_AT_REQUEST* create, - NTL_PROCESS_BUILDER_REQUEST* builder) { - memset(builder, 0, sizeof(*builder)); - builder->Size = sizeof(*builder); - builder->Flags = create->Flags; - builder->RootHandle = create->RootHandle; - builder->Path = create->Path; - builder->CommandLine = create->CommandLine; - builder->CurrentDirectory = create->CurrentDirectory; - builder->SecurityContext = create->SecurityContext; - builder->ModuleRootHandle = create->ModuleRootHandle; - builder->ModuleDirectory = create->ModuleDirectory; - builder->ResolveModuleName = create->ResolveModuleName; - builder->ResolveContext = create->ResolveContext; - builder->StartupCapabilities = create->StartupCapabilities; - builder->StartupCapabilityCount = create->StartupCapabilityCount; - builder->Arguments = create->Arguments; - builder->Environment = create->Environment; - builder->StartupData = create->StartupData; - builder->StartupDataSize = create->StartupDataSize; +static void NtlPreparationRequestFromCreate(const NTL_CREATE_PROCESS_AT_REQUEST* create, + NTL_PREPARE_PROCESS_REQUEST* preparedRequest) { + memset(preparedRequest, 0, sizeof(*preparedRequest)); + preparedRequest->Size = sizeof(*preparedRequest); + preparedRequest->Flags = create->Flags; + preparedRequest->RootHandle = create->RootHandle; + preparedRequest->Path = create->Path; + preparedRequest->CommandLine = create->CommandLine; + preparedRequest->CurrentDirectory = create->CurrentDirectory; + preparedRequest->SecurityContext = create->SecurityContext; + preparedRequest->ModuleRootHandle = create->ModuleRootHandle; + preparedRequest->ModuleDirectory = create->ModuleDirectory; + preparedRequest->ResolveModuleName = create->ResolveModuleName; + preparedRequest->ResolveContext = create->ResolveContext; + preparedRequest->StartupCapabilities = create->StartupCapabilities; + preparedRequest->StartupCapabilityCount = create->StartupCapabilityCount; + preparedRequest->Arguments = create->Arguments; + preparedRequest->Environment = create->Environment; + preparedRequest->StartupData = create->StartupData; + preparedRequest->StartupDataSize = create->StartupDataSize; } static KSTATUS NtlCreateProcessFile(const NTL_CREATE_PROCESS_AT_REQUEST* request, SYS_HANDLE fileHandle) { - NTL_PROCESS_BUILDER_REQUEST builderRequest; - NtlBuilderRequestFromCreate(request, &builderRequest); - NTL_PROCESS_BUILDER builder; - memset(&builder, 0, sizeof(builder)); - KSTATUS status = NtlProcessBuilderPrepare(&builder, &builderRequest, fileHandle); + NTL_PREPARE_PROCESS_REQUEST preparationRequest; + NtlPreparationRequestFromCreate(request, &preparationRequest); + NTL_PROCESS_PREPARATION* preparation = 0; + KSTATUS status = NtlPrepareProcess(&preparationRequest, fileHandle, &preparation); if (!KSUCCESS(status)) return status; - status = NtlProcessBuilderStart(&builder, request->ProcessHandleOut); - if (!KSUCCESS(status)) NtlProcessBuilderAbort(&builder); - return status; + return NtlStartPreparedProcess(preparation, request->ProcessHandleOut); } // Creates and starts one child from a PE file beneath a capability-relative root. diff --git a/code/user/syscall/syscall_nr.inc b/code/user/syscall/syscall_nr.inc index a671226..5098b8c 100644 --- a/code/user/syscall/syscall_nr.inc +++ b/code/user/syscall/syscall_nr.inc @@ -61,145 +61,129 @@ SYS_CREATE_EVENT EQU 52 SYS_SIGNAL_EVENT EQU 53 SYS_CLEAR_EVENT EQU 54 -; Queues and named queues. +; Anonymous queues. SYS_CREATE_QUEUE EQU 55 SYS_SEND_QUEUE EQU 56 SYS_RECV_QUEUE EQU 57 -SYS_PUBLISH_QUEUE EQU 58 -SYS_OPEN_NAMED_QUEUE EQU 59 -SYS_OPEN_NAMED_QUEUE_OWNER_PROCESS EQU 60 -SYS_UNPUBLISH_QUEUE EQU 61 ; LPC-like Ports and well-known system services. -SYS_CREATE_PORT EQU 62 -SYS_CONNECT_PORT EQU 63 -SYS_ACCEPT_PORT EQU 64 -SYS_SEND_PORT EQU 65 -SYS_RECEIVE_PORT EQU 66 -SYS_CALL_PORT EQU 67 -SYS_REPLY_PORT EQU 68 -SYS_DISCONNECT_PORT EQU 69 -SYS_OPEN_PORT_PEER_PROCESS EQU 70 -SYS_REGISTER_SYSTEM_SERVICE EQU 71 -SYS_UNREGISTER_SYSTEM_SERVICE EQU 72 -SYS_CONNECT_SYSTEM_SERVICE EQU 73 +SYS_CREATE_PORT EQU 58 +SYS_CONNECT_PORT EQU 59 +SYS_ACCEPT_PORT EQU 60 +SYS_SEND_PORT EQU 61 +SYS_RECEIVE_PORT EQU 62 +SYS_CALL_PORT EQU 63 +SYS_REPLY_PORT EQU 64 +SYS_DISCONNECT_PORT EQU 65 +SYS_OPEN_PORT_PEER_PROCESS EQU 66 +SYS_REGISTER_SYSTEM_SERVICE EQU 67 +SYS_UNREGISTER_SYSTEM_SERVICE EQU 68 +SYS_CONNECT_SYSTEM_SERVICE EQU 69 ; Mounted-volume manager control plane. -SYS_QUERY_VOLUMES EQU 74 -SYS_OPEN_VOLUME EQU 75 -SYS_WAIT_VOLUME_CHANGE EQU 76 +SYS_QUERY_VOLUMES EQU 70 +SYS_OPEN_VOLUME EQU 71 +SYS_WAIT_VOLUME_CHANGE EQU 72 ; Files and directories. -SYS_OPEN_FILE_AT EQU 77 -SYS_OPEN_DIR_AT EQU 78 -SYS_STAT_AT EQU 79 -SYS_DELETE_AT EQU 80 -SYS_RENAME_AT EQU 81 -SYS_CREATE_DIR_AT EQU 82 -SYS_REMOVE_DIR_AT EQU 83 -SYS_READ_FILE EQU 84 -SYS_WRITE_FILE EQU 85 -SYS_READ_FILE_ASYNC EQU 86 -SYS_WRITE_FILE_ASYNC EQU 87 -SYS_CANCEL_IO EQU 88 -SYS_SEEK_FILE EQU 89 -SYS_GET_FILE_POS EQU 90 -SYS_GET_FILE_SIZE EQU 91 -SYS_GET_FILE_STAT EQU 92 -SYS_TRUNCATE_FILE EQU 93 -SYS_FLUSH_FILE EQU 94 -SYS_LOCK_FILE EQU 95 -SYS_UNLOCK_FILE EQU 96 -SYS_QUERY_FILE_LOCK EQU 97 -SYS_SET_FILE_STATUS EQU 98 -SYS_CREATE_PIPE EQU 99 -SYS_NOTIFY_PIPE EQU 100 -SYS_READ_DIR EQU 101 +SYS_OPEN_FILE_AT EQU 73 +SYS_OPEN_DIR_AT EQU 74 +SYS_STAT_AT EQU 75 +SYS_DELETE_AT EQU 76 +SYS_RENAME_AT EQU 77 +SYS_CREATE_DIR_AT EQU 78 +SYS_REMOVE_DIR_AT EQU 79 +SYS_READ_FILE EQU 80 +SYS_WRITE_FILE EQU 81 +SYS_READ_FILE_ASYNC EQU 82 +SYS_WRITE_FILE_ASYNC EQU 83 +SYS_CANCEL_IO EQU 84 +SYS_SEEK_FILE EQU 85 +SYS_GET_FILE_POS EQU 86 +SYS_GET_FILE_SIZE EQU 87 +SYS_GET_FILE_STAT EQU 88 +SYS_TRUNCATE_FILE EQU 89 +SYS_FLUSH_FILE EQU 90 +SYS_LOCK_FILE EQU 91 +SYS_UNLOCK_FILE EQU 92 +SYS_QUERY_FILE_LOCK EQU 93 +SYS_SET_FILE_STATUS EQU 94 +SYS_CREATE_PIPE EQU 95 +SYS_NOTIFY_PIPE EQU 96 +SYS_READ_DIR EQU 97 ; Console, display, and surfaces. -SYS_WRITE_CONSOLE EQU 102 -SYS_GET_CONSOLE_INFO EQU 103 -SYS_SET_CONSOLE_CURSOR EQU 104 -SYS_FILL_CONSOLE_CHARACTER EQU 105 -SYS_FILL_CONSOLE_ATTRIBUTE EQU 106 -SYS_PUT_SCREEN_CHAR EQU 107 -SYS_CLEAR_SCREEN EQU 108 -SYS_GET_SCREEN_SIZE EQU 109 -SYS_QUERY_DISPLAY_INFO EQU 110 -SYS_QUERY_DISPLAY_MANAGER_INFO EQU 111 -SYS_PRESENT_DISPLAY_MANAGER_SURFACE EQU 112 -SYS_SET_DISPLAY_MANAGER_CURSOR EQU 113 -SYS_WRITE_DISPLAY_MANAGER_CONSOLE EQU 114 -SYS_QUERY_DISPLAY_MANAGER_CONSOLE_INFO EQU 115 -SYS_SET_DISPLAY_MANAGER_CONSOLE_CURSOR EQU 116 -SYS_FILL_DISPLAY_MANAGER_CONSOLE_CHARACTER EQU 117 -SYS_FILL_DISPLAY_MANAGER_CONSOLE_ATTRIBUTE EQU 118 -SYS_PUT_DISPLAY_MANAGER_CONSOLE_CHARACTER EQU 119 -SYS_CLEAR_DISPLAY_MANAGER_CONSOLE EQU 120 -SYS_OPEN_DISPLAY_CLOCK EQU 121 -SYS_CREATE_SURFACE EQU 122 -SYS_MAP_SURFACE EQU 123 -SYS_UNMAP_SURFACE EQU 124 -SYS_QUERY_SURFACE EQU 125 -SYS_COMMIT_SURFACE EQU 126 +SYS_WRITE_CONSOLE EQU 98 +SYS_GET_CONSOLE_INFO EQU 99 +SYS_SET_CONSOLE_CURSOR EQU 100 +SYS_FILL_CONSOLE_CHARACTER EQU 101 +SYS_FILL_CONSOLE_ATTRIBUTE EQU 102 +SYS_PUT_SCREEN_CHAR EQU 103 +SYS_CLEAR_SCREEN EQU 104 +SYS_GET_SCREEN_SIZE EQU 105 +SYS_QUERY_DISPLAY_INFO EQU 106 +SYS_QUERY_DISPLAY_MANAGER_INFO EQU 107 +SYS_PRESENT_DISPLAY_MANAGER_SURFACE EQU 108 +SYS_SET_DISPLAY_MANAGER_CURSOR EQU 109 +SYS_WRITE_DISPLAY_MANAGER_CONSOLE EQU 110 +SYS_QUERY_DISPLAY_MANAGER_CONSOLE_INFO EQU 111 +SYS_SET_DISPLAY_MANAGER_CONSOLE_CURSOR EQU 112 +SYS_FILL_DISPLAY_MANAGER_CONSOLE_CHARACTER EQU 113 +SYS_FILL_DISPLAY_MANAGER_CONSOLE_ATTRIBUTE EQU 114 +SYS_PUT_DISPLAY_MANAGER_CONSOLE_CHARACTER EQU 115 +SYS_CLEAR_DISPLAY_MANAGER_CONSOLE EQU 116 +SYS_OPEN_DISPLAY_CLOCK EQU 117 +SYS_CREATE_SURFACE EQU 118 +SYS_MAP_SURFACE EQU 119 +SYS_UNMAP_SURFACE EQU 120 +SYS_QUERY_SURFACE EQU 121 +SYS_COMMIT_SURFACE EQU 122 ; Native render audio. -SYS_AUDIO_ENUMERATE_ENDPOINTS EQU 127 -SYS_AUDIO_OPEN_STREAM EQU 128 -SYS_AUDIO_WRITE EQU 129 -SYS_AUDIO_ARM_WAIT EQU 130 -SYS_AUDIO_QUERY_STREAM EQU 131 -SYS_AUDIO_START EQU 132 -SYS_AUDIO_PAUSE EQU 133 -SYS_AUDIO_RESUME EQU 134 -SYS_AUDIO_DRAIN EQU 135 -SYS_AUDIO_STOP EQU 136 -SYS_AUDIO_RESET EQU 137 -SYS_AUDIO_SET_VOLUME EQU 138 -SYS_AUDIO_SET_MASTER_VOLUME EQU 139 +SYS_AUDIO_ENUMERATE_ENDPOINTS EQU 123 +SYS_AUDIO_OPEN_STREAM EQU 124 +SYS_AUDIO_WRITE EQU 125 +SYS_AUDIO_ARM_WAIT EQU 126 +SYS_AUDIO_QUERY_STREAM EQU 127 +SYS_AUDIO_START EQU 128 +SYS_AUDIO_PAUSE EQU 129 +SYS_AUDIO_RESUME EQU 130 +SYS_AUDIO_DRAIN EQU 131 +SYS_AUDIO_STOP EQU 132 +SYS_AUDIO_RESET EQU 133 +SYS_AUDIO_SET_VOLUME EQU 134 +SYS_AUDIO_SET_MASTER_VOLUME EQU 135 ; Input. -SYS_READ_INPUT_MANAGER_EVENT EQU 140 -SYS_POLL_KEYBOARD EQU 141 -SYS_IS_KEY_DOWN EQU 142 -SYS_READ_KEYBOARD_CHAR EQU 143 -SYS_READ_KEY EQU 144 -SYS_GET_KEY_MODIFIERS EQU 145 -SYS_QUERY_GLOBAL_INPUT_STATE EQU 146 -SYS_OPEN_INPUT_QUEUE EQU 147 -SYS_READ_INPUT_EVENT EQU 148 -SYS_PEEK_INPUT_EVENT EQU 149 -SYS_PEEK_MOUSE EQU 150 -SYS_READ_MOUSE EQU 151 -SYS_GET_MOUSE_POS EQU 152 +SYS_READ_INPUT_MANAGER_EVENT EQU 136 ; Time, debug, and system information. -SYS_CREATE_TIMER EQU 153 -SYS_SET_TIMER EQU 154 -SYS_CANCEL_TIMER EQU 155 -SYS_GET_TICK EQU 156 -SYS_GET_TICK_FREQUENCY EQU 157 -SYS_GET_SYSTEM_TIME EQU 158 -SYS_PRINT_DEBUG EQU 159 -SYS_QUERY_TRAP_FRAME EQU 160 -SYS_GET_MEMORY_STATUS EQU 161 -SYS_QUERY_KERNEL_STATS EQU 162 +SYS_CREATE_TIMER EQU 137 +SYS_SET_TIMER EQU 138 +SYS_CANCEL_TIMER EQU 139 +SYS_GET_TICK EQU 140 +SYS_GET_TICK_FREQUENCY EQU 141 +SYS_GET_SYSTEM_TIME EQU 142 +SYS_PRINT_DEBUG EQU 143 +SYS_QUERY_TRAP_FRAME EQU 144 +SYS_GET_MEMORY_STATUS EQU 145 +SYS_QUERY_KERNEL_STATS EQU 146 ; Native sockets and polling. -SYS_CREATE_SOCKET EQU 163 -SYS_SOCKET_IO_ASYNC EQU 164 -SYS_BIND_SOCKET EQU 165 -SYS_LISTEN_SOCKET EQU 166 -SYS_ACCEPT_SOCKET EQU 167 -SYS_CONNECT_SOCKET EQU 168 -SYS_SEND_SOCKET EQU 169 -SYS_RECV_SOCKET EQU 170 -SYS_SHUTDOWN_SOCKET EQU 171 -SYS_SEND_TO_SOCKET EQU 172 -SYS_RECV_FROM_SOCKET EQU 173 -SYS_RESOLVE_HOST EQU 174 -SYS_SET_SOCKET_OPTION EQU 175 -SYS_GET_SOCKET_OPTION EQU 176 -SYS_POLL EQU 177 +SYS_CREATE_SOCKET EQU 147 +SYS_SOCKET_IO_ASYNC EQU 148 +SYS_BIND_SOCKET EQU 149 +SYS_LISTEN_SOCKET EQU 150 +SYS_ACCEPT_SOCKET EQU 151 +SYS_CONNECT_SOCKET EQU 152 +SYS_SEND_SOCKET EQU 153 +SYS_RECV_SOCKET EQU 154 +SYS_SHUTDOWN_SOCKET EQU 155 +SYS_SEND_TO_SOCKET EQU 156 +SYS_RECV_FROM_SOCKET EQU 157 +SYS_RESOLVE_HOST EQU 158 +SYS_SET_SOCKET_OPTION EQU 159 +SYS_GET_SOCKET_OPTION EQU 160 +SYS_POLL EQU 161 -SYS_MAX EQU 178 +SYS_MAX EQU 162 diff --git a/code/user/syscall/syscall_stubs.asm b/code/user/syscall/syscall_stubs.asm index cb06d61..21e2c28 100644 --- a/code/user/syscall/syscall_stubs.asm +++ b/code/user/syscall/syscall_stubs.asm @@ -46,7 +46,6 @@ SYSCALL_STUB ProtectProcessVm, SYS_PROTECT_PROCESS_VM SYSCALL_STUB StartProcess, SYS_START_PROCESS SYSCALL_STUB WaitSingle, SYS_WAIT_SINGLE SYSCALL_STUB WaitMany, SYS_WAIT_MANY -SYSCALL_STUB OpenInputQueue, SYS_OPEN_INPUT_QUEUE SYSCALL_STUB GetProcessExitCode, SYS_GET_PROCESS_EXIT_CODE SYSCALL_STUB OpenProcess, SYS_OPEN_PROCESS SYSCALL_STUB KillProcess, SYS_KILL_PROCESS @@ -138,15 +137,11 @@ SYSCALL_STUB SignalEvent, SYS_SIGNAL_EVENT SYSCALL_STUB ClearEvent, SYS_CLEAR_EVENT ; --------------------------------------------------------------------------- -; Queues and named queues +; Anonymous queues ; --------------------------------------------------------------------------- SYSCALL_STUB CreateQueue, SYS_CREATE_QUEUE SYSCALL_STUB SendQueue, SYS_SEND_QUEUE SYSCALL_STUB RecvQueue, SYS_RECV_QUEUE -SYSCALL_STUB PublishQueue, SYS_PUBLISH_QUEUE -SYSCALL_STUB UnpublishQueue, SYS_UNPUBLISH_QUEUE -SYSCALL_STUB OpenNamedQueue, SYS_OPEN_NAMED_QUEUE -SYSCALL_STUB OpenNamedQueueOwnerProcess, SYS_OPEN_NAMED_QUEUE_OWNER_PROCESS ; --------------------------------------------------------------------------- @@ -251,18 +246,7 @@ SYSCALL_STUB AudioSetMasterVolume, SYS_AUDIO_SET_MASTER_VOLUME ; Input ; --------------------------------------------------------------------------- SYSCALL_STUB ReadInputManagerEvent, SYS_READ_INPUT_MANAGER_EVENT -SYSCALL_STUB PollKeyboard, SYS_POLL_KEYBOARD -SYSCALL_STUB IsKeyDown, SYS_IS_KEY_DOWN -SYSCALL_STUB ReadKeyboardChar, SYS_READ_KEYBOARD_CHAR -SYSCALL_STUB ReadKey, SYS_READ_KEY -SYSCALL_STUB GetKeyModifiers, SYS_GET_KEY_MODIFIERS -SYSCALL_STUB QueryGlobalInputState, SYS_QUERY_GLOBAL_INPUT_STATE -SYSCALL_STUB ReadInputEvent, SYS_READ_INPUT_EVENT -SYSCALL_STUB PeekInputEvent, SYS_PEEK_INPUT_EVENT -SYSCALL_STUB PeekMouse, SYS_PEEK_MOUSE -SYSCALL_STUB ReadMouse, SYS_READ_MOUSE -SYSCALL_STUB GetMousePos, SYS_GET_MOUSE_POS ; --------------------------------------------------------------------------- ; Time and debug diff --git a/code/user/system/dwm/dwm.c b/code/user/system/dwm/dwm.c index d7e7200..203df36 100644 --- a/code/user/system/dwm/dwm.c +++ b/code/user/system/dwm/dwm.c @@ -45,16 +45,16 @@ struct DWM_WINDOW { NTL_WINDOW_ID Id; DWM_CLIENT* Owner; DISPLAY_RECT Bounds; - uint32_t Visible; + bool Visible; uint32_t PointerMode; - uint32_t Destroying; + bool Destroying; DWM_SURFACE_BINDING* Surface; DWM_WINDOW* Lower; DWM_WINDOW* Higher; }; typedef struct DWM_STATE { - volatile uint32_t Running; + volatile bool Running; SYS_HANDLE Mutex; SYS_HANDLE DirtyEvent; SYS_HANDLE DisplayManager; @@ -74,11 +74,11 @@ typedef struct DWM_STATE { DWM_WINDOW* PointerWindow; DWM_WINDOW* Captured; DISPLAY_RECT Dirty; - uint32_t DirtyValid; + bool DirtyValid; int32_t PointerX; int32_t PointerY; uint32_t PointerButtons; - uint32_t CursorVisible; + bool CursorVisible; } DWM_STATE; typedef struct DWM_CLIENT_THREAD_CONTEXT { @@ -236,7 +236,7 @@ static KSTATUS DwmSetSessionAuthority(uint32_t operation, bool enabled) { request.Version = NTL_SESSION_DWM_PROTOCOL_VERSION; request.Operation = operation; request.Flags = NTL_SESSION_DWM_FLAG_NONE; - request.Enabled = enabled ? 1u : 0u; + request.Enabled = enabled; NTL_SESSION_DWM_REPLY reply; memset(&reply, 0, sizeof(reply)); @@ -664,8 +664,8 @@ static void DwmFillWindowReplyLocked(const DWM_WINDOW* window, NTL_DESKTOP_REPLY reply->WindowId = window->Id; reply->Bounds = window->Bounds; reply->Visible = window->Visible; - reply->Focused = gDwm.Focused == window ? 1u : 0u; - reply->Captured = gDwm.Captured == window ? 1u : 0u; + reply->Focused = gDwm.Focused == window; + reply->Captured = gDwm.Captured == window; reply->HasSurface = window->Surface ? 1u : 0u; if (window->Surface) { reply->SurfaceWidth = window->Surface->Desc.Width; @@ -821,9 +821,9 @@ static KSTATUS DwmHandleDesktopRequest(DWM_CLIENT* client, status = NTLSTATUS_INVALID_PARAMETER; break; } - if (window->Visible != request->Value) { + if (window->Visible != (request->Value != 0)) { DwmDirtyLocked(&window->Bounds); - window->Visible = request->Value; + window->Visible = request->Value != 0; if (!window->Visible) { DwmReleaseFocusLocked(window); if (gDwm.Captured == window) (void)DwmReleaseCaptureLocked(window); diff --git a/code/user/system/init/service_config.c b/code/user/system/init/service_config.c index 03eaeda..9fa9276 100644 --- a/code/user/system/init/service_config.c +++ b/code/user/system/init/service_config.c @@ -62,7 +62,8 @@ static bool validate_service_id(const char* id) { // Validates the capability-relative native image path accepted by the process loader. static bool validate_image_path(const char* path) { size_t length = path ? strlen(path) : 0; - if (length == 0 || length >= SYS_PATH_MAX || path[0] == '/' || path[length - 1u] == '/') + if (length == 0 || length >= SYS_PATH_MAX || path[0] == '/' || path[length - 1u] == '/' || + !SysSvIsUtf8Text(SysSvFromParts(path, length))) return false; const char* component = path; diff --git a/code/user/system/posixsrv/posixsrv.c b/code/user/system/posixsrv/posixsrv.c index adbf349..9a581f0 100644 --- a/code/user/system/posixsrv/posixsrv.c +++ b/code/user/system/posixsrv/posixsrv.c @@ -1,5 +1,7 @@ // POSIX personality-server bootstrap, stable process identity, shared attributes, and transaction plumbing. +#include +#include #include #include #include @@ -20,7 +22,7 @@ typedef struct POSIXSRV_CONSTRUCTION_STRING { char* Data; uint32_t Length; uint32_t Capacity; - uint32_t Complete; + bool Complete; } POSIXSRV_CONSTRUCTION_STRING; typedef struct POSIXSRV_SPAWN_ACTION { @@ -140,16 +142,16 @@ struct POSIXSRV_PROCESS_RECORD { uint32_t Generation; uint32_t State; uint32_t ConnectionCount; - uint32_t MonitorActive; + bool MonitorActive; POSIXSRV_PROCESS_RECORD* BorrowedHostProcess; uint32_t BorrowedHostGeneration; uint32_t BorrowedNativeThreadId; uint64_t BorrowedToken; - uint32_t BorrowedMonitorActive; + bool BorrowedMonitorActive; uint32_t BorrowedTerminalKind; int32_t BorrowedExitCode; POSIXSRV_EXEC_HOLDER_PLAN* PendingExecHolderPlan; - uint32_t AlarmMonitorActive; + bool AlarmMonitorActive; uint64_t BootstrapToken; SYS_HANDLE BootstrapReadyEvent; uint32_t PendingNativeProcessId; @@ -157,7 +159,7 @@ struct POSIXSRV_PROCESS_RECORD { uint64_t PendingBootstrapToken; SYS_HANDLE PendingBootstrapReadyEvent; SYS_HANDLE PendingBootstrapReleaseEvent; - uint32_t PendingBootstrapReadyObserved; + bool PendingBootstrapReadyObserved; SYS_HANDLE PendingProcessHandle; uint64_t PendingFdInheritanceToken; uint32_t UserId; @@ -236,21 +238,21 @@ static void PosixsrvDestroyTransaction(POSIXSRV_TRANSACTION* transaction); static void PosixsrvFreeProcessRecord(POSIXSRV_PROCESS_RECORD* record); // Returns whether the stable POSIX process still has a committed native generation. -static int PosixsrvProcessHasCommittedGeneration(const POSIXSRV_PROCESS_RECORD* process) { +static bool PosixsrvProcessHasCommittedGeneration(const POSIXSRV_PROCESS_RECORD* process) { return process && (process->State == NTL_POSIX_PROCESS_STATE_RUNNING || process->State == NTL_POSIX_PROCESS_STATE_EXEC_PREPARING || process->State == NTL_POSIX_PROCESS_STATE_EXEC_COMMITTING); } // Returns whether ordinary operations may still run in the committed old generation. -static int PosixsrvCommittedGenerationOperational(const POSIXSRV_CLIENT* client) { +static bool PosixsrvCommittedGenerationOperational(const POSIXSRV_CLIENT* client) { return client && client->Process && client->NativeGeneration == client->Process->Generation && (client->Process->State == NTL_POSIX_PROCESS_STATE_RUNNING || client->Process->State == NTL_POSIX_PROCESS_STATE_EXEC_PREPARING); } // Returns whether this connection belongs to the not-yet-committed replacement generation. -static int PosixsrvClientIsPendingExecGeneration(const POSIXSRV_CLIENT* client) { +static bool PosixsrvClientIsPendingExecGeneration(const POSIXSRV_CLIENT* client) { return client && client->Process && client->Process->PendingGeneration != 0 && client->NativeGeneration == client->Process->PendingGeneration && (client->Process->State == NTL_POSIX_PROCESS_STATE_EXEC_PREPARING || @@ -258,7 +260,7 @@ static int PosixsrvClientIsPendingExecGeneration(const POSIXSRV_CLIENT* client) } // Returns whether this routed client represents one borrowed vfork logical process. -static int PosixsrvClientIsBorrowedProcess(const POSIXSRV_CLIENT* client) { +static bool PosixsrvClientIsBorrowedProcess(const POSIXSRV_CLIENT* client) { return client && client->Process && client->Process->State == NTL_POSIX_PROCESS_STATE_VFORK_BORROWED && client->NativeGeneration == client->Process->Generation; @@ -342,24 +344,24 @@ static POSIXSRV_PROCESS_RECORD* PosixsrvFindPosixProcessLocked(uint32_t posixPid } // Returns whether a process group currently exists in one POSIX session. -static int PosixsrvProcessGroupExistsLocked(uint32_t sessionId, uint32_t processGroupId) { - if (processGroupId == 0) return 0; +static bool PosixsrvProcessGroupExistsLocked(uint32_t sessionId, uint32_t processGroupId) { + if (processGroupId == 0) return false; for (POSIXSRV_PROCESS_RECORD* record = gProcessRegistry; record; record = record->Next) { if (PosixsrvProcessHasCommittedGeneration(record) && record->SessionId == sessionId && record->ProcessGroupId == processGroupId) - return 1; + return true; } - return 0; + return false; } // Returns whether one protocol open-description type requires a retained native handle. -static int PosixsrvOpenDescriptionNeedsHandle(uint32_t type) { +static bool PosixsrvOpenDescriptionNeedsHandle(uint32_t type) { return type == NTL_POSIX_OPEN_TYPE_FILE || type == NTL_POSIX_OPEN_TYPE_SOCKET || type == NTL_POSIX_OPEN_TYPE_PIPE_READ || type == NTL_POSIX_OPEN_TYPE_PIPE_WRITE; } // Returns whether one protocol open-description type is known. -static int PosixsrvOpenDescriptionTypeValid(uint32_t type) { +static bool PosixsrvOpenDescriptionTypeValid(uint32_t type) { return type >= NTL_POSIX_OPEN_TYPE_FILE && type <= NTL_POSIX_OPEN_TYPE_PIPE_WRITE; } @@ -480,8 +482,7 @@ static void PosixsrvReleaseFdInheritanceForProcessLocked(POSIXSRV_PROCESS_RECORD // Returns the process-local rights needed by one imported native capability. static SYS_RIGHTS PosixsrvOpenDescriptionClientRights(POSIXSRV_OPEN_DESCRIPTION* description) { if (!description || !description->Shared) return 0; - uint32_t statusFlags = (uint32_t)_InterlockedCompareExchange( - &description->Shared->StatusFlags, 0, 0); + uint32_t statusFlags = NtlAtomicLoadU32(&description->Shared->StatusFlags); SYS_RIGHTS rights = SYS_RIGHT_QUERY; if (description->Type == NTL_POSIX_OPEN_TYPE_FILE) { rights |= SYS_FILE_RIGHT_LOCK; @@ -771,7 +772,7 @@ static KSTATUS PosixsrvCreateOpenDescription(POSIXSRV_CLIENT* client, shared->Version = NTL_POSIX_SHARED_OPEN_VERSION; shared->Type = type; shared->DescriptionId = id; - shared->StatusFlags = (long)statusFlags; + shared->StatusFlags = statusFlags; holder->Description = description; holder->Next = client->Process->OpenDescriptions; @@ -961,8 +962,7 @@ static KSTATUS PosixsrvSetOpenStatus(POSIXSRV_CLIENT* client, } POSIXSRV_OPEN_DESCRIPTION* description = holder->Description; - uint32_t current = (uint32_t)_InterlockedCompareExchange( - &description->Shared->StatusFlags, 0, 0); + uint32_t current = NtlAtomicLoadU32(&description->Shared->StatusFlags); uint32_t updated = (current & ~(NTL_POSIX_OPEN_STATUS_APPEND | NTL_POSIX_OPEN_STATUS_NONBLOCK)) | (requestedFlags & (NTL_POSIX_OPEN_STATUS_APPEND | @@ -990,7 +990,7 @@ static KSTATUS PosixsrvSetOpenStatus(POSIXSRV_CLIENT* client, } if (KSUCCESS(status)) - _InterlockedExchange(&description->Shared->StatusFlags, (long)updated); + NtlAtomicExchangeU32(&description->Shared->StatusFlags, updated); *statusFlagsOut = KSUCCESS(status) ? updated : current; *typeOut = description->Type; PosixsrvUnlockRegistry(); @@ -1022,20 +1022,20 @@ static const POSIXSRV_GROUP_RECORD* PosixsrvFindGroup(uint32_t lookupKind, } // Validates one length-delimited canonical native current-directory string. -static int PosixsrvValidateCurrentDirectory(const NTL_POSIX_REQUEST* request) { +static bool PosixsrvValidateCurrentDirectory(const NTL_POSIX_REQUEST* request) { if (!request || request->PathLength == 0 || request->PathLength > SYS_PATH_MAX) - return 0; + return false; if (request->Path[0] != '/' || request->Path[request->PathLength] != '\0') - return 0; + return false; return strlen(request->Path) == request->PathLength; } // Validates one optional length-delimited identity lookup name. -static int PosixsrvValidateLookupName(const NTL_POSIX_REQUEST* request) { +static bool PosixsrvValidateLookupName(const NTL_POSIX_REQUEST* request) { if (!request || request->NameLength == 0 || request->NameLength > NTL_POSIX_IDENTITY_NAME_MAX) - return 0; + return false; if (request->Name[request->NameLength] != '\0') - return 0; + return false; return strlen(request->Name) == request->NameLength; } @@ -1176,8 +1176,8 @@ static KSTATUS PosixsrvUnregisterThread(POSIXSRV_CLIENT* client, // Returns whether the server should discard this signal for one process. -static int PosixsrvSignalIgnoredLocked(const POSIXSRV_PROCESS_RECORD* process, int signalNumber) { - if (!process || !NtlPosixSignalValid(signalNumber)) return 0; +static bool PosixsrvSignalIgnoredLocked(const POSIXSRV_PROCESS_RECORD* process, int signalNumber) { + if (!process || !NtlPosixSignalValid(signalNumber)) return false; return (process->IgnoredSignals & NTL_POSIX_SIGNAL_BIT(signalNumber)) != 0; } @@ -1317,10 +1317,10 @@ static KSTATUS PosixsrvSetSignalDisposition(POSIXSRV_CLIENT* client, } // Returns whether a sender is permitted to signal one synthetic POSIX identity. -static int PosixsrvCanSignalLocked(const POSIXSRV_PROCESS_RECORD* sender, +static bool PosixsrvCanSignalLocked(const POSIXSRV_PROCESS_RECORD* sender, const POSIXSRV_PROCESS_RECORD* target) { - if (!sender || !target) return 0; - if (sender->EffectiveUserId == 0) return 1; + if (!sender || !target) return false; + if (sender->EffectiveUserId == 0) return true; return sender->UserId == target->UserId || sender->UserId == target->EffectiveUserId || sender->EffectiveUserId == target->UserId || sender->EffectiveUserId == target->EffectiveUserId; @@ -1341,10 +1341,10 @@ static KSTATUS PosixsrvSendSignal(POSIXSRV_CLIENT* client, int32_t targetPid, in for (POSIXSRV_PROCESS_RECORD* target = gProcessRegistry; target; target = target->Next) { if (!PosixsrvProcessHasCommittedGeneration(target)) continue; - int selected = 0; + bool selected = false; if (targetPid > 0) selected = target->PosixPid == (uint32_t)targetPid; else if (targetPid == 0) selected = target->ProcessGroupId == sender->ProcessGroupId; - else if (targetPid == -1) selected = 1; + else if (targetPid == -1) selected = true; else selected = target->ProcessGroupId == (uint32_t)(-targetPid); if (!selected) continue; ++matched; @@ -1433,7 +1433,7 @@ static void PosixsrvReleaseBorrowedChildrenForHostLocked(POSIXSRV_PROCESS_RECORD } // Returns whether one exited process record has no remaining owner that requires retention. -static int PosixsrvCanFreeProcessLocked(const POSIXSRV_PROCESS_RECORD* record) { +static bool PosixsrvCanFreeProcessLocked(const POSIXSRV_PROCESS_RECORD* record) { return record && record->State == NTL_POSIX_PROCESS_STATE_EXITED && record->ParentPosixPid == 0 && record->ConnectionCount == 0 && !record->MonitorActive && !record->AlarmMonitorActive && !record->BorrowedMonitorActive; @@ -1622,11 +1622,11 @@ static KSTATUS PosixsrvSetAlarm(POSIXSRV_CLIENT* client, } // Tests whether one waiter selection includes the supplied direct child. -static int PosixsrvWaiterMatchesChild(const POSIXSRV_WAITER* waiter, +static bool PosixsrvWaiterMatchesChild(const POSIXSRV_WAITER* waiter, const POSIXSRV_PROCESS_RECORD* child) { - if (!waiter || !child || child->ParentPosixPid != waiter->ParentPosixPid) return 0; + if (!waiter || !child || child->ParentPosixPid != waiter->ParentPosixPid) return false; if (waiter->WaitPid > 0) return child->PosixPid == (uint32_t)waiter->WaitPid; - if (waiter->WaitPid == -1) return 1; + if (waiter->WaitPid == -1) return true; if (waiter->WaitPid == 0) return child->ProcessGroupId == waiter->ParentProcessGroupId; uint64_t group = (uint64_t)(-(int64_t)waiter->WaitPid); return group <= UINT32_MAX && child->ProcessGroupId == (uint32_t)group; @@ -1882,13 +1882,11 @@ static KSTATUS PosixsrvReplyPipe(POSIXSRV_CLIENT* client, reply.OpenDescriptionId = readDescription->DescriptionId; reply.OpenDescriptionType = readDescription->Type; - reply.OpenStatusFlags = (uint32_t)_InterlockedCompareExchange( - &readDescription->Shared->StatusFlags, 0, 0); + reply.OpenStatusFlags = NtlAtomicLoadU32(&readDescription->Shared->StatusFlags); reply.SharedOpenDescription = (uint64_t)(uintptr_t)readDescription->Shared; reply.OpenDescriptionId2 = writeDescription->DescriptionId; reply.OpenDescriptionType2 = writeDescription->Type; - reply.OpenStatusFlags2 = (uint32_t)_InterlockedCompareExchange( - &writeDescription->Shared->StatusFlags, 0, 0); + reply.OpenStatusFlags2 = NtlAtomicLoadU32(&writeDescription->Shared->StatusFlags); reply.SharedOpenDescription2 = (uint64_t)(uintptr_t)writeDescription->Shared; SYS_PORT_HANDLE_TRANSFER transfers[2]; @@ -1920,14 +1918,14 @@ static KSTATUS PosixsrvReplyFdInheritance(POSIXSRV_CLIENT* client, if (!KSUCCESS(status)) return status; POSIXSRV_FD_INHERITANCE* snapshot = PosixsrvFindFdInheritanceLocked(token); - int execImport = snapshot && PosixsrvClientIsPendingExecGeneration(client) && + bool execImport = snapshot && PosixsrvClientIsPendingExecGeneration(client) && client->Process->PendingFdInheritanceToken == token && snapshot->SourceProcess == client->Process && !snapshot->ClaimProcess; int processStateValid = client->Process->State == NTL_POSIX_PROCESS_STATE_RUNNING || client->Process->State == NTL_POSIX_PROCESS_STATE_CONSTRUCTING || client->Process->State == NTL_POSIX_PROCESS_STATE_VFORK_BORROWED || execImport; - int ownershipValid = snapshot && (execImport || snapshot->ClaimProcess == client->Process || + bool ownershipValid = snapshot && (execImport || snapshot->ClaimProcess == client->Process || (!snapshot->ClaimProcess && snapshot->SecurityId == client->Process->SecurityId)); if (!snapshot || !ownershipValid || !processStateValid) { PosixsrvUnlockRegistry(); @@ -1970,8 +1968,7 @@ static KSTATUS PosixsrvReplyFdInheritance(POSIXSRV_CLIENT* client, out->OpenDescriptionId = description->DescriptionId; out->SharedOpenDescription = (uint64_t)(uintptr_t)description->Shared; out->OpenDescriptionType = description->Type; - out->OpenStatusFlags = (uint32_t)_InterlockedCompareExchange( - &description->Shared->StatusFlags, 0, 0); + out->OpenStatusFlags = NtlAtomicLoadU32(&description->Shared->StatusFlags); out->HandleIndex = NTL_POSIX_FD_HANDLE_NONE; if (PosixsrvOpenDescriptionNeedsHandle(description->Type)) { if (description->MasterHandle == SYS_HANDLE_INVALID || handleCount >= NTL_POSIX_FD_IMPORT_BATCH_MAX) { @@ -2782,7 +2779,7 @@ static KSTATUS PosixsrvWaitProcess(POSIXSRV_CLIENT* client, } KSTATUS result = NTLSTATUS_SUCCESS; - int waiterOwned = waiter != (POSIXSRV_WAITER*)0; + bool waiterOwned = waiter != (POSIXSRV_WAITER*)0; for (;;) { POSIXSRV_PROCESS_RECORD* freeRecord = (POSIXSRV_PROCESS_RECORD*)0; KSTATUS status = PosixsrvLockRegistry(); @@ -2799,14 +2796,14 @@ static KSTATUS PosixsrvWaitProcess(POSIXSRV_CLIENT* client, match->ParentPosixPid = parent->PosixPid; match->ParentProcessGroupId = parent->ProcessGroupId; POSIXSRV_PROCESS_RECORD* exited = (POSIXSRV_PROCESS_RECORD*)0; - int hasMatchingChild = 0; + bool hasMatchingChild = false; for (POSIXSRV_PROCESS_RECORD* child = gProcessRegistry; child; child = child->Next) { if (!PosixsrvProcessHasCommittedGeneration(child) && child->State != NTL_POSIX_PROCESS_STATE_VFORK_BORROWED && child->State != NTL_POSIX_PROCESS_STATE_EXITED) continue; if (!PosixsrvWaiterMatchesChild(match, child)) continue; - hasMatchingChild = 1; + hasMatchingChild = true; if (child->State == NTL_POSIX_PROCESS_STATE_EXITED && (!exited || child->PosixPid < exited->PosixPid)) exited = child; @@ -3356,15 +3353,15 @@ static KSTATUS PosixsrvSpawnSetAttributes(POSIXSRV_CLIENT* client, } // Returns whether one absolute path already names a native global namespace root. -static int PosixsrvPathUsesNativeRoot(const char* path) { +static bool PosixsrvPathUsesNativeRoot(const char* path) { static const char* roots[] = { "/SystemRoot", "/Device", "/Sessions", "/Services" }; - if (!path || path[0] != '/') return 0; + if (!path || path[0] != '/') return false; for (size_t i = 0; i < sizeof(roots) / sizeof(roots[0]); ++i) { size_t length = strlen(roots[i]); if (strncmp(path, roots[i], length) == 0 && (path[length] == '\0' || path[length] == '/')) - return 1; + return true; } - return 0; + return false; } // Resolves one POSIX pathname against the server-owned cwd into native global syntax. @@ -3425,7 +3422,7 @@ static KSTATUS PosixsrvCreateUnheldDescriptionLocked(uint32_t type, shared->Version = NTL_POSIX_SHARED_OPEN_VERSION; shared->Type = type; shared->DescriptionId = description->DescriptionId; - shared->StatusFlags = (long)statusFlags; + shared->StatusFlags = statusFlags; description->Next = gOpenDescriptionRegistry; gOpenDescriptionRegistry = description; ++gRegisteredOpenDescriptions; @@ -3573,15 +3570,15 @@ static KSTATUS PosixsrvApplySpawnFileActionsLocked(POSIXSRV_PROCESS_RECORD* pare } // Verifies that every streamed argv/envp element reached a final chunk. -static int PosixsrvConstructionVectorsComplete(const POSIXSRV_CONSTRUCTION* construction) { - if (!construction) return 0; +static bool PosixsrvConstructionVectorsComplete(const POSIXSRV_CONSTRUCTION* construction) { + if (!construction) return false; for (uint32_t i = 0; i < construction->ArgumentCount; ++i) { - if (!construction->Arguments[i].Complete) return 0; + if (!construction->Arguments[i].Complete) return false; } for (uint32_t i = 0; i < construction->EnvironmentCount; ++i) { - if (!construction->Environment[i].Complete) return 0; + if (!construction->Environment[i].Complete) return false; } - return 1; + return true; } // Marks a failed constructing child non-waitable and releases its server-owned state. @@ -3617,12 +3614,12 @@ static KSTATUS PosixsrvPrepareConstruction(POSIXSRV_PROCESS_RECORD* process, SYS_HANDLE securityContext, const NTL_POSIX_BOOTSTRAP_DATA* bootstrap, int synthesizeArgumentZero, - NTL_PROCESS_BUILDER* builderOut, + NTL_PROCESS_PREPARATION** preparationOut, uint32_t* nativePidOut) { if (!process || !construction || !construction->Executable || - securityContext == SYS_HANDLE_INVALID || !bootstrap || !builderOut || !nativePidOut) + securityContext == SYS_HANDLE_INVALID || !bootstrap || !preparationOut || !nativePidOut) return NTLSTATUS_INVALID_PARAMETER; - memset(builderOut, 0, sizeof(*builderOut)); + *preparationOut = 0; *nativePidOut = 0; char executable[SYS_PATH_MAX + 1u]; @@ -3666,7 +3663,7 @@ static KSTATUS PosixsrvPrepareConstruction(POSIXSRV_PROCESS_RECORD* process, environment[i] = SysSvFromParts(construction->Environment[i].Data, construction->Environment[i].Length); - NTL_PROCESS_BUILDER_REQUEST build; + NTL_PREPARE_PROCESS_REQUEST build; memset(&build, 0, sizeof(build)); build.Size = sizeof(build); build.Flags = NTL_PROCESS_LOAD_FLAG_NONE; @@ -3685,7 +3682,7 @@ static KSTATUS PosixsrvPrepareConstruction(POSIXSRV_PROCESS_RECORD* process, build.StartupData = bootstrap; build.StartupDataSize = sizeof(*bootstrap); - status = NtlProcessBuilderPrepare(builderOut, &build, imageFile); + status = NtlPrepareProcess(&build, imageFile, preparationOut); free(arguments); free(environment); (void)SysCloseHandle(systemRoot); @@ -3693,14 +3690,16 @@ static KSTATUS PosixsrvPrepareConstruction(POSIXSRV_PROCESS_RECORD* process, if (!KSUCCESS(status)) return status; SYS_HANDLE preparedProcess = SYS_HANDLE_INVALID; - status = NtlProcessBuilderGetProcessHandle(builderOut, &preparedProcess); + status = NtlPreparedProcessGetHandle(*preparationOut, &preparedProcess); if (!KSUCCESS(status)) { - NtlProcessBuilderAbort(builderOut); + NtlAbortPreparedProcess(*preparationOut); + *preparationOut = 0; return status; } status = SysGetProcessPid(preparedProcess, nativePidOut); if (!KSUCCESS(status) || *nativePidOut == 0) { - NtlProcessBuilderAbort(builderOut); + NtlAbortPreparedProcess(*preparationOut); + *preparationOut = 0; return KSUCCESS(status) ? NTLSTATUS_FAILURE : status; } return NTLSTATUS_SUCCESS; @@ -3818,19 +3817,19 @@ static KSTATUS PosixsrvCommitSpawn(POSIXSRV_CLIENT* client, bootstrap.InitialSignalMask = spawn->SignalMask; bootstrap.IgnoredSignals = child->ExplicitIgnoredSignals; - NTL_PROCESS_BUILDER builder; + NTL_PROCESS_PREPARATION* preparation = 0; status = PosixsrvPrepareConstruction(child, construction, securityContext, &bootstrap, 1, - &builder, &child->NativeProcessId); + &preparation, &child->NativeProcessId); if (!KSUCCESS(status)) goto before_insert_fail; status = PosixsrvLockRegistry(); - if (!KSUCCESS(status)) { NtlProcessBuilderAbort(&builder); goto before_insert_fail; } + if (!KSUCCESS(status)) { NtlAbortPreparedProcess(preparation); goto before_insert_fail; } snapshot = PosixsrvFindFdInheritanceLocked(request->FdInheritanceToken); if (!snapshot || snapshot->SourceProcess != parent || snapshot->ClaimProcess || !PosixsrvCommittedGenerationOperational(client)) { status = snapshot ? NTLSTATUS_ACCESS_DENIED : NTLSTATUS_NOT_FOUND; PosixsrvUnlockRegistry(); - NtlProcessBuilderAbort(&builder); + NtlAbortPreparedProcess(preparation); goto before_insert_fail; } child->Next = gProcessRegistry; @@ -3840,13 +3839,13 @@ static KSTATUS PosixsrvCommitSpawn(POSIXSRV_CLIENT* client, if (!KSUCCESS(status)) { PosixsrvUnlinkProcessLocked(child); PosixsrvUnlockRegistry(); - NtlProcessBuilderAbort(&builder); + NtlAbortPreparedProcess(preparation); goto before_insert_fail; } PosixsrvUnlockRegistry(); SYS_HANDLE processHandle = SYS_HANDLE_INVALID; - status = NtlProcessBuilderStart(&builder, &processHandle); + status = NtlStartPreparedProcess(preparation, &processHandle); if (!KSUCCESS(status)) { PosixsrvFailConstructingProcess(child, (int32_t)status); return status; @@ -4001,7 +4000,7 @@ static KSTATUS PosixsrvCommitExec(POSIXSRV_CLIENT* client, } POSIXSRV_PROCESS_RECORD* process = client->Process; - int borrowedExec = PosixsrvClientIsBorrowedProcess(client); + bool borrowedExec = PosixsrvClientIsBorrowedProcess(client); uint32_t pendingGeneration = 0; uint64_t bootstrapToken = 0; uint64_t fdToken = request->FdInheritanceToken; @@ -4073,10 +4072,10 @@ static KSTATUS PosixsrvCommitExec(POSIXSRV_CLIENT* client, bootstrap.InitialSignalMask = request->SignalMask & ~NTL_POSIX_SIGNAL_UNBLOCKABLE_MASK; bootstrap.IgnoredSignals = process->ExplicitIgnoredSignals; - NTL_PROCESS_BUILDER builder; + NTL_PROCESS_PREPARATION* preparation = 0; uint32_t pendingNativePid = 0; status = PosixsrvPrepareConstruction(process, transaction->Construction, securityContext, - &bootstrap, 0, &builder, &pendingNativePid); + &bootstrap, 0, &preparation, &pendingNativePid); (void)SysCloseHandle(securityContext); securityContext = SYS_HANDLE_INVALID; if (!KSUCCESS(status)) { @@ -4086,7 +4085,7 @@ static KSTATUS PosixsrvCommitExec(POSIXSRV_CLIENT* client, status = PosixsrvLockRegistry(); if (!KSUCCESS(status)) { - NtlProcessBuilderAbort(&builder); + NtlAbortPreparedProcess(preparation); PosixsrvRollbackExec(process, fdToken, SYS_HANDLE_INVALID, 0); return status; } @@ -4095,7 +4094,7 @@ static KSTATUS PosixsrvCommitExec(POSIXSRV_CLIENT* client, process->PendingBootstrapToken != bootstrapToken || process->PendingFdInheritanceToken != fdToken) { PosixsrvUnlockRegistry(); - NtlProcessBuilderAbort(&builder); + NtlAbortPreparedProcess(preparation); PosixsrvRollbackExec(process, fdToken, SYS_HANDLE_INVALID, 0); return NTLSTATUS_OBJECT_DEAD; } @@ -4103,7 +4102,7 @@ static KSTATUS PosixsrvCommitExec(POSIXSRV_CLIENT* client, PosixsrvUnlockRegistry(); SYS_HANDLE pendingProcess = SYS_HANDLE_INVALID; - status = NtlProcessBuilderStart(&builder, &pendingProcess); + status = NtlStartPreparedProcess(preparation, &pendingProcess); if (!KSUCCESS(status)) { PosixsrvRollbackExec(process, fdToken, SYS_HANDLE_INVALID, 0); return status; @@ -4246,13 +4245,13 @@ static KSTATUS PosixsrvCommitExec(POSIXSRV_CLIENT* client, } // Returns whether operation-specific bootstrap fields are all unused. -static int PosixsrvHasNoBootstrap(const NTL_POSIX_REQUEST* request) { +static bool PosixsrvHasNoBootstrap(const NTL_POSIX_REQUEST* request) { return request && request->BootstrapToken == 0 && request->BootstrapPosixPid == 0 && request->BootstrapGeneration == 0; } // Returns whether all P3 shared-attribute payload fields are unused. -static int PosixsrvHasNoAttributePayload(const NTL_POSIX_REQUEST* request) { +static bool PosixsrvHasNoAttributePayload(const NTL_POSIX_REQUEST* request) { return request && request->TargetPosixPid == 0 && request->UserId == 0 && request->GroupId == 0 && request->ProcessGroupId == 0 && request->LookupKind == NTL_POSIX_LOOKUP_NONE && @@ -4262,21 +4261,21 @@ static int PosixsrvHasNoAttributePayload(const NTL_POSIX_REQUEST* request) { } // Returns whether one request carries no descriptor-inheritance payload. -static int PosixsrvHasNoFdInheritancePayload(const NTL_POSIX_REQUEST* request) { +static bool PosixsrvHasNoFdInheritancePayload(const NTL_POSIX_REQUEST* request) { if (!request || request->FdInheritanceToken != 0 || request->FdInheritanceIndex != 0 || request->FdInheritanceCount != 0 || request->FdInheritanceFlags != 0) - return 0; + return false; for (uint32_t i = 0; i < NTL_POSIX_FD_INHERIT_MAX; ++i) { if (request->FdManifest[i].Descriptor != 0 || request->FdManifest[i].DescriptorFlags != 0 || request->FdManifest[i].OpenDescriptionId != 0) - return 0; + return false; } - return 1; + return true; } // Returns whether one request carries no P5 spawn transaction payload. -static int PosixsrvHasNoSpawnPayload(const NTL_POSIX_REQUEST* request) { +static bool PosixsrvHasNoSpawnPayload(const NTL_POSIX_REQUEST* request) { return request && request->ConstructionVectorKind == 0 && request->ConstructionVectorIndex == 0 && request->ConstructionVectorOffset == 0 && request->ConstructionVectorFlags == 0 && request->SpawnActionKind == 0 && request->SpawnFileDescriptor == 0 && @@ -4286,36 +4285,36 @@ static int PosixsrvHasNoSpawnPayload(const NTL_POSIX_REQUEST* request) { } // Returns whether one request carries no child-wait selection payload. -static int PosixsrvHasNoWaitPayload(const NTL_POSIX_REQUEST* request) { +static bool PosixsrvHasNoWaitPayload(const NTL_POSIX_REQUEST* request) { return request && request->WaitPid == 0 && request->WaitOptions == 0; } // Returns whether one request carries no P6 signal payload. -static int PosixsrvHasNoSignalPayload(const NTL_POSIX_REQUEST* request) { +static bool PosixsrvHasNoSignalPayload(const NTL_POSIX_REQUEST* request) { return request && request->SignalTargetPid == 0 && request->SignalNumber == 0 && request->SignalDisposition == 0 && request->SignalReserved == 0 && request->SignalMask == 0 && request->SignalApcRoutine == 0; } // Returns whether one request carries no alarm payload. -static int PosixsrvHasNoAlarmPayload(const NTL_POSIX_REQUEST* request) { +static bool PosixsrvHasNoAlarmPayload(const NTL_POSIX_REQUEST* request) { return request && request->AlarmSeconds == 0 && request->AlarmReserved == 0; } // Returns whether one request carries no borrowed-vfork lifecycle payload. -static int PosixsrvHasNoVforkPayload(const NTL_POSIX_REQUEST* request) { +static bool PosixsrvHasNoVforkPayload(const NTL_POSIX_REQUEST* request) { return request && request->VforkToken == 0 && request->VforkExitCode == 0 && request->VforkReserved == 0; } // Returns whether one request carries no transaction state. -static int PosixsrvHasNoTransaction(const NTL_POSIX_REQUEST* request) { +static bool PosixsrvHasNoTransaction(const NTL_POSIX_REQUEST* request) { return request && request->TransactionId == 0 && request->TransactionKind == NTL_POSIX_TRANSACTION_NONE; } // Returns whether all payload domains other than P6 signals are unused. -static int PosixsrvHasOnlySignalPayload(const NTL_POSIX_REQUEST* request) { +static bool PosixsrvHasOnlySignalPayload(const NTL_POSIX_REQUEST* request) { return request && PosixsrvHasNoBootstrap(request) && PosixsrvHasNoTransaction(request) && PosixsrvHasNoAttributePayload(request) && PosixsrvHasNoFdInheritancePayload(request) && PosixsrvHasNoSpawnPayload(request) && PosixsrvHasNoWaitPayload(request) && diff --git a/code/user/system/rssrv/console.c b/code/user/system/rssrv/console.c index a088c6c..1a1d765 100644 --- a/code/user/system/rssrv/console.c +++ b/code/user/system/rssrv/console.c @@ -187,6 +187,10 @@ static KSTATUS RssrvConsoleMutate(RSSRV_CONSOLE_STATE* console, request->Data.Cursor.Y); break; case NTL_CONSOLE_OP_FILL_CHARACTER: + if ((uint8_t)request->Data.FillCharacter.Character > 0x7Fu) { + status = NTLSTATUS_INVALID_PARAMETER; + break; + } if (request->Data.FillCharacter.Reserved[0] || request->Data.FillCharacter.Reserved[1] || request->Data.FillCharacter.Reserved[2]) { status = NTLSTATUS_INVALID_PARAMETER; @@ -212,6 +216,10 @@ static KSTATUS RssrvConsoleMutate(RSSRV_CONSOLE_STATE* console, &written); break; case NTL_CONSOLE_OP_PUT_CHARACTER: + if ((uint8_t)request->Data.PutCharacter.Character > 0x7Fu) { + status = NTLSTATUS_INVALID_PARAMETER; + break; + } if (request->Data.PutCharacter.Reserved[0] || request->Data.PutCharacter.Reserved[1] || request->Data.PutCharacter.Reserved[2]) { status = NTLSTATUS_INVALID_PARAMETER; diff --git a/code/user/system/rssrv/display.c b/code/user/system/rssrv/display.c index 6168a5f..d93a8dc 100644 --- a/code/user/system/rssrv/display.c +++ b/code/user/system/rssrv/display.c @@ -64,7 +64,7 @@ KSTATUS RssrvDisplayQueryConsoleInfo(RSSRV_DISPLAY_STATE* display, SYS_CONSOLE_I } // Updates the kernel-backed console cursor through delegated authority. -KSTATUS RssrvDisplaySetConsoleCursor(RSSRV_DISPLAY_STATE* display, int x, int y) { +KSTATUS RssrvDisplaySetConsoleCursor(RSSRV_DISPLAY_STATE* display, int32_t x, int32_t y) { if (!display || display->Manager == SYS_HANDLE_INVALID) return NTLSTATUS_INVALID_PARAMETER; return SysSetDisplayManagerConsoleCursor(display->Manager, x, y); @@ -72,7 +72,7 @@ KSTATUS RssrvDisplaySetConsoleCursor(RSSRV_DISPLAY_STATE* display, int x, int y) // Fills kernel-backed console characters through delegated authority. KSTATUS RssrvDisplayFillConsoleCharacter(RSSRV_DISPLAY_STATE* display, char character, - int x, int y, size_t length, size_t* writtenOut) { + int32_t x, int32_t y, size_t length, size_t* writtenOut) { if (!display || display->Manager == SYS_HANDLE_INVALID) return NTLSTATUS_INVALID_PARAMETER; SYS_FILL_CONSOLE_CHARACTER_REQUEST request; @@ -88,7 +88,7 @@ KSTATUS RssrvDisplayFillConsoleCharacter(RSSRV_DISPLAY_STATE* display, char char // Fills kernel-backed console attributes through delegated authority. KSTATUS RssrvDisplayFillConsoleAttribute(RSSRV_DISPLAY_STATE* display, uint16_t attribute, - int x, int y, size_t length, size_t* writtenOut) { + int32_t x, int32_t y, size_t length, size_t* writtenOut) { if (!display || display->Manager == SYS_HANDLE_INVALID) return NTLSTATUS_INVALID_PARAMETER; SYS_FILL_CONSOLE_ATTRIBUTE_REQUEST request; @@ -104,7 +104,7 @@ KSTATUS RssrvDisplayFillConsoleAttribute(RSSRV_DISPLAY_STATE* display, uint16_t // Draws one explicit glyph through delegated console authority. KSTATUS RssrvDisplayPutConsoleCharacter(RSSRV_DISPLAY_STATE* display, char character, - int x, int y, uint32_t foreground, uint32_t background) { + int32_t x, int32_t y, uint32_t foreground, uint32_t background) { if (!display || display->Manager == SYS_HANDLE_INVALID) return NTLSTATUS_INVALID_PARAMETER; SYS_PUT_SCREEN_CHAR_REQUEST request; diff --git a/code/user/system/rssrv/input.c b/code/user/system/rssrv/input.c index 390dd55..5e5bfbd 100644 --- a/code/user/system/rssrv/input.c +++ b/code/user/system/rssrv/input.c @@ -1,5 +1,6 @@ // RSSRV logical input. The kernel reports normalized physical events; RSSRV owns interpretation, pointer state, and routing. +#include #include "rssrv_internal.h" #include #include @@ -35,6 +36,7 @@ static const RSSRV_KEY_CHAR_PAIR gInputCharMap[] = { { INPUT_KEY_COMMA, ',', '<' }, { INPUT_KEY_PERIOD, '.', '>' }, { INPUT_KEY_SLASH, '/', '?' }, { INPUT_KEY_SPACE, ' ', ' ' }, { INPUT_KEY_TAB, '\t', '\t' }, + { INPUT_KEY_ESCAPE, '\x1B', '\x1B' }, { INPUT_KEY_ENTER, '\n', '\n' }, { INPUT_KEY_BACKSPACE, '\b', '\b' }, { INPUT_KEY_KP_DIVIDE, '/', '/' }, { INPUT_KEY_KP_MULTIPLY, '*', '*' }, { INPUT_KEY_KP_MINUS, '-', '-' }, { INPUT_KEY_KP_PLUS, '+', '+' }, @@ -57,7 +59,7 @@ static void RssrvInputUnlock(RSSRV_INPUT_STATE* input) { } // Tests one logical key bit while the input mutex is held. -static int RssrvInputKeyDownLocked(const RSSRV_INPUT_STATE* input, INPUT_KEY key) { +static bool RssrvInputKeyDownLocked(const RSSRV_INPUT_STATE* input, INPUT_KEY key) { return key < NTL_INPUT_KEY_BYTES * 8u && (input->KeyDown[key >> 3] & (uint8_t)(1u << (key & 7u))) != 0; } @@ -70,7 +72,7 @@ static void RssrvInputSetKeyDownLocked(RSSRV_INPUT_STATE* input, INPUT_KEY key, } // Returns whether duplicate physical make events should generate repeated logical key records. -static int RssrvInputKeyIsRepeatable(INPUT_KEY key) { +static bool RssrvInputKeyIsRepeatable(INPUT_KEY key) { switch (key) { case INPUT_KEY_NONE: case INPUT_KEY_LEFT_SHIFT: @@ -83,9 +85,9 @@ static int RssrvInputKeyIsRepeatable(INPUT_KEY key) { case INPUT_KEY_RIGHT_GUI: case INPUT_KEY_CAPS_LOCK: case INPUT_KEY_NUM_LOCK: - return 0; + return false; default: - return 1; + return true; } } @@ -247,15 +249,15 @@ static void RssrvInputRepeatThread(uintptr_t argument) { // Applies one normalized relative mouse event and lets Session 0 own cursor and target routing policy. static void RssrvInputApplyMouse(RSSRV_INPUT_STATE* input, const INPUT_MOUSE_EVENT* event) { if (!input || !event) return; - int x = 0; - int y = 0; - int changed = 0; + int32_t x = 0; + int32_t y = 0; + bool changed = false; if (!KSUCCESS(RssrvInputLock(input))) return; - int nx = input->MouseX + event->dx; - int ny = input->MouseY + event->dy; - int width = input->MouseWidth > 0 ? input->MouseWidth : 1; - int height = input->MouseHeight > 0 ? input->MouseHeight : 1; + int32_t nx = input->MouseX + event->dx; + int32_t ny = input->MouseY + event->dy; + int32_t width = input->MouseWidth > 0 ? input->MouseWidth : 1; + int32_t height = input->MouseHeight > 0 ? input->MouseHeight : 1; if (nx < 0) nx = 0; if (ny < 0) ny = 0; if (nx >= width) nx = width - 1; @@ -313,7 +315,7 @@ static void RssrvInputRawThread(uintptr_t argument) { // Reads or peeks one logical keyboard record, blocking on the queue event when requested. KSTATUS RssrvInputReadKeyRecord(RSSRV_INPUT_STATE* input, - int peek, + bool peek, uint64_t timeoutMilliseconds, INPUT_KEY_RECORD* recordOut) { if (!input || !recordOut) return NTLSTATUS_INVALID_PARAMETER; @@ -351,7 +353,7 @@ KSTATUS RssrvInputFlushKeys(RSSRV_INPUT_STATE* input, uint32_t* discardedOut) { // Reads or peeks one logical mouse record, blocking on the queue event when requested. static KSTATUS RssrvInputGetMouse(RSSRV_INPUT_STATE* input, - int peek, + bool peek, uint64_t timeoutMilliseconds, INPUT_MOUSE_EVENT* eventOut) { if (!input || !eventOut) return NTLSTATUS_INVALID_PARAMETER; @@ -470,12 +472,12 @@ static void RssrvInputSessionThread(uintptr_t argument) { memset(&state, 0, sizeof(state)); switch (request.Operation) { case NTL_INPUT_OP_READ_KEY: - status = RssrvInputReadKeyRecord(input, 0, request.TimeoutMilliseconds, &key); + status = RssrvInputReadKeyRecord(input, false, request.TimeoutMilliseconds, &key); (void)RssrvInputReply(endpoint, info.MessageId, request.Operation, status, KSUCCESS(status) ? &key : 0, 0, 0); break; case NTL_INPUT_OP_PEEK_KEY: - status = RssrvInputReadKeyRecord(input, 1, request.TimeoutMilliseconds, &key); + status = RssrvInputReadKeyRecord(input, true, request.TimeoutMilliseconds, &key); (void)RssrvInputReply(endpoint, info.MessageId, request.Operation, status, KSUCCESS(status) ? &key : 0, 0, 0); break; diff --git a/code/user/system/rssrv/namespace.c b/code/user/system/rssrv/namespace.c index d5e8668..d2d10fc 100644 --- a/code/user/system/rssrv/namespace.c +++ b/code/user/system/rssrv/namespace.c @@ -259,7 +259,8 @@ KSTATUS RssrvNamespaceInitialize(RSSRV_NAMESPACE* namespaceState) { // Validates one canonical absolute native resource path, including the root path "/". KSTATUS RssrvNamespaceValidatePath(const char* path, uint32_t length) { - if (!path || length == 0 || length > NTL_RESOURCE_PATH_MAX || path[0] != '/') + if (!path || length == 0 || length > NTL_RESOURCE_PATH_MAX || path[0] != '/' || + !SysSvIsUtf8Text(SysSvFromParts(path, length))) return NTLSTATUS_INVALID_PARAMETER; if (length == 1) return NTLSTATUS_SUCCESS; diff --git a/code/user/system/rssrv/resource_service.c b/code/user/system/rssrv/resource_service.c index f4a72c3..3b91501 100644 --- a/code/user/system/rssrv/resource_service.c +++ b/code/user/system/rssrv/resource_service.c @@ -1,5 +1,6 @@ // RSSRV resource protocol listeners, sessions, request validation, and capability transfer. +#include #include "rssrv_internal.h" #include #include @@ -7,7 +8,7 @@ typedef struct RSSRV_SESSION { RSSRV_RESOURCE_SERVICE* Service; SYS_HANDLE Endpoint; - uint32_t Administrative; + bool Administrative; } RSSRV_SESSION; // Replies with one fixed resource-protocol reply and no handle transfer. @@ -317,6 +318,11 @@ static uint32_t RssrvDispatchRequest(RSSRV_RESOURCE_SERVICE* service, NTLSTATUS_INVALID_PARAMETER, NTL_RESOURCE_KIND_INVALID); return 0; } + if (!SysSvIsUtf8Text(SysSvFromParts(aux, request.AuxLength))) { + (void)RssrvReplyStatus(endpoint, info->MessageId, operation, + NTLSTATUS_INVALID_PARAMETER, NTL_RESOURCE_KIND_INVALID); + return 0; + } for (uint32_t i = 0; i < request.AuxLength; ++i) { if (aux[i] == '/' || aux[i] == '\\' || aux[i] == '\0') { (void)RssrvReplyStatus(endpoint, info->MessageId, operation, diff --git a/code/user/system/rssrv/rssrv.c b/code/user/system/rssrv/rssrv.c index 7087aee..5323f4f 100644 --- a/code/user/system/rssrv/rssrv.c +++ b/code/user/system/rssrv/rssrv.c @@ -21,7 +21,7 @@ void RssrvSetCurrentThreadName(const char* name) { } // Terminates RSSRV without depending on a compiler runtime return path. -void RssrvExit(int code) { +void RssrvExit(int32_t code) { for (;;) SysExitProcess(code); } diff --git a/code/user/system/rssrv/rssrv_internal.h b/code/user/system/rssrv/rssrv_internal.h index 3176744..350e3ae 100644 --- a/code/user/system/rssrv/rssrv_internal.h +++ b/code/user/system/rssrv/rssrv_internal.h @@ -7,6 +7,7 @@ #include #include #include +#include #define RSSRV_LISTENER_BACKLOG 16u #define RSSRV_SYMBOLIC_LINK_MAX_DEPTH 16u @@ -49,7 +50,7 @@ typedef struct RSSRV_NAMESPACE_BINDING_VIEW { uint32_t Kind; SYS_RIGHTS ExportRights; uint32_t ResolvedLength; - uint32_t Locked; + bool Locked; } RSSRV_NAMESPACE_BINDING_VIEW; typedef struct RSSRV_NAMESPACE_ENTRY { @@ -135,7 +136,7 @@ typedef struct RSSRV_VOLUME_STATE { typedef struct RSSRV_LISTENER_WORKER { RSSRV_RESOURCE_SERVICE* Service; SYS_HANDLE Listener; - uint32_t Administrative; + bool Administrative; } RSSRV_LISTENER_WORKER; struct RSSRV_RESOURCE_SERVICE { @@ -160,7 +161,7 @@ typedef struct RSSRV_STATE { void RssrvSetCurrentThreadName(const char* name); // Terminates RSSRV without depending on a compiler runtime return path. -void RssrvExit(int code); +void RssrvExit(int32_t code); // Initializes the native namespace root and its synchronization object. KSTATUS RssrvNamespaceInitialize(RSSRV_NAMESPACE* namespaceState); @@ -230,13 +231,13 @@ KSTATUS RssrvDisplayPublishDevice(RSSRV_DISPLAY_STATE* display); KSTATUS RssrvDisplayQueryInfo(RSSRV_DISPLAY_STATE* display, SYS_DISPLAY_INFO* infoOut); KSTATUS RssrvDisplayWriteConsole(RSSRV_DISPLAY_STATE* display, const char* data, size_t length); KSTATUS RssrvDisplayQueryConsoleInfo(RSSRV_DISPLAY_STATE* display, SYS_CONSOLE_INFO* infoOut); -KSTATUS RssrvDisplaySetConsoleCursor(RSSRV_DISPLAY_STATE* display, int x, int y); +KSTATUS RssrvDisplaySetConsoleCursor(RSSRV_DISPLAY_STATE* display, int32_t x, int32_t y); KSTATUS RssrvDisplayFillConsoleCharacter(RSSRV_DISPLAY_STATE* display, char character, - int x, int y, size_t length, size_t* writtenOut); + int32_t x, int32_t y, size_t length, size_t* writtenOut); KSTATUS RssrvDisplayFillConsoleAttribute(RSSRV_DISPLAY_STATE* display, uint16_t attribute, - int x, int y, size_t length, size_t* writtenOut); + int32_t x, int32_t y, size_t length, size_t* writtenOut); KSTATUS RssrvDisplayPutConsoleCharacter(RSSRV_DISPLAY_STATE* display, char character, - int x, int y, uint32_t foreground, uint32_t background); + int32_t x, int32_t y, uint32_t foreground, uint32_t background); KSTATUS RssrvDisplayClearConsole(RSSRV_DISPLAY_STATE* display, uint32_t color); // Initializes Session 0, input routing, and display ownership. @@ -266,7 +267,7 @@ KSTATUS RssrvInputInitialize(RSSRV_INPUT_STATE* input, RSSRV_SESSION_STATE* session, SYS_HANDLE manager); KSTATUS RssrvInputStart(RSSRV_INPUT_STATE* input); -KSTATUS RssrvInputReadKeyRecord(RSSRV_INPUT_STATE* input, int peek, +KSTATUS RssrvInputReadKeyRecord(RSSRV_INPUT_STATE* input, bool peek, uint64_t timeoutMilliseconds, INPUT_KEY_RECORD* recordOut); KSTATUS RssrvInputFlushKeys(RSSRV_INPUT_STATE* input, uint32_t* discardedOut); @@ -296,7 +297,7 @@ KSTATUS RssrvVolumesQueryPublishedPath(RSSRV_VOLUME_STATE* volumes, KSTATUS RssrvVolumesStartWorker(RSSRV_VOLUME_STATE* volumes); // Returns whether a path belongs to the RSSRV-owned automatic mounted-volume naming family. -int RssrvVolumesOwnPath(const char* path, uint32_t length); +bool RssrvVolumesOwnPath(const char* path, uint32_t length); // Initializes the public/admin resource protocol component around existing namespace state. KSTATUS RssrvResourceInitialize(RSSRV_RESOURCE_SERVICE* service, diff --git a/code/user/system/rssrv/session.c b/code/user/system/rssrv/session.c index 10cfac1..b8feff7 100644 --- a/code/user/system/rssrv/session.c +++ b/code/user/system/rssrv/session.c @@ -106,7 +106,9 @@ static void RssrvSessionReplyDwm(SYS_HANDLE endpoint, // Applies one DWM focus/capture request while preserving RSSRV as the session authority owner. static KSTATUS RssrvSessionApplyDwmRequest(RSSRV_SESSION_STATE* session, const NTL_SESSION_DWM_REQUEST* request) { - if (!session || !request || request->Enabled > 1u) return NTLSTATUS_INVALID_PARAMETER; + if (!session || !request || + ((const uint8_t*)request)[offsetof(NTL_SESSION_DWM_REQUEST, Enabled)] > 1u) + return NTLSTATUS_INVALID_PARAMETER; KSTATUS status = RssrvSessionLock(session); if (!KSUCCESS(status)) return status; diff --git a/code/user/system/rssrv/volume.c b/code/user/system/rssrv/volume.c index c27f1f0..572c220 100644 --- a/code/user/system/rssrv/volume.c +++ b/code/user/system/rssrv/volume.c @@ -1,5 +1,6 @@ // RSSRV mounted-volume publication and /Device/HarddiskVolumeN synchronization. +#include #include "rssrv_internal.h" #include #include @@ -23,12 +24,12 @@ static RSSRV_VOLUME_PUBLICATION* RssrvFindPublishedVolume(RSSRV_VOLUME_STATE* vo } // Returns whether one kernel volume instance is present in the current manager snapshot. -static int RssrvSnapshotContainsVolume(const SYS_VOLUME_INFO* snapshot, uint32_t count, uint32_t instanceId) { +static bool RssrvSnapshotContainsVolume(const SYS_VOLUME_INFO* snapshot, uint32_t count, uint32_t instanceId) { for (uint32_t i = 0; i < count; ++i) { if (snapshot[i].InstanceId == instanceId) - return 1; + return true; } - return 0; + return false; } // Builds one RSSRV-local /Device/HarddiskVolumeN name without introducing a formatting-runtime dependency. @@ -272,7 +273,7 @@ KSTATUS RssrvVolumesStartWorker(RSSRV_VOLUME_STATE* volumes) { } // Returns whether a path belongs to the RSSRV-owned automatic mounted-volume naming family. -int RssrvVolumesOwnPath(const char* path, uint32_t length) { +bool RssrvVolumesOwnPath(const char* path, uint32_t length) { static const char prefix[] = "/Device/HarddiskVolume"; uint32_t prefixLength = (uint32_t)(sizeof(prefix) - 1u); return path && length >= prefixLength && memcmp(path, prefix, prefixLength) == 0; diff --git a/code/user/tests/all_tests/all_tests.c b/code/user/tests/all_tests/all_tests.c index 6e2e9b3..7a6c461 100644 --- a/code/user/tests/all_tests/all_tests.c +++ b/code/user/tests/all_tests/all_tests.c @@ -152,7 +152,7 @@ static uint32_t alloc_entries(uint32_t capacity, TEST_ENTRY** entriesOut) { req.PageCount = pages; req.BaseOut = &base; - uint32_t status = SysAllocVm(&req); + KSTATUS status = SysAllocVm(&req); if (status != NTLSTATUS_SUCCESS || !base) { *entriesOut = 0; return status ? status : NTLSTATUS_NO_MEMORY; @@ -167,7 +167,7 @@ static uint32_t grow_entries(TEST_ENTRY** entries, uint32_t* capacity, uint32_t uint32_t oldCapacity = *capacity; uint32_t newCapacity = oldCapacity ? oldCapacity * 2u : ALL_TESTS_INITIAL_CAPACITY; TEST_ENTRY* next = 0; - uint32_t status = alloc_entries(newCapacity, &next); + KSTATUS status = alloc_entries(newCapacity, &next); if (status != NTLSTATUS_SUCCESS) return status; if (*entries && count) memcpy(next, *entries, (size_t)count * sizeof(TEST_ENTRY)); @@ -213,7 +213,7 @@ static uint32_t collect_tests(const char* dir, const char* selfName, TEST_ENTRY* *countOut = 0; uint32_t hDir = 0; - uint32_t status = NtlOpenDir(SysSvFromCstr(dir), + KSTATUS status = NtlOpenDir(SysSvFromCstr(dir), SYS_DIR_ACCESS_ENUMERATE | SYS_DIR_ACCESS_OPEN, SYS_DIR_FLAG_NONE, &hDir); @@ -256,7 +256,7 @@ static uint32_t collect_tests(const char* dir, const char* selfName, TEST_ENTRY* // Runs one test process and returns the child's exit code through exitCodeOut. static uint32_t run_test(const char* path, int32_t* exitCodeOut) { uint32_t imageFile = SYS_HANDLE_INVALID; - uint32_t status = NtlOpenFile(SysSvFromCstr(path), + KSTATUS status = NtlOpenFile(SysSvFromCstr(path), SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_QUERY, SYS_FILE_OPEN_EXISTING, SYS_FILE_FLAG_NONE, @@ -311,7 +311,7 @@ int main(void) { TEST_ENTRY* entries = 0; uint32_t count = 0; - uint32_t status = collect_tests(selfDir, selfName, &entries, &count); + KSTATUS status = collect_tests(selfDir, selfName, &entries, &count); if (status != NTLSTATUS_SUCCESS) { print("all_tests: failed to enumerate tests status=0x"); print_hex32(status); diff --git a/code/user/tests/anonymous_queue_smoke/anonymous_queue_smoke.c b/code/user/tests/anonymous_queue_smoke/anonymous_queue_smoke.c new file mode 100644 index 0000000..620a393 --- /dev/null +++ b/code/user/tests/anonymous_queue_smoke/anonymous_queue_smoke.c @@ -0,0 +1,138 @@ +// Native anonymous queue lifetime smoke test. +// Verifies queue message delivery and survival through a derived handle. + +#include +#include +#include + +#define TEST_MESSAGE_TYPE 0x71756575u +#define TEST_MESSAGE_VALUE 0x123456789abcdef0ull + +typedef struct TEST_QUEUE_MESSAGE { + uint32_t Type; + uint32_t Flags; + uint64_t Param[6]; +} TEST_QUEUE_MESSAGE; + +typedef char TEST_QUEUE_MESSAGE_SIZE_CHECK[ + (sizeof(TEST_QUEUE_MESSAGE) == SYS_QUEUE_MESSAGE_SIZE) ? 1 : -1 +]; + +// Prints one NUL-terminated string to the kernel console. +static void print(const char* s) { + SYS_WRITE_CONSOLE_REQUEST req = { sizeof(SYS_WRITE_CONSOLE_REQUEST), SysSvFromCstr(s) }; + SysWriteConsole(&req); +} + +// Prints one unsigned integer to the kernel console. +static void print_u64(uint64_t v) { + char buf[32]; + int i = 0; + if (v == 0) { + print("0"); + return; + } + while (v && i < (int)sizeof(buf)) { + buf[i++] = (char)('0' + (v % 10)); + v /= 10; + } + while (i--) { + char c = buf[i]; + SYS_WRITE_CONSOLE_REQUEST req = { sizeof(SYS_WRITE_CONSOLE_REQUEST), SysSvFromParts(&c, 1) }; + SysWriteConsole(&req); + } +} + +// Terminates the current test process. +static void exit_now(int code) { + while (true) SysExitProcess(code); +} + +// Fails the test with one plain message. +static void fail(const char* msg) { + print("anonymous_queue_smoke: "); + print(msg); + print("\n"); + exit_now(1); +} + +// Fails the test with one status value. +static void fail_status(const char* what, KSTATUS status) { + print("anonymous_queue_smoke: "); + print(what); + print(" status="); + print_u64(status); + print("\n"); + exit_now(1); +} + +// Sends one fixed-size queue message. +static KSTATUS send_message(uint32_t queue, const TEST_QUEUE_MESSAGE* message) { + SYS_QUEUE_SEND_REQUEST req; + memset(&req, 0, sizeof(req)); + req.Size = sizeof(req); + req.QueueHandle = queue; + req.Flags = SYS_QUEUE_FLAG_NONE; + req.TimeoutMilliseconds = 0; + req.Message = message; + return SysSendQueue(&req); +} + +// Receives one fixed-size queue message. +static uint32_t recv_message(uint32_t queue, TEST_QUEUE_MESSAGE* message) { + SYS_QUEUE_RECV_REQUEST req; + memset(&req, 0, sizeof(req)); + req.Size = sizeof(req); + req.QueueHandle = queue; + req.Flags = SYS_QUEUE_FLAG_NONE; + req.TimeoutMilliseconds = 0; + req.MessageOut = message; + return SysRecvQueue(&req); +} + +// Verifies aliases keep an anonymous queue alive after the original handle closes. +static void check_alias_lifetime(void) { + SYS_HANDLE queue = SYS_HANDLE_INVALID; + SYS_HANDLE alias = SYS_HANDLE_INVALID; + KSTATUS status = SysCreateQueue(&queue); + if (!KSUCCESS(status)) fail_status("create queue", status); + + SYS_HANDLE self = SYS_HANDLE_INVALID; + status = SysOpenProcess(SysGetCurrentPid(), SYS_RIGHT_QUERY | SYS_PROCESS_RIGHT_INSERT_HANDLE, &self); + if (!KSUCCESS(status)) fail_status("open self", status); + SYS_DUPLICATE_HANDLE_REQUEST duplicate = {0}; + duplicate.Size = sizeof(duplicate); + duplicate.SourceHandle = queue; + duplicate.TargetProcessHandle = self; + duplicate.DesiredRights = SYS_QUEUE_RIGHT_SEND | SYS_QUEUE_RIGHT_RECEIVE; + duplicate.TargetHandleOut = &alias; + status = SysDuplicateHandle(&duplicate); + if (!KSUCCESS(status)) fail_status("duplicate queue", status); + (void)SysCloseHandle(self); + + TEST_QUEUE_MESSAGE out = {0}; + TEST_QUEUE_MESSAGE in = {0}; + out.Type = TEST_MESSAGE_TYPE; + out.Param[0] = TEST_MESSAGE_VALUE; + status = send_message(queue, &out); + if (!KSUCCESS(status)) fail_status("send queue", status); + status = SysCloseHandle(queue); + if (!KSUCCESS(status)) fail_status("close original", status); + status = recv_message(alias, &in); + if (!KSUCCESS(status)) fail_status("recv via surviving alias", status); + if (in.Type != TEST_MESSAGE_TYPE || in.Param[0] != TEST_MESSAGE_VALUE) + fail("message corrupted"); + + status = recv_message(alias, &in); + if (status != NTLSTATUS_WOULD_BLOCK) fail_status("empty queue", status); + (void)SysCloseHandle(alias); +} + +// Runs the anonymous queue lifetime smoke test. +int main(void) { + print("anonymous_queue_smoke: start\n"); + check_alias_lifetime(); + print("anonymous_queue_smoke: PASS\n"); + exit_now(0); + return 0; +} diff --git a/code/user/tests/apc_return_smoke/apc_return_smoke.c b/code/user/tests/apc_return_smoke/apc_return_smoke.c index 4aefe12..3baa615 100644 --- a/code/user/tests/apc_return_smoke/apc_return_smoke.c +++ b/code/user/tests/apc_return_smoke/apc_return_smoke.c @@ -35,7 +35,7 @@ static void print_u64(uint64_t value) { } // Opens a stable payload for async read completion. -static uint32_t open_test_file(uint32_t* handleOut) { +static KSTATUS open_test_file(uint32_t* handleOut) { static const char* paths[] = { "apc_return_smoke.exe", "/SystemRoot/apps/apc_return_smoke.exe", @@ -46,7 +46,7 @@ static uint32_t open_test_file(uint32_t* handleOut) { for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); ++i) { uint32_t handle = 0; - uint32_t status = NtlOpenFile(SysSvFromCstr(paths[i]), + KSTATUS status = NtlOpenFile(SysSvFromCstr(paths[i]), SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_QUERY, SYS_FILE_OPEN_EXISTING, SYS_FILE_FLAG_NONE, @@ -137,7 +137,7 @@ static void fail_status(const char* what, uint32_t got, uint32_t expected) { int main(void) { print("apc_return_smoke: start\n"); - uint32_t status = SysApcReturn((const SYS_USER_APC_FRAME*)0); + KSTATUS status = SysApcReturn((const SYS_USER_APC_FRAME*)0); if (status != NTLSTATUS_INVALID_PARAMETER) fail_status("inactive null return", status, NTLSTATUS_INVALID_PARAMETER); diff --git a/code/user/tests/async_apc/async_apc.c b/code/user/tests/async_apc/async_apc.c index 7009d6a..f7f7f1f 100644 --- a/code/user/tests/async_apc/async_apc.c +++ b/code/user/tests/async_apc/async_apc.c @@ -20,7 +20,7 @@ static void print(const char* s) { // Tests are usually launched from /apps, so a bare "system/init.exe" // resolves as /apps/system/init.exe and fails. Prefer the test binary itself, // then fall back to canonical SystemRoot or other relative paths. -static uint32_t open_test_file(uint32_t* handleOut, const char** pathOut) { +static KSTATUS open_test_file(uint32_t* handleOut, const char** pathOut) { static const char* paths[] = { "async_apc.exe", "/SystemRoot/apps/async_apc.exe", @@ -30,7 +30,7 @@ static uint32_t open_test_file(uint32_t* handleOut, const char** pathOut) { for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); ++i) { uint32_t h = 0; - uint32_t s = NtlOpenFile(SysSvFromCstr(paths[i]), + KSTATUS s = NtlOpenFile(SysSvFromCstr(paths[i]), SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_QUERY, SYS_FILE_OPEN_EXISTING, SYS_FILE_FLAG_NONE, @@ -90,7 +90,7 @@ static void file_apc(void* context, SYS_IO_STATUS_BLOCK* iosb, uint64_t informat int main(void) { print("async_apc: start\n"); - uint32_t s = SysSetUserApcDispatcher((void*)SysUserApcDispatcher); + KSTATUS s = SysSetUserApcDispatcher((void*)SysUserApcDispatcher); if (s != NTLSTATUS_SUCCESS) { print("async_apc: dispatcher registration failed status="); print_u64(s); diff --git a/code/user/tests/async_cancel/async_cancel.c b/code/user/tests/async_cancel/async_cancel.c index 5162ff0..e359713 100644 --- a/code/user/tests/async_cancel/async_cancel.c +++ b/code/user/tests/async_cancel/async_cancel.c @@ -19,7 +19,7 @@ static void print(const char* s) { // Tests are usually launched from /apps, so a bare "system/init.exe" // resolves as /apps/system/init.exe and fails. Prefer the test binary itself, // then fall back to canonical SystemRoot or other relative paths. -static uint32_t open_test_file(uint32_t* handleOut, const char** pathOut) { +static KSTATUS open_test_file(uint32_t* handleOut, const char** pathOut) { static const char* paths[] = { "async_cancel.exe", "/SystemRoot/apps/async_cancel.exe", @@ -29,7 +29,7 @@ static uint32_t open_test_file(uint32_t* handleOut, const char** pathOut) { for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); ++i) { uint32_t h = 0; - uint32_t s = NtlOpenFile(SysSvFromCstr(paths[i]), + KSTATUS s = NtlOpenFile(SysSvFromCstr(paths[i]), SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_QUERY, SYS_FILE_OPEN_EXISTING, SYS_FILE_FLAG_NONE, @@ -117,7 +117,7 @@ int main(void) { SysExitProcess(1); } - uint32_t cs = SysCancelIo(request); + KSTATUS cs = SysCancelIo(request); print("async_cancel: cancel returned "); print_u64(cs); print("\n"); while (iosb.Status == NTLSTATUS_PENDING) { diff --git a/code/user/tests/audio_stream_smoke/audio_stream_smoke.c b/code/user/tests/audio_stream_smoke/audio_stream_smoke.c index 5847376..d9a0d56 100644 --- a/code/user/tests/audio_stream_smoke/audio_stream_smoke.c +++ b/code/user/tests/audio_stream_smoke/audio_stream_smoke.c @@ -38,6 +38,13 @@ static void print(const char* text) { (void)SysWriteConsole(&request); } +// Emits temporary smoke-test checkpoints without relying exclusively on console drawing. +static void trace_stage(const char* message) { + SYS_PRINT_DEBUG_REQUEST request = { sizeof(SYS_PRINT_DEBUG_REQUEST), SysSvFromCstr(message) }; + if (SysPrintDebug(&request) != NTLSTATUS_SUCCESS) + print(message); +} + // Writes one unsigned integer to the console. static void print_u64(uint64_t value) { char digits[32]; @@ -217,22 +224,30 @@ static void test_master_volume(const SYS_AUDIO_ENDPOINT_INFO* endpoint) { request.Muted = 1u; request.VolumeQ16 = SYS_AUDIO_GAIN_Q16_UNITY / 2u; + trace_stage("audio_stream_smoke: trace master.set.enter\n"); KSTATUS status = SysAudioSetMasterVolume(&request); + trace_stage("audio_stream_smoke: trace master.set.returned\n"); if (status != NTLSTATUS_SUCCESS) fail_status("set master volume", status); + trace_stage("audio_stream_smoke: trace master.query.enter\n"); SYS_AUDIO_ENDPOINT_INFO updated = query_endpoint(endpoint->EndpointId); + trace_stage("audio_stream_smoke: trace master.query.returned\n"); if (updated.MasterVolumeQ16 != SYS_AUDIO_GAIN_Q16_UNITY / 2u || updated.MasterMuted != 1u) fail("master volume snapshot mismatch"); request.VolumeQ16 = SYS_AUDIO_GAIN_Q16_UNITY + 1u; + trace_stage("audio_stream_smoke: trace master.invalid.enter\n"); status = SysAudioSetMasterVolume(&request); + trace_stage("audio_stream_smoke: trace master.invalid.returned\n"); if (status != NTLSTATUS_INVALID_PARAMETER) fail_status("reject master volume above unity", status); request.Muted = endpoint->MasterMuted; request.VolumeQ16 = endpoint->MasterVolumeQ16; + trace_stage("audio_stream_smoke: trace master.restore.enter\n"); status = SysAudioSetMasterVolume(&request); + trace_stage("audio_stream_smoke: trace master.restore.returned\n"); if (status != NTLSTATUS_SUCCESS) fail_status("restore master volume", status); } @@ -720,7 +735,9 @@ int main(void) { print("audio_stream_smoke: endpoint="); print_u64(endpoint->EndpointId); print("\n"); + trace_stage("audio_stream_smoke: trace endpoint.newline.returned\n"); test_master_volume(endpoint); + trace_stage("audio_stream_smoke: trace master.complete\n"); test_stream(endpoint); print("audio_stream_smoke: PASS endpoint="); print_u64(endpoint->EndpointId); diff --git a/code/user/tests/cwd_userspace_smoke/cwd_userspace_smoke.c b/code/user/tests/cwd_userspace_smoke/cwd_userspace_smoke.c index 12df6ad..379a766 100644 --- a/code/user/tests/cwd_userspace_smoke/cwd_userspace_smoke.c +++ b/code/user/tests/cwd_userspace_smoke/cwd_userspace_smoke.c @@ -17,7 +17,7 @@ static void exit_now(int code) { while (true) SysExitProcess(code); } -static void fail_status(const char* what, uint32_t status) { +static void fail_status(const char* what, KSTATUS status) { (void)status; print("cwd_userspace_smoke: "); print(what); print("\n"); exit_now(1); @@ -30,13 +30,13 @@ static void fail(const char* what) { static void get_cwd_or_fail(char out[SYS_PATH_MAX + 1u]) { size_t required = 0; - uint32_t status = NtlGetCurrentDirectory(out, SYS_PATH_MAX + 1u, &required); + KSTATUS status = NtlGetCurrentDirectory(out, SYS_PATH_MAX + 1u, &required); if (status != NTLSTATUS_SUCCESS || required == 0 || required > SYS_PATH_MAX + 1u) fail_status("get cwd", status); } static void set_cwd_or_fail(const char* path, const char* what) { - uint32_t status = NtlSetCurrentDirectory(SysSvFromCstr(path)); + KSTATUS status = NtlSetCurrentDirectory(SysSvFromCstr(path)); if (status != NTLSTATUS_SUCCESS) fail_status(what, status); } @@ -49,7 +49,7 @@ static void expect_cwd(const char* expected) { // Resolves the current absolute resource path and returns the owning namespace-prefix length. static size_t resource_root_length_or_fail(const char* cwd) { SYS_HANDLE session = SYS_HANDLE_INVALID; - uint32_t status = NtlResourceConnect(&session); + KSTATUS status = NtlResourceConnect(&session); if (status != NTLSTATUS_SUCCESS) fail_status("resource connect", status); SYS_HANDLE root = SYS_HANDLE_INVALID; diff --git a/code/user/tests/dirty_writeback/dirty_writeback.c b/code/user/tests/dirty_writeback/dirty_writeback.c index 188fd3c..461c57f 100644 --- a/code/user/tests/dirty_writeback/dirty_writeback.c +++ b/code/user/tests/dirty_writeback/dirty_writeback.c @@ -49,7 +49,7 @@ static void fail(const char* msg) { exit_now(1); } -static void fail_status(const char* what, uint32_t status) { +static void fail_status(const char* what, KSTATUS status) { print("dirty_writeback: "); print(what); print(" failed status="); @@ -64,11 +64,11 @@ static uint32_t query_cache(SYS_FILE_CACHE_STATS* out) { } // Deletes one test path. -static uint32_t delete_path(const char* path) { +static KSTATUS delete_path(const char* path) { return NtlDeleteFile(SysSvFromCstr(path)); } -static uint32_t create_temp(uint32_t* fileOut, const char** pathOut) { +static KSTATUS create_temp(uint32_t* fileOut, const char** pathOut) { // Writable QEMU vvfat maps long-name creation through a host rename and // can abort before the guest receives an I/O status. Keep this file in // 8.3 form so the test measures writeback rather than the host bridge. @@ -79,7 +79,7 @@ static uint32_t create_temp(uint32_t* fileOut, const char** pathOut) { for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); ++i) { uint32_t h = 0; (void)delete_path(paths[i]); - uint32_t s = NtlOpenFile(SysSvFromCstr(paths[i]), + KSTATUS s = NtlOpenFile(SysSvFromCstr(paths[i]), SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_WRITE | SYS_FILE_ACCESS_QUERY, SYS_FILE_CREATE_NEW, SYS_FILE_FLAG_NONE, @@ -94,9 +94,9 @@ static uint32_t create_temp(uint32_t* fileOut, const char** pathOut) { return NTLSTATUS_NOT_FOUND; } -static uint32_t open_path(const char* path, uint32_t* fileOut) { +static KSTATUS open_path(const char* path, uint32_t* fileOut) { uint32_t h = 0; - uint32_t s = NtlOpenFile(SysSvFromCstr(path), + KSTATUS s = NtlOpenFile(SysSvFromCstr(path), SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_QUERY, SYS_FILE_OPEN_EXISTING, SYS_FILE_FLAG_NONE, @@ -116,7 +116,7 @@ static void fill_patterns(void) { static void checked_write(uint32_t file, const void* buffer, size_t size, const char* what) { size_t done = 0; SYS_FILE_IO_REQUEST req = SysMakeFileIoRequest(file, buffer, size, &done); - uint32_t s = SysWriteFile(&req); + KSTATUS s = SysWriteFile(&req); if (s != NTLSTATUS_SUCCESS) fail_status(what, s); if (done != size) fail("short write"); } @@ -125,13 +125,13 @@ static void checked_read(uint32_t file, void* buffer, size_t size, size_t* gotOu size_t got = 0; memset(buffer, 0xCC, size); SYS_FILE_IO_REQUEST req = SysMakeFileIoRequest(file, buffer, size, &got); - uint32_t s = SysReadFile(&req); + KSTATUS s = SysReadFile(&req); if (s != NTLSTATUS_SUCCESS) fail_status(what, s); if (gotOut) *gotOut = got; } static void checked_seek(uint32_t file, uint64_t offset, const char* what) { - uint32_t s = SysSeekFile(file, offset); + KSTATUS s = SysSeekFile(file, offset); if (s != NTLSTATUS_SUCCESS) fail_status(what, s); } diff --git a/code/user/tests/exception_dispatcher_smoke/exception_dispatcher_smoke.c b/code/user/tests/exception_dispatcher_smoke/exception_dispatcher_smoke.c index 114f410..5ed134e 100644 --- a/code/user/tests/exception_dispatcher_smoke/exception_dispatcher_smoke.c +++ b/code/user/tests/exception_dispatcher_smoke/exception_dispatcher_smoke.c @@ -45,7 +45,7 @@ static void fail(const char* message) { } // Fails the test with one status value. -static void fail_status(const char* what, uint32_t status) { +static void fail_status(const char* what, KSTATUS status) { print("exception_dispatcher_smoke: "); print(what); print(" status="); @@ -81,7 +81,7 @@ static void smoke_continue_dispatcher(SYS_EXCEPTION_FRAME* frame) { request.Disposition = SYS_EXCEPTION_DISPOSITION_CONTINUE_EXECUTION; request.Context = &frame->ContextRecord; - uint32_t status = SysContinueException(&request); + KSTATUS status = SysContinueException(&request); fail_status("continue exception returned", status); } @@ -104,7 +104,7 @@ static SYS_EXCEPTION_DISPATCHER_REQUEST get_registration(void) { SYS_EXCEPTION_DISPATCHER_REQUEST request; memset(&request, 0, sizeof(request)); request.Size = sizeof(request); - uint32_t status = SysGetExceptionDispatcher(&request); + KSTATUS status = SysGetExceptionDispatcher(&request); if (status != NTLSTATUS_SUCCESS) fail_status("get dispatcher", status); if (request.Size != sizeof(request)) @@ -150,7 +150,7 @@ int main(void) { request.Size = sizeof(request); request.DispatcherRoutine = smoke_dispatcher; request.DispatcherContext = (void*)(uintptr_t)0x1234; - uint32_t status = SysSetExceptionDispatcher(&request); + KSTATUS status = SysSetExceptionDispatcher(&request); if (status != NTLSTATUS_SUCCESS) fail_status("set dispatcher", status); diff --git a/code/user/tests/exception_smoke/exception_smoke.c b/code/user/tests/exception_smoke/exception_smoke.c index 936df46..ae03b4b 100644 --- a/code/user/tests/exception_smoke/exception_smoke.c +++ b/code/user/tests/exception_smoke/exception_smoke.c @@ -1,5 +1,6 @@ // Native exception pipeline integration smoke test. +#include #include #include #include @@ -53,7 +54,7 @@ static void fail(const char* message) { } // Fails the test with one status value. -static void fail_status(const char* what, uint32_t status) { +static void fail_status(const char* what, KSTATUS status) { print("exception_smoke: "); print(what); print(" status="); @@ -63,7 +64,7 @@ static void fail_status(const char* what, uint32_t status) { } // Returns whether a string view exactly matches a literal string. -static int sv_equals_cstr(SYS_STRING_VIEW sv, const char* literal) { +static bool sv_equals_cstr(SYS_STRING_VIEW sv, const char* literal) { size_t len = strlen(literal); return sv.Length == len && sv.Data && memcmp(sv.Data, literal, len) == 0; } @@ -106,7 +107,7 @@ static void run_child_expect_exit(const char* mode, uint32_t expectedExit) { request.SecurityContext = SYS_SECURITY_CONTEXT_CURRENT; request.ResolveModuleName = NtlResolveSystemModuleName; request.ProcessHandleOut = &child; - uint32_t status = NtlCreateSystemProcess(&request); + KSTATUS status = NtlCreateSystemProcess(&request); if (status != NTLSTATUS_SUCCESS) fail_status("create child", status); @@ -142,7 +143,7 @@ static void child_divide_by_zero(void) { } // Observes an access violation and exits from inside the native handler. -static int access_violation_handler(SYS_EXCEPTION_RECORD* record, +static uint32_t access_violation_handler(SYS_EXCEPTION_RECORD* record, SYS_USER_CONTEXT* context, void* userContext) { (void)context; @@ -171,7 +172,7 @@ static void child_access_violation(void) { } // Returns a resume context with an invalid user RIP. -static int invalid_resume_handler(SYS_EXCEPTION_RECORD* record, +static uint32_t invalid_resume_handler(SYS_EXCEPTION_RECORD* record, SYS_USER_CONTEXT* context, void* userContext) { (void)userContext; @@ -191,7 +192,7 @@ static void child_invalid_resume(void) { } // Attempts to continue a noncontinuable user-raised exception. -static int noncontinuable_handler(SYS_EXCEPTION_RECORD* record, +static uint32_t noncontinuable_handler(SYS_EXCEPTION_RECORD* record, SYS_USER_CONTEXT* context, void* userContext) { (void)context; @@ -216,7 +217,7 @@ static void child_noncontinuable(void) { request.Flags = SYS_RAISE_EXCEPTION_FLAG_NONCONTINUABLE; request.UserCode = SMOKE_NONCONTINUABLE_CODE; - uint32_t status = SysRaiseException(&request); + KSTATUS status = SysRaiseException(&request); exit_now((int32_t)status); } diff --git a/code/user/tests/file_lock_smoke/file_lock_smoke.c b/code/user/tests/file_lock_smoke/file_lock_smoke.c index 27cae6d..4dc85d1 100644 --- a/code/user/tests/file_lock_smoke/file_lock_smoke.c +++ b/code/user/tests/file_lock_smoke/file_lock_smoke.c @@ -1,6 +1,7 @@ // Advisory file-lock smoke test. // Verifies process-owned byte-range conflicts, timeout behavior, and exit cleanup. +#include #include #include #include @@ -45,7 +46,7 @@ static void exit_now(int32_t code) { } // Fails the test with one status value. -static void fail_status(const char* what, uint32_t status) { +static void fail_status(const char* what, KSTATUS status) { print("file_lock_smoke: "); print(what); print(" status="); @@ -68,9 +69,9 @@ static void delete_path(const char* path) { } // Opens one path with lock rights. -static uint32_t open_existing_lockable(const char* path, uint32_t* fileOut) { +static KSTATUS open_existing_lockable(const char* path, uint32_t* fileOut) { uint32_t h = 0; - uint32_t s = NtlOpenFile(SysSvFromCstr(path), + KSTATUS s = NtlOpenFile(SysSvFromCstr(path), SYS_FILE_ACCESS_QUERY | SYS_FILE_ACCESS_LOCK, SYS_FILE_OPEN_EXISTING, SYS_FILE_FLAG_NONE, @@ -81,7 +82,7 @@ static uint32_t open_existing_lockable(const char* path, uint32_t* fileOut) { } // Creates a temporary file on the first writable path that works. -static uint32_t create_temp_file(uint32_t* fileOut, const char** pathOut) { +static KSTATUS create_temp_file(uint32_t* fileOut, const char** pathOut) { static const char* paths[] = { "file_lock_smoke.tmp", }; @@ -89,7 +90,7 @@ static uint32_t create_temp_file(uint32_t* fileOut, const char** pathOut) { for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); i++) { uint32_t h = 0; delete_path(paths[i]); - uint32_t s = NtlOpenFile(SysSvFromCstr(paths[i]), + KSTATUS s = NtlOpenFile(SysSvFromCstr(paths[i]), SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_WRITE | SYS_FILE_ACCESS_QUERY | SYS_FILE_ACCESS_LOCK, SYS_FILE_CREATE_NEW, SYS_FILE_FLAG_NONE, @@ -145,30 +146,30 @@ static uint32_t query_range(uint32_t file, uint32_t mode, uint64_t offset, uint6 } // Splits the child command line into mode and path. -static int parse_child_command(SYS_STRING_VIEW view, char* mode, size_t modeCap, char* path, size_t pathCap) { +static bool parse_child_command(SYS_STRING_VIEW view, char* mode, size_t modeCap, char* path, size_t pathCap) { if (!view.Data || view.Length == 0) - return 0; + return false; size_t sep = 0; while (sep < view.Length && view.Data[sep] != ':') sep++; if (sep == view.Length || sep + 1 >= view.Length) - return 0; + return false; if (sep + 1 > modeCap || view.Length - sep > pathCap) - return 0; + return false; memcpy(mode, view.Data, sep); mode[sep] = '\0'; memcpy(path, view.Data + sep + 1, view.Length - sep - 1); path[view.Length - sep - 1] = '\0'; - return 1; + return true; } // Returns whether two NUL-terminated strings are equal. -static int str_eq(const char* a, const char* b) { +static bool str_eq(const char* a, const char* b) { return strcmp(a, b) == 0; } // Returns whether one mode name belongs to the child command protocol. -static int is_child_mode(const char* mode) { +static bool is_child_mode(const char* mode) { return str_eq(mode, "excl-blocked") || str_eq(mode, "excl-timeout") || str_eq(mode, "excl-wait") || @@ -277,7 +278,7 @@ static void run_child_expect_success(const char* mode, const char* path) { req.SecurityContext = SYS_SECURITY_CONTEXT_CURRENT; req.ResolveModuleName = NtlResolveSystemModuleName; req.ProcessHandleOut = &child; - uint32_t s = NtlCreateSystemProcess(&req); + KSTATUS s = NtlCreateSystemProcess(&req); if (s != NTLSTATUS_SUCCESS) fail_status("create child", s); s = SysWait(child, SYS_WAIT_FLAG_NONE); @@ -308,7 +309,7 @@ static void test_validation(uint32_t file) { LOCK_LENGTH, 0, }; - uint32_t s = SysLockFile(&lockReq); + KSTATUS s = SysLockFile(&lockReq); if (s != NTLSTATUS_INVALID_PARAMETER) fail_status("bad lock size", s); lockReq.Size = sizeof(lockReq); diff --git a/code/user/tests/file_stat_smoke/file_stat_smoke.c b/code/user/tests/file_stat_smoke/file_stat_smoke.c index 4d5391c..aeaa512 100644 --- a/code/user/tests/file_stat_smoke/file_stat_smoke.c +++ b/code/user/tests/file_stat_smoke/file_stat_smoke.c @@ -1,6 +1,7 @@ // Native file metadata smoke test. // Verifies path stat, handle stat, directory metadata, missing paths, and invalid handles without mutating the filesystem. +#include #include #include #include @@ -49,7 +50,7 @@ static void fail(const char* msg) { } // Fails the test with one status value. -static void fail_status(const char* what, uint32_t status) { +static void fail_status(const char* what, KSTATUS status) { print("file_stat_smoke: "); print(what); print(" failed status="); @@ -59,12 +60,12 @@ static void fail_status(const char* what, uint32_t status) { } // Copies one string view into a fixed NUL-terminated buffer. -static int copy_view(char* out, size_t outSize, SYS_STRING_VIEW view) { +static bool copy_view(char* out, size_t outSize, SYS_STRING_VIEW view) { if (!out || outSize == 0 || !view.Data || view.Length + 1 > outSize) - return 0; + return false; memcpy(out, view.Data, view.Length); out[view.Length] = '\0'; - return 1; + return true; } // Builds the containing directory path for one process image path. @@ -97,7 +98,7 @@ static void build_parent_dir(char* out, size_t outSize, const char* path) { } // Opens this process image for read/query metadata checks. -static uint32_t open_self_image(uint32_t* fileOut, uint64_t* sizeOut) { +static KSTATUS open_self_image(uint32_t* fileOut, uint64_t* sizeOut) { SYS_PEB* peb = SysGetPeb(); if (!peb || peb->Magic != SYS_PEB_MAGIC || peb->Version != SYS_ENV_VERSION) return NTLSTATUS_INVALID_PARAMETER; @@ -106,7 +107,7 @@ static uint32_t open_self_image(uint32_t* fileOut, uint64_t* sizeOut) { build_parent_dir(gSelfDir, sizeof(gSelfDir), gSelfPath); uint32_t h = 0; - uint32_t s = NtlOpenFile(SysSvFromCstr(gSelfPath), + KSTATUS s = NtlOpenFile(SysSvFromCstr(gSelfPath), SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_QUERY, SYS_FILE_OPEN_EXISTING, SYS_FILE_FLAG_NONE, @@ -167,7 +168,7 @@ static void check_directory_stat(const SYS_FILE_STAT* stat) { static void test_file_stat(uint32_t file, uint64_t expectedSize) { SYS_FILE_STAT byHandle; memset(&byHandle, 0xA5, sizeof(byHandle)); - uint32_t s = SysGetFileStat(file, &byHandle); + KSTATUS s = SysGetFileStat(file, &byHandle); if (s != NTLSTATUS_SUCCESS) fail_status("get file stat", s); check_regular_stat(&byHandle, expectedSize, "handle stat"); @@ -192,7 +193,7 @@ static void test_file_stat(uint32_t file, uint64_t expectedSize) { static void test_directory_stat(void) { SYS_FILE_STAT stat; memset(&stat, 0xCC, sizeof(stat)); - uint32_t s = NtlStatPath(SysSvFromCstr(gSelfDir), &stat); + KSTATUS s = NtlStatPath(SysSvFromCstr(gSelfDir), &stat); if (s != NTLSTATUS_SUCCESS) fail_status("directory stat", s); check_directory_stat(&stat); } @@ -200,7 +201,7 @@ static void test_directory_stat(void) { // Checks failure status paths that stat callers depend on. static void test_failure_paths(void) { SYS_FILE_STAT stat; - uint32_t s = NtlStatPath(SYS_SV_LITERAL("file_stat_smoke_missing.nope"), &stat); + KSTATUS s = NtlStatPath(SYS_SV_LITERAL("file_stat_smoke_missing.nope"), &stat); if (s != NTLSTATUS_NOT_FOUND) fail_status("missing path status", s); s = SysGetFileStat(0xFFFFFFFFu, &stat); diff --git a/code/user/tests/input_api_smoke/input_api_smoke.c b/code/user/tests/input_api_smoke/input_api_smoke.c deleted file mode 100644 index 374bf4f..0000000 --- a/code/user/tests/input_api_smoke/input_api_smoke.c +++ /dev/null @@ -1,122 +0,0 @@ -// Native input API smoke test. -// Verifies status-returning keyboard and mouse polling contracts without requiring live input. - -#include -#include -#include - -// Prints one NUL-terminated string to the kernel console. -static void print(const char* s) { - SYS_WRITE_CONSOLE_REQUEST req = { sizeof(SYS_WRITE_CONSOLE_REQUEST), SysSvFromCstr(s) }; - SysWriteConsole(&req); -} - -// Prints one unsigned integer to the kernel console. -static void print_u64(uint64_t v) { - char buf[32]; - int i = 0; - if (v == 0) { - print("0"); - return; - } - while (v && i < (int)sizeof(buf)) { - buf[i++] = (char)('0' + (v % 10)); - v /= 10; - } - while (i--) { - char c = buf[i]; - SYS_WRITE_CONSOLE_REQUEST req = { sizeof(SYS_WRITE_CONSOLE_REQUEST), SysSvFromParts(&c, 1) }; - SysWriteConsole(&req); - } -} - -// Terminates the current test process. -static void exit_now(int code) { - while (true) SysExitProcess(code); -} - -// Fails the test with one status value. -static void fail_status(const char* what, uint32_t status) { - print("input_api_smoke: "); - print(what); - print(" status="); - print_u64(status); - print("\n"); - exit_now(1); -} - -// Requires one exact status value. -static void expect_status(const char* what, uint32_t got, uint32_t expected) { - if (got == expected) return; - print("input_api_smoke: "); - print(what); - print(" got="); - print_u64(got); - print(" expected="); - print_u64(expected); - print("\n"); - exit_now(1); -} - -// Program entry point. -int main(void) { - print("input_api_smoke: start\n"); - - expect_status("poll keyboard null", SysPollKeyboard((INPUT_KEY*)0), NTLSTATUS_INVALID_PARAMETER); - expect_status("is key down null", SysIsKeyDown(INPUT_KEY_A, (bool*)0), NTLSTATUS_INVALID_PARAMETER); - expect_status("read key null", SysReadKey((INPUT_KEY*)0), NTLSTATUS_INVALID_PARAMETER); - expect_status("read char null", SysReadKeyboardChar((char*)0), NTLSTATUS_INVALID_PARAMETER); - - bool down = true; - uint32_t status = SysIsKeyDown(INPUT_KEY_A, &down); - if (status != NTLSTATUS_SUCCESS) - fail_status("is key down", status); - - INPUT_KEY key = INPUT_KEY_A; - status = SysPollKeyboard(&key); - if (status != NTLSTATUS_SUCCESS && status != NTLSTATUS_WOULD_BLOCK) - fail_status("poll keyboard", status); - if (status == NTLSTATUS_WOULD_BLOCK && key != INPUT_KEY_NONE) - fail_status("poll keyboard would-block key", key); - - INPUT_KEY_RECORD input; - SYS_INPUT_EVENT_REQUEST inputReq; - inputReq.Size = sizeof(inputReq); - inputReq.QueueHandle = 0; - inputReq.Flags = SYS_INPUT_EVENT_FLAG_NONE; - inputReq.TimeoutMilliseconds = 0; - inputReq.RecordOut = &input; - - expect_status("peek input null", SysPeekInputEvent((const SYS_INPUT_EVENT_REQUEST*)0), NTLSTATUS_INVALID_PARAMETER); - status = SysPeekInputEvent(&inputReq); - if (status != NTLSTATUS_SUCCESS && status != NTLSTATUS_WOULD_BLOCK) - fail_status("peek input", status); - - INPUT_MOUSE_EVENT mouse; - expect_status("peek mouse null", SysPeekMouse((INPUT_MOUSE_EVENT*)0), NTLSTATUS_INVALID_PARAMETER); - expect_status("read mouse null", SysReadMouse((INPUT_MOUSE_EVENT*)0), NTLSTATUS_INVALID_PARAMETER); - - status = SysPeekMouse(&mouse); - if (status != NTLSTATUS_SUCCESS && status != NTLSTATUS_WOULD_BLOCK) - fail_status("peek mouse", status); - - uint32_t readStatus = SysReadMouse(&mouse); - if (status == NTLSTATUS_SUCCESS) { - if (readStatus != NTLSTATUS_SUCCESS) - fail_status("read mouse after peek", readStatus); - } else if (readStatus != NTLSTATUS_WOULD_BLOCK) { - fail_status("read mouse would-block", readStatus); - } - - int x = 0; - int y = 0; - expect_status("get mouse pos null x", SysGetMousePos((int*)0, &y), NTLSTATUS_INVALID_PARAMETER); - expect_status("get mouse pos null y", SysGetMousePos(&x, (int*)0), NTLSTATUS_INVALID_PARAMETER); - status = SysGetMousePos(&x, &y); - if (status != NTLSTATUS_SUCCESS) - fail_status("get mouse pos", status); - - print("input_api_smoke: PASS\n"); - exit_now(0); - return 0; -} diff --git a/code/user/tests/input_model_smoke/input_model_smoke.c b/code/user/tests/input_model_smoke/input_model_smoke.c deleted file mode 100644 index ec46fb2..0000000 --- a/code/user/tests/input_model_smoke/input_model_smoke.c +++ /dev/null @@ -1,135 +0,0 @@ -// Input model smoke test. -// Verifies that input queue handles and request-based reads target one keyboard queue. - -#include -#include - -// Prints one NUL-terminated string to the kernel console. -static void print(const char* s) { - SYS_WRITE_CONSOLE_REQUEST req = { sizeof(SYS_WRITE_CONSOLE_REQUEST), SysSvFromCstr(s) }; - SysWriteConsole(&req); -} - -// Prints one unsigned integer to the kernel console. -static void print_u64(uint64_t v) { - char buf[32]; - int i = 0; - if (v == 0) { - print("0"); - return; - } - while (v && i < (int)sizeof(buf)) { - buf[i++] = (char)('0' + (v % 10)); - v /= 10; - } - while (i--) { - char c = buf[i]; - SYS_WRITE_CONSOLE_REQUEST req = { sizeof(SYS_WRITE_CONSOLE_REQUEST), SysSvFromParts(&c, 1) }; - SysWriteConsole(&req); - } -} - -// Terminates the current test process. -static void exit_now(int code) { - while (true) SysExitProcess(code); -} - -// Fails the test with one status value. -static void fail_status(const char* what, uint32_t status) { - print("input_model_smoke: "); - print(what); - print(" status="); - print_u64(status); - print("\n"); - exit_now(1); -} - -// Requires one exact status value. -static void expect_status(const char* what, uint32_t got, uint32_t expected) { - if (got == expected) return; - print("input_model_smoke: "); - print(what); - print(" got="); - print_u64(got); - print(" expected="); - print_u64(expected); - print("\n"); - exit_now(1); -} - -// Program entry point. -int main(void) { - print("input_model_smoke: start\n"); - - uint32_t inputQueue = 0; - expect_status("open input queue null", SysOpenInputQueue(SYS_INPUT_QUEUE_FLAG_NONE, (uint32_t*)0), NTLSTATUS_INVALID_PARAMETER); - expect_status("open input queue bad flags", SysOpenInputQueue(1, &inputQueue), NTLSTATUS_INVALID_PARAMETER); - - uint32_t status = SysOpenInputQueue(SYS_INPUT_QUEUE_FLAG_NONE, &inputQueue); - if (status != NTLSTATUS_SUCCESS) - fail_status("open input queue", status); - - INPUT_KEY_RECORD record; - SYS_INPUT_EVENT_REQUEST req; - req.Size = sizeof(req); - req.QueueHandle = inputQueue; - req.Flags = SYS_INPUT_EVENT_FLAG_NONE; - req.TimeoutMilliseconds = 0; - req.RecordOut = &record; - - SYS_INPUT_EVENT_REQUEST badReq = req; - badReq.Size = 0; - expect_status("read input bad size", SysReadInputEvent(&badReq), NTLSTATUS_INVALID_PARAMETER); - - badReq = req; - badReq.RecordOut = (INPUT_KEY_RECORD*)0; - expect_status("peek input null out", SysPeekInputEvent(&badReq), NTLSTATUS_INVALID_PARAMETER); - - badReq = req; - badReq.QueueHandle = 0xdeadbeefu; - expect_status("read input bad handle", SysReadInputEvent(&badReq), NTLSTATUS_INVALID_HANDLE); - - SYS_GLOBAL_INPUT_STATE state; - state.Size = sizeof(state); - state.Flags = SYS_GLOBAL_INPUT_STATE_FLAG_NONE; - expect_status("query global input null", SysQueryGlobalInputState((SYS_GLOBAL_INPUT_STATE*)0), NTLSTATUS_INVALID_PARAMETER); - - SYS_GLOBAL_INPUT_STATE badState = state; - badState.Size = 0; - expect_status("query global input bad size", SysQueryGlobalInputState(&badState), NTLSTATUS_INVALID_PARAMETER); - - badState = state; - badState.Flags = 1; - expect_status("query global input bad flags", SysQueryGlobalInputState(&badState), NTLSTATUS_INVALID_PARAMETER); - - status = SysQueryGlobalInputState(&state); - if (status != NTLSTATUS_SUCCESS) - fail_status("query global input", status); - if (state.Size != sizeof(state)) - fail_status("query global input size", state.Size); - if (state.Flags != SYS_GLOBAL_INPUT_STATE_FLAG_NONE) - fail_status("query global input flags", state.Flags); - - status = SysPeekInputEvent(&req); - if (status != NTLSTATUS_SUCCESS && status != NTLSTATUS_WOULD_BLOCK) - fail_status("peek input immediate", status); - - uint32_t waitStatus = SysWaitSingle(inputQueue, SYS_WAIT_FLAG_NONE, 0); - if (status == NTLSTATUS_SUCCESS) { - if (waitStatus != NTLSTATUS_SUCCESS) - fail_status("wait input after readable peek", waitStatus); - } else if (waitStatus != NTLSTATUS_TIMEOUT) { - fail_status("wait empty input immediate", waitStatus); - } - - req.TimeoutMilliseconds = 1; - status = SysReadInputEvent(&req); - if (status != NTLSTATUS_SUCCESS && status != NTLSTATUS_TIMEOUT) - fail_status("read input finite timeout", status); - - SysCloseHandle(inputQueue); - - print("input_model_smoke: PASS\n"); - exit_now(0); - return 0; -} diff --git a/code/user/tests/mmap_smoke/mmap_smoke.c b/code/user/tests/mmap_smoke/mmap_smoke.c index 03c6956..5fea951 100644 --- a/code/user/tests/mmap_smoke/mmap_smoke.c +++ b/code/user/tests/mmap_smoke/mmap_smoke.c @@ -40,7 +40,7 @@ static void exit_now(int code) { SysExitProcess(code); } -static void fail_status(const char* what, uint32_t status) { +static void fail_status(const char* what, KSTATUS status) { print("mmap_smoke: "); print(what); print(" failed status="); @@ -56,7 +56,7 @@ static void fail_msg(const char* msg) { exit_now(1); } -static uint32_t open_first_existing(uint32_t* fileOut, const char** pathOut) { +static KSTATUS open_first_existing(uint32_t* fileOut, const char** pathOut) { static const char* candidates[] = { "mmap_smoke.exe", "/SystemRoot/apps/mmap_smoke.exe", @@ -67,7 +67,7 @@ static uint32_t open_first_existing(uint32_t* fileOut, const char** pathOut) { for (size_t i = 0; i < sizeof(candidates) / sizeof(candidates[0]); ++i) { uint32_t file = 0; - uint32_t s = NtlOpenFile(SysSvFromCstr(candidates[i]), + KSTATUS s = NtlOpenFile(SysSvFromCstr(candidates[i]), SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_QUERY, SYS_FILE_OPEN_EXISTING, SYS_FILE_FLAG_NONE, diff --git a/code/user/tests/module_load_smoke/module_load_smoke.c b/code/user/tests/module_load_smoke/module_load_smoke.c index fe7d43f..0be9026 100644 --- a/code/user/tests/module_load_smoke/module_load_smoke.c +++ b/code/user/tests/module_load_smoke/module_load_smoke.c @@ -1,5 +1,6 @@ // User-mode smoke test for process-local userspace module loading. +#include #include #include #include @@ -26,7 +27,7 @@ static void print_u64(uint64_t v) { static void exit_now(int32_t code) { while (true) SysExitProcess(code); } static void fail(const char* message) { print("module_load_smoke: "); print(message); print("\n"); exit_now(1); } -static void fail_status(const char* what, uint32_t status) { +static void fail_status(const char* what, KSTATUS status) { print("module_load_smoke: "); print(what); print(" status="); print_u64(status); print("\n"); exit_now(1); } @@ -36,12 +37,12 @@ static void check_module_info(const NTL_MODULE_INFO* info) { if (strcmp(info->Name, "posix.dll") != 0) fail("module name mismatch"); } -static int module_list_contains(uintptr_t base) { +static bool module_list_contains(uintptr_t base) { size_t count = 0; - uint32_t status = NtlGetModuleList(0, 0, &count); + KSTATUS status = NtlGetModuleList(0, 0, &count); if (status != NTLSTATUS_BUFFER_TOO_SMALL && status != NTLSTATUS_SUCCESS) fail_status("query module count", status); - if (count == 0) return 0; + if (count == 0) return false; if (count > SIZE_MAX / sizeof(NTL_MODULE_INFO)) fail("module count overflow"); size_t bytes = count * sizeof(NTL_MODULE_INFO); size_t pages = (bytes + 4095u) / 4096u; @@ -60,8 +61,8 @@ static int module_list_contains(uintptr_t base) { size_t copied = count; status = NtlGetModuleList(modules, count, &copied); if (status != NTLSTATUS_SUCCESS) { (void)SysFreeVm(memory); fail_status("query module list", status); } - int found = 0; - for (size_t i = 0; i < copied; ++i) if (modules[i].Base == base) { found = 1; break; } + bool found = false; + for (size_t i = 0; i < copied; ++i) if (modules[i].Base == base) { found = true; break; } (void)SysFreeVm(memory); return found; } @@ -81,7 +82,7 @@ static NTL_MODULE_HANDLE load_module(SYS_HANDLE root, const char* path, req.ResolveModuleName = resolver; req.ModuleHandleOut = &handle; req.InfoOut = info; - uint32_t status = NtlLoadModuleAt(&req); + KSTATUS status = NtlLoadModuleAt(&req); if (status != NTLSTATUS_SUCCESS) fail_status("load module", status); if (handle == NTL_MODULE_HANDLE_INVALID) fail("load returned null handle"); return handle; @@ -91,7 +92,7 @@ static NTL_MODULE_HANDLE load_module(SYS_HANDLE root, const char* path, int main(void) { print("module_load_smoke: start\n"); SYS_HANDLE root = SYS_HANDLE_INVALID; - uint32_t status = NtlResourceOpenSystemRoot(SYS_RIGHT_QUERY | SYS_VOLUME_RIGHT_OPEN, &root); + KSTATUS status = NtlResourceOpenSystemRoot(SYS_RIGHT_QUERY | SYS_VOLUME_RIGHT_OPEN, &root); if (status != NTLSTATUS_SUCCESS) fail_status("open system root", status); NTL_MODULE_HANDLE syscallModule = load_module(root, "system/syscall.dll", diff --git a/code/user/tests/named_queue_lifetime_smoke/named_queue_lifetime_smoke.c b/code/user/tests/named_queue_lifetime_smoke/named_queue_lifetime_smoke.c deleted file mode 100644 index d03668e..0000000 --- a/code/user/tests/named_queue_lifetime_smoke/named_queue_lifetime_smoke.c +++ /dev/null @@ -1,197 +0,0 @@ -// Native named queue lifetime smoke test. -// Verifies explicit name withdrawal and automatic withdrawal on owner handle close. - -#include -#include -#include - -#define TEST_MESSAGE_TYPE 0x71756575u -#define TEST_MESSAGE_VALUE 0x123456789abcdef0ull - -typedef struct TEST_QUEUE_MESSAGE { - uint32_t Type; - uint32_t Flags; - uint64_t Param[6]; -} TEST_QUEUE_MESSAGE; - -typedef char TEST_QUEUE_MESSAGE_SIZE_CHECK[ - (sizeof(TEST_QUEUE_MESSAGE) == SYS_QUEUE_MESSAGE_SIZE) ? 1 : -1 -]; - -// Prints one NUL-terminated string to the kernel console. -static void print(const char* s) { - SYS_WRITE_CONSOLE_REQUEST req = { sizeof(SYS_WRITE_CONSOLE_REQUEST), SysSvFromCstr(s) }; - SysWriteConsole(&req); -} - -// Prints one unsigned integer to the kernel console. -static void print_u64(uint64_t v) { - char buf[32]; - int i = 0; - if (v == 0) { - print("0"); - return; - } - while (v && i < (int)sizeof(buf)) { - buf[i++] = (char)('0' + (v % 10)); - v /= 10; - } - while (i--) { - char c = buf[i]; - SYS_WRITE_CONSOLE_REQUEST req = { sizeof(SYS_WRITE_CONSOLE_REQUEST), SysSvFromParts(&c, 1) }; - SysWriteConsole(&req); - } -} - -// Terminates the current test process. -static void exit_now(int code) { - while (true) SysExitProcess(code); -} - -// Fails the test with one plain message. -static void fail(const char* msg) { - print("named_queue_lifetime_smoke: "); - print(msg); - print("\n"); - exit_now(1); -} - -// Fails the test with one status value. -static void fail_status(const char* what, uint32_t status) { - print("named_queue_lifetime_smoke: "); - print(what); - print(" status="); - print_u64(status); - print("\n"); - exit_now(1); -} - -// Requires one exact status value. -static void expect_status(const char* what, uint32_t got, uint32_t expected) { - if (got == expected) return; - print("named_queue_lifetime_smoke: "); - print(what); - print(" got="); - print_u64(got); - print(" expected="); - print_u64(expected); - print("\n"); - exit_now(1); -} - -// Publishes one queue under a short test name. -static uint32_t publish_queue(const char* name, uint32_t queue) { - SYS_PUBLISH_QUEUE_REQUEST req; - memset(&req, 0, sizeof(req)); - req.Size = sizeof(req); - req.Name = SysSvFromCstr(name); - req.QueueHandle = queue; - return SysPublishQueue(&req); -} - -// Withdraws one named queue publication. -static uint32_t unpublish_queue(const char* name) { - SYS_UNPUBLISH_QUEUE_REQUEST req; - memset(&req, 0, sizeof(req)); - req.Size = sizeof(req); - req.Name = SysSvFromCstr(name); - return SysUnpublishQueue(&req); -} - -// Opens one named queue publication. -static uint32_t open_named_queue(const char* name, uint32_t* out) { - SYS_OPEN_NAMED_QUEUE_REQUEST req; - memset(&req, 0, sizeof(req)); - req.Size = sizeof(req); - req.Name = SysSvFromCstr(name); - req.HandleOut = out; - return SysOpenNamedQueue(&req); -} - -// Sends one fixed-size queue message. -static uint32_t send_message(uint32_t queue, const TEST_QUEUE_MESSAGE* message) { - SYS_QUEUE_SEND_REQUEST req; - memset(&req, 0, sizeof(req)); - req.Size = sizeof(req); - req.QueueHandle = queue; - req.Flags = SYS_QUEUE_FLAG_NONE; - req.TimeoutMilliseconds = 0; - req.Message = message; - return SysSendQueue(&req); -} - -// Receives one fixed-size queue message. -static uint32_t recv_message(uint32_t queue, TEST_QUEUE_MESSAGE* message) { - SYS_QUEUE_RECV_REQUEST req; - memset(&req, 0, sizeof(req)); - req.Size = sizeof(req); - req.QueueHandle = queue; - req.Flags = SYS_QUEUE_FLAG_NONE; - req.TimeoutMilliseconds = 0; - req.MessageOut = message; - return SysRecvQueue(&req); -} - -// Verifies explicit unpublish removes only the name, not existing handles. -static void check_explicit_unpublish(void) { - uint32_t queue = 0; - uint32_t opened = 0; - uint32_t status = SysCreateQueue(&queue); - if (status != NTLSTATUS_SUCCESS) fail_status("create queue", status); - - status = publish_queue("ntl.queue.explicit", queue); - if (status != NTLSTATUS_SUCCESS) fail_status("publish queue", status); - - status = open_named_queue("ntl.queue.explicit", &opened); - if (status != NTLSTATUS_SUCCESS) fail_status("open published queue", status); - if (!opened) fail("opened handle is zero"); - - status = unpublish_queue("ntl.queue.explicit"); - if (status != NTLSTATUS_SUCCESS) fail_status("unpublish queue", status); - - uint32_t missing = 0; - status = open_named_queue("ntl.queue.explicit", &missing); - expect_status("open withdrawn name", status, NTLSTATUS_NOT_FOUND); - - TEST_QUEUE_MESSAGE out; - TEST_QUEUE_MESSAGE in; - memset(&out, 0, sizeof(out)); - memset(&in, 0, sizeof(in)); - out.Type = TEST_MESSAGE_TYPE; - out.Param[0] = TEST_MESSAGE_VALUE; - status = send_message(queue, &out); - if (status != NTLSTATUS_SUCCESS) fail_status("send after unpublish", status); - status = recv_message(opened, &in); - if (status != NTLSTATUS_SUCCESS) fail_status("recv after unpublish", status); - if (in.Type != TEST_MESSAGE_TYPE || in.Param[0] != TEST_MESSAGE_VALUE) - fail("message mismatch after unpublish"); - - SysCloseHandle(opened); - SysCloseHandle(queue); -} - -// Verifies closing the publishing handle withdraws its names. -static void check_close_withdraws_name(void) { - uint32_t queue = 0; - uint32_t status = SysCreateQueue(&queue); - if (status != NTLSTATUS_SUCCESS) fail_status("create close queue", status); - - status = publish_queue("ntl.queue.close", queue); - if (status != NTLSTATUS_SUCCESS) fail_status("publish close queue", status); - - SysCloseHandle(queue); - - uint32_t opened = 0; - status = open_named_queue("ntl.queue.close", &opened); - expect_status("open close-withdrawn name", status, NTLSTATUS_NOT_FOUND); -} - -// Runs the named queue lifetime smoke test. -int main(void) { - print("named_queue_lifetime_smoke: start\n"); - check_explicit_unpublish(); - check_close_withdraws_name(); - print("named_queue_lifetime_smoke: PASS\n"); - exit_now(0); - return 0; -} diff --git a/code/user/tests/native_exception_runtime_smoke/native_exception_runtime_smoke.c b/code/user/tests/native_exception_runtime_smoke/native_exception_runtime_smoke.c index 8ee774e..02f77c7 100644 --- a/code/user/tests/native_exception_runtime_smoke/native_exception_runtime_smoke.c +++ b/code/user/tests/native_exception_runtime_smoke/native_exception_runtime_smoke.c @@ -1,5 +1,6 @@ // Native exception runtime smoke test. This file verifies process-start registration and handler dispatch. +#include #include #include #include @@ -47,7 +48,7 @@ static void fail(const char* message) { } // Fails the test with one status value. -static void fail_status(const char* what, uint32_t status) { +static void fail_status(const char* what, KSTATUS status) { print("native_exception_runtime_smoke: "); print(what); print(" status="); @@ -77,7 +78,7 @@ static void verify_startup_registration(void) { ((char*)&request)[i] = 0; request.Size = sizeof(request); - uint32_t status = SysGetExceptionDispatcher(&request); + KSTATUS status = SysGetExceptionDispatcher(&request); if (status != NTLSTATUS_SUCCESS) fail_status("get dispatcher", status); if (!request.DispatcherRoutine) @@ -97,7 +98,7 @@ static void verify_breakpoint_record(SYS_EXCEPTION_RECORD* record, SYS_USER_CONT } // Records a first-chance exception and asks the dispatcher to continue searching. -static int search_handler(SYS_EXCEPTION_RECORD* record, SYS_USER_CONTEXT* context, void* userContext) { +static uint32_t search_handler(SYS_EXCEPTION_RECORD* record, SYS_USER_CONTEXT* context, void* userContext) { if (userContext != (void*)(uintptr_t)0x1111) fail("search handler context mismatch"); verify_breakpoint_record(record, context); @@ -106,7 +107,7 @@ static int search_handler(SYS_EXCEPTION_RECORD* record, SYS_USER_CONTEXT* contex } // Records a first-chance exception and asks the kernel to resume the saved context. -static int continue_handler(SYS_EXCEPTION_RECORD* record, SYS_USER_CONTEXT* context, void* userContext) { +static uint32_t continue_handler(SYS_EXCEPTION_RECORD* record, SYS_USER_CONTEXT* context, void* userContext) { if (userContext != (void*)(uintptr_t)0x2222) fail("continue handler context mismatch"); verify_breakpoint_record(record, context); diff --git a/code/user/tests/native_raise_exception_smoke/native_raise_exception_smoke.c b/code/user/tests/native_raise_exception_smoke/native_raise_exception_smoke.c index 81d4f0a..898a710 100644 --- a/code/user/tests/native_raise_exception_smoke/native_raise_exception_smoke.c +++ b/code/user/tests/native_raise_exception_smoke/native_raise_exception_smoke.c @@ -1,5 +1,6 @@ // Native user-raised exception smoke test. +#include #include #include #include @@ -48,7 +49,7 @@ static void fail(const char* message) { } // Fails the test with one status value. -static void fail_status(const char* what, uint32_t status) { +static void fail_status(const char* what, KSTATUS status) { print("native_raise_exception_smoke: "); print(what); print(" status="); @@ -73,14 +74,14 @@ static void expect_status(const char* what, uint32_t got, uint32_t expected) { // Reads aggregate exception counters. static void query_exception_stats(SYS_EXCEPTION_STATS* out) { - uint32_t status = SysQueryKernelStats(SYS_KERNEL_STATS_EXCEPTION, out, sizeof(*out), 0); + KSTATUS status = SysQueryKernelStats(SYS_KERNEL_STATS_EXCEPTION, out, sizeof(*out), 0); expect_status("query exception stats", status, NTLSTATUS_SUCCESS); if (out->Size != sizeof(*out) || out->Version != SYS_STATS_VERSION) fail("exception stats ABI mismatch"); } // Handles the user-raised exception and verifies its native record payload. -static int smoke_handler(SYS_EXCEPTION_RECORD* record, SYS_USER_CONTEXT* context, void* userContext) { +static uint32_t smoke_handler(SYS_EXCEPTION_RECORD* record, SYS_USER_CONTEXT* context, void* userContext) { if (userContext != (void*)(uintptr_t)0x111E) fail("handler context mismatch"); if (!record || !context) @@ -123,7 +124,7 @@ int main(void) { request.ParameterCount = 2; request.Parameters = args; - uint32_t status = SysRaiseException(&request); + KSTATUS status = SysRaiseException(&request); expect_status("raise exception", status, NTLSTATUS_SUCCESS); if (gHandlerHits != 1) fail("handler was not called exactly once"); diff --git a/code/user/tests/net_irp_smoke/net_irp_smoke.c b/code/user/tests/net_irp_smoke/net_irp_smoke.c index 4ad8b2c..14deffc 100644 --- a/code/user/tests/net_irp_smoke/net_irp_smoke.c +++ b/code/user/tests/net_irp_smoke/net_irp_smoke.c @@ -48,7 +48,7 @@ static void fail(const char* message) { } // Fails the test with one status value. -static void fail_status(const char* what, uint32_t status) { +static void fail_status(const char* what, KSTATUS status) { print("net_irp_smoke: "); print(what); print(" status="); @@ -61,7 +61,7 @@ static void fail_status(const char* what, uint32_t status) { static SYS_NET_IRP_STATS query_stats(void) { SYS_NET_IRP_STATS stats; memset(&stats, 0, sizeof(stats)); - uint32_t status = SysQueryKernelStats(SYS_KERNEL_STATS_NET_IRP, &stats, sizeof(stats), 0); + KSTATUS status = SysQueryKernelStats(SYS_KERNEL_STATS_NET_IRP, &stats, sizeof(stats), 0); if (status != NTLSTATUS_SUCCESS) fail_status("query net irp stats", status); if (stats.Size != sizeof(stats)) @@ -93,7 +93,7 @@ static void set_u32_option(uint32_t socket, uint32_t option, uint32_t value) { req.Option = option; req.Value = &value; req.ValueSize = sizeof(value); - uint32_t status = SysSetSocketOption(socket, &req); + KSTATUS status = SysSetSocketOption(socket, &req); if (status != NTLSTATUS_SUCCESS) fail_status("set socket option", status); } @@ -101,7 +101,7 @@ static void set_u32_option(uint32_t socket, uint32_t option, uint32_t value) { // Waits for an async socket request to publish a terminal IOSB status. static void wait_for_async(uint32_t event, SYS_IO_STATUS_BLOCK* iosb, const char* what) { for (uint32_t i = 0; i < 200u && iosb->Status == NTLSTATUS_PENDING; ++i) { - uint32_t status = SysWait(event, 0); + KSTATUS status = SysWait(event, 0); if (status != NTLSTATUS_SUCCESS && status != NTLSTATUS_ALERTED && status != NTLSTATUS_KERNEL_APC && status != NTLSTATUS_USER_APC) { fail_status(what, status); @@ -114,7 +114,7 @@ static void wait_for_async(uint32_t event, SYS_IO_STATUS_BLOCK* iosb, const char // Verifies nonblocking UDP receive returns would-block through the IRP path. static void check_nonblocking_receive(void) { uint32_t udp = 0; - uint32_t status = SysCreateSocket(SYS_AF_INET, SYS_SOCK_DGRAM, SYS_IPPROTO_UDP, &udp); + KSTATUS status = SysCreateSocket(SYS_AF_INET, SYS_SOCK_DGRAM, SYS_IPPROTO_UDP, &udp); if (status != NTLSTATUS_SUCCESS) fail_status("create udp socket", status); @@ -152,7 +152,7 @@ static void check_nonblocking_receive(void) { // Verifies a pending async UDP receive can be cancelled and waited through its request handle. static void check_async_receive_cancel(void) { uint32_t udp = 0; - uint32_t status = SysCreateSocket(SYS_AF_INET, SYS_SOCK_DGRAM, SYS_IPPROTO_UDP, &udp); + KSTATUS status = SysCreateSocket(SYS_AF_INET, SYS_SOCK_DGRAM, SYS_IPPROTO_UDP, &udp); if (status != NTLSTATUS_SUCCESS) fail_status("create cancel udp socket", status); @@ -214,7 +214,7 @@ static void check_async_receive_cancel(void) { if (!enteredIrp) fail("async recvfrom did not enter the IRP path"); - uint32_t cancelStatus = SysCancelIo(requestHandle); + KSTATUS cancelStatus = SysCancelIo(requestHandle); if (cancelStatus != NTLSTATUS_SUCCESS && cancelStatus != NTLSTATUS_PENDING) fail_status("cancel async recvfrom", cancelStatus); @@ -236,7 +236,7 @@ static void check_async_receive_cancel(void) { // Tries a host-backed HTTP transfer to prove async send and receive success paths. static void try_http_async_success(void) { uint32_t tcp = 0; - uint32_t status = SysCreateSocket(SYS_AF_INET, SYS_SOCK_STREAM, SYS_IPPROTO_TCP, &tcp); + KSTATUS status = SysCreateSocket(SYS_AF_INET, SYS_SOCK_STREAM, SYS_IPPROTO_TCP, &tcp); if (status != NTLSTATUS_SUCCESS) { print("net_irp_smoke: http success path skipped create status="); print_u64(status); diff --git a/code/user/tests/pcache_invalidate/pcache_invalidate.c b/code/user/tests/pcache_invalidate/pcache_invalidate.c index dc71ab1..7bee140 100644 --- a/code/user/tests/pcache_invalidate/pcache_invalidate.c +++ b/code/user/tests/pcache_invalidate/pcache_invalidate.c @@ -44,7 +44,7 @@ static void fail(const char* msg) { exit_now(1); } -static void fail_status(const char* what, uint32_t status) { +static void fail_status(const char* what, KSTATUS status) { print("pcache_invalidate: "); print(what); print(" failed status="); @@ -62,7 +62,7 @@ static void delete_path(const char* path) { (void)NtlDeleteFile(SysSvFromCstr(path)); } -static uint32_t create_temp(uint32_t* fileOut, const char** pathOut) { +static KSTATUS create_temp(uint32_t* fileOut, const char** pathOut) { static const char* paths[] = { "pcache_invalidate.tmp", }; @@ -70,7 +70,7 @@ static uint32_t create_temp(uint32_t* fileOut, const char** pathOut) { for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); ++i) { uint32_t h = 0; delete_path(paths[i]); - uint32_t s = NtlOpenFile(SysSvFromCstr(paths[i]), + KSTATUS s = NtlOpenFile(SysSvFromCstr(paths[i]), SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_WRITE | SYS_FILE_ACCESS_QUERY, SYS_FILE_CREATE_NEW, SYS_FILE_FLAG_NONE, @@ -95,7 +95,7 @@ static void fill_patterns(void) { static void checked_write(uint32_t file, const void* buffer, size_t size, const char* what) { size_t done = 0; SYS_FILE_IO_REQUEST req = SysMakeFileIoRequest(file, buffer, size, &done); - uint32_t s = SysWriteFile(&req); + KSTATUS s = SysWriteFile(&req); if (s != NTLSTATUS_SUCCESS) fail_status(what, s); if (done != size) fail("short write"); } @@ -104,7 +104,7 @@ static void checked_read(uint32_t file, void* buffer, size_t size, size_t* gotOu size_t got = 0; memset(buffer, 0xCC, size); SYS_FILE_IO_REQUEST req = SysMakeFileIoRequest(file, buffer, size, &got); - uint32_t s = SysReadFile(&req); + KSTATUS s = SysReadFile(&req); if (s != NTLSTATUS_SUCCESS) fail_status(what, s); if (gotOut) *gotOut = got; } diff --git a/code/user/tests/pcache_mmap_read_reuse/pcache_mmap_read_reuse.c b/code/user/tests/pcache_mmap_read_reuse/pcache_mmap_read_reuse.c index 5cd6a9d..60807be 100644 --- a/code/user/tests/pcache_mmap_read_reuse/pcache_mmap_read_reuse.c +++ b/code/user/tests/pcache_mmap_read_reuse/pcache_mmap_read_reuse.c @@ -41,7 +41,7 @@ static void fail(const char* msg) { exit_now(1); } -static void fail_status(const char* what, uint32_t status) { +static void fail_status(const char* what, KSTATUS status) { print("pcache_mmap_read_reuse: "); print(what); print(" failed status="); @@ -55,7 +55,7 @@ static uint32_t query_cache(SYS_FILE_CACHE_STATS* out) { return SysQueryKernelStats(SYS_KERNEL_STATS_FILE_CACHE, out, sizeof(*out), 0); } -static uint32_t open_test_file(uint32_t* out, const char** pathOut) { +static KSTATUS open_test_file(uint32_t* out, const char** pathOut) { static const char* paths[] = { "pcache_mmap_read_reuse.exe", "/SystemRoot/apps/pcache_mmap_read_reuse.exe", @@ -67,7 +67,7 @@ static uint32_t open_test_file(uint32_t* out, const char** pathOut) { for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); ++i) { uint32_t h = 0; - uint32_t s = NtlOpenFile(SysSvFromCstr(paths[i]), + KSTATUS s = NtlOpenFile(SysSvFromCstr(paths[i]), SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_QUERY, SYS_FILE_OPEN_EXISTING, SYS_FILE_FLAG_NONE, diff --git a/code/user/tests/pcache_multithread/pcache_multithread.c b/code/user/tests/pcache_multithread/pcache_multithread.c index d0a3671..c3bf90a 100644 --- a/code/user/tests/pcache_multithread/pcache_multithread.c +++ b/code/user/tests/pcache_multithread/pcache_multithread.c @@ -52,7 +52,7 @@ static void fail(const char* msg) { exit_now(1); } -static void fail_status(const char* what, uint32_t status) { +static void fail_status(const char* what, KSTATUS status) { print("pcache_multithread: "); print(what); print(" failed status="); @@ -70,7 +70,7 @@ static void delete_path(const char* path) { (void)NtlDeleteFile(SysSvFromCstr(path)); } -static uint32_t create_temp(uint32_t* fileOut, const char** pathOut) { +static KSTATUS create_temp(uint32_t* fileOut, const char** pathOut) { static const char* paths[] = { "pcache_multithread.tmp", }; @@ -78,7 +78,7 @@ static uint32_t create_temp(uint32_t* fileOut, const char** pathOut) { for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); ++i) { uint32_t h = 0; delete_path(paths[i]); - uint32_t s = NtlOpenFile(SysSvFromCstr(paths[i]), + KSTATUS s = NtlOpenFile(SysSvFromCstr(paths[i]), SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_WRITE | SYS_FILE_ACCESS_QUERY, SYS_FILE_CREATE_NEW, SYS_FILE_FLAG_NONE, @@ -120,7 +120,7 @@ static void thread_main(uintptr_t arg) { sizeof(SYS_MAP_FILE_REQUEST), ctx->Mapping, SYS_MAP_ACCESS_READ, SYS_MAP_FLAG_NONE, 0, TEST_SIZE, &base }; - uint32_t s = SysMapFile(&mapFile); + KSTATUS s = SysMapFile(&mapFile); if (s != NTLSTATUS_SUCCESS || !base) { ctx->Status = s ? s : NTLSTATUS_INVALID_PARAMETER; SysExitThread(1); @@ -144,7 +144,7 @@ static void thread_main(uintptr_t arg) { } s = validate_mapping((const uint8_t*)base); - uint32_t fs = SysFreeVm(base); + KSTATUS fs = SysFreeVm(base); if (s != NTLSTATUS_SUCCESS) { ctx->Status = s; SysExitThread(1); diff --git a/code/user/tests/pcache_pressure/pcache_pressure.c b/code/user/tests/pcache_pressure/pcache_pressure.c index 389b182..1a5e76a 100644 --- a/code/user/tests/pcache_pressure/pcache_pressure.c +++ b/code/user/tests/pcache_pressure/pcache_pressure.c @@ -2,6 +2,7 @@ // Reads this test executable through SysReadFile and checks cache behavior when // the image is large enough to apply pressure. +#include #include #include #include @@ -44,7 +45,7 @@ static void fail(const char* msg) { exit_now(1); } -static void fail_status(const char* what, uint32_t status) { +static void fail_status(const char* what, KSTATUS status) { print("pcache_pressure: "); print(what); print(" failed status="); @@ -60,16 +61,16 @@ static uint32_t query_cache(SYS_FILE_CACHE_STATS* out) { static char gSelfPath[SYS_PATH_MAX]; // Copies a string view into a fixed NUL-terminated buffer. -static int copy_view(char* out, size_t outSize, SYS_STRING_VIEW view) { +static bool copy_view(char* out, size_t outSize, SYS_STRING_VIEW view) { if (!out || outSize == 0 || !view.Data || view.Length + 1 > outSize) - return 0; + return false; memcpy(out, view.Data, view.Length); out[view.Length] = '\0'; - return 1; + return true; } // Opens this process image as the cache-pressure input file. -static uint32_t open_self_image(uint32_t* fileOut, const char** pathOut, uint64_t* sizeOut) { +static KSTATUS open_self_image(uint32_t* fileOut, const char** pathOut, uint64_t* sizeOut) { SYS_PEB* peb = SysGetPeb(); if (!peb || peb->Magic != SYS_PEB_MAGIC || peb->Version != SYS_ENV_VERSION) return NTLSTATUS_INVALID_PARAMETER; @@ -77,7 +78,7 @@ static uint32_t open_self_image(uint32_t* fileOut, const char** pathOut, uint64_ return NTLSTATUS_INVALID_PARAMETER; uint32_t h = 0; - uint32_t s = NtlOpenFile(SysSvFromCstr(gSelfPath), + KSTATUS s = NtlOpenFile(SysSvFromCstr(gSelfPath), SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_QUERY, SYS_FILE_OPEN_EXISTING, SYS_FILE_FLAG_NONE, diff --git a/code/user/tests/pcache_smoke/pcache_smoke.c b/code/user/tests/pcache_smoke/pcache_smoke.c index f466bdf..00e596b 100644 --- a/code/user/tests/pcache_smoke/pcache_smoke.c +++ b/code/user/tests/pcache_smoke/pcache_smoke.c @@ -53,7 +53,7 @@ static uint32_t query_cache(SYS_FILE_CACHE_STATS* out) { return SysQueryKernelStats(SYS_KERNEL_STATS_FILE_CACHE, out, sizeof(*out), 0); } -static uint32_t open_self(uint32_t* out) { +static KSTATUS open_self(uint32_t* out) { static const char* paths[] = { "pcache_smoke.exe", "/SystemRoot/apps/pcache_smoke.exe", @@ -62,7 +62,7 @@ static uint32_t open_self(uint32_t* out) { for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); ++i) { uint32_t h = 0; - uint32_t s = NtlOpenFile(SysSvFromCstr(paths[i]), + KSTATUS s = NtlOpenFile(SysSvFromCstr(paths[i]), SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_QUERY, SYS_FILE_OPEN_EXISTING, SYS_FILE_FLAG_NONE, diff --git a/code/user/tests/posix_fd_smoke/posix_fd_smoke.c b/code/user/tests/posix_fd_smoke/posix_fd_smoke.c index 9abbcc2..41d4dd1 100644 --- a/code/user/tests/posix_fd_smoke/posix_fd_smoke.c +++ b/code/user/tests/posix_fd_smoke/posix_fd_smoke.c @@ -1,5 +1,6 @@ // User-mode smoke test for POSIX descriptor and open-description semantics. +#include #include #include #include @@ -58,7 +59,7 @@ static void fail_error(const char* message, int error) { #define INHERIT_PIPE_B_WRITE 15 // Returns whether this invocation is the descriptor-inheritance child. -static int is_inheritance_child(void) { +static bool is_inheritance_child(void) { const char* command = posix_get_command_line(); return command && strstr(command, "--inherit-child") != NULL; } diff --git a/code/user/tests/posix_process_smoke/posix_process_smoke.c b/code/user/tests/posix_process_smoke/posix_process_smoke.c index 7a862c5..8660bb1 100644 --- a/code/user/tests/posix_process_smoke/posix_process_smoke.c +++ b/code/user/tests/posix_process_smoke/posix_process_smoke.c @@ -1,5 +1,6 @@ // User-mode smoke test for server-owned POSIX spawn and child lifecycle semantics. +#include #include #include #include @@ -40,7 +41,7 @@ static void fail(const char* message) { } // Returns whether the command-line compatibility view contains one child marker. -static int child_mode(const char* marker) { +static bool child_mode(const char* marker) { const char* command = posix_get_command_line(); return command && strstr(command, marker) != (char*)0; } diff --git a/code/user/tests/posix_service_smoke/posix_service_smoke.c b/code/user/tests/posix_service_smoke/posix_service_smoke.c index 8ad0e0e..a7a9f1b 100644 --- a/code/user/tests/posix_service_smoke/posix_service_smoke.c +++ b/code/user/tests/posix_service_smoke/posix_service_smoke.c @@ -1,5 +1,6 @@ // Native smoke test for posixsrv discovery, stable PID registration, and bounded transactions. +#include #include #include #include @@ -27,17 +28,17 @@ static void fail(const char* text) { } // Returns whether the current command line contains one ASCII token. -static int command_contains(const char* token) { +static bool command_contains(const char* token) { SYS_PEB* peb = SysGetPeb(); if (!peb || peb->Magic != SYS_PEB_MAGIC || peb->Version != SYS_ENV_VERSION || !token) - return 0; + return false; size_t tokenLength = strlen(token); - if (tokenLength == 0 || tokenLength > peb->CommandLine.Length) return 0; + if (tokenLength == 0 || tokenLength > peb->CommandLine.Length) return false; for (size_t i = 0; i + tokenLength <= peb->CommandLine.Length; ++i) { if (memcmp(peb->CommandLine.Data + i, token, tokenLength) == 0) - return 1; + return true; } - return 0; + return false; } // Resolves /Services/posix through RSSRV and connects one independent client endpoint. diff --git a/code/user/tests/posix_signal_smoke/posix_signal_smoke.c b/code/user/tests/posix_signal_smoke/posix_signal_smoke.c index 9cebc77..88fd661 100644 --- a/code/user/tests/posix_signal_smoke/posix_signal_smoke.c +++ b/code/user/tests/posix_signal_smoke/posix_signal_smoke.c @@ -1,5 +1,6 @@ // Runtime coverage for P6 POSIX signal delivery, waits, spawn state, and exceptions. +#include #include #include #include @@ -39,7 +40,7 @@ static void fail(const char* message) { } // Returns whether the compatibility command line contains one child marker. -static int child_mode(const char* marker) { +static bool child_mode(const char* marker) { const char* command = posix_get_command_line(); return command && marker && strstr(command, marker) != (char*)0; } diff --git a/code/user/tests/process_builder_smoke/process_builder_smoke.c b/code/user/tests/process_builder_smoke/process_builder_smoke.c index 2788a5f..b623992 100644 --- a/code/user/tests/process_builder_smoke/process_builder_smoke.c +++ b/code/user/tests/process_builder_smoke/process_builder_smoke.c @@ -1,8 +1,10 @@ -// Userspace process-builder smoke test for inert construction inputs and child bootstrap vectors. +// Userspace prepared-process smoke test for inert construction and child bootstrap vectors. +#include #include #include #include +#include #include #include #include @@ -16,13 +18,13 @@ static void exit_now(int code) { } // Compares one native string view with a C string. -static int view_equals_cstr(SYS_STRING_VIEW view, const char* text) { +static bool view_equals_cstr(SYS_STRING_VIEW view, const char* text) { size_t length = strlen(text); return view.Length == length && (!length || memcmp(view.Data, text, length) == 0); } // Detects the child instance through the diagnostic command line. -static int is_child(void) { +static bool is_child(void) { SYS_PEB* peb = SysGetPeb(); static const char marker[] = "process-builder-child"; return peb && peb->CommandLine.Length == sizeof(marker) - 1u && @@ -63,7 +65,7 @@ static void run_child(void) { exit_now(0); } -// Runs the parent half through the reusable userspace process builder wrapper. +// Runs the parent half through prepared-process and convenience construction paths. static int run_parent(void) { SYS_PEB* peb = SysGetPeb(); if (!peb || peb->Magic != SYS_PEB_MAGIC || peb->Version != SYS_ENV_VERSION) @@ -121,6 +123,109 @@ static int run_parent(void) { request.StartupData = &startupWord; request.StartupDataSize = sizeof(startupWord); request.ProcessHandleOut = &child; + // Exercise the public opaque preparation lifecycle without starting the child. + SYS_HANDLE systemRoot = SYS_HANDLE_INVALID; + status = NtlResourceOpenSystemRoot(SYS_RIGHT_QUERY | SYS_VOLUME_RIGHT_OPEN, &systemRoot); + if (!KSUCCESS(status)) { + (void)SysCloseHandle(imageFile); + (void)SysCloseHandle(event); + return 8; + } + NTL_PREPARE_PROCESS_REQUEST prepare; + memset(&prepare, 0, sizeof(prepare)); + prepare.Size = sizeof(prepare); + prepare.Flags = NTL_PROCESS_LOAD_FLAG_NONE; + prepare.RootHandle = systemRoot; + prepare.Path = peb->ImagePath; + prepare.CommandLine = request.CommandLine; + prepare.SecurityContext = request.SecurityContext; + prepare.ModuleRootHandle = systemRoot; + prepare.ModuleDirectory = SYS_SV_LITERAL("system"); + prepare.ResolveModuleName = request.ResolveModuleName; + prepare.StartupCapabilities = request.StartupCapabilities; + prepare.StartupCapabilityCount = request.StartupCapabilityCount; + prepare.Arguments = request.Arguments; + prepare.Environment = request.Environment; + prepare.StartupData = request.StartupData; + prepare.StartupDataSize = request.StartupDataSize; + + // Invalid requests never publish a partially constructed preparation. + NTL_PROCESS_PREPARATION* preparation = 0; + uint32_t savedSize = prepare.Size; + prepare.Size = 0; + status = NtlPrepareProcess(&prepare, imageFile, &preparation); + prepare.Size = savedSize; + if (status != NTLSTATUS_INVALID_PARAMETER || preparation != 0) { + (void)SysCloseHandle(systemRoot); + (void)SysCloseHandle(imageFile); + (void)SysCloseHandle(event); + return 16; + } + status = NtlPrepareProcess(&prepare, imageFile, &preparation); + if (!KSUCCESS(status) || !preparation) { + (void)SysCloseHandle(systemRoot); + (void)SysCloseHandle(imageFile); + (void)SysCloseHandle(event); + return 9; + } + SYS_HANDLE borrowed = SYS_HANDLE_INVALID; + uint32_t nativePid = 0; + status = NtlPreparedProcessGetHandle(preparation, &borrowed); + if (!KSUCCESS(status) || borrowed == SYS_HANDLE_INVALID || + !KSUCCESS(SysGetProcessPid(borrowed, &nativePid)) || nativePid == 0) { + NtlAbortPreparedProcess(preparation); + (void)SysCloseHandle(systemRoot); + (void)SysCloseHandle(imageFile); + (void)SysCloseHandle(event); + return 10; + } + NtlAbortPreparedProcess(preparation); + + // A second preparation starts the child. Start consumes its allocation; + // callers must not also abort or close the previously borrowed handle. + preparation = 0; + status = NtlPrepareProcess(&prepare, imageFile, &preparation); + if (!KSUCCESS(status) || !preparation) { + (void)SysCloseHandle(systemRoot); + (void)SysCloseHandle(imageFile); + (void)SysCloseHandle(event); + return 11; + } + status = NtlStartPreparedProcess(preparation, &child); + (void)SysCloseHandle(systemRoot); + if (!KSUCCESS(status) || child == SYS_HANDLE_INVALID) { + (void)SysCloseHandle(imageFile); + (void)SysCloseHandle(event); + return 12; + } + status = SysWait(event, SYS_WAIT_FLAG_NONE); + if (!KSUCCESS(status)) { + (void)SysKillProcess(child, (int32_t)NTLSTATUS_PROCESS_KILLED); + (void)SysCloseHandle(child); + (void)SysCloseHandle(imageFile); + (void)SysCloseHandle(event); + return 13; + } + status = SysWait(child, SYS_WAIT_FLAG_NONE); + int32_t directExitCode = -1; + if (KSUCCESS(status)) status = SysGetProcessExitCode(child, &directExitCode); + (void)SysCloseHandle(child); + child = SYS_HANDLE_INVALID; + if (!KSUCCESS(status) || directExitCode != 0) { + (void)SysCloseHandle(imageFile); + (void)SysCloseHandle(event); + return 14; + } + + // Preserve coverage for NtlCreateSystemProcess's convenience wrapper. + (void)SysCloseHandle(event); + status = SysCreateEvent(0, 0, &event); + if (!KSUCCESS(status)) { + (void)SysCloseHandle(imageFile); + return 15; + } + startupCapability.SourceHandle = event; + request.ProcessHandleOut = &child; status = NtlCreateSystemProcess(&request); (void)SysCloseHandle(imageFile); if (!KSUCCESS(status)) { diff --git a/code/user/tests/process_exit_multithread_smoke/process_exit_multithread_smoke.c b/code/user/tests/process_exit_multithread_smoke/process_exit_multithread_smoke.c index 1e4858d..3e51a75 100644 --- a/code/user/tests/process_exit_multithread_smoke/process_exit_multithread_smoke.c +++ b/code/user/tests/process_exit_multithread_smoke/process_exit_multithread_smoke.c @@ -1,5 +1,6 @@ // Whole-process exit regression test. A blocked sibling must not keep the process alive. +#include #include #include #include @@ -33,7 +34,7 @@ static void fail(const char* message) { } // Returns whether one string view exactly matches one NUL-terminated string. -static int view_equals_cstr(SYS_STRING_VIEW view, const char* text) { +static bool view_equals_cstr(SYS_STRING_VIEW view, const char* text) { size_t length = strlen(text); return view.Data && view.Length == length && memcmp(view.Data, text, length) == 0; } diff --git a/code/user/tests/process_protection_smoke/process_protection_smoke.c b/code/user/tests/process_protection_smoke/process_protection_smoke.c index 71ecb73..4eb33d9 100644 --- a/code/user/tests/process_protection_smoke/process_protection_smoke.c +++ b/code/user/tests/process_protection_smoke/process_protection_smoke.c @@ -41,7 +41,7 @@ static void exit_now(int32_t code) { } // Fails the test with one status value. -static void fail_status(const char* operation, uint32_t status) { +static void fail_status(const char* operation, KSTATUS status) { print("process_protection_smoke: "); print(operation); print(" status="); @@ -53,7 +53,7 @@ static void fail_status(const char* operation, uint32_t status) { // Verifies one protected process remains alive after a kill attempt. static void verify_protected_process(const SYS_PROCESS_INFO* process) { uint32_t handle = 0; - uint32_t status = SysOpenProcess(process->ProcessId, SYS_PROCESS_RIGHT_TERMINATE | SYS_RIGHT_WAIT | SYS_RIGHT_QUERY, &handle); + KSTATUS status = SysOpenProcess(process->ProcessId, SYS_PROCESS_RIGHT_TERMINATE | SYS_RIGHT_WAIT | SYS_RIGHT_QUERY, &handle); if (status != NTLSTATUS_SUCCESS) fail_status("open protected process", status); @@ -75,7 +75,7 @@ void main(void) { SYS_PROCESS_INFO processes[MAX_PROCESSES]; size_t count = 0; - uint32_t status = SysGetProcessList(processes, MAX_PROCESSES, &count); + KSTATUS status = SysGetProcessList(processes, MAX_PROCESSES, &count); if (status != NTLSTATUS_SUCCESS) fail_status("query process list", status); diff --git a/code/user/tests/realpath_smoke/realpath_smoke.c b/code/user/tests/realpath_smoke/realpath_smoke.c index b5218b3..a9b590e 100644 --- a/code/user/tests/realpath_smoke/realpath_smoke.c +++ b/code/user/tests/realpath_smoke/realpath_smoke.c @@ -16,7 +16,7 @@ static void exit_now(int code) { while (true) SysExitProcess(code); } -static void fail_status(const char* what, uint32_t status) { +static void fail_status(const char* what, KSTATUS status) { (void)status; print("realpath_smoke: "); print(what); print("\n"); exit_now(1); @@ -30,7 +30,7 @@ static void fail(const char* what) { static void expect_realpath(const char* input, const char* expected) { char out[SYS_PATH_MAX + 1u]; size_t required = 0; - uint32_t status = NtlRealPath(SysSvFromCstr(input), out, sizeof(out), &required); + KSTATUS status = NtlRealPath(SysSvFromCstr(input), out, sizeof(out), &required); if (status != NTLSTATUS_SUCCESS) fail_status("realpath", status); if (required != strlen(expected) + 1u) fail("required size mismatch"); if (strcmp(out, expected) != 0) fail("resolved path mismatch"); @@ -43,7 +43,7 @@ int main(void) { char cwd[SYS_PATH_MAX + 1u]; size_t cwdRequired = 0; - uint32_t status = NtlGetCurrentDirectory(cwd, sizeof(cwd), &cwdRequired); + KSTATUS status = NtlGetCurrentDirectory(cwd, sizeof(cwd), &cwdRequired); if (status != NTLSTATUS_SUCCESS) fail_status("get cwd", status); char expected[SYS_PATH_MAX + 1u]; diff --git a/code/user/tests/resource_service_smoke/resource_service_smoke.c b/code/user/tests/resource_service_smoke/resource_service_smoke.c index a13094f..91ba15f 100644 --- a/code/user/tests/resource_service_smoke/resource_service_smoke.c +++ b/code/user/tests/resource_service_smoke/resource_service_smoke.c @@ -202,6 +202,23 @@ int main(void) { if (status != NTLSTATUS_NOT_FOUND) return fail("rejected bind visibility", status); + // Well-formed multibyte identities are accepted as paths without normalization. + static const char validPath[] = { '/', 'X', (char)0xC3, (char)0xA9 }; + status = NtlResourceQuery(session, SysSvFromParts(validPath, sizeof(validPath)), &kind); + if (status != NTLSTATUS_NOT_FOUND) + return fail("valid UTF-8 namespace path rejected", status); + + // Namespace identities are UTF-8 byte strings, never arbitrary byte paths. + static const char invalidPath[] = { '/', 'B', 'a', 'd', (char)0xED, (char)0xA0, (char)0x80 }; + status = NtlResourceQuery(session, SysSvFromParts(invalidPath, sizeof(invalidPath)), &kind); + if (status != NTLSTATUS_INVALID_PARAMETER) + return fail("malformed UTF-8 namespace path accepted", status); + + static const char embeddedNulPath[] = { '/', 'A', 0, 'B' }; + status = NtlResourceQuery(session, SysSvFromParts(embeddedNulPath, sizeof(embeddedNulPath)), &kind); + if (status != NTLSTATUS_INVALID_PARAMETER) + return fail("embedded NUL namespace path accepted", status); + status = NtlResourceQuery(session, SYS_SV_LITERAL("/SystemRoot/../Bad"), &kind); if (status != NTLSTATUS_INVALID_PARAMETER) return fail("canonical path rejection", status); diff --git a/code/user/tests/security_smoke/security_smoke.c b/code/user/tests/security_smoke/security_smoke.c index 382285f..2ddec3b 100644 --- a/code/user/tests/security_smoke/security_smoke.c +++ b/code/user/tests/security_smoke/security_smoke.c @@ -102,10 +102,6 @@ static int run_restricted_child(uint32_t parentPid) { status = SysQueryKernelStats(SYS_KERNEL_STATS_SCHEDULER, (void*)0, 0, &required); if (status != NTLSTATUS_ACCESS_DENIED) return 14; - SYS_HANDLE inputQueue = SYS_HANDLE_INVALID; - status = SysOpenInputQueue(0, &inputQueue); - if (status != NTLSTATUS_ACCESS_DENIED) return 15; - SYS_DISPLAY_INFO displayInfo; memset(&displayInfo, 0, sizeof(displayInfo)); status = SysQueryDisplayInfo(&displayInfo); @@ -142,16 +138,6 @@ static int run_restricted_child(uint32_t parentPid) { status = SysAudioEnumerateEndpoints(&enumerateAudio); if (status != NTLSTATUS_ACCESS_DENIED) return 22; - SYS_HANDLE queue = SYS_HANDLE_INVALID; - status = SysCreateQueue(&queue); - if (!KSUCCESS(status)) return 23; - SYS_PUBLISH_QUEUE_REQUEST publishQueue = { - sizeof(publishQueue), SysSvFromCstr("security_smoke.restricted"), queue - }; - status = SysPublishQueue(&publishQueue); - (void)SysCloseHandle(queue); - if (status != NTLSTATUS_ACCESS_DENIED) return 24; - SYS_HANDLE listener = SYS_HANDLE_INVALID; SYS_CREATE_PORT_REQUEST createPort; memset(&createPort, 0, sizeof(createPort)); diff --git a/code/user/tests/shared_mmap/shared_mmap.c b/code/user/tests/shared_mmap/shared_mmap.c index 682656b..3807e07 100644 --- a/code/user/tests/shared_mmap/shared_mmap.c +++ b/code/user/tests/shared_mmap/shared_mmap.c @@ -50,7 +50,7 @@ static void print_hex_uintptr(uintptr_t value) { static void print_vm_region(const char* name, const void* address) { SYS_VM_REGION_INFO info; memset(&info, 0, sizeof(info)); - uint32_t status = SysQueryVm((void*)address, &info); + KSTATUS status = SysQueryVm((void*)address, &info); print("shared_mmap: vm "); print(name); @@ -83,7 +83,7 @@ static void fail(const char* msg) { exit_now(1); } -static void fail_status(const char* what, uint32_t status) { +static void fail_status(const char* what, KSTATUS status) { print("shared_mmap: "); print(what); print(" failed status="); @@ -121,7 +121,7 @@ static void delete_path(const char* path) { (void)NtlDeleteFile(SysSvFromCstr(path)); } -static uint32_t create_temp(uint32_t* fileOut, const char** pathOut) { +static KSTATUS create_temp(uint32_t* fileOut, const char** pathOut) { static const char* paths[] = { "shared_mmap.tmp", }; @@ -129,7 +129,7 @@ static uint32_t create_temp(uint32_t* fileOut, const char** pathOut) { for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); ++i) { uint32_t h = 0; delete_path(paths[i]); - uint32_t s = NtlOpenFile(SysSvFromCstr(paths[i]), + KSTATUS s = NtlOpenFile(SysSvFromCstr(paths[i]), SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_WRITE | SYS_FILE_ACCESS_QUERY, SYS_FILE_CREATE_NEW, SYS_FILE_FLAG_NONE, @@ -143,9 +143,9 @@ static uint32_t create_temp(uint32_t* fileOut, const char** pathOut) { return NTLSTATUS_NOT_FOUND; } -static uint32_t open_path(const char* path, uint32_t* fileOut) { +static KSTATUS open_path(const char* path, uint32_t* fileOut) { uint32_t h = 0; - uint32_t s = NtlOpenFile(SysSvFromCstr(path), + KSTATUS s = NtlOpenFile(SysSvFromCstr(path), SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_WRITE | SYS_FILE_ACCESS_QUERY, SYS_FILE_OPEN_EXISTING, SYS_FILE_FLAG_NONE, @@ -156,14 +156,14 @@ static uint32_t open_path(const char* path, uint32_t* fileOut) { } static void checked_seek(uint32_t file, uint64_t offset, const char* what) { - uint32_t s = SysSeekFile(file, offset); + KSTATUS s = SysSeekFile(file, offset); if (s != NTLSTATUS_SUCCESS) fail_status(what, s); } static void checked_write(uint32_t file, const void* buffer, size_t size, const char* what) { size_t done = 0; SYS_FILE_IO_REQUEST req = SysMakeFileIoRequest(file, buffer, size, &done); - uint32_t s = SysWriteFile(&req); + KSTATUS s = SysWriteFile(&req); if (s != NTLSTATUS_SUCCESS) fail_status(what, s); if (done != size) fail("short write"); } @@ -172,7 +172,7 @@ static void checked_read(uint32_t file, void* buffer, size_t size, size_t* gotOu size_t got = 0; memset(buffer, 0xCC, size); SYS_FILE_IO_REQUEST req = SysMakeFileIoRequest(file, buffer, size, &got); - uint32_t s = SysReadFile(&req); + KSTATUS s = SysReadFile(&req); if (s != NTLSTATUS_SUCCESS) fail_status(what, s); if (gotOut) *gotOut = got; } diff --git a/code/user/tests/socket_addr_smoke/socket_addr_smoke.c b/code/user/tests/socket_addr_smoke/socket_addr_smoke.c index 83fe3b9..1ce39fb 100644 --- a/code/user/tests/socket_addr_smoke/socket_addr_smoke.c +++ b/code/user/tests/socket_addr_smoke/socket_addr_smoke.c @@ -45,7 +45,7 @@ static void fail(const char* message) { } // Fails the test with one status value. -static void fail_status(const char* what, uint32_t status) { +static void fail_status(const char* what, KSTATUS status) { print("socket_addr_smoke: "); print(what); print(" status="); @@ -73,7 +73,7 @@ static void check_ipv4_resolve(void) { req.AddressOut = &address; req.AddressSizeInOut = &addressSize; - uint32_t status = SysResolveHost(&req); + KSTATUS status = SysResolveHost(&req); if (status != NTLSTATUS_SUCCESS) fail_status("resolve ipv4", status); if (addressSize != sizeof(address)) fail("resolve ipv4 size mismatch"); if (address.Family != SYS_AF_INET || address.Size != sizeof(address)) fail("resolve ipv4 tag mismatch"); @@ -94,7 +94,7 @@ static void check_ipv6_resolve(void) { req.AddressOut = &address; req.AddressSizeInOut = &addressSize; - uint32_t status = SysResolveHost(&req); + KSTATUS status = SysResolveHost(&req); if (status != NTLSTATUS_SUCCESS) fail_status("resolve ipv6", status); if (addressSize != sizeof(address)) fail("resolve ipv6 size mismatch"); if (address.Family != SYS_AF_INET6 || address.Size != sizeof(address)) fail("resolve ipv6 tag mismatch"); @@ -108,7 +108,7 @@ static void check_ipv6_resolve(void) { static void check_socket_address_validation(void) { uint32_t ipv4 = 0; uint32_t ipv6 = 0; - uint32_t status = SysCreateSocket(SYS_AF_INET, SYS_SOCK_DGRAM, SYS_IPPROTO_UDP, &ipv4); + KSTATUS status = SysCreateSocket(SYS_AF_INET, SYS_SOCK_DGRAM, SYS_IPPROTO_UDP, &ipv4); if (status != NTLSTATUS_SUCCESS) fail_status("create ipv4 udp", status); status = SysCreateSocket(SYS_AF_INET6, SYS_SOCK_DGRAM, SYS_IPPROTO_UDP, &ipv6); diff --git a/code/user/tests/stats_abi_smoke/stats_abi_smoke.c b/code/user/tests/stats_abi_smoke/stats_abi_smoke.c index ef1a922..f1b5728 100644 --- a/code/user/tests/stats_abi_smoke/stats_abi_smoke.c +++ b/code/user/tests/stats_abi_smoke/stats_abi_smoke.c @@ -45,7 +45,7 @@ static void fail(const char* what) { } // Fails the test with one status value. -static void fail_status(const char* what, uint32_t status) { +static void fail_status(const char* what, KSTATUS status) { print("stats_abi_smoke: "); print(what); print(" status="); @@ -70,7 +70,7 @@ static void expect_status(const char* what, uint32_t got, uint32_t expected) { // Verifies fixed-size stats classes report and fill their public header. static void check_fixed_stats(void) { size_t required = 0; - uint32_t status = SysQueryKernelStats(SYS_KERNEL_STATS_SCHEDULER, 0, 0, &required); + KSTATUS status = SysQueryKernelStats(SYS_KERNEL_STATS_SCHEDULER, 0, 0, &required); if (status != NTLSTATUS_SUCCESS) fail_status("scheduler size", status); if (required != sizeof(SYS_SCHEDULER_STATS)) fail("scheduler required size mismatch"); @@ -125,7 +125,7 @@ static void check_fixed_stats(void) { // Verifies CPU stats use a size-reported trailing-record layout. static void check_cpu_stats(void) { size_t required = 0; - uint32_t status = SysQueryKernelStats(SYS_KERNEL_STATS_CPU, 0, 0, &required); + KSTATUS status = SysQueryKernelStats(SYS_KERNEL_STATS_CPU, 0, 0, &required); if (status != NTLSTATUS_SUCCESS) fail_status("cpu size", status); if (required < sizeof(SYS_CPU_STATS) + sizeof(SYS_CPU_STAT)) fail("cpu required size too small"); diff --git a/code/user/tests/surface_invalid_rect/surface_invalid_rect.c b/code/user/tests/surface_invalid_rect/surface_invalid_rect.c index 91d3085..7840a18 100644 --- a/code/user/tests/surface_invalid_rect/surface_invalid_rect.c +++ b/code/user/tests/surface_invalid_rect/surface_invalid_rect.c @@ -40,7 +40,7 @@ static void fail(const char* msg) { exit_now(1); } -static void fail_status(const char* what, uint32_t status) { +static void fail_status(const char* what, KSTATUS status) { print("surface_invalid_rect: "); print(what); print(" failed status="); @@ -73,7 +73,7 @@ int main(void) { req.Format = SYS_SURFACE_FORMAT_XRGB8888; req.Flags = SYS_SURFACE_FLAG_NONE; req.HandleOut = &bad; - uint32_t s = SysCreateSurface(&req); + KSTATUS s = SysCreateSurface(&req); expect_status("zero width", s, NTLSTATUS_INVALID_PARAMETER); req.Width = SURFACE_W; diff --git a/code/user/tests/surface_multithread_commit/surface_multithread_commit.c b/code/user/tests/surface_multithread_commit/surface_multithread_commit.c index 8a4b389..cbb547b 100644 --- a/code/user/tests/surface_multithread_commit/surface_multithread_commit.c +++ b/code/user/tests/surface_multithread_commit/surface_multithread_commit.c @@ -16,7 +16,7 @@ typedef struct WORKER_STATE { uint32_t Surface; uint8_t* Base; uint32_t Pitch; - uint32_t Status; + KSTATUS Status; uint32_t IterationsDone; } WORKER_STATE; @@ -53,7 +53,7 @@ static void fail(const char* msg) { exit_now(1); } -static void fail_status(const char* what, uint32_t status) { +static void fail_status(const char* what, KSTATUS status) { print("surface_multithread_commit: "); print(what); print(" failed status="); @@ -79,7 +79,7 @@ static void worker_main(uintptr_t arg) { } DISPLAY_RECT rect = { 0, (int32_t)y0, (int32_t)SURFACE_W, (int32_t)h }; - uint32_t s = SysCommitSurface(st->Surface, &rect, 1, 0); + KSTATUS s = SysCommitSurface(st->Surface, &rect, 1, 0); if (s != NTLSTATUS_SUCCESS) { st->Status = s; SysExitThread(1); @@ -104,7 +104,7 @@ int main(void) { req.Format = SYS_SURFACE_FORMAT_XRGB8888; req.Flags = SYS_SURFACE_FLAG_NONE; req.HandleOut = &surface; - uint32_t s = SysCreateSurface(&req); + KSTATUS s = SysCreateSurface(&req); if (s != NTLSTATUS_SUCCESS) fail_status("create surface", s); void* base = 0; diff --git a/code/user/tests/surface_smoke/surface_smoke.c b/code/user/tests/surface_smoke/surface_smoke.c index 7b23232..113af0f 100644 --- a/code/user/tests/surface_smoke/surface_smoke.c +++ b/code/user/tests/surface_smoke/surface_smoke.c @@ -40,7 +40,7 @@ static void fail(const char* msg) { exit_now(1); } -static void fail_status(const char* what, uint32_t status) { +static void fail_status(const char* what, KSTATUS status) { print("surface_smoke: "); print(what); print(" failed status="); @@ -61,7 +61,7 @@ int main(void) { req.Format = SYS_SURFACE_FORMAT_XRGB8888; req.Flags = SYS_SURFACE_FLAG_NONE; req.HandleOut = &surface; - uint32_t s = SysCreateSurface(&req); + KSTATUS s = SysCreateSurface(&req); if (s != NTLSTATUS_SUCCESS) fail_status("create surface", s); if (!surface) fail("surface handle is zero"); diff --git a/code/user/tests/syscall_contract_smoke/syscall_contract_smoke.c b/code/user/tests/syscall_contract_smoke/syscall_contract_smoke.c index 0652eaa..417eddd 100644 --- a/code/user/tests/syscall_contract_smoke/syscall_contract_smoke.c +++ b/code/user/tests/syscall_contract_smoke/syscall_contract_smoke.c @@ -1,6 +1,7 @@ // Native syscall contract smoke test. // Verifies public syscall ABI contracts, tick units, and process exit-code round trips. +#include #include #include #include @@ -39,14 +40,14 @@ static void exit_now(int32_t code) { } // Checks whether a string view equals a NUL-terminated string. -static int view_equals_cstr(SYS_STRING_VIEW view, const char* text) { +static bool view_equals_cstr(SYS_STRING_VIEW view, const char* text) { size_t n = 0; while (text[n]) n++; - if (view.Length != n || (!view.Data && n != 0)) return 0; + if (view.Length != n || (!view.Data && n != 0)) return false; for (size_t i = 0; i < n; i++) { - if (view.Data[i] != text[i]) return 0; + if (view.Data[i] != text[i]) return false; } - return 1; + return true; } // Fails the test with one plain message. @@ -58,7 +59,7 @@ static void fail(const char* msg) { } // Fails the test with one status value. -static void fail_status(const char* what, uint32_t status) { +static void fail_status(const char* what, KSTATUS status) { print("syscall_contract_smoke: "); print(what); print(" status="); @@ -70,7 +71,7 @@ static void fail_status(const char* what, uint32_t status) { // Verifies the documented tick unit without relying on wall-clock precision. static void check_tick_contract(void) { uint64_t frequency = 0; - uint32_t s = SysGetTickFrequency(&frequency); + KSTATUS s = SysGetTickFrequency(&frequency); if (s != NTLSTATUS_SUCCESS) fail_status("query tick frequency", s); if (!frequency) fail("tick frequency was zero"); @@ -100,7 +101,7 @@ static void check_exit_code_contract(void) { req.ResolveModuleName = NtlResolveSystemModuleName; req.ProcessHandleOut = &child; - uint32_t s = NtlCreateSystemProcess(&req); + KSTATUS s = NtlCreateSystemProcess(&req); if (s != NTLSTATUS_SUCCESS) fail_status("create child", s); s = SysWait(child, SYS_WAIT_FLAG_NONE); @@ -120,7 +121,7 @@ static void check_request_size_contract(void) { sizeof(SYS_PRINT_DEBUG_REQUEST) - 1u, SysSvFromCstr("size check must not print"), }; - uint32_t s = SysPrintDebug(&request); + KSTATUS s = SysPrintDebug(&request); if (s != NTLSTATUS_INVALID_PARAMETER) fail_status("undersized request was accepted", s); @@ -130,6 +131,73 @@ static void check_request_size_contract(void) { fail_status("oversized request was accepted", s); } +// Exercises strict Unicode scalar boundaries independent of the filesystem. +static void check_utf8_contract(void) { + static const char valid[] = { 'A', (char)0xC3, (char)0xA9, + (char)0xE2, (char)0x82, (char)0xAC, + (char)0xF4, (char)0x8F, (char)0xBF, (char)0xBF }; + static const char overlong[] = { (char)0xC0, (char)0xAF }; + static const char surrogate[] = { (char)0xED, (char)0xA0, (char)0x80 }; + static const char tooLarge[] = { (char)0xF4, (char)0x90, (char)0x80, (char)0x80 }; + static const char truncated[] = { (char)0xE2, (char)0x82 }; + static const char nul[] = { 'A', 0, 'B' }; + SYS_STRING_VIEW good = SysSvFromParts(valid, sizeof(valid)); + if (!SysSvIsUtf8Text(good)) { print("utf8: valid rejected\n"); exit_now(1); } + if (SysSvIsUtf8(SysSvFromParts(overlong, sizeof(overlong))) || + SysSvIsUtf8(SysSvFromParts(surrogate, sizeof(surrogate))) || + SysSvIsUtf8(SysSvFromParts(tooLarge, sizeof(tooLarge))) || + SysSvIsUtf8(SysSvFromParts(truncated, sizeof(truncated))) || + SysSvIsUtf8Text(SysSvFromParts(nul, sizeof(nul)))) { + print("utf8: invalid accepted\n"); exit_now(1); + } + if (!SysSvIsUtf8(SysSvFromParts(nul, sizeof(nul))) || + !SysSvIsUtf8Text(SYS_SV_NULL) || + SysSvIsUtf8(SysSvFromParts((const char*)0, 1u))) { + print("utf8: byte span mismatch\n"); exit_now(1); + } + // ext4 enumeration exposes lossy UTF-8 only for display; no raw byte + // sequence or partial multibyte scalar may leak into a directory entry. + static const char invalidName[] = { 'a', (char)0xFF, 'b' }; + static const char displayName[] = { 'a', (char)0xEF, (char)0xBF, (char)0xBD, 'b', 0 }; + char converted[16]; + size_t copied = SysSvCopyUtf8WithReplacement( + SysSvFromParts(invalidName, sizeof(invalidName)), converted, sizeof(converted)); + if (copied != sizeof(displayName) - 1u || memcmp(converted, displayName, sizeof(displayName)) != 0 || + !SysSvIsUtf8Text(SysSvFromParts(converted, copied))) { + print("utf8: replacement projection mismatch\n"); exit_now(1); + } + char shortName[5]; + copied = SysSvCopyUtf8WithReplacement( + SysSvFromParts(invalidName, sizeof(invalidName)), shortName, sizeof(shortName)); + if (copied != 4u || shortName[4] != '\0' || + !SysSvIsUtf8Text(SysSvFromParts(shortName, copied))) { + print("utf8: replacement truncation split a scalar\n"); exit_now(1); + } + static const char truncatedScalar[] = { 'x', (char)0xE2, (char)0x82 }; + copied = SysSvCopyUtf8WithReplacement( + SysSvFromParts(truncatedScalar, sizeof(truncatedScalar)), converted, sizeof(converted)); + if (copied != 7u || !SysSvIsUtf8Text(SysSvFromParts(converted, copied))) { + print("utf8: truncated sequence escaped replacement\n"); exit_now(1); + } + copied = SysSvCopyUtf8WithReplacement(good, converted, sizeof(converted)); + if (copied != sizeof(valid) || memcmp(converted, valid, sizeof(valid)) != 0) { + print("utf8: valid scalars modified by replacement\n"); exit_now(1); + } + static const char embeddedNul[] = { 'a', 0, 'b' }; + copied = SysSvCopyUtf8WithReplacement( + SysSvFromParts(embeddedNul, sizeof(embeddedNul)), converted, sizeof(converted)); + if (copied != 5u || !SysSvIsUtf8Text(SysSvFromParts(converted, copied))) { + print("utf8: embedded NUL escaped replacement\n"); exit_now(1); + } + static const char composed[] = { (char)0xC3, (char)0xA9 }; + static const char decomposed[] = { 'e', (char)0xCC, (char)0x81 }; + if (!SysSvIsUtf8Text(SysSvFromParts(decomposed, sizeof(decomposed))) || + SysSvEqual(SysSvFromParts(composed, sizeof(composed)), + SysSvFromParts(decomposed, sizeof(decomposed)))) { + print("utf8: identity was normalized\n"); exit_now(1); + } +} + // Runs the syscall ABI contract smoke test. int main(void) { SYS_PEB* peb = SysGetPeb(); @@ -138,6 +206,7 @@ int main(void) { } print("syscall_contract_smoke: start\n"); + check_utf8_contract(); check_tick_contract(); check_request_size_contract(); check_exit_code_contract(); diff --git a/code/user/tests/thread_name_smoke/thread_name_smoke.c b/code/user/tests/thread_name_smoke/thread_name_smoke.c index 2ae20b8..56f3773 100644 --- a/code/user/tests/thread_name_smoke/thread_name_smoke.c +++ b/code/user/tests/thread_name_smoke/thread_name_smoke.c @@ -1,5 +1,6 @@ // Native thread-description smoke test. +#include #include #include #include @@ -59,6 +60,27 @@ int main(void) { if (set_name(SYS_HANDLE_INVALID, TEST_NAME_MAIN) != NTLSTATUS_SUCCESS) fail("set current name failed"); + // Native thread descriptions accept well-formed UTF-8 and reject malformed bytes. + static const char unicodeName[] = "thread-\xC3\xA9"; + if (set_name(SYS_HANDLE_INVALID, unicodeName) != NTLSTATUS_SUCCESS) + fail("valid UTF-8 thread name rejected"); + + static const char invalidName[] = { 'b', (char)0xC0, (char)0xAF, 0 }; + if (set_name(SYS_HANDLE_INVALID, invalidName) != NTLSTATUS_INVALID_PARAMETER) + fail("overlong UTF-8 thread name accepted"); + + SYS_SET_THREAD_NAME_REQUEST nulRequest; + memset(&nulRequest, 0, sizeof(nulRequest)); + nulRequest.Size = sizeof(nulRequest); + nulRequest.ThreadHandle = SYS_HANDLE_INVALID; + static const char nulName[] = { 'a', 0, 'b' }; + nulRequest.Name = SysSvFromParts(nulName, sizeof(nulName)); + if (SysSetThreadName(&nulRequest) != NTLSTATUS_INVALID_PARAMETER) + fail("embedded NUL thread name accepted"); + + if (set_name(SYS_HANDLE_INVALID, TEST_NAME_MAIN) != NTLSTATUS_SUCCESS) + fail("restore current thread name failed"); + char name[SYS_THREAD_NAME_MAX]; size_t length = 0; KSTATUS status = SysQueryThreadName(SYS_HANDLE_INVALID, name, sizeof(name), &length); @@ -102,13 +124,13 @@ int main(void) { if (status != NTLSTATUS_SUCCESS) fail("thread enumeration failed"); - int foundWorker = 0; + bool foundWorker = false; size_t visible = threadCount < TEST_MAX_THREADS ? threadCount : TEST_MAX_THREADS; for (size_t i = 0; i < visible; ++i) { if (threads[i].ThreadId == tid) { if (strcmp(threads[i].Name, TEST_NAME_WORKER) != 0) fail("thread-list name mismatch"); - foundWorker = 1; + foundWorker = true; break; } } diff --git a/code/user/tests/timer_smoke/timer_smoke.c b/code/user/tests/timer_smoke/timer_smoke.c index 2964927..5c3cfb6 100644 --- a/code/user/tests/timer_smoke/timer_smoke.c +++ b/code/user/tests/timer_smoke/timer_smoke.c @@ -35,7 +35,7 @@ static void exit_now(int code) { } // Fails the test with one status value. -static void fail_status(const char* what, uint32_t status) { +static void fail_status(const char* what, KSTATUS status) { print("timer_smoke: "); print(what); print(" status="); @@ -58,7 +58,7 @@ static void expect_status(const char* what, uint32_t got, uint32_t expected) { } // Arms a timer through the public request ABI. -static uint32_t set_timer(uint32_t timer, uint64_t dueMs, uint64_t periodMs) { +static KSTATUS set_timer(uint32_t timer, uint64_t dueMs, uint64_t periodMs) { SYS_SET_TIMER_REQUEST req = { sizeof(req), timer, @@ -74,7 +74,7 @@ static void test_validation(void) { uint32_t timer = 0; expect_status("create timer null", SysCreateTimer((uint32_t*)0), NTLSTATUS_INVALID_PARAMETER); - uint32_t s = SysCreateTimer(&timer); + KSTATUS s = SysCreateTimer(&timer); if (s != NTLSTATUS_SUCCESS) fail_status("create validation timer", s); expect_status("set timer null", SysSetTimer((const SYS_SET_TIMER_REQUEST*)0), NTLSTATUS_INVALID_PARAMETER); @@ -100,7 +100,7 @@ static void test_validation(void) { // Verifies one-shot timers wake waiters and are consumed by the wait. static void test_one_shot(void) { uint32_t timer = 0; - uint32_t s = SysCreateTimer(&timer); + KSTATUS s = SysCreateTimer(&timer); if (s != NTLSTATUS_SUCCESS) fail_status("create one-shot timer", s); expect_status("unset immediate wait", SysWaitSingle(timer, SYS_WAIT_FLAG_NONE, 0), NTLSTATUS_TIMEOUT); @@ -117,7 +117,7 @@ static void test_one_shot(void) { // Verifies cancellation clears an armed timer and its retained signal. static void test_cancel(void) { uint32_t timer = 0; - uint32_t s = SysCreateTimer(&timer); + KSTATUS s = SysCreateTimer(&timer); if (s != NTLSTATUS_SUCCESS) fail_status("create cancel timer", s); s = set_timer(timer, 100, 0); @@ -143,7 +143,7 @@ static void test_cancel(void) { // Verifies resetting an active timer replaces its previous deadline. static void test_reset_changes_deadline(void) { uint32_t timer = 0; - uint32_t s = SysCreateTimer(&timer); + KSTATUS s = SysCreateTimer(&timer); if (s != NTLSTATUS_SUCCESS) fail_status("create reset timer", s); s = set_timer(timer, 100, 0); @@ -160,7 +160,7 @@ static void test_reset_changes_deadline(void) { // Verifies a periodic timer can wake more than once. static void test_periodic(void) { uint32_t timer = 0; - uint32_t s = SysCreateTimer(&timer); + KSTATUS s = SysCreateTimer(&timer); if (s != NTLSTATUS_SUCCESS) fail_status("create periodic timer", s); s = set_timer(timer, 10, 20); diff --git a/code/user/tests/win32_thread_suspend_smoke/win32_thread_suspend_smoke.c b/code/user/tests/win32_thread_suspend_smoke/win32_thread_suspend_smoke.c index a282453..267e071 100644 --- a/code/user/tests/win32_thread_suspend_smoke/win32_thread_suspend_smoke.c +++ b/code/user/tests/win32_thread_suspend_smoke/win32_thread_suspend_smoke.c @@ -25,8 +25,8 @@ __declspec(dllimport) DWORD WaitForSingleObject(HANDLE handle, DWORD millisecond __declspec(dllimport) void Sleep(DWORD milliseconds); __declspec(dllimport) BOOL CloseHandle(HANDLE handle); -static volatile long gStarted; -static volatile long gRelease; +static volatile int32_t gStarted; +static volatile int32_t gRelease; // Writes one test message to the native console. static void print(const char* text) { diff --git a/code/user/tests/winmm_waveout_smoke/winmm_waveout_smoke.c b/code/user/tests/winmm_waveout_smoke/winmm_waveout_smoke.c index ee94ae6..f807cab 100644 --- a/code/user/tests/winmm_waveout_smoke/winmm_waveout_smoke.c +++ b/code/user/tests/winmm_waveout_smoke/winmm_waveout_smoke.c @@ -1,5 +1,7 @@ // WinMM waveform-output smoke test over the native render-stream personality path. +#include +#include #include #include #include @@ -127,9 +129,9 @@ static int16_t gS16Frames[TEST_FRAMES * 2u]; static uint8_t gS24Frames[TEST_FRAMES * 2u * 3u]; static int16_t gControlFrames[TEST_CONTROL_FRAMES * 2u]; static HANDLE gFunctionEvent; -static volatile long gOpenCallbacks; -static volatile long gDoneCallbacks; -static volatile long gCloseCallbacks; +static int32_t gOpenCallbacks; +static int32_t gDoneCallbacks; +static int32_t gCloseCallbacks; // Writes one NUL-terminated string to the kernel console. static void print(const char* text) { @@ -205,12 +207,12 @@ static void __stdcall wave_callback(HWAVEOUT waveOut, (void)parameter1; (void)parameter2; if (message == WOM_OPEN) - _InterlockedIncrement(&gOpenCallbacks); + NtlAtomicIncrement32(&gOpenCallbacks); else if (message == WOM_DONE) { - _InterlockedIncrement(&gDoneCallbacks); + NtlAtomicIncrement32(&gDoneCallbacks); (void)SetEvent(gFunctionEvent); } else if (message == WOM_CLOSE) - _InterlockedIncrement(&gCloseCallbacks); + NtlAtomicIncrement32(&gCloseCallbacks); } // Waits for one callback event with a finite deadline. diff --git a/code/user/tests/would_block_smoke/would_block_smoke.c b/code/user/tests/would_block_smoke/would_block_smoke.c index 56e817a..bfa15a0 100644 --- a/code/user/tests/would_block_smoke/would_block_smoke.c +++ b/code/user/tests/would_block_smoke/would_block_smoke.c @@ -86,7 +86,7 @@ static void fail(const char* msg) { } // Fails the test with one status value. -static void fail_status(const char* what, uint32_t status) { +static void fail_status(const char* what, KSTATUS status) { print("would_block_smoke: "); print(what); print(" status="); @@ -144,7 +144,7 @@ static void fill_until_would_block(uint32_t queue) { // Verifies queue try and finite-timeout status distinctions. static void test_queue_empty_and_full(void) { uint32_t queue = 0; - uint32_t s = SysCreateQueue(&queue); + KSTATUS s = SysCreateQueue(&queue); if (s != NTLSTATUS_SUCCESS) fail_status("create empty queue", s); TEST_QUEUE_MESSAGE msg = {0}; @@ -165,7 +165,7 @@ static void test_queue_empty_and_full(void) { // Verifies that an infinite receive composes with a sender wakeup. static void test_queue_infinite_receive_wakeup(void) { uint32_t queue = 0; - uint32_t s = SysCreateQueue(&queue); + KSTATUS s = SysCreateQueue(&queue); if (s != NTLSTATUS_SUCCESS) fail_status("create wake queue", s); TEST_SENDER_CONTEXT ctx = { queue, NTLSTATUS_PENDING }; @@ -199,7 +199,7 @@ static void test_queue_infinite_receive_wakeup(void) { // Verifies that request metadata is part of the queue ABI contract. static void test_queue_request_validation(void) { uint32_t queue = 0; - uint32_t s = SysCreateQueue(&queue); + KSTATUS s = SysCreateQueue(&queue); if (s != NTLSTATUS_SUCCESS) fail_status("create validation queue", s); TEST_QUEUE_MESSAGE msg = {0}; diff --git a/docs.md b/docs.md index 6ff5d14..e9da84f 100644 --- a/docs.md +++ b/docs.md @@ -37,7 +37,7 @@ nt-lite is a clean-design hobby kernel. It is not a stable compatibility platfor ## Breaking changes -Breaking changes are allowed. When making one: +Breaking changes are encouraged. When making one: 1. Define the new model. 2. Move every user to it. @@ -50,12 +50,10 @@ Breaking changes are allowed. When making one: - Generate patches from the repository root. - Paths must be rooted as `code/...`. - `git apply ` should work from the repository root. -- Do not require `--directory=code`. - Keep generated artifacts out of the source tree unless requested. - When asked to provide the full tree, also provide the patch. - The patch is for review; the full tree is for reproducing the exact baseline. - Avoid optimistic claims. -- Say exactly whether a change was apply-checked, syntax-checked, built, booted, or runtime-tested. - Do not introduce compatibility aliases unless they are intentionally supported API. ## Comment style diff --git a/ntlite_remaining_roadmap.md b/ntlite_remaining_roadmap.md new file mode 100644 index 0000000..36332c1 --- /dev/null +++ b/ntlite_remaining_roadmap.md @@ -0,0 +1,3154 @@ +# nt-lite remaining roadmap + +**Status:** living design specification +**Scope:** unresolved architecture, implementation work, and validation still required. +**Intent:** completed work and repository-wide development guidance are intentionally omitted; the source tree and development notes are authoritative for those. +**Current implementation focus:** P7 is complete and runtime-validated. POSIX is intentionally paused before P8 while native ABI cleanup, KFS identity/lifetime work, and native security are corrected at their owning layers. After those prerequisites, resume the revised P8, then P9/P10. + +Current near-term sequence: + +```text +UABI.1 fixed-width/std-bool/KSTATUS cleanup and ABI assertions +UABI.2 explicit UTF-8 native text contract and validation +UABI.3 opaque prepared-process transaction API +UABI.4 remove stale named-queue publication and legacy global-input paths +KFS.1-7 native backing identity/lifetime/link/rename/times/directory reset +SEC.0-15 native principals/ACLs/authorization/accounts/audit +P8 revised POSIX filesystem projection + FIFOs +P9 tty/termios +P10 leaf APIs/profile closure +``` + +--- + +# Part I — driver lifecycle direction + +## 1. Driver lifecycle overhaul + +The next major kernel architecture task is a real driver manager with graph-based startup and shutdown. + +The driver manager must solve four related problems together: + +1. configuration and startup dependencies; +2. driver/module state tracking; +3. graceful system shutdown; +4. future demand loading, unload, and reload. + +`drivers.ini` becomes the declarative source of lifecycle policy. Runtime device topology remains owned by the I/O/PnP manager. + +A static configured dependency graph and a runtime device stack are separate structures: + +```text +configured module graph runtime device topology +----------------------- ----------------------- +usb-hcd -> usb PCI PDO +usb -> usb-storage | +network-adapter -> network-stack v +filesystem after storage xHCI FDO + | + v + USB PDO + | + v + storage FDO +``` + +The first graph answers when modules may start/stop. The second answers which concrete devices are above/below one another and therefore how actual hardware is quiesced and removed. + +--- + +# Part II — `drivers.ini` v2 and the driver manager + +## 2. Design goals + +The new driver configuration must have these properties: + +- File order has no lifecycle meaning. +- Driver classes are declared in configuration, not compiled into the kernel. +- Class names are opaque lifecycle identities to the kernel. +- A new provider driver can join an existing class without editing consumers. +- Alternatives are represented as multiple members of one class. +- Classes may depend on other classes. +- Individual drivers may additionally have exact driver dependencies for genuine implementation coupling. +- Class dependencies and exact driver dependencies participate in one effective lifecycle model. +- Startup dependencies automatically determine reverse shutdown ordering. +- The bootloader does not understand dependency policy. +- The kernel is the authoritative parser/validator/scheduler. +- The format remains deliberately small and strict, similar in spirit to `services.ini`. +- The design must admit later `demand`, deferred initialization, unload, reload, and cascade reload without replacing the model. + +--- + +## 3. Bootloader/kernel responsibility split + +### 3.1 Bootloader + +The UEFI loader remains intentionally dumb. + +It reads `drivers.ini` only far enough to locate driver descriptors and identify: + +```text +image= +start=boot +``` + +For every `start=boot` driver it: + +1. loads the raw `.sys` image into boot-owned pages; +2. records the configured driver ID and image name with the blob; +3. includes the raw `drivers.ini` bytes in `BOOT_INFO`; +4. passes the boot-driver blob list and configuration blob to the kernel. + +The bootloader does **not**: + +- build the class graph; +- resolve `requires`; +- resolve `after`; +- decide startup order; +- interpret `critical`; +- determine class availability; +- propagate dependency failures; +- initialize a driver image. + +The bootloader performs only the parsing needed to safely identify and preload boot images. The kernel re-parses the same configuration authoritatively. + +### 3.2 Boot ABI + +`BOOT_INFO` should gain a raw `DriversIni` blob. The boot-driver handoff should retain enough identity to match every preloaded blob to a configured driver without relying on list position. + +Conceptually: + +```c +typedef struct BOOT_DRIVER_FILE { + char DriverId[64]; + char Image[64]; + uintptr_t PhysicalBase; + uint32_t Length; + uint32_t PageCount; +} BOOT_DRIVER_FILE; +``` + +The exact ABI may be shaped differently, but driver identity must be explicit. + +The kernel copies/parses the configuration before reclaiming the bootloader pages. The same parsed configuration remains authoritative for the later runtime phase; it should not need to re-read a potentially different `drivers.ini` after mounting the system root. + +### 3.3 Kernel + +Before calling any configured `DriverEntry`, the kernel: + +1. parses the full file; +2. validates every class and driver descriptor; +3. validates boot blob/config correspondence; +4. constructs class and exact-driver graphs; +5. validates the combined effective graph; +6. computes initial eligibility; +7. initializes drivers according to graph state. + +Malformed lifecycle configuration is an initialization failure, not something silently repaired with fallback ordering. + +--- + +## 4. Configuration grammar + +All class sections appear before driver sections. This is primarily a human-readability rule and is also easy for the strict parser to enforce. + +Initial grammar: + +```ini +; Lifecycle classes first. + +[class.platform] +requires= +after= + +[class.bus] +requires= +after=platform + +[class.usb-hcd] +requires=bus +after= + +[class.usb] +requires=usb-hcd +after= + +; Driver descriptors follow. + +[driver.pci] +image=pci.sys +start=boot +class=bus +critical=true +requires= +after= +``` + +### 4.1 Class keys + +Every class has exactly: + +```text +requires= +after= +``` + +Both keys are required even when empty. + +### 4.2 Driver keys + +Every driver initially has exactly: + +```text +image= +start= +class= +critical= +requires= +after= +``` + +All keys are required. + +Initial `start=` values: + +```text +boot +runtime +disabled +``` + +Planned later values include: + +```text +demand +manual +``` + +A separate `deferred` policy may be introduced only if it has semantics distinct from `demand` and graph waiting. + +### 4.3 Identity rules + +Class IDs and driver IDs are separate namespaces. + +Recommended syntax: + +```text +[A-Za-z0-9._-]+ +``` + +with a bounded length suitable for fixed diagnostic buffers. + +IDs are case-sensitive. + +The `image=` field names one file under the system driver directory. Paths outside that directory are not part of the initial format. + +One configured driver ID maps to one module image. Duplicate enabled image mappings should be rejected unless a future concrete use case establishes multi-instance image configuration semantics. + +--- + +## 5. Class semantics + +Classes are **config-defined lifecycle groups**, not kernel enums and not hardcoded subsystem taxonomy. + +The kernel must never contain policy such as: + +```c +if (Class == DriverClassStorage) { ... } +``` + +It knows only opaque class IDs and graph edges. + +A driver initially belongs to exactly one lifecycle class. If a future architecture genuinely needs multi-class membership, change the format deliberately rather than smuggling it into comma syntax now. + +### 5.1 `requires=` — availability edge + +For a class: + +```ini +[class.usb] +requires=usb-hcd +after= +``` + +`requires=usb-hcd` means: + +> The `usb` class may begin only after `usb-hcd` becomes available. + +A class becomes **Available** as soon as at least one eligible member driver reaches `Started`. Availability describes a loaded module/provider contract, not the presence of a physical device. A successfully initialized NIC driver can make `network-adapter` available even when no NIC is currently enumerated; hotplug and device existence remain PnP/device-topology concerns. + +This is intentionally “one provider from the class,” not “every member must succeed.” + +Example: + +```text +usb-hcd class + xhci Started + ehci Failed + ohci Failed + +result: usb-hcd is Available +``` + +A dependent `usb` class may proceed. + +If a requirement lists multiple classes, all listed classes are required. Provider alternatives belong as multiple drivers inside one class; the format does not need a `requires_any` expression language. + +### 5.2 `after=` — settlement/barrier edge + +For a class: + +```ini +[class.filesystem] +requires= +after=storage-controller,usb-storage +``` + +`after=` means: + +> Wait until the referenced class has settled for the current automatic startup phase, regardless of whether any member succeeded. + +This is useful when the consumer only needs all likely providers to have had their initialization opportunity before it registers or probes. + +A filesystem class can therefore start on a machine with no storage controller at all; later hotplug remains possible. + +### 5.3 Class state + +The scheduler should represent at least: + +```text +Pending +Available +SettledAvailable +SettledUnavailable +Blocked +``` + +Useful interpretation: + +- **Pending:** eligible member work or dependencies remain unresolved. +- **Available:** at least one member reached `Started`; more members may still be pending. +- **SettledAvailable:** all automatic members for the current phase are terminal and at least one started. +- **SettledUnavailable:** all automatic members are terminal and none started. +- **Blocked:** the class cannot proceed because a required class settled unavailable or an unsatisfiable dependency was discovered. `Blocked` is terminal for barrier purposes. + +A class with no started member and no remaining eligible provider in the current startup epoch settles unavailable. A member that started in an earlier phase continues to make its class available in later phases. When a later phase introduces additional automatic members, availability may remain true while settlement reopens until those new members reach terminal states. + +A `requires` dependent may become eligible at **Available**; it need not wait for settlement. A required class that reaches `SettledUnavailable` or `Blocked` without an already-started provider cannot satisfy the edge. + +An `after` dependent waits for a terminal class state: `SettledAvailable`, `SettledUnavailable`, or `Blocked`. + +### 5.4 No cardinality policy + +The driver manager does not enforce “singleton class” or “only one network stack.” + +Multiple members may start in any class. If a subsystem eventually requires exclusivity, that policy belongs in configuration/start selection or in the subsystem that owns the resource, not in generic class machinery. + +--- + +## 6. Driver-level exact dependencies + +Drivers retain exact dependencies for real module-to-module coupling. + +Example: + +```ini +[driver.foo-filter] +image=foo_filter.sys +start=runtime +class=foo-filter +critical=false +requires=foo-core +after= +``` + +Driver-section references name **driver IDs**, while class-section references name **class IDs**. + +Initial graph domains remain simple: + +```text +[class.*].requires -> class IDs +[class.*].after -> class IDs +[driver.*].requires -> driver IDs +[driver.*].after -> driver IDs +``` + +Cross-domain references are unnecessary because membership composes the two graphs. + +### 6.1 Driver `requires=` + +An exact required driver must reach `Started` before the dependent driver may start. + +If it reaches a terminal failure/blocked state, the dependent becomes dependency-blocked. + +### 6.2 Driver `after=` + +An exact `after` target need only reach a terminal startup state. Its failure does not by itself prevent the dependent from starting. + +### 6.3 Combined cycle validation + +Validating the class DAG and driver DAG separately is insufficient. + +Class membership plus exact driver edges can create an effective cycle even when each individual graph is acyclic. The kernel must validate the combined scheduling constraints before initialization. + +--- + +## 7. Driver state model + +The driver manager should track configured module state independently from `DRIVER_OBJECT` device-driver objects. + +Suggested module states: + +```text +Disabled +Preloaded +Waiting +Loading +Starting +Started +Blocked +Failed +Stopping +Stopped +Unloading +Unloaded +``` + +Future demand loading may add an explicit idle/requested state if useful. + +A configured driver module owns: + +```text +identity +image name +start policy +lifecycle class +critical flag +exact requires/after edges +load/module base +startup status +registered DRIVER_OBJECTs +shutdown status +reload generation +``` + +One module may register more than one `DRIVER_OBJECT`; the module manager must not assume a one-to-one relationship even if driver decomposition gradually makes that common. + +--- + +## 8. Startup phases + +### 8.1 Boot phase + +All `start=boot` images are already resident as raw blobs from the bootloader. + +The kernel validates the complete graph, then starts eligible boot drivers according to class and exact-driver constraints. + +A boot driver may not have an automatic hard dependency whose only possible configured providers are runtime drivers. Such a configuration is unsatisfiable during boot and should be rejected. + +### 8.2 Runtime phase + +After the system-root volume is available, `start=runtime` modules become eligible. + +Runtime modules may depend on already-started boot modules/classes or other runtime modules/classes. Boot-started drivers continue contributing to class availability. If a class gains additional `start=runtime` members, its runtime settlement barrier is reopened until those members are attempted; an already-started member can still satisfy a `requires` edge while that happens. + +The kernel loads runtime images from the system driver directory according to graph eligibility rather than file order. + +### 8.3 Future demand phase + +`start=demand` should mean: + +- the descriptor participates in graph validation; +- the module is not started automatically merely because prerequisites are available; +- a PnP match, explicit request, or dependency demand can request it; +- its hard dependency closure is started first; +- failure is returned to the requester and reflected in module/class state. + +This allows future device discovery to trigger drivers naturally without replacing the graph model. + +--- + +## 9. Criticality and failure propagation + +### 9.1 Ordinary driver failure + +When a driver fails to load or `DriverEntry` returns failure: + +1. mark the module `Failed`; +2. update its class availability/settlement; +3. block exact hard dependents when appropriate; +4. block class hard dependents only if the required class ultimately cannot become available; +5. allow `after` dependents once the relevant target settles. + +### 9.2 `critical=true` + +For automatic system startup, a critical configured driver represents a requirement for successful system initialization. + +A critical driver that: + +- fails to load; +- fails import resolution; +- fails `DriverEntry`; +- becomes blocked by an unsatisfied hard dependency; + +causes startup to fail with explicit dependency diagnostics. + +Criticality should not be silently inherited by every provider in a multi-provider class. If one class member is critical, that is a property of that configured driver. + +For future manual reload/demand operations, `critical` does not imply that a user-requested reload failure must bugcheck an already-running system; those operations need separate transactional failure semantics. + +--- + +## 10. Validation rules + +The parser/graph validator rejects at least: + +- duplicate class IDs; +- duplicate driver IDs; +- duplicate keys; +- unknown keys; +- malformed section names; +- class sections after driver sections; +- missing required keys; +- invalid `start` values; +- invalid booleans; +- invalid IDs or image names; +- unknown class membership; +- unknown class dependencies; +- unknown driver dependencies; +- self-dependencies; +- duplicate entries in dependency lists; +- the same target present in both `requires` and `after` for one descriptor; +- class cycles; +- driver cycles; +- combined effective scheduling cycles; +- enabled boot dependencies impossible to satisfy during the boot phase; +- `start=boot` images missing from the bootloader handoff; +- unexpected or duplicate boot image handoff entries; +- duplicate enabled module-image identities unless deliberately supported later. + +No fallback ordered list is synthesized when the graph is invalid. + +--- + +## 11. Determinism and scheduling + +Graph edges are the only lifecycle ordering contract. + +When multiple drivers are simultaneously eligible with no ordering edge between them, the initial implementation may run them serially using a deterministic lexicographic driver-ID tie-breaker for reproducible logs. + +That tie-breaker is explicitly **not** an API or dependency contract. + +Once the driver manager is stable, independent graph nodes may be initialized concurrently if profiling shows value. `requires=Available` and `after=Settled` semantics are intentionally defined so concurrency does not require a format change. + +--- + +## 12. Worked class graph + +A useful initial class graph after USB decomposition could look like this: + +```ini +[class.platform] +requires= +after= + +[class.bus] +requires= +after=platform + +[class.storage-controller] +requires=bus +after= + +[class.usb-hcd] +requires=bus +after= + +[class.usb] +requires=usb-hcd +after= + +[class.usb-storage] +requires=usb +after= + +[class.usb-hid] +requires=usb +after= + +[class.filesystem] +requires= +after=storage-controller,usb-storage + +[class.audio] +requires=bus +after= + +[class.display] +requires=bus +after= + +[class.network-adapter] +requires=bus +after= + +[class.network-stack] +requires=network-adapter +after= +``` + +Graphically: + +```text + platform + | + | after + v + bus + _____________|____________________ + / | | \ + v v v v + storage-controller usb-hcd audio network-adapter + | | + | requires | requires + v v + usb network-stack + / \ + v v + usb-storage usb-hid + | + | barrier together with storage-controller + v + filesystem +``` + +The exact class names are configuration policy and may evolve. The kernel has no semantic knowledge of them. + +--- + +## 13. Worked driver configuration + +A target configuration after splitting the current USB image could be: + +```ini +[class.platform] +requires= +after= + +[class.bus] +requires= +after=platform + +[class.storage-controller] +requires=bus +after= + +[class.usb-hcd] +requires=bus +after= + +[class.usb] +requires=usb-hcd +after= + +[class.usb-storage] +requires=usb +after= + +[class.usb-hid] +requires=usb +after= + +[class.filesystem] +requires= +after=storage-controller,usb-storage + +[class.audio] +requires=bus +after= + +[class.display] +requires=bus +after= + +[class.network-adapter] +requires=bus +after= + +[class.network-stack] +requires=network-adapter +after= + +[driver.cereal] +image=cereal.sys +start=boot +class=platform +critical=false +requires= +after= + +[driver.ps2] +image=ps2.sys +start=boot +class=platform +critical=false +requires= +after= + +[driver.pci] +image=pci.sys +start=boot +class=bus +critical=true +requires= +after= + +[driver.hda] +image=hda.sys +start=boot +class=audio +critical=false +requires= +after= + +[driver.ahci] +image=ahci.sys +start=boot +class=storage-controller +critical=false +requires= +after= + +[driver.ata] +image=ata.sys +start=boot +class=storage-controller +critical=false +requires= +after= + +; Future NVMe requires no filesystem edits. +;[driver.nvme] +;image=nvme.sys +;start=boot +;class=storage-controller +;critical=false +;requires= +;after= + +[driver.xhci] +image=xhci.sys +start=boot +class=usb-hcd +critical=false +requires= +after= + +[driver.usb] +image=usb.sys +start=boot +class=usb +critical=false +requires= +after= + +[driver.usbhid] +image=usbhid.sys +start=boot +class=usb-hid +critical=false +requires= +after= + +[driver.usbstor] +image=usbstor.sys +start=boot +class=usb-storage +critical=false +requires= +after= + +[driver.fat] +image=fat.sys +start=boot +class=filesystem +critical=false +requires= +after= + +[driver.ntfs] +image=ntfs.sys +start=boot +class=filesystem +critical=false +requires= +after= + +[driver.ext4] +image=ext4.sys +start=boot +class=filesystem +critical=false +requires= +after= + +[driver.vmwaresvga] +image=vmwaresvga.sys +start=runtime +class=display +critical=false +requires= +after= + +[driver.rtl8139] +image=rtl8139.sys +start=runtime +class=network-adapter +critical=false +requires= +after= + +[driver.rtl816x] +image=rtl816x.sys +start=runtime +class=network-adapter +critical=false +requires= +after= + +[driver.lwip] +image=lwip.sys +start=runtime +class=network-stack +critical=false +requires= +after= +``` + +The important property is structural rather than the exact sample names: + +- adding NVMe means adding one `storage-controller` member; +- adding EHCI/OHCI means adding another `usb-hcd` member; +- adding a NIC means adding another `network-adapter` member; +- adding another network stack does not require a generic-driver-manager singleton rule; +- consumers depend on lifecycle classes rather than enumerating every implementation. + +--- + +## 14. USB driver decomposition + +Split the current USB implementation into four module ownership domains: + +```text +xhci.sys + xHCI PCI hardware/controller mechanics + +usb.sys + generic USB bus/core, enumeration, hubs, descriptors, configuration, + composite/interface enumeration + +usbhid.sys + USB HID function driver + +usbstor.sys + USB mass-storage function/transport driver +``` + +### 14.1 `xhci.sys` + +Owns xHCI-specific mechanism: + +- PCI BAR/resources; +- xHCI capability/operational/runtime registers; +- DCBAA; +- scratchpads; +- command/event/transfer rings; +- TRBs; +- interrupters; +- slots/endpoints; +- controller DMA; +- xHCI port state; +- controller reset/start/stop; +- hardware interrupt/DPC processing. + +Lifecycle class: `usb-hcd`. + +### 14.2 `usb.sys` + +Owns controller-independent USB behavior: + +- host-controller consumption/binding; +- USB addressing; +- standard control transfers; +- descriptors; +- configurations/interfaces; +- hub topology; +- enumeration; +- composite/interface PDO creation; +- USB device identity/matching. + +Lifecycle class: `usb` with `requires=usb-hcd` at the class level. + +The runtime HCD/USB contract should use I/O-manager device/interface mechanisms rather than direct imports from a specific HCD implementation. `usb.sys` must not know that the provider is xHCI rather than EHCI/OHCI/a future virtual HCD. + +### 14.3 `usbhid.sys` + +Owns: + +- HID descriptors; +- report parsing; +- boot keyboard/mouse protocol; +- conversion into the existing logical input mechanism. + +Lifecycle class: `usb-hid` with class-level `requires=usb`. + +### 14.4 `usbstor.sys` + +Owns: + +- USB mass-storage transport; +- BOT initially; +- UAS later if desired; +- SCSI command transport over USB; +- USB storage device lifetime and block-device publication. + +Lifecycle class: `usb-storage` with class-level `requires=usb`. + +### 14.5 Kept together initially + +Hub and composite enumeration remain inside generic `usb.sys`. They can become separate images later only if independent lifecycle/reload value justifies the extra module boundaries. + +--- + +# Part III — graceful driver and device shutdown + +## 15. Shutdown goals + +System shutdown must become a real lifecycle transition rather than jumping from user-mode shutdown directly to ACPI S5 while drivers and devices remain active. + +The shutdown contract must ensure: + +- filesystems have a chance to flush/freeze; +- pending cached writes are drained; +- network/audio/display workers stop coherently; +- storage flushes complete while controllers remain operational; +- DMA engines are disabled before poweroff; +- interrupt sources are masked/stopped; +- worker threads no longer reference device/module state being torn down; +- shutdown cannot hang forever on one broken driver; +- failure diagnostics remain available before the final hardware power transition. + +--- + +## 16. Separate system shutdown, PnP removal, and unload + +Three lifecycle operations remain distinct: + +### System shutdown + +The machine is powering off. Devices should quiesce and hardware should stop. Module images do not need to be unmapped before S5. + +### PnP removal + +A concrete runtime device is disappearing. Existing PnP query/remove/surprise/remove semantics remain device-topology operations. + +### Driver unload + +A module image is being unloaded or reloaded. All owned devices/resources must already be detached or quiesced sufficiently for code removal. + +`DriverUnload` remains an unload callback; it should not be overloaded to mean “the whole machine is shutting down.” + +--- + +## 17. Proposed shutdown state machine + +System shutdown should be explicit: + +```text +Running + | + v +UserShutdown + | init stops user-mode services and session processes + v +KernelQuiescing + | prevent creation of new ordinary work that would outlive shutdown + v +DriverPrepare + | reverse dependency graph; flush/freeze/stop producers + v +DeviceCommit + | stop DMA/IRQs and hardware activity using runtime topology + v +DriverCommit + | stop remaining module-global workers/resources + v +FinalFlushDiagnostics + | + v +HAL S5 +``` + +The exact division between device and module callbacks should be implemented so the same primitives can later support reload without conflating system shutdown with removal. + +--- + +## 18. Driver shutdown API shape + +Add an explicit shutdown contract separate from `DriverUnload`. + +A two-phase model is preferred: + +```text +Prepare +Commit +``` + +### Prepare + +May: + +- flush buffered state; +- reject new ordinary requests; +- drain work queues; +- stop higher-level protocol activity; +- freeze filesystem mutation; +- send final device flushes; +- report failure. + +Dependencies remain alive while a consumer prepares. + +### Commit + +May: + +- disable device DMA; +- disable controller interrupts; +- stop hardware engines; +- terminate device workers; +- transition hardware into a final safe state. + +Commit is bounded and best-effort. Once shutdown has committed, the system continues toward poweroff even if a driver reports failure. + +The API may be represented as a module callback, a `DRIVER_OBJECT` callback, a shutdown/power IRP, or a combination. The final implementation should support both module-global state and concrete device stacks without duplicating shutdown order in each driver. + +A likely shape is: + +```text +configured module graph + orders module prepare/commit callbacks + +runtime DEVICE_OBJECT topology + orders per-device shutdown/power IRPs +``` + +--- + +## 19. Shutdown ordering + +Startup dependency edges reverse naturally for shutdown. + +Example: + +```text +startup: + bus -> usb-hcd -> usb -> usb-storage -> filesystem + +shutdown: + filesystem -> usb-storage -> usb -> usb-hcd -> bus +``` + +Likewise: + +```text +startup: + bus -> network-adapter -> network-stack + +shutdown: + network-stack -> network-adapter -> bus +``` + +`after` edges also reverse for shutdown because they express lifecycle barriers. + +Within module order, runtime device topology determines the correct per-device traversal: upper consumers/children quiesce before lower providers/parents. + +No separate hand-authored shutdown order should exist in configuration. + +--- + +## 20. Shutdown timeouts and failure policy + +Every shutdown wait is bounded. + +For each driver/device phase record: + +```text +start timestamp +completion timestamp +status +outstanding IRP count +worker count if available +``` + +Prepare failures/timeouts are logged. The kernel may attempt remaining independent drivers and then continue shutdown according to policy; one noncritical driver cannot create an infinite shutdown hang. + +Commit failure is diagnostic. Hardware poweroff continues once all bounded commit attempts finish. + +Before HAL S5, print a compact shutdown summary if any component failed or timed out. + +--- + +# Part IV — future driver-manager features + +## 21. Demand loading + +Once the graph manager is stable, add `start=demand`. + +Possible triggers: + +- PnP device ID match; +- an explicit kernel management request; +- a dependency demand from another requested driver; +- later, controlled user-mode administration if a secure management API is introduced. + +Demand loading resolves the hard dependency closure first. The class graph remains applicable; no alternate demand-only dependency system is introduced. + +--- + +## 22. Driver unload + +Implement real module unload only after lifetime accounting is trustworthy. + +Requirements: + +- prevent new device binds to the target module; +- identify all `DRIVER_OBJECT`s registered by the module; +- remove/quiesce all owned runtime devices; +- drain outstanding IRPs; +- stop module worker threads/timers/DPCs; +- unregister interrupt handlers; +- release DMA mappings/adapters; +- call `DriverUnload`/module unload callback; +- unregister exported module identity/import references; +- unmap image pages only after no executable/data references remain. + +The loader must track import/module references strongly enough to reject unloading a module that still supplies imported symbols to another loaded kernel image. + +--- + +## 23. Driver reload + +Reload builds on unload plus the existing graph. + +Initial operation: + +```text +reload driver-id +``` + +is allowed only when no started hard dependent requires the target and device lifetime permits removal. + +Later: + +```text +reload --cascade driver-id +``` + +may compute the reverse hard-dependency closure, quiesce dependents first, reload the target, and restart the closure in forward graph order. + +Class availability may temporarily change during reload. Dependents must not observe a provider as available while its module is stopped. + +Reload should be transactional where possible: an ordinary reload failure should return an error rather than intentionally crash an otherwise running system. + +--- + +## 24. Driver-manager observability + +Add structured diagnostics sufficient to answer: + +- which classes exist; +- class dependencies; +- class current state; +- class members; +- which drivers are configured; +- start policy; +- current module state; +- load/start status; +- blocking dependency; +- module base/image identity; +- registered `DRIVER_OBJECT`s; +- device count; +- shutdown/reload status. + +A debug graph dump should show both class and driver edges and explain why a waiting/blocked driver is not eligible. + +Later expose a read-only native query interface for system tools rather than parsing debug logs. + +--- + +# Part V — desktop/session work + +## 25. Move the native console into the desktop + +The largest remaining architectural cleanup in the current graphical stack is to make the RSSRV native console a real DWM-managed content source. + +Target ownership: + +```text +RSSRV + owns console state, input queue, cells/text semantics + +console renderer/client + renders console state into a kernel surface + +DWM + owns the window, z-order, decorations, composition and physical presentation +``` + +`/Sessions/0/Console` remains the native console object. The graphical desktop displays it; DWM does not become the console authority. + +Once this path works, moving keyboard focus between console and graphical windows becomes one native desktop/session mechanism instead of a transitional competition between console and DWM targets. + +--- + +## 26. Delete stale direct input/console/display application paths + +After the console is fully on the intended RSSRV/DWM path, remove old ambient application-facing input and text-console APIs. + +Migration rule: + +1. prove the canonical RSSRV/DWM path; +2. migrate every normal caller and test; +3. retain only low-level manager/device mechanisms needed by RSSRV/kernel tests; +4. remove stale application ABI in one breaking patch; +5. shift syscall numbers as required. + +Physical display presentation remains capability-restricted to DisplayManager authority. Client surface commit never becomes physical scanout. + +--- + +## 27. DWM protocol and compositor growth + +DWM is authoritative for native top-level window geometry, interactive move/resize, z-order, activation/focus, capture, decorations, clipping/occlusion, and composition. Applications own client content and may request bounds/minimize/maximize/fullscreen, but a client request is not an unconditional placement command. Interactive move/resize is DWM-driven: DWM hit-tests server-side decoration, captures the pointer, updates authoritative bounds, and sends a configure event so the client redraws. + +Use server-side native decorations as the canonical model, with explicit borderless/none modes for applications that need them. The native frame protocol does not become application-defined client-side chrome. Keep theme metrics/fonts/colors/borders/shadows/button layout/animations in a shared userspace theme schema/library used by DWM and the native UI toolkit; do not introduce a theme service until cross-process state actually requires one. + +Keep shell policy separate from DWM. DWM owns windows/monitors/work areas/activation/focus/z/composition/decorations. The shell owns taskbar/dock/start/launcher/icons/notifications/file-manager/wallpaper policy as a privileged DWM client and may reserve work areas or enumerate/activate/minimize top-level windows through explicit capabilities. + +Treat focus, activation, pointer capture, z-order, and attention as distinct state. Background applications cannot force activation merely by asking; user gestures/shell policy grant it. Security-sensitive desktop operations such as global window enumeration, capture pixels, input injection, secure UI, always-on-top system UI, work-area reservation, global theme selection, and display topology require explicit authority. + +Build richer behavior on the current native desktop protocol rather than introducing a Win32-specific compositor contract. + +Remaining native window features should include: + +- window title/application identity metadata; +- close-request event and explicit client response/lifetime semantics; +- move/resize interaction; +- decorations; +- minimum/maximum size policy if needed; +- activation policy distinct from raw focus requests; +- better z-order groups if concrete use cases require them; +- multiple damage rectangles rather than whole-window dirtying; +- region/occlusion tracking; +- compositor clipping/region algebra; +- incremental composition scheduling; +- optional frame/present timing and future vblank integration; +- explicit monitor/display geometry once multiple displays exist. + +Keep CPU composition as the correctness baseline. GPU acceleration can replace selected raster/composition mechanisms later without moving window policy into the kernel. + +--- + +## 28. DWM restart and reconstruction + +DWM is a natural future `restart=on-failure` service only after restart semantics are defined. + +A restart design must decide what persists outside DWM: + +- client desktop connections; +- window identities; +- attached surface capabilities; +- bounds/visibility; +- z-order; +- focus/capture ownership; +- cursor mode; +- decorations/title metadata. + +Two acceptable broad models are: + +1. **reconstruct:** RSSRV or another durable owner retains enough session/window registration state for a replacement DWM to rebuild; +2. **loss policy:** DWM restart explicitly invalidates graphical windows/connections and clients reconnect/recreate. + +Choose one and make failure visible to clients. Do not enable automatic restart while the behavior is accidental. + +Once defined, DWM may use the fixed service restart budget: at most three automatic restart attempts in any sliding five-minute window, with deliberate stop/shutdown excluded from the budget. + +--- + +# Part VI — POSIX remaining work + +## 29. POSIX direction + +Target a useful POSIX.1-1990-shaped personality while preserving nt-lite extensions and the native process model. + +The process model is intentionally **not** Unix process cloning: + +- `fork()` is deliberately unsupported. Do not add copy-on-write address-space cloning, Unix parentage, or a kernel `fork` compatibility path. +- `vfork()` is the constrained pre-exec construction primitive. Its implementation may temporarily borrow the parent's native address space and caller stack, but it remains a POSIX personality mechanism. +- `execve()` is a `posixsrv` transaction. `posix.dll` only marshals the request and exports inherited POSIX state; it does not load the image, create the replacement process, commit PID identity, or kill the old process on its own. +- `posix_spawn()` is also server-owned process construction, not `fork()+exec` emulation. +- POSIX PID identity is owned by `posixsrv`, not by the kernel native PID allocator. + +The kernel must not gain POSIX-named process, signal, session, process-group, or exec mechanisms. If the implementation exposes a missing substrate, add the smallest useful **generic native mechanism** and keep POSIX policy in userspace. + +The public API/profile manifest remains the source of truth for whether each POSIX entry is implemented, partial, planned, or deliberately unsupported. `fork()` must be marked deliberately unsupported rather than left ambiguously planned. + +PE32+ subsystem value 7 is reserved as the POSIX personality marker for future loader selection, but it keeps the native PEB/TEB layout and native GS contract. Finishing POSIX does not depend on changing that layout. + +--- + +## 30. `posixsrv.exe` authority and protocol + +Introduce `posixsrv` as the shared POSIX policy authority. It is an init-managed noncritical service with an explicit RSSRV dependency. Start with `restart=never`; do not add restart until client reconnection and server-state reconstruction have a defined contract. + +`posixsrv` owns shared state that cannot be process-local without breaking POSIX semantics: + +```text +stable POSIX PID allocation +native-process generation bound to each POSIX PID +parent/child launch associations +child exit records and wait collection +uid/euid/gid/egid +supplementary groups +POSIX process groups/session IDs +current directory identity used by server-owned spawn/exec +cross-process and process-group signal routing/pending state +synthetic POSIX filesystem metadata +fake hard-link aliases +FIFO namespace metadata +passwd/group database +POSIX tty line discipline and foreground process-group state +``` + +It does not own native console hardware/session routing. POSIX tty remains an adapter above RSSRV console/session mechanisms. + +The protocol is versioned and capability-oriented. Port sender identity is kernel-stamped and is never trusted from a client payload. Large `argv`, `envp`, descriptor manifests, or metadata transactions are streamed/chunked or transferred through generic mappings; do not raise Port inline/handle limits merely for POSIX. + +A server process record should be conceptually close to: + +```text +POSIX_PROCESS_RECORD + Pid + ParentPid + ProcessGroupId + SessionId + + NativeProcessHandle + NativePid + NativeGeneration + State + Constructing + Running + VforkBorrowed + ExecPreparing + ExecCommitting + Exited + + Credentials + SupplementaryGroups + CurrentDirectory + + ProcessPendingSignals + + ChildList / wait records + ExitStatus +``` + +`NativeProcessHandle` and `NativePid` are replaceable implementation identities. `Pid` is the stable POSIX identity. + +Every POSIX process receives a server-issued bootstrap token/generation in userspace startup data. A replacement image proves that it initialized successfully with that token before `posixsrv` commits an exec transition. + +--- + +## 31. Stable POSIX process lifecycle + +### Stable PID and parentage + +`getpid()` returns the cached `posixsrv` PID from process/thread personality state. It must not expose `SysGetCurrentPid()` once server-owned identity is active. + +`getppid()` uses the server launch association. There is no kernel process-parent field, Unix reparenting, adoption, or init-reaper model. Parent death and orphaned child records need one explicit POSIX policy, but native process ancestry never becomes authoritative. + +A process can move through multiple native generations while retaining one POSIX PID: + +```text +POSIX pid 42 + generation 1 -> native pid 113 + execve() + generation 2 -> native pid 126 + execve() + generation 3 -> native pid 141 +``` + +A native generation retired by exec is **not** a POSIX process exit. It produces no waitable child completion and no `SIGCHLD` solely because the old native process died. + +### Server-owned `execve()` + +`execve()` in `posix.dll` is a thin client operation: + +```text +capture path/argv/envp +capture the caller's logical descriptor table and FD_CLOEXEC state +export required native capabilities/open-description state +send EXEC transaction to posixsrv + +failure before commit: + receive errno + return -1 + +success: + never return to application code +``` + +`posixsrv` performs the replacement transaction: + +1. resolve the executable according to the POSIX process current directory/path facade; +2. validate the image before changing the old process; +3. build a new native process with the userspace process builder; +4. install inherited non-`FD_CLOEXEC` descriptors and reconstruct process-local wrappers; +5. build `argv`, `envp`, loader/bootstrap data, signal state, credentials, pgid/sid, and cwd; +6. start the replacement behind a userspace POSIX bootstrap barrier; +7. wait for the new runtime to report READY with the server-issued exec token; +8. enter `ExecCommitting`, queue new process-directed signals instead of routing them to the retiring generation; +9. retire the old native generation; +10. bind the stable POSIX PID to the new generation, apply queued signal state, and release the new image. + +For an ordinary process, the old `execve()` caller remains blocked in its Port call and `posixsrv` terminates the retained old native process handle. There is no success reply. + +An exec transaction must either fail while the old image is still valid or reach the committed replacement state. Do not expose a user-visible interval in which the POSIX PID disappears merely because native generations are changing. + +Signal exec semantics are applied by the personality: caught handlers reset to default, ignored dispositions remain ignored where required, the calling thread's blocked mask becomes the initial replacement-thread mask, and process-pending signals survive according to the selected POSIX profile. + +### `vfork()` + +Implement `vfork()` without cloning a native process or address space. + +The intended mechanism is a temporary borrowed native thread: + +```text +native process A + parent calling thread -> POSIX pid 20 + borrowed child thread -> POSIX pid 21, state=VforkBorrowed +``` + +The parent calling thread is suspended until the borrowed child either `_exit()`s or successfully `execve()`s. Other native threads in the parent process need not be frozen unless testing proves a stricter contract is required. + +The child receives a distinct stable POSIX PID and thread-local logical process state. It must not overwrite the parent's process-global POSIX state. A child descriptor-table overlay references the same inherited open descriptions while keeping descriptor-number mutations logically separate from the parent. + +The architecture-specific `vfork` trampoline may borrow the parent's user stack while the parent is blocked. It must make the child continuation observe a return value of zero and the parent continuation eventually observe the child POSIX PID. This is userspace continuation machinery, not a kernel clone operation. + +Initial supported child behavior is deliberately narrow: `_exit()` and `execve()` are the safe terminal operations. Do not add kernel complexity to make arbitrary post-`vfork` mutation safe. + +For `vfork()+execve()`: + +1. the borrowed child sends the normal EXEC request under its child POSIX identity; +2. `posixsrv` builds a new independent native process for the child PID; +3. the replacement reports READY behind its bootstrap barrier; +4. `posixsrv` commits the child PID to the new native generation; +5. the borrowed child receives an internal retire result and calls `SysExitThread()` without returning to application code; +6. the parent waits on the borrowed native thread handle, so its user stack cannot be reused until the child is actually gone; +7. once the borrowed thread is dead, `posixsrv` releases the parent and replacement image. + +For `vfork()+_exit()`, the server records a genuine POSIX child exit and the borrowed thread self-terminates; the parent then resumes and may collect the child through `wait`/`waitpid`. + +### `wait` and `_exit` + +`wait()`/`waitpid()` observe `POSIX_PROCESS_RECORD` completion, not raw native process exit events. Native process handles remain useful mechanisms for observing generations, but only `posixsrv` decides whether a native exit means POSIX process death or exec retirement. + +Implement: + +```text +_exit +getpid +getppid +wait +waitpid including WNOHANG +``` + +Child exit records remain collectible after native object teardown until the POSIX parent collects them or the selected orphan policy discards them. + +--- + +## 32. Credentials, groups, sessions, and identity database + +Keep POSIX identity metadata separate from native security authority. Unix mode/uid/gid data is a personality facade unless an explicit bridge to native security is designed later. + +Implement shared server state and APIs for: + +```text +getuid/geteuid/getgid/getegid +setuid/setgid +getgroups/getlogin +getpgrp/setpgid/setsid +getpwnam/getpwuid +getgrnam/getgrgid +``` + +The initial passwd/group database may be synthesized/configured rather than requiring `/etc/passwd` or `/etc/group`. + +Credential policy is explicit: synthetic effective uid 0 is currently the credential-changing authority. `setuid()` by uid 0 replaces both real and effective uid; `setgid()` is authorized by effective uid 0 and replaces both real and effective gid. After dropping uid 0, the current API cannot regain it. + +This is transitional POSIX personality state, not the final native security model. Once SEC lands, POSIX uid/gid/group identities map onto stable native principal IDs and POSIX permission APIs become projections of native security descriptors. Do not preserve a second synthetic filesystem-security authority in `posixsrv`. + +Process-group/session state is authoritative in `posixsrv` because it is consumed by `kill`, tty foreground-group policy, spawn attributes, and future job-control behavior. + +Move the authoritative POSIX current directory into the process record, or otherwise give `posixsrv` a stable capability/path representation sufficient to resolve relative server-owned `spawn`/`exec` requests. `chdir`/`fchdir` must update that authority consistently. + +--- + +## 33. POSIX process construction, environment, and descriptor inheritance + +### Userspace native prepared-process transaction + +Replace the public fixed-size `NTL_PROCESS_BUILDER.Opaque[...]` storage with a genuinely opaque userspace preparation object. The implementation owns its allocation and may change size without freezing private loader state into the public ABI. + +Target shape: + +```text +NtlPrepareProcess(...) -> NTL_PROCESS_PREPARATION* +NtlPreparedProcessGetHandle(...) -> borrowed process capability +NtlStartPreparedProcess(...) -> consumes preparation +NtlAbortPreparedProcess(...) -> consumes preparation without start +``` + +A valid `NtlStartPreparedProcess()` call consumes the preparation on both success and ordinary start failure. This gives the transaction one terminal operation and removes cleanup ambiguity. + +The preparation should permit `posixsrv` to: + +```text +create inert native process +load main PE and dependency modules +inspect PE32+ subsystem/personality +install arbitrary inherited native handles/capabilities +build loader/personality bootstrap data +start the process +retain the process capability +``` + +`NtlCreateProcessAt` remains a convenience wrapper over this mechanism for ordinary native callers. If a service needs the initial native thread capability, expose it generically from the prepared-process transaction rather than adding personality-specific thread lookup. + +### `argv` and environment + +Add a userspace process bootstrap representation for `argv` and `envp`. `environ`/`getenv` are process-local runtime views of the environment block; `posix_spawn` and `execve` may replace it. + +Do not require a kernel-defined POSIX environment block. The kernel only validates that generic loader/bootstrap memory is user-readable as it already does for native loader data. + +Implement: + +```text +getenv +environ +posix_spawn envp +execve envp +``` + +### Transferable POSIX open descriptions + +Before process inheritance, make the existing open-description model transferable across native processes without changing its same-process `dup` semantics. + +Requirements: + +- descriptor flags such as `FD_CLOEXEC` remain descriptor-local; +- mutable status belongs to the shared open description; +- duplicate regular-file native handles continue to share the native file object's offset and append state; +- duplicate socket handles continue to share native socket status; +- pipe endpoint state and `O_NONBLOCK` must remain shared across inherited duplicates; +- the replacement/child gets process-local wrapper allocations around shared/native backing state; +- closing one process-local wrapper must not invalidate another process's inherited description. + +The current shared pipe fast path also needs a supported way to map the pipe backing into an inheriting/replacement process. Do not rely on the current shared-PML4 accident. First try to express this with a reusable native mapping object. If the kernel lacks the needed mechanism, add one generic shared-memory/section mapping capability and use it for pipe backing rather than adding spawn/exec-specific VM syscalls. + +Descriptor export uses a logical manifest of unique open descriptions plus FD-to-description references. Port handle transfer is batched within existing limits; do not raise the eight-handle message limit just to transfer a large descriptor table. + +### `posix_spawn()` + +`posix_spawn()` sends construction policy to `posixsrv`; the server allocates a new POSIX PID, builds the native process, installs inherited descriptors, and returns the stable POSIX PID only after the child bootstrap has succeeded. + +Support: + +- `envp`; +- file actions: close, dup2, open; +- process-group attribute; +- signal mask/default attributes once signal state exists; +- cwd inheritance; +- exact open-description sharing and `FD_CLOEXEC` behavior. + +The server applies file actions to the logical descriptor manifest before the new process is released. The child must not briefly execute with the wrong descriptor table. + +--- + +## 34. POSIX signals + +Implement signal policy in `posix.dll`/`posixsrv` over a personality-neutral asynchronous native mechanism. + +Ownership split: + +```text +POSIX process-local/shared runtime state + dispositions + +POSIX thread state + blocked mask + thread-pending set + +posixsrv + process-pending set + process-group routing + target-thread selection policy + alarm/process lifecycle association +``` + +Implement: + +```text +sigemptyset/sigfillset/sigaddset/sigdelset/sigismember +sigaction +sigprocmask +sigpending +sigsuspend +kill +alarm +pause +sleep +signal +raise +``` + +Per-thread registration must let `posixsrv` identify a target native thread capability without trusting user-supplied numeric IDs alone. + +The current kernel already has user APC machinery for asynchronous I/O, but userspace has no generic operation that lets a service queue an asynchronous notification to an arbitrary thread capability. If no existing mechanism can satisfy signal delivery, expose the smallest generic native thread-targeted user-APC/notification primitive with explicit handle rights. Do **not** add `SysKillPosix`, kernel signal masks, or kernel signal numbers. + +Signal handlers run through a POSIX userspace dispatcher. Blocked/process-pending state remains POSIX policy. A generic APC is only the mechanism used to interrupt the selected user thread and enter the dispatcher. + +Translate selected native user exceptions into POSIX signals at the personality boundary, including access/page faults, illegal instruction, and arithmetic faults. Kernel exception codes remain personality-neutral. + +Process-group `kill` routing uses server pgid/session state. Tty-generated `VINTR`/`VQUIT` later reuses the same signal routing path. + +--- + +## 35. POSIX filesystem facade + +P8 is deferred until KFS and SEC establish the native semantics it projects. Do not land the previous synthetic-metadata/fake-hard-link design as an intermediate architecture. + +The revised POSIX filesystem facade should implement: + +```text +rewinddir +creat +umask +link +mkdir +mkfifo +rmdir +rename +access +chmod/fchmod +chown/fchown +utime +pathconf/fpathconf +``` + +Native filesystem identity, open-after-unlink lifetime, hard links, rename replacement, timestamps, directory reset, and security are owned below `posixsrv`. POSIX translates those semantics rather than recreating them. + +### Permissions and ownership + +- uid/gid/group values map to native principal IDs through the POSIX/account layer; +- `chmod()` transforms the owner/group/everyone projection of the native ACL; +- `chown()` updates native owner/owning-group state subject to native privilege checks; +- `access()` evaluates the POSIX view of native authorization; +- `umask` remains POSIX creation-time restriction policy and is not persisted as a kernel concept. + +### Hard links and pathname operations + +`link()` uses the native filesystem hard-link operation. NTFS and ext4 providers implement real links; filesystems such as FAT may return `NTLSTATUS_NOT_SUPPORTED`. POSIX must not maintain fake alias tables or synthetic link counts. + +`rename()` uses native replace-existing semantics and maps cross-volume operations through the canonical native status mapper. `utime()` uses the generic native file-information setter. `rewinddir()` uses native directory reset rather than close-and-reopen-by-path. + +### FIFOs + +`mkfifo()` remains a POSIX namespace/rendezvous feature owned by `posixsrv`; data transport continues to use native pipe endpoints/shared backing. Blocking/nonblocking FIFO-open semantics must not use busy polling. + +Native/RSSRV symbolic links and POSIX filesystem symbolic-link semantics remain separate namespaces/features. + +--- + +## 36. POSIX tty/termios + +Layer POSIX terminal behavior over RSSRV's native console/input mechanisms: + +```text +kernel device input/display + -> RSSRV native console/session + -> posixsrv tty/line discipline + -> POSIX tty open description +``` + +Implement: + +```text +ctermid/ttyname/isatty +cfgetispeed/cfgetospeed +cfsetispeed/cfsetospeed +tcgetattr/tcsetattr +tcsendbreak +tcdrain +tcflush +tcflow +tcgetpgrp/tcsetpgrp +``` + +Initial useful flags/control characters: + +```text +ICANON ECHO ECHOE ISIG +VINTR VQUIT VERASE VKILL VEOF VMIN VTIME +OPOST ONLCR +``` + +Foreground POSIX process-group policy belongs in `posixsrv`; RSSRV retains actual native input/session authority. + +`ISIG`, `VINTR`, and `VQUIT` route through the same server signal mechanism implemented earlier. Do not create a second tty-specific signaling path. + +--- + +## 37. POSIX staged implementation plan + +The following is the active implementation sequence. Finish each stage with focused runtime coverage before beginning the next architectural move. + +### P0 — status boundary and profile audit + +Implementation state: final cleanup bundled with P2. The first P0/P1 patch was runtime-validated. The bundled P2 patch finishes the ABI cleanup by giving POSIX errno a deliberate nt-lite-owned numbering and replacing Windows-numbered native exception codes with compact nt-lite exception classes. Win32 now translates native exceptions explicitly at the personality boundary. Runtime validation of that bundled ABI break is still required before marking P0 complete. + +Goal: make later POSIX APIs fail consistently before adding more calls. + +Work: + +- audit the POSIX profile manifest against exported headers and current implementations; +- explicitly mark `fork()` unsupported and `vfork`/`execve` planned; +- add missing personality-neutral native status distinctions needed by remaining POSIX work, including directory/type errors, non-empty directory, cross-device operation, no child, exec format, long name, too many links, read-only filesystem, and file too large; +- make `PosixStatusToErrno` the one complete POSIX mapper; +- assign public POSIX errno values from one compact nt-lite-owned table rather than preserving accidental Linux/BSD/newlib numeric inheritance; third-party errno domains remain private and translate through `KSTATUS`; +- keep native exception IDs personality-neutral and nt-lite-owned; Win32 maps them to Windows exception values and the later POSIX signal layer maps them to signals; user-raised exceptions carry an opaque personality/application code separately from the native exception class; +- remove POSIX-local ad-hoc status mappings as each distinction becomes canonical. + +Exit criteria: + +- one native status maps to one intentional POSIX errno; +- public errno numbers have one documented nt-lite provenance and no dependency on third-party numeric coincidence; +- native exception IDs contain no Win32 values and every personality translation is explicit; +- no new stage is allowed to add a private errno switch in an API implementation; +- profile manifest exactly describes the starting surface. + +### P1 — `posixsrv` service and protocol skeleton + +Implementation state: complete and runtime-validated. The generic service READY contract exports one public Port listener, init publishes it at `/Services/posix` through RSSRV, and the versioned connection/transaction bootstrap protocol plus native smoke coverage pass. No POSIX process semantics were added to the kernel. + +Goal: establish the shared authority before moving policy into it. + +Work: + +- add the real generic-service startup/READY adapter; +- add init service configuration with explicit RSSRV dependency and `restart=never`; +- define a versioned `posixsrv` Port protocol; +- implement connection lifecycle, kernel-stamped client identity validation, request dispatch, diagnostics, and disconnect cleanup; +- define server-side transaction IDs so large/multi-message spawn/exec/FD transfers are bounded and cancelable; +- provide a native test client before moving POSIX APIs. + +Exit criteria: + +- service starts, reports READY, accepts multiple clients, rejects malformed protocol versions, and cleans up disconnected transactions; +- no POSIX process semantics have been duplicated in the kernel. + +### P2 — stable POSIX PID registry and bootstrap + +Implementation state: started in the bundled ABI-cleanup/P2 patch. `posixsrv` now allocates stable POSIX PIDs, retains an attenuated native process capability to observe genuine termination, recovers the same identity across Port reconnects, and the POSIX runtime registers itself during startup. `getpid()` now reads thread-selected logical POSIX identity. Generic opaque process-loader startup data and reserved POSIX bootstrap token/generation fields are present for later server-owned spawn/exec construction; nonzero bootstrap tokens are intentionally not activated in P2. + +Goal: sever POSIX process identity from native PID identity. + +Work: + +- add server POSIX PID allocator and `POSIX_PROCESS_RECORD` lifecycle; +- add bootstrap token/generation data to userspace process startup metadata; +- register the initial POSIX runtime with the server and cache stable PID in `POSIX_PROCESS_STATE`; +- make POSIX thread state identify its logical POSIX process rather than blindly assuming one process-global identity; +- change `getpid()` to return stable POSIX PID; +- retain/native-open process capabilities needed to observe termination; +- distinguish genuine exit from disconnect/protocol failure. + +Tests: + +- stable PID differs safely from native PID; +- multiple processes receive unique stable PIDs; +- disconnect does not masquerade as process exit, and native process death transitions the correct record; +- reconnecting the same live native generation recovers the same stable POSIX PID; +- thread attachment inherits the same logical POSIX identity; +- generic startup data reaches a child unchanged and reserved zero-token bootstrap framing is validated; +- no `SysGetCurrentPid()` value leaks through `getpid()`. + +### P3 — shared process attributes: cwd, credentials, pgid/sid, identity database + +Goal: establish state needed by spawn, signals, filesystem metadata, and tty before those subsystems use it. + +Current implementation, awaiting runtime validation: + +- `POSIX_PROCESS_RECORD` owns canonical native cwd, uid/euid/gid/egid, supplementary groups, login identity, pgid and sid; +- initial standalone registrations begin as synthetic root and as their own process-group/session leader; +- POSIX `chdir()` updates the native process-local resolver first, publishes the canonical result to `posixsrv`, and rolls back the native cwd if the server commit fails; +- POSIX `getcwd()` reads the server-owned cwd so tests cannot accidentally validate only the native mirror; +- synthetic uid 0 is the explicit credential-changing authority; dropping it through `setuid()` is irreversible with the current API; +- a minimal server-owned identity database provides `root`/`user` and `root`/`users` records; +- `getuid/geteuid/getgid/getegid`, `setuid/setgid`, `getgroups/getlogin`, `getpgrp/setsid`, `getpwnam/getpwuid/getgrnam/getgrgid` are implemented; +- `setpgid` is PARTIAL until P5 establishes authoritative parent/child launch associations; self/session validation is already server-owned; +- `fchdir` is not in the current POSIX.1-1990 public profile and is not introduced merely to satisfy an internal planning bullet. + +P3 runtime gate: + +- server registration reports the correct cwd/credentials/pgid/sid and synthesized login; +- `chdir` followed by server-backed `getcwd` proves cwd synchronization; +- process-group/session leader rejection is correct; +- uid 0 can drop gid/uid and cannot regain authority afterward; +- passwd/group lookup agrees with the synthesized database. + +Inheritance of cwd/credentials and successful parent-driven `setpgid` are P5 acceptance tests because P5 is the stage that makes parent/child launch association authoritative. + +### P4 — transferable descriptors and open descriptions + +Goal: make FD inheritance correct before implementing server-owned process creation, using the SASOS directly rather than introducing a conventional shared-memory mapping API. + +The VM audit established the ownership rule: + +- user virtual addresses are globally meaningful through the shared PML4; +- page faults resolve against the global VAD list, so another process may directly use a pointer into a live owner's VAD; +- native process teardown destroys that process's VADs and frames, so shared POSIX state must not be allocated in an ordinary client if it must outlive that client; +- therefore long-lived shared POSIX control/data allocations belong to `posixsrv`, while each native process retains only process-local capability handles/wrappers. + +#### P4.1 — server-owned shared open descriptions + +Status: **implemented and runtime validated.** + +- protocol v4 adds stable open-description IDs and pointers to `posixsrv`-owned shared control blocks; +- each native process keeps a local wrapper containing its local native handle plus the shared description pointer/ID; +- `FD_CLOEXEC` remains descriptor-local and never enters the shared control block; +- `dup`/`dup2`/`F_DUPFD` remain local and do not create extra server holders; +- `posixsrv` retains one attenuated master capability for file/socket/pipe descriptions and one holder per POSIX process/open-description pair; +- genuine native-process death releases every holder even if userspace did not close its descriptors; +- `F_GETFL` is a direct shared-memory read; +- `F_SETFL` is serialized by `posixsrv`, which changes native file/socket state through the master capability and only then publishes `StatusFlags`; +- `setsockopt(SO_NONBLOCK)` uses that same authority instead of maintaining a second status path; +- P4.1 left the pipe ring VAD client-owned as its one remaining lifetime exception; P4.2 below removes that exception; +- no kernel API or VM change is introduced. + +Validation for P4.1: + +- existing descriptor duplication/file-offset tests must continue to pass; +- existing `F_SETFL` sharing/append tests exercise server-serialized file status; +- socket nonblocking behavior must remain coherent between `F_SETFL`, `setsockopt`, and `F_GETFL`; +- service smoke directly dereferences a `posixsrv`-owned shared description, checks create/set/release accounting, rejects double release, and proves process-death cleanup of an intentionally leaked holder. + +#### P4.2 — server-owned pipe backing + +Status: **implemented; runtime validation pending.** + +- protocol v5 adds `CREATE_PIPE`, reply-side capability transfer, a second open-description result slot, and shared-description data-plane pointers; +- `SysCreatePipe()` is invoked only by `posixsrv`, so the resulting `SYS_PIPE_SHARED_STATE` VAD is owned by the long-lived personality server while clients use the same globally meaningful pointer; +- the process-owned `POSIX_PIPE_MAPPING` lifetime wrapper and client-side `SysFreeVm()` path are deleted; +- `posixsrv` retains the read/write master endpoint capabilities and transfers attenuated aliases to the requesting client; +- ordinary `read`, `write`, `poll`, blocking waits, notifications, and `PIPE_BUF` handling continue to use the shared-ring/native-endpoint fast path with no byte pumping through the server; +- each pipe endpoint shared description points at the same server-owned ring through `SharedObjectData`; +- local wrappers close their native aliases before releasing the server holder, so the server may free backing only after the client no longer owns that endpoint capability; +- the server closes each master endpoint only when its description loses the final holder, preserving kernel-object EOF/broken-pipe semantics; +- the server frees the pipe VAD only after both endpoint descriptions are destroyed; +- `posix_service_smoke` checks two-description accounting, direct shared-state visibility, master/alias close ordering, and final cleanup; existing `posix_pipe_smoke` remains the end-to-end data/readiness/lifetime test; +- no kernel API, VAD, or syscall change is introduced. + +Validation for P4.2 remains part of the combined P4 runtime gate below. + +#### P4.3 — cross-process descriptor export/import + +Status: **implemented; runtime validation pending.** + +- protocol v6 adds bounded descriptor-inheritance snapshots, one-shot import tokens, batched description metadata/capability transfer, and an explicit child-lifetime arm operation; +- a snapshot contains exact descriptor numbers, descriptor-local flags, and stable open-description IDs; shared status and pipe data-plane pointers are not copied or reconstructed; +- `FD_CLOEXEC` filtering is performed while the parent snapshots its descriptor table, and descriptor-local flags remain process-local in the receiver; +- `posixsrv` pins every unique referenced open description while the snapshot exists and creates at most one holder per receiving POSIX process/open-description pair; +- each unique inherited native-backed description receives one fresh process-local capability alias from the server-retained master handle, in batches bounded by `SYS_PORT_MAX_TRANSFER_HANDLES`; +- duplicate inherited descriptors in one process share one local open-description wrapper and therefore one native alias; +- the receiving process reconstructs exact descriptor numbers from the manifest before user entry continues; +- the current partial `posix_spawn()` uses this inheritance transport so P4 can be exercised before P5 moves process construction into `posixsrv`; +- after native child creation, the parent arms the snapshot by transferring a restricted child process capability to `posixsrv`; only that native child PID may claim an armed snapshot; +- an arm monitor contains only `{token, native pid, process handle}` rather than a raw snapshot pointer: child exit before import reclaims the snapshot, while successful import makes the monitor a harmless no-op; +- once armed, source-process exit no longer destroys an unclaimed snapshot; once claimed, the receiver process lifetime owns incomplete-import cleanup; +- `posix_fd_smoke` now exercises two real native processes and verifies shared file offset, cross-process shared `F_SETFL`, `FD_CLOEXEC` filtering/locality, inherited pipe status, cross-process pipe I/O, EOF, and broken-pipe transitions; +- no kernel API, VM, or syscall change is introduced. + +Combined P4 runtime gate: + +- full `all_tests` passes with P4.1 + P4.2 + P4.3; +- `posix_service_smoke` passes shared-description/pipe ownership and cleanup checks; +- `posix_pipe_smoke` passes blocking/nonblocking, EOF, broken pipe, poll, and `PIPE_BUF`; +- `posix_fd_smoke` passes the new cross-process inheritance scenario; +- no regression appears in unrelated VM/page-cache tests; +- after this gate P4 is complete and P5 becomes the active stage. + +### P5 — userspace builder, environment, server-owned `posix_spawn`, and wait + +Goal: make ordinary POSIX child creation entirely server-owned. + +Work: + +- refactor native userspace process construction into a builder API; +- let `posixsrv` resolve/load the executable and retain the native process handle; +- add userspace bootstrap `argv`/`envp` data and implement `environ`/`getenv`; +- allocate the POSIX child PID before starting the image; +- install inherited descriptors and apply close/dup2/open file actions before child release; +- support cwd and process-group spawn attributes; +- register child bootstrap READY before returning the child PID; +- replace the fixed process-local spawn table with server child associations; +- implement `_exit`, `getppid`, `wait`, `waitpid`, and `WNOHANG` over server exit records. + +Tests: + +- env replacement/inheritance; +- file actions including collisions and close-on-exec descriptors; +- parent/child relationship independent of native PID; +- child exits before parent waits; +- multiple children exit out of order; +- `WNOHANG` and `ECHILD` behavior; +- process construction failure leaves no leaked POSIX PID or native process. + +### P6 — signals and generic asynchronous delivery substrate + +Goal: complete signal semantics before exec/vfork need to preserve/reset them. + +Work: + +- implement signal-set operations, dispositions, per-thread masks, pending sets, `raise`, and local dispatch; +- register every POSIX native thread with `posixsrv` using retained/transferred thread capabilities; +- if required, expose a generic thread-targeted user APC/notification syscall and explicit capability right; reuse existing kernel APC machinery rather than adding POSIX semantics; +- implement cross-process/process-group `kill` routing; +- implement `sigsuspend`, `pause`, `alarm`, and `sleep` interaction with pending delivery; +- translate selected native exceptions at the POSIX boundary; +- complete `posix_spawn` signal-mask/default attributes. + +Tests: + +- per-thread blocked masks; +- pending signal becomes deliverable after unblock; +- process-directed delivery chooses an eligible thread; +- process-group routing; +- signal interrupts POSIX blocking operations correctly; +- caught/ignored/default dispositions; +- nested and repeated delivery remains bounded; +- exception-to-signal translation does not alter native exception ABI. + +### P7 — `execve()` replacement and `vfork()` + +Implementation state: **complete and runtime-validated by the full `all_tests` suite.** P7.1 `execve()` replacement, P7.2 borrowed logical-child substrate, and P7.3 public `vfork()` Option A server-monitor finalization are all complete. The generic `SysResumeThreadAndWait` primitive atomically resumes the borrowed thread and waits for its death without a userspace return window. + +Goal: complete the non-fork POSIX process model. + +Work: + +- add EXEC prepare/READY/commit transaction to `posixsrv`; +- implement descriptor export with `FD_CLOEXEC` filtering; +- create replacement native generation behind bootstrap barrier; +- preserve stable POSIX PID, parent, credentials, cwd, pgid/sid, process-pending signals, and inherited descriptors; +- reset/preserve signal state according to exec semantics; +- retire the old native generation without generating child exit; +- implement assembly `vfork` continuation/trampoline; +- create borrowed child thread + logical child POSIX process state and descriptor overlay; +- suspend parent on the borrowed thread handle; +- implement `vfork()+_exit` and `vfork()+execve` retirement handshakes; +- make `fork()` return `-1`/`ENOSYS` if it remains declared at all; do not emulate it. + +Tests: + +- successful exec retains POSIX PID while native PID changes; +- failed exec returns with the old image/process state intact; +- exec does not satisfy parent's `waitpid`; +- `FD_CLOEXEC` filtering; +- environment/argv replacement; +- signal disposition/mask semantics across exec; +- repeated exec generations; +- `vfork` child observes zero, parent observes child PID; +- parent cannot resume while child still uses its borrowed stack; +- `vfork()+_exit` creates a waitable child exit; +- `vfork()+execve` produces an independent running child without native process cloning. + +### P8 — revised POSIX filesystem projection and FIFO namespace + +Status: **paused until KFS and SEC complete the required native substrate.** + +Goal: finish the POSIX filesystem facade by projecting native identity, links, timestamps, directory iteration, and security rather than synthesizing competing filesystem semantics in `posixsrv`. + +Work: + +- map POSIX uid/gid/groups to native principals; +- implement `chmod/chown/access` as projections of native security descriptors; +- keep `umask` as POSIX creation-time restriction policy; +- implement `link()` with the native hard-link operation; +- use native rename-replace and cross-device status; +- use native file-information time mutation for `utime`; +- use native directory reset for `rewinddir`; +- finish remaining path/directory APIs and canonical errno mapping; +- implement FIFO namespace/rendezvous records in `posixsrv`; +- use native pipe endpoint/shared backing transfer for FIFO data transport. + +Tests: + +- native owner/group/mode projection is coherent across reopen and hard-link aliases; +- umask restricts creation without granting rights absent from inherited native policy; +- real hard-link data/link-count behavior; +- open-after-unlink and rename-replacement edge cases; +- timestamp mutation and directory reset; +- FIFO blocking/nonblocking open, data transfer, EOF, broken-pipe, and cleanup. + +### P9 — tty/termios + +Goal: finish interactive POSIX process behavior over RSSRV. + +Work: + +- add tty object/open-description adapter over RSSRV console/session capabilities; +- implement canonical/raw input queues and output postprocessing; +- implement speed/control APIs and drain/flush/flow operations; +- implement foreground pgid state in `posixsrv`; +- route `VINTR`/`VQUIT` through the signal subsystem; +- implement `ctermid`, `ttyname`, `isatty`, `tcgetpgrp`, and `tcsetpgrp`. + +Tests: + +- canonical line completion/editing/EOF; +- raw VMIN/VTIME behavior; +- echo and output newline translation; +- foreground/background process-group behavior selected by the profile; +- `VINTR`/`VQUIT` signal routing; +- disconnect/console loss cleanup. + +### P10 — leaf APIs, cleanup, and POSIX completion gate + +Goal: close the profile rather than leave a nearly-finished personality. + +Work: + +- implement remaining leaf APIs such as `uname`, `time/times`, `sysconf`, and any unfinished `pathconf/fpathconf` cases; +- audit every public POSIX header/export against the profile manifest; +- remove old process-local spawn associations and any compatibility paths replaced by `posixsrv`; +- remove stale local errno mappings and temporary process-construction helpers; +- document deliberate non-support such as `fork()`; +- run the full native + POSIX test suite after each cleanup patch. + +POSIX is complete for this roadmap when: + +- all shared process policy has one `posixsrv` owner; +- `spawn`, `wait`, `execve`, and `vfork` follow the stable-PID model; +- `fork()` is deliberately unsupported with no hidden clone substrate; +- inherited descriptors/open descriptions are correct across processes; +- signals are asynchronous and process-group aware without kernel POSIX policy; +- filesystem metadata/aliases/FIFOs are coherent; +- tty/termios uses RSSRV rather than bypassing native session authority; +- the profile manifest accurately describes every exposed POSIX API; +- the full test suite passes without relying on the shared-PML4 accident for cross-process correctness. + +--- + +# Part VI-A — native ABI, filesystem, and security prerequisites + +## UABI — native interface cleanup before KFS + +Perform the public/native ABI cleanup as separate architectural moves before changing filesystem/security contracts. + +### UABI.1 — fixed-width types, booleans, statuses, and atomics + +- use `` widths where persistent/shared/module interfaces require an exact width; +- use `` and `true`/`false` for genuine predicates; +- use `KSTATUS` for native status-returning APIs rather than raw integer aliases; +- keep POSIX/Win32/UEFI/third-party externally mandated types intact; +- keep compiler `_Interlocked*` conventions confined behind typed `` helpers; +- add ABI layout assertions around important shared structs; +- sweep implementation state and callbacks as well as public syscalls; do not mechanically rewrite ordinary local arithmetic indices. + +### UABI.2 — UTF-8 native text contract + +nt-lite native text is UTF-8. `SYS_STRING_VIEW` lengths are byte lengths. Paths, resource names, module names, thread descriptions, window titles, account names, service names, and other retained native text use UTF-8. Validate malformed UTF-8 at trust/ownership boundaries before namespace or persistent state accepts it. + +Do not impose Unicode normalization or Unicode case folding on native identity. Byte-exact valid UTF-8 remains the native namespace identity unless one subsystem explicitly documents an ASCII-only compatibility rule. Byte payloads remain byte payloads and do not become text merely because they use `char*`. + +Single-character APIs must not pretend one UTF-8 byte is one Unicode character. Where an interface genuinely represents one character, use a Unicode scalar value or a separate text event; keep sophisticated composition/IME work for the input/desktop stack. + +### UABI.3+ — prepared process and stale path deletion + +- replace fixed-size opaque process-builder storage with the prepared-process transaction from Section 33; +- remove the legacy kernel-global input convenience/snapshot syscalls once all callers use RSSRV's capability-scoped logical-input path; +- remove the kernel named-queue publication namespace while retaining anonymous queue objects if still useful; RSSRV remains the global resource namespace owner; +- keep `SysResolveHost` and its existing comment until networking is actually redesigned rather than cosmetically hiding the current lwIP-in-kernel shortcut. + +## KFS — coherent native filesystem substrate + +### KFS.1 — backing identity and coherent file cache + +Introduce an internal refcounted `IO_FILE_BACKING` (name illustrative) distinct from `FILE_OBJECT`. Independent opens retain separate open descriptions/offsets but converge on one backing identity for shared size, page-cache identity, mapped-file coherence, and file security. + +Provider identity is mounted volume + stable `FileId` + generation/reuse discriminator. NTFS MFT sequence and ext4 inode generation should be wired where available. FAT needs a provider-specific identity policy that remains correct across rename. + +### KFS.2 — unlink/open lifetime + +Native unlink removes one directory link. A backing with link count zero survives while native opens/mappings retain it and is reclaimed after the final backing reference disappears. Providers must support this contract rather than having the VFS fake it. + +### KFS.3 — native hard links + +Add one generic capability-relative native hard-link operation. Source and destination authority are independently checked, cross-volume returns `NTLSTATUS_CROSS_DEVICE`, NTFS maps to `ntfs_link`, ext4 maps to `ext4_flink`, and FAT may return `NTLSTATUS_NOT_SUPPORTED`. + +### KFS.4 — rename replacement + +Native rename supports atomic destination replacement when the provider can do so and reports cross-volume operations canonically. Do not preserve adapters that only support rename-to-nonexistent as the generic contract. + +### KFS.5 — generic file-information mutation + +Add one generic setter for mutable file information, including persistent timestamps. Persistent filesystem timestamps use a small UTC timestamp type rather than `SYS_SYSTEM_TIME`, which contains current-clock provenance/timezone fields. + +### KFS.6 — directory reset + +Add provider reset/restart-directory support and a native operation so `rewinddir()` does not close and reopen by pathname after rename. Do not invent seekdir/telldir semantics until required. + +### KFS.7 — validation + +Runtime tests cover alias/cache coherence, independent-open coherence, open-after-unlink, hard-link counts/data identity, rename replacement, timestamps, directory reset, mmap/page-cache aliases, and provider-specific unsupported cases. + +## SEC — native principals, ACLs, authorization, accounts, and audit + +### Security model + +Use one simple native nt-lite security descriptor rather than filesystem-dependent kernel semantics. A descriptor stores concrete owner and owning-group principal IDs plus an access ACL and directory creation policy (`FileAcl` and `DirectoryAcl`). ACL subjects support specific principal IDs plus `OWNER`, `OWNING_GROUP`, and `EVERYONE`. + +For all matching subjects: + +```text +allowed = union of matching allow masks +denied = union of matching deny masks +effective = allowed & ~denied +``` + +ACE order has no meaning; deny always wins. Owner status implicitly grants security-descriptor mutation authority but does not grant object data read/write/delete. + +Authorization is capability-first: ACL/security is checked when authority is acquired/opened. Once a capability is granted, possession is sufficient. Explicit capability transfer/derivation delegates the granted authority without rechecking the receiver ACL. ACL/security changes affect future acquisitions; existing capabilities remain valid unless explicitly revoked. + +### Principal and context model + +Human names remain userspace data. The kernel sees stable opaque `PRINCIPAL_ID`s and immutable/materialized `SECURITY_CONTEXT`s containing primary principal, groups/roles, privileges, restrictions, authentication/session identity, and a unique context/audit ID. Do not reuse current `SecurityId` as an account ID; rename that concept to `SecurityContextId`/equivalent when SEC1 lands. + +A userspace account/authentication service owns name↔principal mapping, password hashes/secrets, group database, account state, profile/home data, and login policy. Access checks never synchronously call that service; group membership is materialized into the security context. + +### Inheritance and filesystem translation + +Directory `AccessAcl` controls the directory itself. Creation policy contains independent file and directory ACL templates. Creation materializes the relevant template into the child; a new directory recursively copies the parent's creation templates unless an authorized creator supplies different ones. Inheritance occurs at creation only. + +POSIX mode translation maps owner/group/other bits to `OWNER`/`OWNING_GROUP`/`EVERYONE`; named POSIX ACL entries map to specific principals and the POSIX ACL mask is folded into effective translated rights. NTFS descriptors translate best-effort into the simpler native flavor; exact Windows round-trip fidelity is not a goal. ext4 ownership/mode/ACL data translates to the same native semantics. + +Security metadata belongs to KFS backing identity, not to a pathname or one `FILE_OBJECT`. + +### Privileges, restriction, and audit + +Do not encode Unix root as a native kernel special case. Use separable privileges such as security administration, take ownership, backup-read, restore-write, context creation, impersonation, raw storage, driver load, process debugging, service administration, and shutdown. POSIX root maps to an appropriate native context. + +Make restricted/sandbox contexts first-class and add kernel-stamped audit records for security-context creation, privilege use, failed access checks, security/ownership changes, capability delegation/revocation, cross-process opens, raw storage/driver/service administration, and similar authority transitions. A userspace service persists/filters audit data. + +### SASOS threat boundary + +The current shared-PML4 SASOS model intentionally does **not** claim inter-process userspace memory confidentiality/integrity. Accounts and ACLs protect kernel-mediated authority; hostile userspace may still inspect live userspace mappings under this model. Do not silently introduce page-table isolation as part of SEC unless that threat-model decision changes. + +### SEC staged sequence + +```text +SEC0 threat model + rights taxonomy +SEC1 principal IDs + SECURITY_CONTEXT v2 +SEC2 native security descriptor/ACL +SEC3 generic access check +SEC4 object-manager authorization +SEC5 filesystem provider security contract +SEC6 ext4 translation +SEC7 NTFS translation +SEC8 creation/inheritance +SEC9 ownership/security mutation +SEC10 privilege architecture +SEC11 restricted/sandbox contexts +SEC12 account/authentication service +SEC13 audit subsystem +SEC14 process/device/service/IPC security sweep +SEC15 personality mappings/migration +``` + +--- + +# Part VII — native status, loader identity, and personality runtime + +## 38. Native status cleanup + +Keep kernel/native status values personality-neutral and make mapping complete at personality boundaries. + +Add missing distinctions as needed, including equivalents for: + +```text +not a directory +is a directory +directory not empty +cross-device operation +no child +exec format error +name too long +too many links +read-only filesystem +file too large +``` + +Then maintain exactly one complete mapper per personality: + +```text +KSTATUS -> Win32 LastError +KSTATUS -> POSIX errno +``` + +The POSIX-required slice is Stage P0 and may be completed before the broader Win32 mapper cleanup. Remove ad-hoc per-call mappings after the canonical mappers cover the necessary status space. + +--- + +## 39. Loader module identity + +Before the planned CRT/personality DLL split, change loaded-module identity from requested basename to resolved file/object identity. + +A loaded image needs: + +```text +resolved file identity +canonical module object +requested-name aliases +reference/import counts +``` + +Two names referring to the same backing image must not create two independent runtime instances. + +This is required before making aliases such as `msvcrt.dll` refer to the same module identity as the nt-lite CRT. + +It also becomes important for kernel driver unload/reload because the kernel loader must know which loaded modules import from which provider modules. + +--- + +## 40. PE32+ subsystem personality selection and Win32 userspace GS shim + +Use the PE32+ Optional Header `Subsystem` value as the loader's initial personality selector. This is userspace image-loader policy; do not teach the kernel PE loader about Win32 or POSIX personalities. + +Initial mapping: + +```text +Subsystem 1 IMAGE_SUBSYSTEM_NATIVE + nt-lite native personality + current SYS_PEB / SYS_TEB layout + initial user GS base -> native TEB + GS:[0] -> native PEB pointer under the native TEB ABI + +Subsystem 2 IMAGE_SUBSYSTEM_WINDOWS_GUI + Win32 personality + Win64-compatible PEB/TEB userspace shim + GUI startup policy + +Subsystem 3 IMAGE_SUBSYSTEM_WINDOWS_CUI + Win32 personality + same Win64-compatible PEB/TEB shape as subsystem 2 + console-oriented Win32 startup policy + +Subsystem 7 IMAGE_SUBSYSTEM_POSIX_CUI + POSIX personality marker + keep native SYS_PEB / SYS_TEB layout + no GS layout change +``` + +Subsystem 7 exists so future loader/runtime policy can identify POSIX images without inventing a new executable format. Finishing POSIX does not require converting existing POSIX runtime state away from the native TEB layout. + +### Win32 process bootstrap + +For subsystems 2 and 3, the userspace process loader loads/chooses a Win32 bootstrap shim and starts that shim before the application's original PE entry point. + +The initial thread may begin with the normal native bootstrap environment so generic nt-lite process startup remains unchanged. The Win32 bootstrap then: + +1. captures any native loader/process data it still needs; +2. constructs the Win64-compatible PEB and initial TEB in userspace; +3. places the Win32 PEB pointer at TEB offset `0x60`; +4. initializes only fields actually required by the compatibility layer/runtime; +5. executes `WRGSBASE` to make the Win32 TEB the application-visible GS base; +6. transfers control to the original image entry point. + +Win32 `CreateThread` similarly uses a userspace thread bootstrap: the native thread begins on its kernel-created stack/native bootstrap TEB, constructs the Win32 TEB using the known native stack bounds/thread ID, switches GS with `WRGSBASE`, then calls the Win32 start routine. + +Raw native `SysCreateThread` remains a native extension. Win32 ABI guarantees apply to threads created through the Win32 shim. + +### Generic FS/GS architectural-state requirement + +FS and GS bases are ordinary per-thread architectural state. The kernel preserves them because the scheduler preserves machine state, not because either register has a personality-specific meaning. + +Keep the kernel-owned native TEB pointer separate from the live architectural bases: + +```text +THREAD.Teb / TebSize + kernel-owned native bootstrap allocation and lifetime metadata + +thread saved machine context + FsBase + GsBase +``` + +At native user-thread creation, initialize `GsBase = THREAD.Teb`. This preserves the existing convenient native ABI: a new thread starts with GS addressing its native TEB and does not need a replacement syscall to rediscover state that was previously ambient. Initialize FS according to the native ABI's existing rule. + +After the first user instruction, there is no invariant connecting `THREAD.Teb` to the current FS or GS base. Userspace may execute `WRFSBASE` and `WRGSBASE` arbitrarily. It may point either base at a personality TEB/TLS block, ordinary process memory, zero, or any other user-selected address. The kernel must not validate or infer personality from those values. + +On kernel entry, `swapgs` places the interrupted user GS base in `IA32_KERNEL_GS_BASE` while active GS addresses trusted per-CPU kernel state. If a context switch occurs in kernel mode, save the outgoing thread's user GS base from `IA32_KERNEL_GS_BASE` into its architectural context and load the incoming thread's saved `GsBase` into `IA32_KERNEL_GS_BASE`. Save/restore FSBASE as normal thread architectural state. The eventual `swapgs` on user return restores the incoming thread's opaque user-selected GS base. + +The kernel may retain `THREAD.Teb` for native bootstrap, APC/runtime bookkeeping, and cleanup, but it must never assert that `THREAD.Teb == GsBase` after thread startup. Do not add `GetTeb`, `SetPersonalityGs`, or similar syscalls merely to replace what userspace can do directly with FSGSBASE. + +Verify/enable CR4.FSGSBASE as generic architectural substrate if it is not already enabled. Kernel code must not use user FS as kernel-private state, and no Win32 or POSIX PEB/TEB structure enters the scheduler. + +### Native helper boundary + +After a Win32 thread switches GS, native inline helpers that assume `SYS_TEB` at GS can no longer be called blindly. The Win32 bootstrap must cache/native-wrap any process-loader state it needs before switching, and Win32-facing libraries must use their Win32 TEB/PEB view. + +Low-level syscall stubs remain GS-independent. Do not add hidden GS swaps around every syscall merely to keep native helper code working inside Win32 processes. + +### Validation + +Future tests must include: + +- subsystem 1 keeps native `GS:[0]` behavior; +- subsystems 2/3 execute ordinary compiled `gs:[60h]` PEB access; +- preemption/migration after `WRGSBASE` preserves the Win32 TEB; +- Win32-created secondary threads receive independent TEBs; +- subsystem 7 selects POSIX personality policy while retaining native PEB/TEB layout; +- unknown/unsupported subsystem values fail deliberately rather than silently selecting native. + +--- + +## 41. CRT and personality DLL split + +After module identity is correct, split monolithic compatibility/runtime modules along real ownership boundaries. + +### Native/POSIX runtime + +```text +crt.dll + malloc/free/realloc + memory/string CRT + stdio + environment + locale + C time bindings + process startup/termination + exit/atexit + setjmp family as appropriate + +posix.dll + POSIX FD APIs + signals + credentials/process groups + termios + POSIX filesystem facade + POSIX extensions +``` + +### Win32-facing split + +Move toward real module names/ownership: + +```text +kernel32.dll +user32.dll +gdi32.dll +ole32.dll +winmm.dll +``` + +The PE subsystem shim chooses personality startup; it does not replace module identity. Aliases belong in loader/module-name policy, not in PE import parsing. + +--- + +# Part VIII — minimal WinDbg KD support + +## 42. Basic serial KD target + +Add a deliberately small WinDbg-compatible KD target after POSIX work. The goal is source/symbol debugging, breakpoints, registers, memory inspection, and panic entry—not Windows kernel emulation. + +### Scope + +Initial supported debugger behavior should be limited to the useful core: + +```text +connect / reset / reconnect +host break-in +exception stop notification +get/set AMD64 register context +read/write virtual memory +software breakpoint insert/remove +continue +single-step +kernel debug print forwarding where practical +minimal kernel/module identity sufficient for symbols +``` + +Physical-memory, MSR, I/O-port manipulation, crash-dump writing, KDNET, and rich debugger extension commands are not initial requirements. + +Do not fabricate `EPROCESS`, `ETHREAD`, NT pool headers, Windows IRPs, or other Windows internals merely so Windows-specific `!process`/`!thread` extensions appear to work. A future nt-lite WinDbg extension may inspect native structures explicitly. + +### Serial transport + +Start with a polling 16550 transport, normally COM1 in QEMU: + +```text +KdSerialPutByte +KdSerialGetByte +KdSerialPollBreakIn +``` + +The debugger transport must not depend on the scheduler, normal serial input driver, interrupt-driven I/O, heap allocation, or ordinary locks. + +COM1 is already used by HAL serial logging and the serial input driver. KD mode therefore claims the selected UART exclusively at boot. When KD owns COM1: + +- HAL must not register the ordinary COM1 text-output endpoint on that UART; +- the normal serial input driver must not bind it; +- pre-attach diagnostics continue through other output paths; +- once attached, debugger-oriented output may use KD DEBUG_IO rather than intermixing raw text with KD packets. + +Use a boot/debug configuration switch to select KD serial ownership. Do not make debugger packet auto-detection race normal console traffic. + +### KD protocol core + +Implement the classic KD packet framing/acknowledgement/reset subset required by current WinDbg serial sessions. Keep protocol definitions isolated under a KD compatibility layer. + +The target translates between WinDbg AMD64 context structures and nt-lite trap/thread context structures. nt-lite internal structures remain canonical. + +Debugger memory accesses must be fault-contained: an invalid WinDbg address returns a failed manipulate request rather than recursively panicking the target. + +### Exception and panic integration + +`#BP`, `#DB`, explicit debugger break, and fatal exception/panic paths may enter KD when it is enabled and attached. + +The normal panic path remains available when KD is disabled or cannot establish a session. KD failure must not make panic diagnostics disappear. + +Host break-in may initially be polled from safe recurring kernel points/idle. Do not require the ordinary serial IRQ driver for Ctrl+Break. + +### SMP rendezvous + +Single-vCPU QEMU is acceptable for first protocol bring-up, but SMP stop-the-world is required before KD is considered complete. + +When one CPU becomes debugger owner: + +```text +owner CPU enters KD + -> send debugger-stop IPI to other online CPUs + -> each CPU saves minimal architectural context and enters a lockless hold loop + -> owner speaks KD +continue + -> publish release generation + -> held CPUs resume +``` + +The rendezvous must use debugger-owned atomics/IPIs only. It must not wait on scheduler mutexes or ordinary kernel objects because KD is expected to work when those systems are damaged. + +### Symbols and module identity + +Expose the smallest synthetic KD version/module view WinDbg needs to associate the running kernel and later drivers with their PE/PDB images. + +The compatibility view is debugger metadata only. It does not make nt-lite module/process/thread objects adopt Windows layouts. + +### KD implementation stages + +**K0 — UART ownership and packet loop** + +- boot-time KD serial option; +- exclusive COM1 claim; +- polling RX/TX with bounded hardware waits; +- packet framing, checksum, ACK/resend/reset; +- deterministic test harness against captured/constructed KD packets. + +**K1 — single-CPU debugger stop** + +- `int3`/debug exception entry; +- AMD64 context translation; +- read/write virtual memory; +- breakpoint add/remove; +- continue and trap-flag single-step; +- WinDbg connection on one-vCPU QEMU. + +**K2 — panic and host break-in** + +- panic/fatal-exception entry when attached; +- safe host break-in polling; +- invalid debugger memory access recovery; +- debugger detach/reconnect behavior. + +**K3 — SMP freeze/release** + +- debugger owner arbitration; +- stop IPI and per-CPU context capture; +- release generation; +- breakpoint/panic testing under SMP load. + +**K4 — symbols/module polish** + +- minimal KD version block; +- kernel module record and driver module notifications as needed; +- PDB packaging/path documentation; +- optional nt-lite-specific WinDbg extension later, outside the initial KD acceptance gate. + +KD is complete for this roadmap when WinDbg can attach over serial to SMP QEMU, break in, stop all CPUs, inspect/modify registers and virtual memory, set/clear a kernel software breakpoint, single-step, continue, and enter on panic without depending on the ordinary serial driver. + +--- + +# Part IX — USER/GDI and display architecture + +## 43. Native desktop stays below Win32 USER/GDI + +DWM is the native session desktop/window manager, not a Win32 server. + +Future USER/GDI libraries translate Win32 ABI semantics onto native DWM/RSSRV/surface facilities. + +Initial useful USER/GDI surface may include: + +```text +CreateWindow / DestroyWindow / ShowWindow / SetWindowPos +BeginPaint / EndPaint +GetMessage / PeekMessage / DispatchMessage / PostMessage +CreateCompatibleDC +CreateCompatibleBitmap +SelectObject +DeleteObject +BitBlt +PatBlt +FillRect +TextOut +``` + +A GDI bitmap may use a kernel surface as backing, but kernel surfaces remain generic pixel storage rather than becoming HBITMAP/HDC/window objects. + +Only introduce `winsrv.exe` if genuinely shared Win32-only server state appears. + +### X11 compatibility service + +Add X11 as a userspace compatibility service over the native desktop rather than teaching DWM or the kernel X semantics. `x11srv.exe` is a sibling of `posixsrv.exe`: `posixsrv` remains the POSIX process/filesystem/signal authority, while `x11srv` owns X protocol state such as XIDs, atoms, properties, selections, grabs, pixmaps, colormaps, and X event routing. + +Target layering: + +```text +X applications + | + libX11 + | + x11srv.exe + | + native DWM/RSSRV/surface protocols + | + kernel surfaces/display/input mechanisms +``` + +Do not require a socket merely to imitate Unix when both endpoints are local. An initial nt-lite `libX11` transport may use native Ports; add TCP/network-transparent X later only as an explicit transport. + +Support two explicit presentation modes rather than pretending they have identical window-manager semantics: + +**Rootless mode** + +- map each X top-level window to a native DWM top-level window; +- keep X child-window hierarchy and X-only resources inside `x11srv`; +- DWM remains authoritative for physical placement, activation, z-order, capture, and outer decorations; +- translate X configure/map/focus requests into native DWM requests and report resulting geometry/state back through X events; +- do not claim that a traditional X window manager is authoritative in this mode; X WM behavior may be constrained or redundant because the native DWM owns the real desktop. + +**Nested mode** + +- expose one native DWM window as the X root desktop; +- keep all X windows, stacking, focus, decorations, and workspace policy inside that root; +- allow traditional X window managers such as `twm` to operate normally within the nested X desktop; +- treat the entire nested X desktop as one ordinary native DWM client from the host desktop's perspective. + +Input remains native-session controlled: RSSRV/DWM choose the native focused/captured target, then `x11srv` translates delivered input into X events and implements X event masks/grabs within the authority granted by the native desktop. X grabs must not become unrestricted native input capture. + +Start rendering with userspace software X rasterization into kernel surfaces. CPU composition remains the correctness baseline; acceleration may replace selected rendering mechanisms later without moving X protocol policy into the kernel or DWM. + +`x11srv` holds whatever native desktop capabilities are required to represent X clients. Ordinary X clients do not receive DWM, physical scanout, input-injection, or display-manager authority merely because they can connect to the X server. + +--- + +## 43A. Native 2D graphics and text layers + +Applications may render directly into mapped surfaces, but ordinary applications should not have to hand-roll clipping, fills, blits, scaling, composition, or glyph-mask rendering. Add a userspace native 2D library without adopting GDI's kernel object/DC model. + +The native 2D context is userspace state only: target surface, clip, transform, and blend mode. Initial operations should cover clear/fill, copy/blit, stretch, alpha composition, clipping, simple geometry, and glyph-mask/glyph-run composition. Direct pixel access remains first-class for games, emulators, X11, Mesa/software renderers, and custom engines. + +Text is a separate layer: UTF-8 decoding, font discovery/loading, metrics, shaping/layout, glyph rasterization, and glyph cache produce glyph runs/masks that the 2D layer composites. Do not make a byte-oriented `TextOutA`-style contract the native text architecture. + +CPU software rendering is the correctness baseline. Initial canonical surface formats should remain intentionally small (for example premultiplied BGRA8888, opaque BGRX8888, and A8 masks) until real requirements justify more. + +Future `gdi32.dll`, the native UI toolkit, DWM decorations, and `x11srv` may all consume these primitives, but none of them owns the underlying surface ABI. + +## 44. Display miniport/raster split + +Refactor the display-driver boundary so hardware miniports do not own generic raster policy. + +### Hardware/video miniport + +Owns: + +- adapter lifecycle; +- mode enumeration; +- mode set; +- framebuffer/VRAM mappings; +- scanout/present; +- vblank/interrupt/fence mechanism; +- hardware cursor; +- optional low-level acceleration/DMA primitives. + +### Generic raster/display layer + +Owns: + +- software copy/blit/fill/composite/stretch correctness paths; +- clipping and format conversion; +- generic raster/present helpers; +- capability selection and substitution of optional hardware acceleration. + +### Optional display/render engine + +Use an NT-3.x-inspired ownership split without importing GDI/DDI semantics. A hardware miniport owns adapter mechanisms; an optional display/render engine exposes acceleration capabilities such as copy, fill, blit, composition, synchronization/fences, and command submission. The generic layer chooses accelerated operations only when advertised and always retains the software fallback. + +Do not expose one syscall per drawing primitive. When real hardware acceleration requires kernel submission, prefer validated batched command buffers referencing explicit surface/context capabilities. Applications do not receive unrestricted GPU MMIO, DMA, VRAM, or physical scanout authority. + +Long-term GPU objects may include device/context, memory, GPU virtual-address space, command submission, and fence/synchronization capabilities. Display (modes, scanout, vblank, cursor) and render (memory, contexts, execution) are separate interfaces even when one driver module implements both. + +Target layering: + +```text +DWM / native gfx / future GDI / x11srv / Mesa + | + generic graphics/display class + / \ + software raster acceleration dispatch + | + display/render engine + | + video miniport + | + hardware +``` + +The compositor/window manager remains userspace and owns composition policy, not hardware mechanism. + +### Experimental modern-GPU ports + +Keep modern-GPU ports as consumers of the native GPU ABI rather than design authorities. A plausible NVIDIA experiment is to port the open NVIDIA kernel-module OS-abstraction boundary onto nt-lite and use GSP where supported, rather than emulate a Linux kernel. For native graphics userspace, prefer a Mesa/NVK path with an nt-lite `nvkmd`-style backend over making Linux DRM the native ABI. Proprietary NVIDIA userspace, CUDA, or media stacks may later use a deliberately boxed Linux/NVIDIA compatibility personality; that compatibility ABI must not leak into native nt-lite graphics interfaces. + +--- + +# Part X — remaining driver cleanup + +## 45. Driver source/module decomposition + +Split only where ownership/lifecycle boundaries are real. + +Priority candidates: + +1. USB into `xhci.sys`, `usb.sys`, `usbhid.sys`, `usbstor.sys` as specified above. +2. xHCI source further into controller/ring/transfer/interrupt pieces if the post-split HCD remains monolithic. +3. NTFS where ntfs-3g integration and nt-lite glue are unnecessarily entangled. +4. VMware SVGA into hardware miniport versus generic display/raster responsibilities. +5. Other very large driver translation units discovered during work. + +Do not split files merely to increase file count. + +--- + +## 46. Input cleanup + +Finish moving repeat/typematic policy into the logical input owner so physical keyboard drivers deliver physical state and RSSRV owns logical repeat behavior consistently. + +Add personality wrappers for thread naming when useful: + +```text +kernel32 SetThreadDescription/GetThreadDescription +pthread_setname_np/pthread_getname_np +``` + +These are small cleanup items and should not block the larger lifecycle work. + +--- + +# Part X-A — native networking and compatibility-driver experiments + +## Native network-device model remains canonical + +The native network stack talks to a neutral `NET_DEVICE`/packet abstraction. Compatibility drivers adapt to it; they do not redefine native packet, DMA, PnP, or network policy. Networking eventually moves protocol processing such as lwIP out of the kernel where practical, using kernel-managed capability/waitable endpoints and efficient shared data transfer analogous to pipes. `SysResolveHost` remains an acknowledged temporary shortcut until that redesign is implemented. + +## NDIS subset + +A useful NDIS subset is reasonable as a compatibility layer. Target NDIS 6 Ethernet **miniport** behavior first; do not initially implement Windows protocol drivers or filter drivers. Translate miniport lifecycle, NBL/NB/MDL packet views, basic OIDs, interrupts, DMA, PCI/MMIO, timers/work, and status indication onto native nt-lite mechanisms. Start with copying packet translation; optimize later. + +Binary `.sys` compatibility additionally requires a boxed Win32 kernel-driver environment (`ntoskrnl`/HAL/NDIS imports, PE driver loading, observable IRQL constraints, configuration/registry projection, and possibly framework dependencies). Keep that namespace under a compatibility subsystem rather than exporting fake Windows APIs as native kernel interfaces. + +Do not begin with Wi-Fi, protocol/filter drivers, SR-IOV, advanced offloads, or KMDF. A first success criterion is an unmodified direct NDIS 6 Ethernet miniport reaching DHCP/ping through the native stack. + +## Native Wi-Fi ports and reverse engineering + +Full binary compatibility for KMDF + NetAdapterCx + WiFiCx is a distant research target, not a prerequisite. A much more reasonable path for unsupported vendor hardware is machine-assisted reverse engineering followed by a clean native port: + +```text +vendor binary + metadata + -> hardware/firmware protocol specification + -> native nt-lite driver +``` + +Recover firmware loading/boot, register programming, DMA/ring descriptors, interrupts, firmware command/event formats, TX/RX queue mechanics, scan/connect/key operations, and reset/recovery. Do not mechanically transliterate decompiler output or preserve Windows framework object lifetimes. + +Keep Wi-Fi policy primarily in a future userspace `wifisrv`: discovery, saved networks, credentials, association/roaming policy, and UI-facing state. The kernel driver owns time-critical hardware/firmware mechanisms, TX/RX, primitive scan/radio/key operations, and events. The exact split may tighten where firmware timing requires it. + +--- + +# Part XI — implementation sequence + +## 47. Recommended execution order + +P0-P7 are complete; P7 is runtime-validated by the full suite. The active work is now the generic substrate that must exist before POSIX P8 can be correct. Do not land synthetic P8 architecture merely to keep the old stage numbering moving. + +### Active milestone — native ABI cleanup + +Execute UABI.1-UABI.4 as independent reviewable changes. UABI.1 is a whole-tree semantic type sweep, not merely public-syscall cosmetics; real MSVC build/runtime validation remains the gate. UABI.2 makes UTF-8 the explicit native text contract. UABI.3 removes the fixed-size process-builder storage. UABI.4 deletes stale duplicated namespaces/input paths after callers are migrated. + +### Active milestone after UABI — KFS then SEC + +Execute KFS.1-KFS.7 before filesystem ACL integration. Then execute the SEC sequence so file security attaches to coherent backing identity. Revisit rights taxonomy before freezing hard-link/open/security syscalls. + +### Resume POSIX after native prerequisites + +```text +P8 revised filesystem projection + FIFOs +P9 tty/termios +P10 leaf APIs/profile closure +``` + +Do not start the future Win32 GS shim, KD, NDIS compatibility, or experimental GPU ports during this sequence unless the active native milestone independently needs the same generic primitive. + +### Parked milestone A — `drivers.ini` v2 parser and graph manager + +- define public/internal config structures; +- extend `BOOT_INFO` with raw `drivers.ini` and explicit boot-driver identities; +- replace ordered-list UEFI parsing with minimal descriptor preload scanning; +- implement strict kernel parser; +- implement class and driver graph validation; +- implement state machines and deterministic scheduler; +- initialize current drivers through the graph; +- remove ordered `[boot]`/`[runtime]` initialization paths. + +### Parked milestone B — graceful driver/device shutdown + +- define Prepare/Commit shutdown interfaces; +- add module/device lifetime accounting required by shutdown; +- implement reverse effective-graph shutdown; +- implement runtime device-topology shutdown traversal; +- flush/freeze filesystems and caches; +- stop storage/network/audio/display/USB hardware and DMA; +- add bounded timeouts and shutdown summary diagnostics; +- call HAL S5 only after lifecycle shutdown completes. + +### Parked milestone C — USB split + +- extract xHCI HCD; +- establish neutral HCD device/interface contract; +- extract generic USB core; +- extract USB HID; +- extract USB storage; +- place all four into lifecycle classes; +- verify QEMU USB input/storage and real-hardware USB paths; +- verify shutdown ordering through `usb-storage -> usb -> usb-hcd`. + +### Parked milestone D — unload/reload substrate + +- module import/reference tracking; +- PnP/device ownership accounting by module; +- real `DriverUnload` callers; +- manual unload/reload; +- optional cascade reload; +- only then add `start=demand`. + +### Parked milestone E — finish native desktop migration + +- render RSSRV console through a surface/window; +- make desktop focus the normal console/GUI transition; +- delete stale direct application input/console/display paths; +- improve damage regions; +- add title/close/move/resize/decorations; +- define DWM restart/loss behavior. + +### Future milestone F — native status remainder, loader identity, and personality selection + +- complete non-POSIX KSTATUS/Win32 mapping cleanup; +- resolved-file module identity; +- import/reference tracking; +- PE subsystem personality selection; +- generic mutable user-GS persistence; +- userspace Win32 PEB/TEB bootstrap for subsystems 2/3; +- subsystem 7 POSIX marker with native layout; +- `crt.dll`/`posix.dll` and Win32-facing DLL ownership split. + +### Future milestone G — minimal serial KD + +- K0-K4 from Section 42; +- keep transport independent of ordinary COM1 logging/input; +- stop at basic WinDbg source/symbol debugging before considering KDNET or Windows-specific extension compatibility. + +### Future milestone H — richer USER/GDI + +- build USER/GDI over native DWM/RSSRV protocols; +- retain kernel surface distinction; +- optimize only after functionality/profiling establishes hot paths. + +### Future milestone I — X11 compatibility service + +- add `x11srv.exe` as a sibling userspace service rather than putting X protocol state in `posixsrv` or DWM; +- implement local `libX11` transport over native IPC first; +- implement nested mode with one native DWM window as the X root and full traditional X window-manager authority inside it; +- implement rootless mode mapping X top-levels to native DWM windows while retaining native DWM authority over physical desktop policy; +- keep X child windows/resources, selections, properties, grabs, pixmaps, and event routing inside `x11srv`; +- translate native input/capture into X semantics without giving X clients ambient native input authority; +- render through native surfaces with software rasterization as the initial correctness path; +- add network-transparent/TCP X only after the local server and security model are stable. + +### Future milestone J — display stack split and remaining driver cleanup + +- hardware miniport versus generic raster/display layer; +- VMware SVGA cleanup; +- NTFS/source decomposition where justified; +- remaining large-driver ownership cleanup. + +### Future milestone K — native 2D/text stack and accelerated graphics + +- userspace native 2D raster library with direct-surface escape hatch; +- native UTF-8 text/font/shaping/glyph stack; +- shared theme schema for DWM chrome and native UI toolkit; +- optional display/render acceleration interface with software fallback; +- batched validated command submission only when real accelerated hardware needs it; +- later Mesa/native Vulkan WSI work over the native GPU ABI. + +### Future milestone L — network-driver compatibility experiments + +- narrow NDIS 6 Ethernet miniport compatibility layer over native `NET_DEVICE`; +- prove one unmodified miniport through DHCP/ping before expanding; +- keep KMDF/NetAdapterCx/WiFiCx binary compatibility explicitly experimental; +- prefer clean native Wi-Fi ports derived from documented/reverse-engineered hardware/firmware protocols; +- introduce `wifisrv` when native Wi-Fi policy work begins. + +### Experimental milestone M — modern NVIDIA path + +- port the open NVIDIA kernel-module OS abstraction only if it fits the native GPU interfaces; +- bring up PCI/BAR/GSP/interrupt/modeset before render acceleration; +- prototype Mesa/NVK with an nt-lite kernel backend before attempting proprietary Linux graphics userspace; +- keep any Linux/NVIDIA proprietary userspace compatibility environment isolated from the native GPU ABI. + +--- + +# Part XII — validation matrix + +## 48. Driver-manager tests + +Add deterministic unit/kernel tests for parsing, graph validation, state semantics, and boot handoff: + +- valid empty dependency lists; +- duplicate section/key rejection; +- unknown key rejection; +- malformed IDs; +- class-after-driver rejection; +- missing key rejection; +- class/driver/combined cycles; +- missing or disabled providers; +- boot dependency available only at runtime; +- provider alternatives with mixed success/failure; +- `requires` versus `after` settlement behavior; +- critical blocked-driver startup failure; +- configured boot blob identity matching and raw-config handoff. + +--- + +## 49. Shutdown tests + +Create synthetic drivers for successful prepare/commit, prepare failure/timeout, commit failure, draining workers, outstanding IRPs, and DMA/interrupt state. + +Verify reverse graph order, storage flush-before-controller shutdown, USB-storage/HID/core before HCD, network stack before adapters, and eventual HAL shutdown after bounded noncritical failures. + +--- + +## 50. Reload tests + +Before enabling reload generally, test: + +- unload with no devices; +- unload with attached devices rejected or properly removed; +- unload with outstanding IRP; +- unload with active import dependent rejected; +- reload success and DriverEntry failure; +- dependent closure calculation/cascade order; +- class availability changes and rebinding. + +--- + +## Current runtime regression investigation — DOOM first-frame stall + +A current DOOM run reaches `doom: entering game loop` and the asynchronous SoundFont loader reaches `doom: MIDI synthesizer ready`, but no frame becomes visible afterward. The first game-loop path contains three blocking boundaries worth isolating before changing architecture: display-clock pacing, the first RSSRV logical-input query, and acquisition of the audio/core mutex. Use one-shot first-frame diagnostics and a bounded display-clock wait to identify the exact boundary. Do not attribute the stall to MIDI merely because the last log line comes from the asynchronous synth-loader thread. + +## 51. Desktop tests + +Extend desktop tests to cover console-window visibility/focus, close, move/resize, capture, relative lock, damage clipping, overlapping composition, disconnect cleanup, and the chosen DWM crash/restart contract. + +### X11 compatibility tests + +When `x11srv` is implemented, cover: + +- nested X root creation inside one native DWM window; +- a traditional X window manager moving/decorating/stacking clients correctly in nested mode; +- rootless top-level creation, map/unmap, configure, focus, and close translation through DWM; +- X child windows remaining server-internal rather than becoming native windows; +- X grabs bounded by native DWM/RSSRV capture authority; +- selections/properties and disconnect resource cleanup; +- software raster output and damage propagation to native surfaces; +- rootless mode retaining DWM authority when an X client or X WM requests conflicting geometry/z-order; +- X clients having no physical scanout, global input-injection, or unrelated native-window authority. + +--- + +## 51A. UABI/KFS/security tests + +UABI gates include standalone compilation of every owned public header, ABI-layout assertions, compiler-warning-clean builds across changed callers, explicit checks that compatibility/vendor ABIs were not mechanically rewritten, and the full runtime suite after each ABI break. UTF-8 tests cover malformed sequences, boundary validation, embedded-NUL policy, and byte-exact namespace behavior. + +KFS tests cover independent-open/cache/mmap coherence, unlink with live opens, real hard-link counts/data identity, rename replacement/cross-device errors, timestamp mutation, and directory reset. Security tests cover order-independent deny-wins ACL evaluation, owner security-mutation authority without implicit data authority, creation-time inheritance, existing-capability survival across ACL changes, explicit revocation, principal/group materialization, provider translation, restrictions/privileges, and audit records. + +## 51B. Graphics/network compatibility tests + +Graphics tests retain CPU software rendering as the reference result and compare accelerated fill/copy/composite paths against it. Validate that ordinary applications cannot acquire physical scanout/MMIO/DMA authority, and that DWM policy remains unchanged when the backend changes. + +NDIS experiments must prove lifecycle, PCI/MMIO, interrupt, DMA, RX/TX, basic OIDs, link state, clean halt/unload, and DHCP/ping through the native stack before adding wider compatibility. Native Wi-Fi ports stage firmware boot, event/command transport, scan, RX/TX, association, key installation, DHCP/ping, reset, and suspend/resume. + +## 52. POSIX tests + +The POSIX suite is stage-gated, not one final smoke test. + +### Process identity and construction + +- stable POSIX PID independent of native PID; +- `getppid` launch association; +- spawn environment and file actions; +- inherited descriptor/open-description sharing; +- `wait`/`waitpid`/`WNOHANG` and child collection; +- failed spawn cleanup; +- successful exec retains POSIX PID while changing native generation; +- failed exec leaves the old image usable; +- repeated exec; +- `FD_CLOEXEC` filtering; +- `vfork()+execve` and `vfork()+_exit`; +- explicit `fork()` unsupported behavior if exported. + +### Signals + +- per-thread masks and pending sets; +- process-directed target selection; +- process-group routing; +- handler/default/ignore transitions; +- signal interruption of waits/I/O; +- exec disposition/mask behavior; +- exception translation. + +### Filesystem facade + +- POSIX owner/group/mode projection over native principals/ACLs; +- umask creation-time restriction; +- native hard-link aliases and link counts; +- open-after-unlink and rename-replacement behavior; +- timestamp mutation and directory reset; +- pathconf/fpathconf; +- FIFO open/rendezvous, nonblocking, EOF, broken-pipe, cleanup. + +### Tty/termios + +- canonical/raw behavior; +- VMIN/VTIME; +- echo/editing/output postprocessing; +- foreground process groups; +- VINTR/VQUIT signal routing. + +### End-to-end integration + +Run a shell-like scenario: + +```text +parent POSIX process + -> pipe + -> spawn producer with envp + dup2 file action + -> spawn consumer in a process group + -> signal the group + -> waitpid children + -> exec replacement retaining parent POSIX PID +``` + +The full native `all_tests` suite must still pass after every POSIX stage. + +--- + +## 53. PE subsystem/personality tests + +Future personality tests must verify subsystem 1 native layout, subsystem 2/3 Win32 `gs:[60h]`, GS persistence across preemption/migration, independent secondary-thread TEBs, subsystem 7 native-layout POSIX selection, and deliberate rejection of unsupported subsystem values. + +--- + +## 54. KD tests + +Future minimal KD validation must cover: + +- packet checksum/ACK/resend/reset unit tests; +- COM1 exclusive ownership in KD mode; +- WinDbg serial attach on one-vCPU QEMU; +- register read/write; +- valid and invalid virtual-memory access; +- software breakpoint add/remove; +- single-step/continue; +- host break-in; +- panic entry; +- reconnect after debugger reset where supported; +- SMP stop/release with workload on every CPU; +- no dependency on the ordinary serial input/logging driver while stopped. + +--- + +# Part XIII — architectural acceptance criteria + +## 55. Driver subsystem + +The driver lifecycle overhaul is complete when file order cannot affect startup semantics, the bootloader only preloads boot images/raw config, the kernel owns validation/scheduling, startup reverses into graceful shutdown, shutdown waits are bounded, and unload/reload/demand loading use the same graph/lifetime model. + +## 56. Desktop/session subsystem + +The desktop architecture reaches its next stable point when console is rendered through DWM as native desktop content, RSSRV remains console/input/session authority, DWM remains graphical policy, stale ambient application paths are gone, and DWM crash/restart behavior is explicit. + +## 57. POSIX subsystem + +The POSIX personality is complete for this roadmap when: + +- `posixsrv` owns stable POSIX PIDs and all genuinely shared POSIX policy; +- native PID/process generations are replaceable implementation details; +- `fork()` is deliberately unsupported and no COW/process-clone substrate exists for it; +- `vfork()` and `execve()` use the server-owned model in Section 31; +- `posix_spawn`, wait, environment, and descriptor file actions are complete; +- inherited open descriptions remain coherent across processes; +- signals are asynchronous, per-thread-mask aware, and process-group routable without kernel POSIX semantics; +- tty, identity, and process groups/sessions have one coherent POSIX owner while filesystem identity/links/security remain native KFS/SEC authority; +- POSIX filesystem permission/link/time APIs project native semantics and FIFOs remain coherently owned by `posixsrv`; +- the POSIX profile manifest accurately describes every exposed API; +- full native and POSIX runtime suites pass. + +## 58. Runtime/personality subsystem + +The future loader/runtime cleanup is complete when: + +- module identity is backing-file/object based; +- imports create real provider references; +- PE subsystem selects native, Win32 GUI/CUI, or POSIX personality intentionally; +- subsystems 2/3 receive userspace Win64 PEB/TEB state with persistent user GS; +- subsystem 7 remains native-layout POSIX unless deliberately redesigned; +- aliases resolve to one module instance; +- kernel module unloading can prove no import/lifetime references remain; +- `crt.dll`, `posix.dll`, and Win32-facing DLLs have distinct ownership boundaries. + +## 59. Minimal KD subsystem + +Basic KD support is complete when WinDbg can attach to SMP QEMU over an exclusively-owned polling serial transport, break in, freeze all CPUs, inspect/modify AMD64 registers and virtual memory, set/clear a software breakpoint, single-step, continue, and enter on panic while nt-lite retains its own kernel object layouts. + + +## 60. X11 compatibility subsystem + +X11 compatibility is complete for this roadmap when `x11srv` remains a userspace translation service over the native desktop, nested mode supports a conventional X root and traditional X window manager, rootless mode integrates individual X top-levels without surrendering native DWM authority, X-specific state remains out of the kernel/DWM, native input/capture authority is preserved, and local X clients can render through native surfaces without receiving ambient desktop/display authority. + +--- + +## 61. Native filesystem/security prerequisite subsystem + +The KFS/security prerequisite reaches its stable point when independent opens and aliases share one coherent backing identity; unlink/open lifetime, real hard links, rename replacement, timestamps, and directory reset are native provider contracts; file security attaches to backing identity; ACL evaluation and principal contexts are generic kernel concepts; account secrets/names remain userspace-owned; and POSIX/NTFS/ext4 all translate into one native security flavor rather than defining competing kernel models. + +## 62. Native graphics subsystem + +The graphics architecture reaches its stable point when mapped surfaces remain the fundamental client rendering ABI, the optional native 2D/text libraries do not require kernel GDI objects, software rendering is a complete correctness path, hardware miniports expose mechanisms rather than UI policy, accelerated render/display engines are optional substitutions, DWM remains userspace policy, and future Mesa/GDI/X11 backends can target the same native primitives. + +## 63. Compatibility-driver experiments + +NDIS, Linux/NVIDIA userspace, KMDF-class frameworks, and reverse-engineered vendor drivers remain compatibility or porting projects layered over stable native DMA/PnP/network/GPU primitives. Success in one compatibility target must not make its foreign ABI the native nt-lite contract. + +# Target architecture + +```text +UEFI loader + | + | loads kernel/HAL + configured boot driver blobs + | passes raw drivers.ini without dependency policy + v +kernel + | + | generic mechanisms only: + | process/thread/VM/handles/Ports/APCs/waits + | driver/device lifecycle + | native exception/trap context + | + +------------------------- init.exe -------------------------+ + | | + | rssrv.exe + | / | \ + | / | \ + | posixsrv.exe x11srv.exe dwm.exe + | | / \ | + | stable POSIX policy libX11 native DWM -----+ + | | | | | + | posix.dll X/POSIX native gfx/text | + | | apps | | + | POSIX apps native UI future USER/GDI + | + +-- future auth/account service -> principal names/secrets/groups -> kernel SECURITY_CONTEXT creation + +-- future wifisrv -> Wi-Fi discovery/association/credential policy -> native Wi-Fi device class + | + +-- userspace PE loader + | | + | +-- subsystem 1 -> native SYS_PEB/SYS_TEB + | +-- subsystem 2 -> Win32 GUI shim -> Win64 PEB/TEB via WRGSBASE + | +-- subsystem 3 -> Win32 CUI shim -> Win64 PEB/TEB via WRGSBASE + | +-- subsystem 7 -> POSIX marker, native PEB/TEB layout + | + +-- optional KD core + | + +-- exclusive polling 16550 serial transport + +-- WinDbg compatibility packets/context view + +-- SMP debugger stop/release +``` + +The important separation is: + +```text +configuration graph = module lifecycle policy +runtime device graph = actual hardware/device relationships +services graph = user-mode service lifecycle +RSSRV session state = native session authority +DWM = replaceable graphical policy +posixsrv = stable POSIX identity and shared POSIX policy +KFS backing = native file identity/cache/lifetime/security attachment point +SECURITY_CONTEXT/ACL = kernel-mediated principal authority, not account-name/password storage +DWM = native window/composition policy; native gfx/text remains optional userspace rendering support +native NET_DEVICE/GPU = canonical hardware abstractions; NDIS/NVIDIA/Linux compatibility stays outside them +PE subsystem selector = userspace personality bootstrap policy +KD compatibility view = debugger protocol metadata, not Windows kernel architecture +``` + +Each graph/state domain has one owner and one purpose. Personality implementations may request generic native mechanisms, but they do not move their policy into the kernel.