replace nob with ms' build, move disk creation to build.bat, major comment cleanup, remove old and stupid tests, overhaul interrupt management with per-CPU vector allocation, affinity migration and safe teardown, add proper IOAPIC routing and PCI MSI/MSI-X/INTx support, migrate drivers to the new interrupt API, integrate ACPICA with a proper OS services layer, move ACPI firmware events, power button handling and S5 shutdown into acpi.sys, add parallel driver startup and atomic publication, replace PnP arrays with dynamic lists, redesign service startup again to be asynchronous, add delayed startup mode for services, allow omitting keys in services.ini, fix acpi ec deferred work handling, fix kernel threads having garbage exit code.
This commit is contained in:
1 parent
d36f19509c
commit
36eef8f58d
696 files changed
+147281
-7530
No files matched your search
@@ -0,0 +1 @@
|
||||
DIRS=doom edit httpget memtop ping resolve shell_native shell_posix shell_win32 tcpserve udp video
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <ntlite/native_heap.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <intrin.h>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <ntlite/native_heap.h>
|
||||
#include <string.h>
|
||||
#include <intrin.h>
|
||||
#include <ntlite/syscall.h>
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
!include $(BASEDIR)\makefile.def
|
||||
@@ -0,0 +1,7 @@
|
||||
TARGETNAME=doom
|
||||
TARGETPATH=obj
|
||||
TARGETTYPE=PROGRAM
|
||||
FLAVOR=USER
|
||||
TARGETLIBS="$(BASEDIR)\bin\syscall.lib" "$(BASEDIR)\bin\libc.lib" "$(BASEDIR)\bin\native_runtime.lib"
|
||||
EXTRA_LFLAGS=/ENTRY:NtlNativeStart
|
||||
SOURCES=@/code/user/apps/doom/doom.c @/code/user/apps/doom/doom_audio.c
|
||||
@@ -70,7 +70,6 @@ typedef struct SCREEN_CELL {
|
||||
static SCREEN_CELL gScreenCache[MAX_SCREEN_COLS * MAX_SCREEN_ROWS];
|
||||
static bool gScreenCacheValid = false;
|
||||
|
||||
// Implements the invalidate screen cache operation.
|
||||
static void invalidate_screen_cache(void) {
|
||||
gScreenCacheValid = false;
|
||||
}
|
||||
@@ -127,7 +126,6 @@ static void draw_text(int col, int row, const char* s, uint32_t fg, uint32_t bg)
|
||||
while (*s && col < gCols) put_cell(col++, row, *s++, fg, bg);
|
||||
}
|
||||
|
||||
// Implements the msg set operation.
|
||||
static void msg_set(const char* s) {
|
||||
size_t n = strlen(s);
|
||||
if (n >= sizeof(gMessage)) n = sizeof(gMessage) - 1;
|
||||
@@ -158,7 +156,6 @@ static void append_u32(char* b, size_t cap, size_t* p, uint32_t v) {
|
||||
while (n > 0) append_char(b, cap, p, tmp[--n]);
|
||||
}
|
||||
|
||||
// Implements the key to char operation.
|
||||
static char key_to_char(INPUT_KEY key, uint32_t mods) {
|
||||
if (key >= INPUT_KEY_A && key <= INPUT_KEY_Z) {
|
||||
bool shifted = (mods & INPUT_MOD_SHIFT) != 0;
|
||||
@@ -188,7 +185,6 @@ static char key_to_char(INPUT_KEY key, uint32_t mods) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Implements the clamp cursor operation.
|
||||
static void clamp_cursor(void) {
|
||||
if (gLineCount < 1) gLineCount = 1;
|
||||
if (gCy < 0) gCy = 0;
|
||||
@@ -197,7 +193,6 @@ static void clamp_cursor(void) {
|
||||
if (gCx > gLines[gCy].len) gCx = gLines[gCy].len;
|
||||
}
|
||||
|
||||
// Implements the ensure visible operation.
|
||||
static void ensure_visible(void) {
|
||||
int textRows = gRows - 2;
|
||||
if (textRows < 1) textRows = 1;
|
||||
@@ -211,7 +206,6 @@ static void ensure_visible(void) {
|
||||
if (gLeft < 0) gLeft = 0;
|
||||
}
|
||||
|
||||
// Implements the render operation.
|
||||
static void render(void) {
|
||||
update_screen_size();
|
||||
|
||||
@@ -275,7 +269,6 @@ static void insert_char_at_cursor(char c) {
|
||||
gDirty = true;
|
||||
}
|
||||
|
||||
// Implements the newline at cursor operation.
|
||||
static void newline_at_cursor(void) {
|
||||
if (gLineCount >= MAX_LINES) { msg_set("too many lines"); return; }
|
||||
|
||||
@@ -297,7 +290,6 @@ static void newline_at_cursor(void) {
|
||||
gDirty = true;
|
||||
}
|
||||
|
||||
// Implements the backspace at cursor operation.
|
||||
static void backspace_at_cursor(void) {
|
||||
if (gCx > 0) {
|
||||
LINE* ln = &gLines[gCy];
|
||||
@@ -401,7 +393,6 @@ static void save_file(void) {
|
||||
msg_set("saved");
|
||||
}
|
||||
|
||||
// Implements the reset buffer operation.
|
||||
static void reset_buffer(void) {
|
||||
gLineCount = 1;
|
||||
gLines[0].len = 0;
|
||||
@@ -450,13 +441,11 @@ static void load_file(void) {
|
||||
msg_set(truncated ? "loaded, truncated long file" : "loaded");
|
||||
}
|
||||
|
||||
// Implements the skip spaces operation.
|
||||
static const char* skip_spaces(const char* s) {
|
||||
while (s && (*s == ' ' || *s == '\t')) s++;
|
||||
return s;
|
||||
}
|
||||
|
||||
// Implements the skip token operation.
|
||||
static const char* skip_token(const char* s) {
|
||||
s = skip_spaces(s);
|
||||
if (!s || !*s) return s;
|
||||
@@ -500,7 +489,6 @@ static void parse_filename_from_cmdline(void) {
|
||||
strcpy(gFilename, "untitled.txt");
|
||||
}
|
||||
|
||||
// Implements the editor loop operation.
|
||||
static void editor_loop(void) {
|
||||
while (true) {
|
||||
render();
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
!include $(BASEDIR)\makefile.def
|
||||
@@ -0,0 +1,7 @@
|
||||
TARGETNAME=edit
|
||||
TARGETPATH=obj
|
||||
TARGETTYPE=PROGRAM
|
||||
FLAVOR=USER
|
||||
TARGETLIBS="$(BASEDIR)\bin\syscall.lib" "$(BASEDIR)\bin\libc.lib" "$(BASEDIR)\bin\native_runtime.lib"
|
||||
EXTRA_LFLAGS=/ENTRY:NtlNativeStart
|
||||
SOURCES=@/code/user/apps/edit/edit.c
|
||||
@@ -58,7 +58,6 @@ static void append_u8(char* dst, size_t cap, size_t* off, uint8_t v) {
|
||||
}
|
||||
}
|
||||
|
||||
// Implements the format ipv4 operation.
|
||||
static void format_ipv4(char* dst, size_t cap, const uint8_t ip[4]) {
|
||||
size_t off = 0;
|
||||
if (!dst || cap == 0) return;
|
||||
@@ -81,7 +80,6 @@ static void append_hex16(char* dst, size_t cap, size_t* off, uint16_t value) {
|
||||
}
|
||||
}
|
||||
|
||||
// Implements the format ipv6 operation.
|
||||
static void format_ipv6(char* dst, size_t cap, const uint8_t ip[16]) {
|
||||
size_t off = 0;
|
||||
if (!dst || cap == 0) return;
|
||||
@@ -93,7 +91,6 @@ static void format_ipv6(char* dst, size_t cap, const uint8_t ip[16]) {
|
||||
}
|
||||
}
|
||||
|
||||
// Implements the format socket address operation.
|
||||
static void format_socket_address(char* dst, size_t cap, const SYS_SOCKET_ADDRESS* address) {
|
||||
if (!address || address->Family == SYS_AF_INET)
|
||||
format_ipv4(dst, cap, address ? address->Address : (const uint8_t*)"\0\0\0\0");
|
||||
@@ -131,7 +128,6 @@ static bool parse_ipv4(const char* s, uint8_t ip[4]) {
|
||||
return *s == 0;
|
||||
}
|
||||
|
||||
// Implements the next token operation.
|
||||
static const char* next_token(const char* s, char* out, size_t outSize) {
|
||||
if (!s || !out || outSize == 0) return NULL;
|
||||
while (*s == ' ' || *s == '\t') s++;
|
||||
@@ -146,13 +142,11 @@ static const char* next_token(const char* s, char* out, size_t outSize) {
|
||||
return s;
|
||||
}
|
||||
|
||||
// Implements the usage operation.
|
||||
static void usage(void) {
|
||||
print("usage: httpget.exe [host-or-ipv4] [path]\n");
|
||||
print("default: httpget.exe 10.0.2.2 /\n");
|
||||
}
|
||||
|
||||
// Implements the resolve host operation.
|
||||
static KSTATUS resolve_host(const char* host, SYS_SOCKET_ADDRESS* address) {
|
||||
uint8_t ip[4];
|
||||
if (parse_ipv4(host, ip)) {
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
!include $(BASEDIR)\makefile.def
|
||||
@@ -0,0 +1,7 @@
|
||||
TARGETNAME=httpget
|
||||
TARGETPATH=obj
|
||||
TARGETTYPE=PROGRAM
|
||||
FLAVOR=USER
|
||||
TARGETLIBS="$(BASEDIR)\bin\syscall.lib" "$(BASEDIR)\bin\libc.lib" "$(BASEDIR)\bin\native_runtime.lib"
|
||||
EXTRA_LFLAGS=/ENTRY:NtlNativeStart
|
||||
SOURCES=@/code/user/apps/httpget/httpget.c
|
||||
@@ -0,0 +1 @@
|
||||
!include $(BASEDIR)\makefile.def
|
||||
@@ -0,0 +1,7 @@
|
||||
TARGETNAME=memtop
|
||||
TARGETPATH=obj
|
||||
TARGETTYPE=PROGRAM
|
||||
FLAVOR=USER
|
||||
TARGETLIBS="$(BASEDIR)\bin\syscall.lib" "$(BASEDIR)\bin\libc.lib" "$(BASEDIR)\bin\native_runtime.lib"
|
||||
EXTRA_LFLAGS=/ENTRY:NtlNativeStart
|
||||
SOURCES=@/code/user/apps/memtop/memtop.c
|
||||
@@ -0,0 +1 @@
|
||||
!include $(BASEDIR)\makefile.def
|
||||
@@ -53,7 +53,6 @@ static void print_rtt_us(uint64_t us) {
|
||||
print_u64(frac);
|
||||
}
|
||||
|
||||
// Implements the format ipv4 operation.
|
||||
static void format_ipv4(char* out, size_t outSize, const uint8_t ip[4]) {
|
||||
if (!out || outSize == 0) return;
|
||||
size_t off = 0;
|
||||
@@ -70,7 +69,6 @@ static void format_ipv4(char* out, size_t outSize, const uint8_t ip[4]) {
|
||||
out[off < outSize ? off : outSize - 1] = 0;
|
||||
}
|
||||
|
||||
// Implements the next token operation.
|
||||
static const char* next_token(const char* s, char* out, size_t outSize) {
|
||||
if (!s || !out || outSize == 0) return NULL;
|
||||
while (*s == ' ' || *s == '\t') s++;
|
||||
@@ -130,7 +128,6 @@ static bool parse_u32(const char* s, uint32_t* out) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Implements the resolve host operation.
|
||||
static KSTATUS resolve_host(const char* host, uint8_t ip[4]) {
|
||||
if (parse_ipv4(host, ip))
|
||||
return NTLSTATUS_SUCCESS;
|
||||
@@ -159,7 +156,6 @@ static KSTATUS resolve_host(const char* host, uint8_t ip[4]) {
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Implements the icmp checksum operation.
|
||||
static uint16_t icmp_checksum(const uint8_t* data, size_t length) {
|
||||
uint32_t sum = 0;
|
||||
while (length >= 2) {
|
||||
@@ -223,7 +219,6 @@ static bool parse_echo_reply(const uint8_t* buf, size_t len, uint16_t ident, uin
|
||||
return false;
|
||||
}
|
||||
|
||||
// Implements the usage operation.
|
||||
static void usage(void) {
|
||||
print("usage: apps/ping.exe [host-or-ipv4] [count]\n");
|
||||
print("default: apps/ping.exe 10.0.2.2 4\n");
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
TARGETNAME=ping
|
||||
TARGETPATH=obj
|
||||
TARGETTYPE=PROGRAM
|
||||
FLAVOR=USER
|
||||
TARGETLIBS="$(BASEDIR)\bin\syscall.lib" "$(BASEDIR)\bin\libc.lib" "$(BASEDIR)\bin\native_runtime.lib"
|
||||
EXTRA_LFLAGS=/ENTRY:NtlNativeStart
|
||||
SOURCES=@/code/user/apps/ping/ping.c
|
||||
@@ -0,0 +1 @@
|
||||
!include $(BASEDIR)\makefile.def
|
||||
@@ -29,7 +29,6 @@ static void print_u64(uint64_t v) {
|
||||
while (n > 0) print_char(buf[--n]);
|
||||
}
|
||||
|
||||
// Implements the next token operation.
|
||||
static const char* next_token(const char* s, char* out, size_t outSize) {
|
||||
if (!s || !out || outSize == 0) return NULL;
|
||||
while (*s == ' ' || *s == '\t') s++;
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
TARGETNAME=resolve
|
||||
TARGETPATH=obj
|
||||
TARGETTYPE=PROGRAM
|
||||
FLAVOR=USER
|
||||
TARGETLIBS="$(BASEDIR)\bin\syscall.lib" "$(BASEDIR)\bin\libc.lib" "$(BASEDIR)\bin\native_runtime.lib"
|
||||
EXTRA_LFLAGS=/ENTRY:NtlNativeStart
|
||||
SOURCES=@/code/user/apps/resolve/resolve.c
|
||||
@@ -0,0 +1 @@
|
||||
!include $(BASEDIR)\makefile.def
|
||||
@@ -128,7 +128,6 @@ static void printUInt64(uint64_t n) {
|
||||
while (i > 0) { char c[2] = { buf[--i], '\0' }; print(c); }
|
||||
}
|
||||
|
||||
// Implements the u64 Digits operation.
|
||||
static uint32_t u64Digits(uint64_t n) {
|
||||
uint32_t digits = 1;
|
||||
while (n >= 10u) { n /= 10u; digits++; }
|
||||
@@ -833,7 +832,6 @@ static bool readLine(char* buf, int len) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Implements the trim operation.
|
||||
static char* trim(char* s) {
|
||||
while (*s == ' ') s++;
|
||||
int len = (int)strlen(s);
|
||||
@@ -1001,7 +999,6 @@ static void cmd_volume(const char* args) {
|
||||
print("\n");
|
||||
}
|
||||
|
||||
// Implements the cmd stats operation.
|
||||
static void cmd_stats(void) {
|
||||
SYS_SCHEDULER_STATS sched;
|
||||
SYS_PMM_STATS pmm;
|
||||
@@ -1416,7 +1413,6 @@ static void cmd_stats(void) {
|
||||
SysFreeVm(cpuBuffer);
|
||||
}
|
||||
|
||||
// Implements the cmd time operation.
|
||||
static void cmd_time(void) {
|
||||
SYS_SYSTEM_TIME t;
|
||||
KSTATUS status = SysGetSystemTime(&t);
|
||||
@@ -1476,7 +1472,6 @@ static void printDisplayStats(const char* title, const DISPLAY_STATS* st) {
|
||||
print(" hide="); printUInt64(st->CursorHideCount); print("\n");
|
||||
}
|
||||
|
||||
// Implements the cmd display operation.
|
||||
static void cmd_display(void) {
|
||||
SYS_DISPLAY_INFO info;
|
||||
KSTATUS status = SysQueryDisplayInfo(&info);
|
||||
@@ -1673,7 +1668,6 @@ static void cmd_surfbench(void) {
|
||||
shellPrintFramebufferBenchmark("full", &full);
|
||||
}
|
||||
|
||||
// Implements the cmd mem operation.
|
||||
static void cmd_mem(void) {
|
||||
SYS_MEMORY_STATUS st;
|
||||
if (SysGetMemoryStatus(&st) != 0) {
|
||||
@@ -1767,7 +1761,6 @@ static void printThreadFlags(uint32_t flags) {
|
||||
printStringWidth(buf, 7);
|
||||
}
|
||||
|
||||
// Implements the cmd ps operation.
|
||||
static void cmd_ps(void) {
|
||||
SYS_PROCESS_INFO procs[MAX_PROCS];
|
||||
size_t count = 0;
|
||||
@@ -1825,7 +1818,6 @@ static void cmd_ps(void) {
|
||||
}
|
||||
}
|
||||
|
||||
// Implements the cmd threads operation.
|
||||
static void cmd_threads(const char* args) {
|
||||
uint32_t pid = 0;
|
||||
if (!parseUInt(args, &pid)) {
|
||||
@@ -1922,7 +1914,6 @@ static void cmd_kill(const char* args) {
|
||||
}
|
||||
}
|
||||
|
||||
// Implements the cmd write operation.
|
||||
static void cmd_write(const char* args) {
|
||||
char filename[MAX_CMD]; int i = 0;
|
||||
while (args[i] != ' ' && args[i] != '\0' && i < MAX_CMD - 1) {
|
||||
@@ -2083,7 +2074,6 @@ static bool shellTryResourceDir(const char* target) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Implements the cmd dir operation.
|
||||
static void cmd_dir(const char* path) {
|
||||
uint32_t hDir;
|
||||
const char* target = (path && *path != '\0') ? path : ".";
|
||||
@@ -2131,7 +2121,6 @@ static void cmd_dir(const char* path) {
|
||||
SysFreeVm(entries);
|
||||
}
|
||||
|
||||
// Implements the cmd type operation.
|
||||
static void cmd_type(const char* filename) {
|
||||
uint32_t hFile;
|
||||
if (open_file(filename, &hFile) != 0) { print("type: cannot open file\n"); return; }
|
||||
@@ -2144,7 +2133,6 @@ static void cmd_type(const char* filename) {
|
||||
SysCloseHandle(hFile); print("\n");
|
||||
}
|
||||
|
||||
// Implements the extract Program Path operation.
|
||||
static bool extractProgramPath(const char* cmdline, char* out, size_t outSize) {
|
||||
if (!cmdline || !out || outSize == 0) return false;
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
TARGETNAME=shell_native
|
||||
TARGETPATH=obj
|
||||
TARGETTYPE=PROGRAM
|
||||
FLAVOR=USER
|
||||
TARGETLIBS="$(BASEDIR)\bin\syscall.lib" "$(BASEDIR)\bin\libc.lib" "$(BASEDIR)\bin\native_runtime.lib"
|
||||
EXTRA_LFLAGS=/ENTRY:NtlNativeStart
|
||||
SOURCES=@/code/user/apps/shell_native/shell_native.c
|
||||
@@ -0,0 +1 @@
|
||||
!include $(BASEDIR)\makefile.def
|
||||
@@ -37,7 +37,6 @@ static void readLine(char* buf, int len) {
|
||||
buf[pos] = '\0';
|
||||
}
|
||||
|
||||
// Implements the split Command Line operation.
|
||||
static int splitCommandLine(char* line, char** argv, int maxArgv) {
|
||||
int argc = 0;
|
||||
char* p = line;
|
||||
@@ -62,7 +61,6 @@ static int splitCommandLine(char* line, char** argv, int maxArgv) {
|
||||
return argc;
|
||||
}
|
||||
|
||||
// Implements the trim operation.
|
||||
static char* trim(char* s) {
|
||||
while (*s == ' ') s++;
|
||||
int len = (int)strlen(s);
|
||||
@@ -71,7 +69,6 @@ static char* trim(char* s) {
|
||||
return s;
|
||||
}
|
||||
|
||||
// Implements the cmd help operation.
|
||||
static void cmd_help() {
|
||||
print("Available commands:\n"
|
||||
" help - Show this list\n"
|
||||
@@ -86,7 +83,6 @@ static void cmd_help() {
|
||||
" exit - Close the shell\n");
|
||||
}
|
||||
|
||||
// Implements the cmd write operation.
|
||||
static void cmd_write(const char* args) {
|
||||
char filename[MAX_CMD]; int i = 0;
|
||||
while (args[i] != ' ' && args[i] != '\0' && i < MAX_CMD - 1) {
|
||||
@@ -145,7 +141,6 @@ static void shellSortDirEntries(SHELL_POSIX_DIR_ENTRY* entries, size_t count) {
|
||||
}
|
||||
}
|
||||
|
||||
// Implements the cmd dir operation.
|
||||
static void cmd_dir(const char* path) {
|
||||
const char* target = (path && *path != '\0') ? path : ".";
|
||||
DIR* d = opendir(target);
|
||||
@@ -186,7 +181,6 @@ static void cmd_dir(const char* path) {
|
||||
free(entries);
|
||||
}
|
||||
|
||||
// Implements the cmd type operation.
|
||||
static void cmd_type(const char* filename) {
|
||||
int fd = open(filename, 0); // O_RDONLY
|
||||
if (fd < 0) { print("type: cannot open file\n"); return; }
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
TARGETNAME=shell_posix
|
||||
TARGETPATH=obj
|
||||
TARGETTYPE=PROGRAM
|
||||
FLAVOR=POSIX
|
||||
TARGETLIBS="$(BASEDIR)\bin\posix.lib" "$(BASEDIR)\bin\syscall.lib" "$(BASEDIR)\bin\libc.lib" "$(BASEDIR)\bin\posix_start.lib"
|
||||
EXTRA_LFLAGS=/ENTRY:PosixStart
|
||||
SOURCES=@/code/user/apps/shell_posix/shell_posix.c
|
||||
@@ -0,0 +1 @@
|
||||
!include $(BASEDIR)\makefile.def
|
||||
@@ -58,7 +58,6 @@ static void readLine(char* buf, int len) {
|
||||
buf[pos] = '\0';
|
||||
}
|
||||
|
||||
// Implements the trim operation.
|
||||
static char* trim(char* s) {
|
||||
while (*s == ' ') s++;
|
||||
int len = (int)strlen(s);
|
||||
@@ -82,7 +81,6 @@ static void cmd_help() {
|
||||
" exit - Close the shell\n");
|
||||
}
|
||||
|
||||
// Implements the cmd write operation.
|
||||
static void cmd_write(const char* args) {
|
||||
char filename[MAX_PATH]; int i = 0;
|
||||
while (args[i] != ' ' && args[i] != '\0' && i < MAX_PATH - 1) {
|
||||
@@ -145,7 +143,6 @@ static void shellSortDirEntries(SHELL_WIN32_DIR_ENTRY* entries, size_t count) {
|
||||
}
|
||||
}
|
||||
|
||||
// Implements the cmd dir operation.
|
||||
static void cmd_dir(const char* path) {
|
||||
WIN32_FIND_DATAA fd;
|
||||
char search_path[MAX_PATH];
|
||||
@@ -200,7 +197,6 @@ static void cmd_dir(const char* path) {
|
||||
HeapFree(heap, 0, entries);
|
||||
}
|
||||
|
||||
// Implements the cmd type operation.
|
||||
static void cmd_type(const char* filename) {
|
||||
HANDLE hFile = CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
|
||||
if (hFile == INVALID_HANDLE_VALUE) { print("type: cannot open file\n"); return; }
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
TARGETNAME=shell_win32
|
||||
TARGETPATH=obj
|
||||
TARGETTYPE=PROGRAM
|
||||
FLAVOR=USER
|
||||
TARGETLIBS="$(BASEDIR)\bin\win32.lib" "$(BASEDIR)\bin\syscall.lib" "$(BASEDIR)\bin\libc.lib" "$(BASEDIR)\bin\native_runtime.lib"
|
||||
EXTRA_LFLAGS=/ENTRY:NtlNativeStart
|
||||
SOURCES=@/code/user/apps/shell_win32/shell_win32.c
|
||||
@@ -0,0 +1 @@
|
||||
!include $(BASEDIR)\makefile.def
|
||||
@@ -0,0 +1,7 @@
|
||||
TARGETNAME=tcpserve
|
||||
TARGETPATH=obj
|
||||
TARGETTYPE=PROGRAM
|
||||
FLAVOR=USER
|
||||
TARGETLIBS="$(BASEDIR)\bin\syscall.lib" "$(BASEDIR)\bin\libc.lib" "$(BASEDIR)\bin\native_runtime.lib"
|
||||
EXTRA_LFLAGS=/ENTRY:NtlNativeStart
|
||||
SOURCES=@/code/user/apps/tcpserve/tcpserve.c
|
||||
@@ -70,7 +70,6 @@ static void print_socket_address(const SYS_SOCKET_ADDRESS* address) {
|
||||
}
|
||||
}
|
||||
|
||||
// Implements the next token operation.
|
||||
static const char* next_token(const char* s, char* out, size_t outSize) {
|
||||
if (!s || !out || outSize == 0) return NULL;
|
||||
while (*s == ' ' || *s == '\t') s++;
|
||||
@@ -99,7 +98,6 @@ static bool parse_port(const char* s, uint16_t* out) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Implements the usage operation.
|
||||
static void usage(void) {
|
||||
print("usage: tcpserve.exe [-4|-6] [port]\n");
|
||||
print("default: tcpserve.exe 8080\n");
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
!include $(BASEDIR)\makefile.def
|
||||
@@ -0,0 +1,7 @@
|
||||
TARGETNAME=udp
|
||||
TARGETPATH=obj
|
||||
TARGETTYPE=PROGRAM
|
||||
FLAVOR=USER
|
||||
TARGETLIBS="$(BASEDIR)\bin\syscall.lib" "$(BASEDIR)\bin\libc.lib" "$(BASEDIR)\bin\native_runtime.lib"
|
||||
EXTRA_LFLAGS=/ENTRY:NtlNativeStart
|
||||
SOURCES=@/code/user/apps/udp/udp.c
|
||||
@@ -70,7 +70,6 @@ static void print_socket_address(const SYS_SOCKET_ADDRESS* address) {
|
||||
}
|
||||
}
|
||||
|
||||
// Implements the next token operation.
|
||||
static const char* next_token(const char* s, char* out, size_t outSize) {
|
||||
if (!s || !out || outSize == 0) return NULL;
|
||||
while (*s == ' ' || *s == '\t') s++;
|
||||
@@ -129,7 +128,6 @@ static bool parse_port(const char* s, uint16_t* out) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Implements the usage operation.
|
||||
static void usage(void) {
|
||||
print("usage:\n");
|
||||
print(" udp.exe send <host|address> <port> [message]\n");
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
!include $(BASEDIR)\makefile.def
|
||||
@@ -0,0 +1,7 @@
|
||||
TARGETNAME=video
|
||||
TARGETPATH=obj
|
||||
TARGETTYPE=PROGRAM
|
||||
FLAVOR=USER
|
||||
TARGETLIBS="$(BASEDIR)\bin\syscall.lib" "$(BASEDIR)\bin\libc.lib" "$(BASEDIR)\bin\native_runtime.lib" "$(BASEDIR)\code\user\apps\video\bink2w64.lib"
|
||||
EXTRA_LFLAGS=/ENTRY:NtlNativeStart
|
||||
SOURCES=@/code/user/apps/video/video.c
|
||||
@@ -0,0 +1,2 @@
|
||||
DIRS=runtime syscall personalities system apps
|
||||
OPTIONAL_DIRS=tests
|
||||
@@ -0,0 +1 @@
|
||||
DIRS=posix win32
|
||||
@@ -0,0 +1 @@
|
||||
!include $(BASEDIR)\makefile.def
|
||||
@@ -127,7 +127,6 @@ __declspec(dllexport) char* realpath(const char* path, char* resolved_path) {
|
||||
return PosixCopyResolvedPath(posix, resolved_path);
|
||||
}
|
||||
|
||||
// Implements the opendir operation.
|
||||
__declspec(dllexport) DIR* opendir(const char* path) {
|
||||
char native[512];
|
||||
PosixTranslatePath(path, native, sizeof(native));
|
||||
@@ -145,7 +144,6 @@ __declspec(dllexport) DIR* opendir(const char* path) {
|
||||
return d;
|
||||
}
|
||||
|
||||
// Implements the readdir operation.
|
||||
__declspec(dllexport) struct dirent* readdir(DIR* d) {
|
||||
if (!d) { errno = EBADF; return (struct dirent*)0; }
|
||||
KDIR_ENTRY ke;
|
||||
@@ -161,7 +159,6 @@ __declspec(dllexport) struct dirent* readdir(DIR* d) {
|
||||
return &d->Current;
|
||||
}
|
||||
|
||||
// Implements the closedir operation.
|
||||
__declspec(dllexport) int closedir(DIR* d) {
|
||||
if (!d) { errno = EBADF; return -1; }
|
||||
SysCloseHandle(d->KHandle);
|
||||
@@ -204,7 +201,6 @@ static int PosixFillSyntheticStat(mode_t type, struct stat* out) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Implements the stat operation.
|
||||
__declspec(dllexport) int stat(const char* path, struct stat* out) {
|
||||
if (!path || !out) { errno = EINVAL; return -1; }
|
||||
|
||||
@@ -219,7 +215,6 @@ __declspec(dllexport) int stat(const char* path, struct stat* out) {
|
||||
return PosixFillStat(&nativeStat, out);
|
||||
}
|
||||
|
||||
// Implements the fstat operation.
|
||||
__declspec(dllexport) int fstat(int fd, struct stat* out) {
|
||||
if (!out) { errno = EINVAL; return -1; }
|
||||
|
||||
|
||||
@@ -9,12 +9,10 @@
|
||||
#include "posix_internal.h"
|
||||
#include "posix_fd.h"
|
||||
|
||||
// Implements the bswap16 operation.
|
||||
static uint16_t bswap16(uint16_t x) {
|
||||
return (uint16_t)((x << 8) | (x >> 8));
|
||||
}
|
||||
|
||||
// Implements the bswap32 operation.
|
||||
static uint32_t bswap32(uint32_t x) {
|
||||
return ((x & 0x000000FFu) << 24) |
|
||||
((x & 0x0000FF00u) << 8) |
|
||||
@@ -22,13 +20,9 @@ static uint32_t bswap32(uint32_t x) {
|
||||
((x & 0xFF000000u) >> 24);
|
||||
}
|
||||
|
||||
// Implements the htons operation.
|
||||
__declspec(dllexport) uint16_t htons(uint16_t x) { return bswap16(x); }
|
||||
// Implements the ntohs operation.
|
||||
__declspec(dllexport) uint16_t ntohs(uint16_t x) { return bswap16(x); }
|
||||
// Implements the htonl operation.
|
||||
__declspec(dllexport) uint32_t htonl(uint32_t x) { return bswap32(x); }
|
||||
// Implements the ntohl operation.
|
||||
__declspec(dllexport) uint32_t ntohl(uint32_t x) { return bswap32(x); }
|
||||
|
||||
// Parses byte.
|
||||
@@ -148,7 +142,6 @@ static char* AppendHex16(char* out, uint16_t value) {
|
||||
return out;
|
||||
}
|
||||
|
||||
// Implements the inet addr operation.
|
||||
__declspec(dllexport) in_addr_t inet_addr(const char* text) {
|
||||
uint8_t b[4];
|
||||
if (!text || !ParseIpv4Bytes(text, b)) return INADDR_NONE;
|
||||
@@ -158,7 +151,6 @@ __declspec(dllexport) in_addr_t inet_addr(const char* text) {
|
||||
return out;
|
||||
}
|
||||
|
||||
// Implements the inet ntoa operation.
|
||||
__declspec(dllexport) char* inet_ntoa(struct in_addr in) {
|
||||
static char buf[16];
|
||||
const uint8_t* p = (const uint8_t*)&in.s_addr;
|
||||
@@ -175,7 +167,6 @@ __declspec(dllexport) char* inet_ntoa(struct in_addr in) {
|
||||
return buf;
|
||||
}
|
||||
|
||||
// Implements the inet pton operation.
|
||||
__declspec(dllexport) int inet_pton(int af, const char* src, void* dst) {
|
||||
if (!src || !dst) { errno = EINVAL; return -1; }
|
||||
if (af == AF_INET) {
|
||||
@@ -196,7 +187,6 @@ __declspec(dllexport) int inet_pton(int af, const char* src, void* dst) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Implements the inet ntop operation.
|
||||
__declspec(dllexport) const char* inet_ntop(int af, const void* src, char* dst, socklen_t size) {
|
||||
if (!src || !dst) { errno = EINVAL; return (const char*)0; }
|
||||
if (af == AF_INET) {
|
||||
@@ -223,7 +213,6 @@ __declspec(dllexport) const char* inet_ntop(int af, const void* src, char* dst,
|
||||
return (const char*)0;
|
||||
}
|
||||
|
||||
// Implements the sock Addr To Sys operation.
|
||||
static int SockAddrToSys(const struct sockaddr* addr, socklen_t addrlen, SYS_SOCKET_ADDRESS* out) {
|
||||
if (!addr || !out) { errno = EINVAL; return -1; }
|
||||
memset(out, 0, sizeof(*out));
|
||||
@@ -252,7 +241,6 @@ static int SockAddrToSys(const struct sockaddr* addr, socklen_t addrlen, SYS_SOC
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Implements the to Sock Addr operation.
|
||||
static void SysToSockAddr(const SYS_SOCKET_ADDRESS* in, struct sockaddr* addr, socklen_t* addrlen) {
|
||||
if (!in || !addr || !addrlen) return;
|
||||
|
||||
@@ -691,7 +679,6 @@ static unsigned ParsePort(const char* s) {
|
||||
return *s == '\0' ? v : 0;
|
||||
}
|
||||
|
||||
// Implements the resolve Name operation.
|
||||
static int ResolveNameFamily(const char* name, int family, SYS_SOCKET_ADDRESS* out) {
|
||||
if (!name || !out) { errno = EINVAL; return -1; }
|
||||
memset(out, 0, sizeof(*out));
|
||||
@@ -739,7 +726,6 @@ static int ResolveNameAny(const char* name, int family, SYS_SOCKET_ADDRESS* out)
|
||||
return ResolveNameFamily(name, AF_INET, out);
|
||||
}
|
||||
|
||||
// Implements the gethostbyname operation.
|
||||
__declspec(dllexport) struct hostent* gethostbyname(const char* name) {
|
||||
static struct hostent h;
|
||||
static char hname[256];
|
||||
@@ -804,7 +790,6 @@ __declspec(dllexport) int getaddrinfo(const char* node, const char* service,
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Implements the freeaddrinfo operation.
|
||||
__declspec(dllexport) void freeaddrinfo(struct addrinfo* res) {
|
||||
if (res) free(res);
|
||||
}
|
||||
@@ -38,7 +38,6 @@ typedef struct {
|
||||
uint64_t SignalMask;
|
||||
} PTHREAD_TRAMP;
|
||||
|
||||
// Implements the pthread Trampoline operation.
|
||||
static void PthreadTrampoline(uintptr_t raw) {
|
||||
PTHREAD_TRAMP* tr = (PTHREAD_TRAMP*)raw;
|
||||
void* (*fn)(void*) = tr->Fn;
|
||||
@@ -110,7 +109,6 @@ __declspec(dllexport) int pthread_create(pthread_t* t, const pthread_attr_t* att
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Implements the pthread join operation.
|
||||
__declspec(dllexport) int pthread_join(pthread_t t, void** retval) {
|
||||
KSTATUS s = SysWait(t.Handle, SYS_WAIT_FLAG_NONE);
|
||||
if (s != 0) return ESRCH;
|
||||
@@ -122,7 +120,6 @@ __declspec(dllexport) int pthread_join(pthread_t t, void** retval) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Implements the pthread exit operation.
|
||||
__declspec(dllexport) void pthread_exit(void* retval) {
|
||||
uint32_t tid = SysGetCurrentTid();
|
||||
POSIX_PTHREAD_SLOT* slot = PthreadSlotFind(tid);
|
||||
@@ -132,7 +129,6 @@ __declspec(dllexport) void pthread_exit(void* retval) {
|
||||
while (1); // unreachable
|
||||
}
|
||||
|
||||
// Implements the pthread mutex init operation.
|
||||
__declspec(dllexport) int pthread_mutex_init(pthread_mutex_t* m, const pthread_mutexattr_t* attr) {
|
||||
(void)attr;
|
||||
uint32_t h;
|
||||
@@ -142,26 +138,22 @@ __declspec(dllexport) int pthread_mutex_init(pthread_mutex_t* m, const pthread_m
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Implements the pthread mutex destroy operation.
|
||||
__declspec(dllexport) int pthread_mutex_destroy(pthread_mutex_t* m) {
|
||||
SysCloseHandle(m->Handle);
|
||||
m->Handle = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Implements the pthread mutex lock operation.
|
||||
__declspec(dllexport) int pthread_mutex_lock(pthread_mutex_t* m) {
|
||||
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) {
|
||||
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) {
|
||||
KSTATUS s = SysReleaseMutex(m->Handle);
|
||||
return s == 0 ? 0 : EINVAL;
|
||||
|
||||
@@ -80,7 +80,7 @@ vfork_wait:
|
||||
mov ecx, dword ptr [r12 + VF_ThreadHandle]
|
||||
lea rdx, [r12 + VF_PreviousSuspendCount]
|
||||
mov eax, SYS_RESUME_THREAD_AND_WAIT
|
||||
int 40h
|
||||
int 02Fh
|
||||
mov r9d, eax
|
||||
|
||||
; The child may have overwritten every byte of the old vfork/helper frames.
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
TARGETNAME=posix
|
||||
TARGETPATH=obj
|
||||
TARGETTYPE=DYNLINK
|
||||
IMAGE_KIND=DLL
|
||||
FLAVOR=POSIX
|
||||
EXTRA_CFLAGS=/DNTL_POSIX_BUILD /DUSER_HEAP_EXPORTS /DNTL_EXCEPTION_EXPORTS
|
||||
TARGETLIBS="$(BASEDIR)\bin\syscall.lib" "$(BASEDIR)\bin\libc.lib"
|
||||
EXTRA_LFLAGS=/SUBSYSTEM:NATIVE /DYNAMICBASE /HIGHENTROPYVA
|
||||
EXTRA_ASFLAGS=/I"$(BASEDIR)\code\user\syscall"
|
||||
INCLUDES=$(BASEDIR)\code\user\syscall
|
||||
SOURCES=@/code/user/runtime/heap.c @/code/user/runtime/heap_exports.c @/code/user/runtime/native_exception.c @/code/user/personalities/posix/posix_dir.c @/code/user/personalities/posix/posix_dlfcn.c @/code/user/personalities/posix/posix_fd.c \
|
||||
@/code/user/personalities/posix/posix_io.c @/code/user/personalities/posix/posix_mman.c @/code/user/personalities/posix/posix_path.c @/code/user/personalities/posix/posix_proc.c @/code/user/personalities/posix/posix_runtime.c @/code/user/personalities/posix/posix_signal.c \
|
||||
@/code/user/personalities/posix/posix_socket.c @/code/user/personalities/posix/posix_thread.c @/code/user/personalities/posix/posix_vfork.c @/code/user/personalities/posix/posix_vfork.asm
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <ntlite/native_heap.h>
|
||||
#include <ntlite/syscall.h>
|
||||
|
||||
#if defined(__SSE2__) || defined(_M_X64) || defined(_M_IX86)
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
#include <ntlite/atomic.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <ntlite/native_heap.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <intrin.h>
|
||||
@@ -148,7 +148,6 @@ typedef struct _MEMORY_BASIC_INFORMATION {
|
||||
DWORD Type;
|
||||
} MEMORY_BASIC_INFORMATION;
|
||||
|
||||
// Implements the win Protect To Sys Vm operation.
|
||||
static uint32_t WinProtectToSysVm(DWORD protect) {
|
||||
switch (protect) {
|
||||
case PAGE_NOACCESS: return SYS_VM_NOACCESS;
|
||||
@@ -182,7 +181,6 @@ static uint32_t WinFileMapAccessToSys(DWORD desiredAccess) {
|
||||
return access;
|
||||
}
|
||||
|
||||
// Implements the protect Vm To Win operation.
|
||||
static DWORD SysProtectVmToWin(uint32_t protect) {
|
||||
switch (protect) {
|
||||
case SYS_VM_NOACCESS: return PAGE_NOACCESS;
|
||||
@@ -279,7 +277,6 @@ static void TranslatePath(const char* input, char* output) {
|
||||
*dst = '\0';
|
||||
}
|
||||
|
||||
// Implements the k32 Open File Native operation.
|
||||
static KSTATUS K32OpenFileNative(const char* path,
|
||||
uint32_t access,
|
||||
uint32_t disposition,
|
||||
@@ -287,23 +284,19 @@ static KSTATUS K32OpenFileNative(const char* path,
|
||||
return NtlOpenFile(SysSvFromCstr(path), access, disposition, SYS_FILE_FLAG_NONE, handleOut);
|
||||
}
|
||||
|
||||
// Implements the k32 Delete File Native operation.
|
||||
static KSTATUS K32DeleteFileNative(const char* path) {
|
||||
return NtlDeleteFile(SysSvFromCstr(path));
|
||||
}
|
||||
|
||||
// Implements the k32 Open Dir Native operation.
|
||||
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 KSTATUS K32SetCwdNative(const char* path) {
|
||||
return NtlSetCurrentDirectory(SysSvFromCstr(path));
|
||||
}
|
||||
|
||||
// Implements the k32 Get Cwd Native operation.
|
||||
static KSTATUS K32GetCwdNative(char* buffer, size_t capacity, size_t* lengthOut) {
|
||||
size_t required = 0;
|
||||
KSTATUS status = NtlGetCurrentDirectory(buffer, capacity, &required);
|
||||
@@ -347,12 +340,10 @@ static KSTATUS K32CreateProcessNative(const char* path, uint32_t imageFile, uint
|
||||
return NtlCreateSystemProcess(&req);
|
||||
}
|
||||
|
||||
// Implements the k32 Write Console Span operation.
|
||||
static void K32WriteConsoleSpan(const char* data, size_t length) {
|
||||
(void)NtlConsoleWrite(SYS_HANDLE_INVALID, data, length, 0);
|
||||
}
|
||||
|
||||
// Implements the k32 Debug Print A operation.
|
||||
static void K32DebugPrintA(const char* s) {
|
||||
if (!s) return;
|
||||
SYS_PRINT_DEBUG_REQUEST req = { sizeof(SYS_PRINT_DEBUG_REQUEST), SysSvFromCstr(s) };
|
||||
@@ -373,7 +364,6 @@ typedef struct WIN32_THREAD_ENV {
|
||||
|
||||
static WIN32_THREAD_ENV gFallbackThreadEnv;
|
||||
|
||||
// Implements the k32 Thread Env operation.
|
||||
static WIN32_THREAD_ENV* K32ThreadEnv(void) {
|
||||
SYS_TEB* teb = SysGetTeb();
|
||||
if (!teb || teb->Magic != SYS_TEB_MAGIC) return &gFallbackThreadEnv;
|
||||
@@ -388,7 +378,6 @@ static WIN32_THREAD_ENV* K32ThreadEnv(void) {
|
||||
return env;
|
||||
}
|
||||
|
||||
// Implements the k32 Last Error Slot operation.
|
||||
static DWORD* K32LastErrorSlot(void) {
|
||||
return &K32ThreadEnv()->LastError;
|
||||
}
|
||||
@@ -398,7 +387,6 @@ static DWORD* K32LastErrorSlot(void) {
|
||||
// Returns a stable nonzero identifier for the calling thread.
|
||||
static DWORD K32CurrentTidOrOne(void);
|
||||
|
||||
// Implements the file Exists A operation.
|
||||
static BOOL KiFileExistsA(const char* path) {
|
||||
if (!path || !*path) return FALSE;
|
||||
char native[MAX_PATH];
|
||||
@@ -483,7 +471,6 @@ __declspec(dllexport) DWORD SearchPathA(const char* lpPath, const char* lpFileNa
|
||||
// GetLastError / SetLastError (single-threaded stub).
|
||||
static SYS_PEB* gProcessPeb = NULL;
|
||||
|
||||
// Implements the peb operation.
|
||||
static SYS_PEB* Win32Peb(void) {
|
||||
if (!gProcessPeb) gProcessPeb = SysGetPeb();
|
||||
return gProcessPeb;
|
||||
@@ -558,7 +545,6 @@ __declspec(dllexport) void* HeapAlloc(void* heap, DWORD flags, size_t size) {
|
||||
return ptr;
|
||||
}
|
||||
|
||||
// Implements the heap Free operation.
|
||||
__declspec(dllexport) BOOL HeapFree(void* heap, DWORD flags, void* ptr) {
|
||||
(void)heap; (void)flags;
|
||||
// Win32's HeapFree doesn't need the size; we pass 0 and the user-mode heap carries the block size.
|
||||
@@ -624,7 +610,6 @@ __declspec(dllexport) void* VirtualAlloc(void* lpAddress, size_t dwSize,
|
||||
return base;
|
||||
}
|
||||
|
||||
// Implements the virtual Free operation.
|
||||
__declspec(dllexport) BOOL VirtualFree(void* lpAddress, size_t dwSize, DWORD dwFreeType) {
|
||||
(void)dwSize; (void)dwFreeType;
|
||||
if (!lpAddress) return TRUE;
|
||||
@@ -1255,13 +1240,11 @@ __declspec(dllexport) HANDLE HeapCreate(DWORD flOptions,
|
||||
return (HANDLE)(uintptr_t)1; // non-NULL sentinel
|
||||
}
|
||||
|
||||
// Implements the heap Destroy operation.
|
||||
__declspec(dllexport) BOOL HeapDestroy(HANDLE hHeap) {
|
||||
(void)hHeap;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Implements the heap Size operation.
|
||||
__declspec(dllexport) size_t HeapSize(HANDLE heap, DWORD flags, const void* ptr) {
|
||||
(void)heap; (void)flags;
|
||||
size_t size = _malloc_usable_size(ptr);
|
||||
@@ -1297,7 +1280,6 @@ __declspec(dllexport) DWORD GetCurrentProcessId(void) { return (DWORD)SysGetCurr
|
||||
// Tiny TLS index allocator; slot values are stored per thread in WIN32_THREAD_ENV.
|
||||
static uint64_t gTlsBitmap = 1; // reserve slot 0
|
||||
|
||||
// Implements the tls Alloc operation.
|
||||
__declspec(dllexport) DWORD TlsAlloc(void) {
|
||||
for (DWORD i = 1; i < WIN32_TLS_SLOTS; i++) {
|
||||
uint64_t bit = 1ULL << i;
|
||||
@@ -1307,7 +1289,6 @@ __declspec(dllexport) DWORD TlsAlloc(void) {
|
||||
return 0xFFFFFFFFUL;
|
||||
}
|
||||
|
||||
// Implements the tls Free operation.
|
||||
__declspec(dllexport) BOOL TlsFree(DWORD dwTlsIndex) {
|
||||
if (dwTlsIndex >= WIN32_TLS_SLOTS || !(gTlsBitmap & (1ULL << dwTlsIndex))) { gLastError = 87; return FALSE; }
|
||||
WIN32_THREAD_ENV* env = K32ThreadEnv();
|
||||
@@ -1316,7 +1297,6 @@ __declspec(dllexport) BOOL TlsFree(DWORD dwTlsIndex) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Implements the tls Get Value operation.
|
||||
__declspec(dllexport) void* TlsGetValue(DWORD dwTlsIndex) {
|
||||
if (dwTlsIndex >= WIN32_TLS_SLOTS || !(gTlsBitmap & (1ULL << dwTlsIndex))) { gLastError = 87; return NULL; }
|
||||
WIN32_THREAD_ENV* env = K32ThreadEnv();
|
||||
@@ -1324,7 +1304,6 @@ __declspec(dllexport) void* TlsGetValue(DWORD dwTlsIndex) {
|
||||
return env->TlsSlots[dwTlsIndex];
|
||||
}
|
||||
|
||||
// Implements the tls Set Value operation.
|
||||
__declspec(dllexport) BOOL TlsSetValue(DWORD dwTlsIndex, void* lpTlsValue) {
|
||||
if (dwTlsIndex >= WIN32_TLS_SLOTS || !(gTlsBitmap & (1ULL << dwTlsIndex))) { gLastError = 87; return FALSE; }
|
||||
WIN32_THREAD_ENV* env = K32ThreadEnv();
|
||||
@@ -1336,7 +1315,6 @@ __declspec(dllexport) BOOL TlsSetValue(DWORD dwTlsIndex, void* lpTlsValue) {
|
||||
#define WIN32_HMODULE_EXE ((HANDLE)(uintptr_t)0x00400000UL)
|
||||
#define WIN32_HMODULE_WIN32 ((HANDLE)(uintptr_t)0x57333232UL) // W322
|
||||
|
||||
// Implements the stricmp A operation.
|
||||
static int KiStricmpA(const char* a, const char* b) {
|
||||
while (*a && (((unsigned char)*a | 0x20) == ((unsigned char)*b | 0x20))) { a++; b++; }
|
||||
return ((unsigned char)*a | 0x20) - ((unsigned char)*b | 0x20);
|
||||
@@ -1370,7 +1348,6 @@ static BOOL KiIsWin32DllNameA(const char* name) {
|
||||
KiMatchesWin32DllNameA(name, "win32");
|
||||
}
|
||||
|
||||
// Implements the dbg Write A operation.
|
||||
static void KiDbgWriteA(const char* s) {
|
||||
if (s) K32WriteConsoleSpan(s, strlen(s));
|
||||
}
|
||||
@@ -1532,7 +1509,6 @@ __declspec(dllexport) DWORD GetModuleFileNameA(HANDLE hModule,
|
||||
return copy;
|
||||
}
|
||||
|
||||
// Implements the terminate Process operation.
|
||||
__declspec(dllexport) BOOL TerminateProcess(HANDLE hProcess, unsigned int uExitCode) {
|
||||
if (hProcess == GetCurrentProcess()) {
|
||||
SysExitProcess((int32_t)uExitCode);
|
||||
@@ -1744,7 +1720,6 @@ __declspec(dllexport) BOOL ResetEvent(HANDLE hEvent) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Implements the extract Application Path A operation.
|
||||
static BOOL ExtractApplicationPathA(const char* commandLine, char* out, size_t outSize) {
|
||||
if (!commandLine || !out || outSize == 0) return FALSE;
|
||||
|
||||
@@ -1906,7 +1881,6 @@ __declspec(dllexport) uintptr_t SetThreadAffinityMask(HANDLE hThread,
|
||||
return 1; // previous mask: CPU 0 only
|
||||
}
|
||||
|
||||
// Implements the sleep operation.
|
||||
__declspec(dllexport) void Sleep(DWORD dwMilliseconds) {
|
||||
SysSleep(dwMilliseconds);
|
||||
}
|
||||
@@ -1917,20 +1891,17 @@ __declspec(dllexport) void Sleep(DWORD dwMilliseconds) {
|
||||
#define STD_OUTPUT_HANDLE ((DWORD)-11)
|
||||
#define STD_ERROR_HANDLE ((DWORD)-12)
|
||||
|
||||
// Implements the k32 Is Std Handle Value operation.
|
||||
static BOOL K32IsStdHandleValue(HANDLE h, DWORD stdHandle) {
|
||||
uintptr_t value = (uintptr_t)h;
|
||||
return value == (uintptr_t)stdHandle || value == (uintptr_t)(intptr_t)(int32_t)stdHandle;
|
||||
}
|
||||
|
||||
// Implements the k32 Is Console Output Handle operation.
|
||||
static BOOL K32IsConsoleOutputHandle(HANDLE h) {
|
||||
return h && h != INVALID_HANDLE_VALUE &&
|
||||
(K32IsStdHandleValue(h, STD_OUTPUT_HANDLE) ||
|
||||
K32IsStdHandleValue(h, STD_ERROR_HANDLE));
|
||||
}
|
||||
|
||||
// Implements the k32 Set Console Last Error operation.
|
||||
static void K32SetConsoleLastError(KSTATUS status) {
|
||||
switch (status) {
|
||||
case NTLSTATUS_SUCCESS:
|
||||
@@ -2206,12 +2177,10 @@ static void SplitFindPath(const char* searchPath,
|
||||
#define FILE_ATTRIBUTE_ARCHIVE 0x00000020UL
|
||||
#define FILE_ATTRIBUTE_NORMAL 0x00000080UL
|
||||
|
||||
// Implements the k32 Dir Is Leap Year operation.
|
||||
static bool K32DirIsLeapYear(unsigned int year) {
|
||||
return ((year % 4u) == 0u && (year % 100u) != 0u) || ((year % 400u) == 0u);
|
||||
}
|
||||
|
||||
// Implements the k32 Dir Days In Month operation.
|
||||
static unsigned int K32DirDaysInMonth(unsigned int year, unsigned int month) {
|
||||
static const unsigned char days[12] = { 31,28,31,30,31,30,31,31,30,31,30,31 };
|
||||
if (month < 1u || month > 12u) return 31u;
|
||||
@@ -2219,7 +2188,6 @@ static unsigned int K32DirDaysInMonth(unsigned int year, unsigned int month) {
|
||||
return days[month - 1u];
|
||||
}
|
||||
|
||||
// Implements the k32 Dir Calendar To Epoch Seconds operation.
|
||||
static uint64_t K32DirCalendarToEpochSeconds(const SYS_SYSTEM_TIME* st) {
|
||||
uint64_t days = 0;
|
||||
for (unsigned int y = 1970u; y < st->Year; y++)
|
||||
@@ -2230,7 +2198,6 @@ static uint64_t K32DirCalendarToEpochSeconds(const SYS_SYSTEM_TIME* st) {
|
||||
return (((days * 24u) + st->Hour) * 60u + st->Minute) * 60u + st->Second;
|
||||
}
|
||||
|
||||
// Implements the k32 Dir Store File Time operation.
|
||||
static void K32DirStoreFileTime(FILETIME* ft, const SYS_SYSTEM_TIME* st) {
|
||||
if (!ft || !st || !(st->Flags & SYS_TIME_FLAG_VALID) || st->Year < 1970u) {
|
||||
if (ft) { ft->dwLowDateTime = 0; ft->dwHighDateTime = 0; }
|
||||
@@ -2245,7 +2212,6 @@ static void K32DirStoreFileTime(FILETIME* ft, const SYS_SYSTEM_TIME* st) {
|
||||
ft->dwHighDateTime = (DWORD)(value >> 32);
|
||||
}
|
||||
|
||||
// Implements the kdir To Find Data operation.
|
||||
static void KdirToFindData(const KDIR_ENTRY* ke, WIN32_FIND_DATAA* fd) {
|
||||
// Zero the entire struct (covers all FILETIME and reserved fields).
|
||||
unsigned char* p = (unsigned char*)fd;
|
||||
@@ -2554,7 +2520,6 @@ __declspec(dllexport) BOOL InitializeCriticalSectionAndSpinCount(CRITICAL_SECTIO
|
||||
}
|
||||
// Releases critical Section.
|
||||
__declspec(dllexport) void DeleteCriticalSection(CRITICAL_SECTION* lpCS) { (void)lpCS; }
|
||||
// Implements the enter Critical Section operation.
|
||||
__declspec(dllexport) void EnterCriticalSection(CRITICAL_SECTION* lpCS) {
|
||||
if (!lpCS) return;
|
||||
DWORD tid = K32CurrentTidOrOne();
|
||||
@@ -2571,7 +2536,6 @@ __declspec(dllexport) void EnterCriticalSection(CRITICAL_SECTION* lpCS) {
|
||||
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();
|
||||
@@ -2584,7 +2548,6 @@ __declspec(dllexport) void LeaveCriticalSection(CRITICAL_SECTION* lpCS) {
|
||||
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();
|
||||
@@ -2826,7 +2789,6 @@ static long long TscFreqCalibrate(void) {
|
||||
return (int64_t)(delta_tsc * 1000000ULL / delta_us);
|
||||
}
|
||||
|
||||
// Implements the tsc Freq Get operation.
|
||||
static long long TscFreqGet(void) {
|
||||
int64_t cached = NtlAtomicLoad64(&gTscFreq);
|
||||
if (cached)
|
||||
@@ -2855,12 +2817,10 @@ __declspec(dllexport) BOOL QueryPerformanceCounter(LARGE_INTEGER* lpPerformanceC
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Implements the k32 Is Leap Year operation.
|
||||
static BOOL K32IsLeapYear(unsigned int year) {
|
||||
return ((year % 4u) == 0u && (year % 100u) != 0u) || ((year % 400u) == 0u);
|
||||
}
|
||||
|
||||
// Implements the k32 Days In Month operation.
|
||||
static unsigned int K32DaysInMonth(unsigned int year, unsigned int month) {
|
||||
static const unsigned char days[12] = { 31,28,31,30,31,30,31,31,30,31,30,31 };
|
||||
if (month == 2u && K32IsLeapYear(year)) return 29u;
|
||||
@@ -2868,7 +2828,6 @@ static unsigned int K32DaysInMonth(unsigned int year, unsigned int month) {
|
||||
return days[month - 1u];
|
||||
}
|
||||
|
||||
// Implements the k32 Calendar To Epoch Seconds operation.
|
||||
static uint64_t K32CalendarToEpochSeconds(const SYSTEMTIME* st) {
|
||||
uint64_t days = 0;
|
||||
for (unsigned int y = 1970u; y < st->wYear; y++)
|
||||
@@ -2879,14 +2838,12 @@ static uint64_t K32CalendarToEpochSeconds(const SYSTEMTIME* st) {
|
||||
return (((days * 24u) + st->wHour) * 60u + st->wMinute) * 60u + st->wSecond;
|
||||
}
|
||||
|
||||
// Implements the k32 Epoch To File Time operation.
|
||||
static uint64_t K32EpochToFileTime(uint64_t epochSeconds, uint32_t nanosecond) {
|
||||
const uint64_t WINDOWS_EPOCH_BIAS_SECONDS = 11644473600ULL;
|
||||
return (epochSeconds + WINDOWS_EPOCH_BIAS_SECONDS) * 10000000ULL +
|
||||
((uint64_t)nanosecond / 100ULL);
|
||||
}
|
||||
|
||||
// Implements the k32 Store File Time operation.
|
||||
static void K32StoreFileTime(FILETIME* ft, uint64_t value) {
|
||||
ft->dwLowDateTime = (DWORD)(value & 0xFFFFFFFFULL);
|
||||
ft->dwHighDateTime = (DWORD)(value >> 32);
|
||||
@@ -3663,7 +3620,6 @@ __declspec(dllexport) void RaiseException(DWORD dwExceptionCode, DWORD dwExcepti
|
||||
SysExitProcess((int32_t)status);
|
||||
}
|
||||
|
||||
// Implements the unhandled Exception Filter operation.
|
||||
__declspec(dllexport) long UnhandledExceptionFilter(EXCEPTION_POINTERS* ExceptionInfo) {
|
||||
(void)ExceptionInfo;
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
@@ -3698,7 +3654,6 @@ __declspec(dllexport) BOOL RtlAddFunctionTable(RUNTIME_FUNCTION* FunctionTable,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Implements the rtl Delete Function Table operation.
|
||||
__declspec(dllexport) BOOL RtlDeleteFunctionTable(RUNTIME_FUNCTION* FunctionTable) {
|
||||
(void)FunctionTable;
|
||||
return TRUE;
|
||||
@@ -3729,7 +3684,6 @@ __declspec(dllexport) void* RtlVirtualUnwind(unsigned long HandlerType,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Implements the rtl Capture Context operation.
|
||||
__declspec(dllexport) void RtlCaptureContext(void* ContextRecord) {
|
||||
// Zero out the context record so callers don't read garbage.
|
||||
if (!ContextRecord) return;
|
||||
@@ -3813,7 +3767,6 @@ __declspec(dllexport) int GetLocaleInfoA(unsigned int Locale, DWORD LCType,
|
||||
|
||||
static int gFlsUsed[FLS_MAX_SLOTS]; // 0 = free, 1 = allocated
|
||||
|
||||
// Implements the fls Alloc operation.
|
||||
__declspec(dllexport) DWORD FlsAlloc(void* lpCallback) {
|
||||
(void)lpCallback;
|
||||
for (int i = 0; i < FLS_MAX_SLOTS; i++) {
|
||||
@@ -3827,7 +3780,6 @@ __declspec(dllexport) DWORD FlsAlloc(void* lpCallback) {
|
||||
return (DWORD)FLS_OUT_OF_INDEXES;
|
||||
}
|
||||
|
||||
// Implements the fls Get Value operation.
|
||||
__declspec(dllexport) void* FlsGetValue(DWORD dwFlsIndex) {
|
||||
if (dwFlsIndex >= FLS_MAX_SLOTS || !gFlsUsed[dwFlsIndex]) {
|
||||
gLastError = 87; // ERROR_INVALID_PARAMETER
|
||||
@@ -3836,7 +3788,6 @@ __declspec(dllexport) void* FlsGetValue(DWORD dwFlsIndex) {
|
||||
return K32ThreadEnv()->FlsSlots[dwFlsIndex];
|
||||
}
|
||||
|
||||
// Implements the fls Set Value operation.
|
||||
__declspec(dllexport) BOOL FlsSetValue(DWORD dwFlsIndex, void* lpFlsData) {
|
||||
if (dwFlsIndex >= FLS_MAX_SLOTS || !gFlsUsed[dwFlsIndex]) {
|
||||
gLastError = 87;
|
||||
@@ -3846,7 +3797,6 @@ __declspec(dllexport) BOOL FlsSetValue(DWORD dwFlsIndex, void* lpFlsData) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Implements the fls Free operation.
|
||||
__declspec(dllexport) BOOL FlsFree(DWORD dwFlsIndex) {
|
||||
if (dwFlsIndex >= FLS_MAX_SLOTS || !gFlsUsed[dwFlsIndex]) {
|
||||
gLastError = 87;
|
||||
@@ -3861,5 +3811,4 @@ __declspec(dllexport) BOOL FlsFree(DWORD dwFlsIndex) {
|
||||
// We have no security cookie; return the pointer unchanged.
|
||||
|
||||
__declspec(dllexport) void* EncodePointer(void* Ptr) { return Ptr; }
|
||||
// Implements the decode Pointer operation.
|
||||
__declspec(dllexport) void* DecodePointer(void* Ptr) { return Ptr; }
|
||||
@@ -0,0 +1 @@
|
||||
!include $(BASEDIR)\makefile.def
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <ntlite/native_heap.h>
|
||||
#include <ntlite/syscall.h>
|
||||
|
||||
// Types.
|
||||
@@ -43,7 +43,6 @@ __declspec(dllexport) HRESULT CoInitializeEx(void* reserved, unsigned long coIni
|
||||
return S_OK; // Pretend success; no actual apartment is created.
|
||||
}
|
||||
|
||||
// Implements the co Uninitialize operation.
|
||||
__declspec(dllexport) void CoUninitialize(void) {
|
||||
// No-op.
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
TARGETNAME=win32
|
||||
TARGETPATH=obj
|
||||
TARGETTYPE=DYNLINK
|
||||
IMAGE_KIND=DLL
|
||||
FLAVOR=USER
|
||||
EXTRA_CFLAGS=/DUSER_HEAP_EXPORTS /DNTL_EXCEPTION_EXPORTS
|
||||
TARGETLIBS="$(BASEDIR)\bin\syscall.lib" "$(BASEDIR)\bin\libc.lib"
|
||||
EXTRA_LFLAGS=/SUBSYSTEM:NATIVE /DYNAMICBASE /HIGHENTROPYVA
|
||||
SOURCES=@/code/user/runtime/heap.c @/code/user/runtime/heap_exports.c @/code/user/runtime/native_exception.c @/code/user/personalities/win32/gdi32.c @/code/user/personalities/win32/kernel32.c @/code/user/personalities/win32/ole32.c \
|
||||
@/code/user/personalities/win32/user32.c @/code/user/personalities/win32/winmm.c @/code/user/personalities/win32/winmm_waveout.c
|
||||
@@ -14,7 +14,6 @@ typedef struct tagPAINTSTRUCT {
|
||||
HDC hdc; BOOL fErase; RECT rcPaint; BOOL fRestore; BOOL fIncUpdate; char res[32];
|
||||
} PAINTSTRUCT;
|
||||
|
||||
// Implements the user32 Debug Print A operation.
|
||||
static void User32DebugPrintA(const char* s) {
|
||||
if (!s) return;
|
||||
SYS_PRINT_DEBUG_REQUEST req = { sizeof(SYS_PRINT_DEBUG_REQUEST), SysSvFromCstr(s) };
|
||||
@@ -31,7 +30,6 @@ __declspec(dllexport) HDC BeginPaint(HWND hwnd, PAINTSTRUCT* lpPaint) {
|
||||
return (HDC)1;
|
||||
}
|
||||
|
||||
// Implements the end Paint operation.
|
||||
__declspec(dllexport) BOOL EndPaint(HWND hwnd, const PAINTSTRUCT* lp) { (void)hwnd; (void)lp; return 1; }
|
||||
|
||||
// --- Message Loop Stubs ---.
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <ntlite/native_heap.h>
|
||||
#include <string.h>
|
||||
#include <intrin.h>
|
||||
#include <ntlite/syscall.h>
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
DIRS=native_runtime posix_start
|
||||
@@ -94,7 +94,6 @@ static void SplitBlock(HEAP_BLOCK* block, size_t size) {
|
||||
block->Next = tail;
|
||||
}
|
||||
|
||||
// Implements the coalesce Block operation.
|
||||
static void CoalesceBlock(HEAP_BLOCK* block) {
|
||||
if (block->Next && block->Next->Magic == USER_HEAP_MAGIC && block->Next->Free) {
|
||||
HEAP_BLOCK* next = block->Next;
|
||||
@@ -111,7 +110,6 @@ static void CoalesceBlock(HEAP_BLOCK* block) {
|
||||
}
|
||||
}
|
||||
|
||||
// Implements the add Segment operation.
|
||||
static HEAP_SEGMENT* AddSegment(size_t payloadSize) {
|
||||
size_t need = sizeof(HEAP_SEGMENT) + sizeof(HEAP_BLOCK) + AlignSize(payloadSize);
|
||||
size_t pages = (need + USER_HEAP_PAGE_SIZE - 1) / USER_HEAP_PAGE_SIZE;
|
||||
@@ -160,7 +158,6 @@ static void RemoveSegmentIfEmpty(HEAP_SEGMENT* seg) {
|
||||
SysFreeVm(seg);
|
||||
}
|
||||
|
||||
// Implements the malloc operation.
|
||||
void* _malloc(size_t size) {
|
||||
if (!size) return 0;
|
||||
size = AlignSize(size);
|
||||
@@ -209,7 +206,6 @@ void _free(void* ptr) {
|
||||
HeapUnlock();
|
||||
}
|
||||
|
||||
// Implements the calloc operation.
|
||||
void* _calloc(size_t count, size_t size) {
|
||||
if (count && size > ((size_t)-1) / count)
|
||||
return 0;
|
||||
@@ -241,7 +237,6 @@ size_t _malloc_usable_size(const void* ptr) {
|
||||
return size;
|
||||
}
|
||||
|
||||
// Implements the realloc operation.
|
||||
void* _realloc(void* ptr, size_t newSize) {
|
||||
if (!ptr) return _malloc(newSize);
|
||||
if (!newSize) {
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
!include $(BASEDIR)\makefile.def
|
||||
@@ -0,0 +1,5 @@
|
||||
TARGETNAME=native_runtime
|
||||
TARGETPATH=obj
|
||||
TARGETTYPE=LIBRARY
|
||||
FLAVOR=USER
|
||||
SOURCES=@/code/user/runtime/heap.c @/code/user/runtime/heap_exports.c @/code/user/runtime/native_exception.c @/code/user/runtime/native_start.c
|
||||
@@ -0,0 +1 @@
|
||||
!include $(BASEDIR)\makefile.def
|
||||
@@ -0,0 +1,5 @@
|
||||
TARGETNAME=posix_start
|
||||
TARGETPATH=obj
|
||||
TARGETTYPE=LIBRARY
|
||||
FLAVOR=POSIX
|
||||
SOURCES=@/code/user/runtime/posix_start.c
|
||||
@@ -0,0 +1 @@
|
||||
DIRS=dynamic static
|
||||
@@ -0,0 +1 @@
|
||||
!include $(BASEDIR)\makefile.def
|
||||
@@ -0,0 +1,12 @@
|
||||
TARGETNAME=syscall
|
||||
TARGETPATH=obj
|
||||
TARGETTYPE=DYNLINK
|
||||
IMAGE_KIND=DLL
|
||||
FLAVOR=USER
|
||||
EXTRA_CFLAGS=/DNTL_SYSCALL_BUILD
|
||||
TARGETLIBS="$(BASEDIR)\bin\libc.lib"
|
||||
EXTRA_LFLAGS=/SUBSYSTEM:NATIVE /DYNAMICBASE /HIGHENTROPYVA
|
||||
EXTRA_ASFLAGS=/I"$(BASEDIR)\code\user\syscall"
|
||||
INCLUDES=$(BASEDIR)\code\user\syscall
|
||||
SOURCES=@/code/user/syscall/pe_loader.c @/code/user/syscall/module_loader.c @/code/user/syscall/process_loader.c @/code/user/syscall/resource.c @/code/user/syscall/input_service.c @/code/user/syscall/console_service.c \
|
||||
@/code/user/syscall/desktop.c @/code/user/syscall/path.c @/code/user/syscall/syscall_stubs.asm
|
||||
@@ -0,0 +1 @@
|
||||
!include $(BASEDIR)\makefile.def
|
||||
@@ -0,0 +1,9 @@
|
||||
TARGETNAME=syscall_s
|
||||
TARGETPATH=obj
|
||||
TARGETTYPE=LIBRARY
|
||||
FLAVOR=USER
|
||||
EXTRA_CFLAGS=/DNTL_SYSCALL_STATIC
|
||||
EXTRA_ASFLAGS=/DNTL_SYSCALL_STATIC /I"$(BASEDIR)\code\user\syscall"
|
||||
INCLUDES=$(BASEDIR)\code\user\syscall
|
||||
SOURCES=@/code/user/syscall/pe_loader.c @/code/user/syscall/module_loader.c @/code/user/syscall/process_loader.c @/code/user/syscall/resource.c @/code/user/syscall/input_service.c @/code/user/syscall/console_service.c \
|
||||
@/code/user/syscall/desktop.c @/code/user/syscall/path.c @/code/user/syscall/syscall_stubs.asm
|
||||
@@ -1,4 +1,4 @@
|
||||
; syscall_stubs.asm - user-mode INT 0x40 stubs for syscall.dll and syscall_s.lib (MASM64)
|
||||
; syscall_stubs.asm - user-mode INT 0x2F stubs for syscall.dll and syscall_s.lib (MASM64)
|
||||
;
|
||||
; Calling convention:
|
||||
; RAX = syscall number.
|
||||
@@ -30,7 +30,7 @@ ELSE
|
||||
Sys&name PROC EXPORT
|
||||
ENDIF
|
||||
mov rax, nr
|
||||
int 40h
|
||||
int 02Fh
|
||||
ret
|
||||
Sys&name ENDP
|
||||
ENDM
|
||||
@@ -104,7 +104,7 @@ ENDIF
|
||||
mov rbx, [rsp] ; restore original user RBX
|
||||
add rsp, 8
|
||||
mov rax, SYS_APC_RETURN
|
||||
int 40h
|
||||
int 02Fh
|
||||
|
||||
SysUserApcDispatcher_Failed:
|
||||
jmp SysUserApcDispatcher_Failed
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
DIRS=dwm init posixsrv rssrv
|
||||
@@ -3,7 +3,7 @@
|
||||
#include <stdint.h>
|
||||
#include <ntlite/bootstrap_capabilities.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <ntlite/native_heap.h>
|
||||
#include <string.h>
|
||||
#include <ntlite/desktop.h>
|
||||
#include <ntlite/resource.h>
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
!include $(BASEDIR)\makefile.def
|
||||
@@ -0,0 +1,7 @@
|
||||
TARGETNAME=dwm
|
||||
TARGETPATH=obj
|
||||
TARGETTYPE=PROGRAM
|
||||
FLAVOR=USER
|
||||
TARGETLIBS="$(BASEDIR)\bin\syscall_s.lib" "$(BASEDIR)\bin\libc.lib" "$(BASEDIR)\bin\native_runtime.lib"
|
||||
EXTRA_LFLAGS=/ENTRY:_start
|
||||
SOURCES=@/code/user/system/dwm/dwm.c
|
||||
+328
-102
@@ -21,6 +21,7 @@
|
||||
typedef enum INIT_SERVICE_STATE {
|
||||
InitServiceStopped = 0,
|
||||
InitServiceStarting,
|
||||
InitServiceRestartPending,
|
||||
InitServiceReady,
|
||||
InitServiceStopping,
|
||||
InitServiceFailed,
|
||||
@@ -31,10 +32,16 @@ typedef struct INIT_SERVICE {
|
||||
SYS_HANDLE Process;
|
||||
SYS_HANDLE ControlEndpoint;
|
||||
INIT_SERVICE_STATE State;
|
||||
uint64_t StartedTick;
|
||||
uint64_t RestartTick;
|
||||
} INIT_SERVICE;
|
||||
|
||||
static INIT_SERVICE_CONFIG gServiceConfig;
|
||||
static INIT_SERVICE gServices[INIT_SERVICE_MAX_COUNT];
|
||||
static uint64_t gTickFrequency;
|
||||
static INIT_RESTART_BUDGET gRestartBudgets[INIT_SERVICE_MAX_COUNT];
|
||||
_Static_assert(INIT_SERVICE_MAX_COUNT + 2u <= SYS_WAIT_MANY_MAX_HANDLES,
|
||||
"Service supervisor exceeds WaitMany capacity");
|
||||
|
||||
// Capability sources are referenced by name; the catalog owns READY handles
|
||||
// and borrows kernel-provided bootstrap handles for the lifetime of init.
|
||||
@@ -82,13 +89,12 @@ static void forget_service_grants(const INIT_SERVICE_DESCRIPTOR* provider) {
|
||||
}
|
||||
}
|
||||
|
||||
// Keeps the named READY exports for subsequent dependent services. Ownership of
|
||||
// an export handle passes to the catalog only when retainHandles is true.
|
||||
// READY exports enter the catalog atomically after name and capacity validation.
|
||||
static KSTATUS register_ready_exports(const INIT_SERVICE_DESCRIPTOR* provider,
|
||||
const NTL_SERVICE_CONTROL_MESSAGE* message,
|
||||
const SYS_HANDLE* handles, uint32_t count,
|
||||
bool retainHandles) {
|
||||
if (!provider || !message || count != message->ExportCount) return NTLSTATUS_INVALID_PARAMETER;
|
||||
const NTL_SERVICE_CONTROL_MESSAGE* message,
|
||||
const SYS_HANDLE* handles, uint32_t count) {
|
||||
if (!provider || !message || (count && !handles) || count != message->ExportCount ||
|
||||
count > SYS_PORT_MAX_TRANSFER_HANDLES) return NTLSTATUS_INVALID_PARAMETER;
|
||||
if (gGrantSourceCount + count > INIT_GRANT_SOURCE_MAX) return NTLSTATUS_LIMIT_REACHED;
|
||||
char names[SYS_PORT_MAX_TRANSFER_HANDLES][SYS_STARTUP_CAPABILITY_NAME_MAX + 1u];
|
||||
for (uint32_t i = 0; i < count; ++i) {
|
||||
@@ -100,16 +106,17 @@ static KSTATUS register_ready_exports(const INIT_SERVICE_DESCRIPTOR* provider,
|
||||
memcpy(names[i] + left + 1u, message->ExportNames[i], right + 1u);
|
||||
for (uint32_t j = 0; j < gGrantSourceCount; ++j)
|
||||
if (strcmp(names[i], gGrantSources[j].Name) == 0) return NTLSTATUS_OBJECT_NAME_COLLISION;
|
||||
for (uint32_t j = 0; j < i; ++j)
|
||||
if (strcmp(names[i], names[j]) == 0) return NTLSTATUS_OBJECT_NAME_COLLISION;
|
||||
}
|
||||
for (uint32_t i = 0; i < count; ++i) {
|
||||
KSTATUS status = add_grant_source(names[i], handles[i],
|
||||
SYS_RIGHT_QUERY | SYS_PORT_RIGHT_CONNECT,
|
||||
provider, retainHandles);
|
||||
if (!KSUCCESS(status)) {
|
||||
// All names and available capacity were validated before mutation.
|
||||
forget_service_grants(provider);
|
||||
return status;
|
||||
}
|
||||
INIT_GRANT_SOURCE* source = &gGrantSources[gGrantSourceCount++];
|
||||
memset(source, 0, sizeof(*source));
|
||||
memcpy(source->Name, names[i], strlen(names[i]) + 1u);
|
||||
source->Handle = handles[i];
|
||||
source->RightsCeiling = SYS_RIGHT_QUERY | SYS_PORT_RIGHT_CONNECT;
|
||||
source->Provider = provider;
|
||||
source->OwnsHandle = true;
|
||||
}
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
@@ -296,10 +303,12 @@ static KSTATUS start_service_process(SYS_HANDLE systemVolume,
|
||||
service->Descriptor->GrantCount > SYS_STARTUP_CAPABILITY_MAX - 1u)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
if (service->Process != SYS_HANDLE_INVALID || service->ControlEndpoint != SYS_HANDLE_INVALID ||
|
||||
(service->State != InitServiceStopped && service->State != InitServiceFailed))
|
||||
(service->State != InitServiceStopped && service->State != InitServiceFailed &&
|
||||
service->State != InitServiceRestartPending))
|
||||
return NTLSTATUS_FAILURE;
|
||||
|
||||
service->State = InitServiceStarting;
|
||||
service->StartedTick = SysGetTick();
|
||||
|
||||
SYS_HANDLE controlListener = SYS_HANDLE_INVALID;
|
||||
KSTATUS status = create_control_listener(&controlListener);
|
||||
@@ -341,7 +350,23 @@ static KSTATUS start_service_process(SYS_HANDLE systemVolume,
|
||||
create.StartupCapabilities = startup;
|
||||
create.StartupCapabilityCount = capabilityCount;
|
||||
create.ProcessHandleOut = &process;
|
||||
SYS_HANDLE privilegedContext = SYS_HANDLE_INVALID;
|
||||
if (service->Descriptor->Privileges) {
|
||||
SYS_DERIVE_SECURITY_CONTEXT_REQUEST derive = {0};
|
||||
derive.Size = sizeof(derive);
|
||||
derive.SourceContext = SYS_SECURITY_CONTEXT_CURRENT;
|
||||
derive.EffectivePrivileges = service->Descriptor->Privileges;
|
||||
derive.ContextHandleOut = &privilegedContext;
|
||||
status = SysDeriveSecurityContext(&derive);
|
||||
if (!KSUCCESS(status)) {
|
||||
(void)SysCloseHandle(controlListener);
|
||||
service->State = InitServiceFailed;
|
||||
return status;
|
||||
}
|
||||
create.SecurityContext = privilegedContext;
|
||||
}
|
||||
status = NtlCreateProcessAt(&create);
|
||||
if (privilegedContext != SYS_HANDLE_INVALID) (void)SysCloseHandle(privilegedContext);
|
||||
if (!KSUCCESS(status)) {
|
||||
(void)SysCloseHandle(controlListener);
|
||||
service->State = InitServiceFailed;
|
||||
@@ -364,7 +389,7 @@ static KSTATUS start_service_process(SYS_HANDLE systemVolume,
|
||||
}
|
||||
|
||||
// Waits for one service READY notification and receives its service-defined export handles.
|
||||
static KSTATUS wait_service_ready(INIT_SERVICE* service,
|
||||
static KSTATUS wait_service_ready(INIT_SERVICE* service, uint64_t timeout,
|
||||
SYS_HANDLE* exportsOut,
|
||||
uint32_t exportCapacity,
|
||||
uint32_t* exportCountOut,
|
||||
@@ -392,7 +417,7 @@ static KSTATUS wait_service_ready(INIT_SERVICE* service,
|
||||
receive.HandlesOut = exportsOut;
|
||||
receive.HandleCapacity = exportCapacity;
|
||||
receive.InfoOut = &info;
|
||||
receive.TimeoutMilliseconds = service->Descriptor->ReadyTimeoutMilliseconds;
|
||||
receive.TimeoutMilliseconds = timeout;
|
||||
KSTATUS status = SysReceivePort(&receive);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
|
||||
@@ -415,23 +440,13 @@ static KSTATUS wait_service_ready(INIT_SERVICE* service,
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Every service uses the same process, READY, export and failure path.
|
||||
static KSTATUS start_managed_service(SYS_HANDLE systemVolume,
|
||||
SYS_HANDLE securityContext, INIT_SERVICE* service) {
|
||||
KSTATUS status = start_service_process(systemVolume, securityContext, service);
|
||||
if (!KSUCCESS(status)) {
|
||||
debug_print("[init] process or startup grant creation failed for ");
|
||||
debug_print(service->Descriptor->Id);
|
||||
debug_print(": ");
|
||||
debug_status(status);
|
||||
debug_print("\n");
|
||||
return status;
|
||||
}
|
||||
|
||||
// Consumes one READY record, transferring published handles on success.
|
||||
static KSTATUS finish_service_ready(INIT_SERVICE* service, uint64_t timeout) {
|
||||
KSTATUS status;
|
||||
SYS_HANDLE handles[SYS_PORT_MAX_TRANSFER_HANDLES];
|
||||
uint32_t count = 0;
|
||||
NTL_SERVICE_CONTROL_MESSAGE ready;
|
||||
status = wait_service_ready(service, handles, SYS_PORT_MAX_TRANSFER_HANDLES, &count, &ready);
|
||||
status = wait_service_ready(service, timeout, handles, SYS_PORT_MAX_TRANSFER_HANDLES, &count, &ready);
|
||||
if (!KSUCCESS(status)) {
|
||||
debug_print("[init] READY receive failed for ");
|
||||
debug_print(service->Descriptor->Id);
|
||||
@@ -450,7 +465,7 @@ static KSTATUS start_managed_service(SYS_HANDLE systemVolume,
|
||||
status = NTLSTATUS_INVALID_PARAMETER;
|
||||
}
|
||||
if (KSUCCESS(status)) {
|
||||
status = register_ready_exports(descriptor, &ready, handles, count, true);
|
||||
status = register_ready_exports(descriptor, &ready, handles, count);
|
||||
if (KSUCCESS(status))
|
||||
for (uint32_t i = 0; i < count; ++i) handles[i] = SYS_HANDLE_INVALID;
|
||||
}
|
||||
@@ -469,6 +484,225 @@ static KSTATUS start_managed_service(SYS_HANDLE systemVolume,
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Schedules a bounded retry for failed startup or a matching process exit.
|
||||
static bool queue_service_restart(INIT_SERVICE* service) {
|
||||
if (!service || service->Descriptor->RestartPolicy == InitServiceRestartNever) return false;
|
||||
uint32_t index = (uint32_t)(service - gServices);
|
||||
if (index >= gServiceConfig.ServiceCount ||
|
||||
!InitConsumeRestartBudget(&gRestartBudgets[index], SysGetTick(), gTickFrequency,
|
||||
service->Descriptor->RestartLimit,
|
||||
service->Descriptor->RestartWindowMilliseconds)) return false;
|
||||
uint64_t now = SysGetTick();
|
||||
uint64_t delay = (uint64_t)service->Descriptor->RestartDelayMilliseconds * gTickFrequency;
|
||||
delay = delay / 1000u + (delay % 1000u != 0u);
|
||||
service->RestartTick = delay <= UINT64_MAX - now ? now + delay : UINT64_MAX;
|
||||
service->State = InitServiceRestartPending;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Starts eligible services, leaving READY completion to the supervisor.
|
||||
static KSTATUS launch_services(bool includeDelayed, SYS_HANDLE volume,
|
||||
SYS_HANDLE context) {
|
||||
for (uint32_t order = 0; order < gServiceConfig.StartupCount; ++order) {
|
||||
uint16_t index = gServiceConfig.StartupOrder[order];
|
||||
INIT_SERVICE* service = &gServices[index];
|
||||
const INIT_SERVICE_DESCRIPTOR* descriptor = service->Descriptor;
|
||||
if (service->State != InitServiceStopped ||
|
||||
(descriptor->StartPolicy != InitServiceStartAuto &&
|
||||
(!includeDelayed || descriptor->StartPolicy != InitServiceStartDelayed))) continue;
|
||||
bool pending = false, failed = false;
|
||||
for (uint32_t d = 0; d < descriptor->DependencyCount; ++d) {
|
||||
INIT_SERVICE_STATE state = gServices[descriptor->Dependencies[d]].State;
|
||||
if (state == InitServiceFailed) failed = true;
|
||||
else if (state != InitServiceReady) pending = true;
|
||||
}
|
||||
if (pending && !failed) continue;
|
||||
if (!failed) {
|
||||
for (uint32_t d = 0; d < descriptor->AfterCount; ++d) {
|
||||
const INIT_SERVICE* predecessor = &gServices[descriptor->After[d]];
|
||||
INIT_SERVICE_STATE state = predecessor->State;
|
||||
if (predecessor->Descriptor->StartPolicy == InitServiceStartDisabled) continue;
|
||||
if (state == InitServiceStopped || state == InitServiceStarting ||
|
||||
state == InitServiceRestartPending) {
|
||||
pending = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (pending && !failed) continue;
|
||||
if (failed) {
|
||||
service->State = InitServiceFailed;
|
||||
debug_print("[init] service blocked: ");
|
||||
debug_print(descriptor->Id);
|
||||
debug_print("\n");
|
||||
} else {
|
||||
KSTATUS status = start_service_process(volume, context, service);
|
||||
if (KSUCCESS(status)) continue;
|
||||
debug_print("[init] service launch failed: ");
|
||||
debug_print(descriptor->Id);
|
||||
debug_print(" ");
|
||||
debug_status(status);
|
||||
debug_print("\n");
|
||||
}
|
||||
if (!failed && service->State == InitServiceFailed && queue_service_restart(service)) continue;
|
||||
if (descriptor->Critical) return NTLSTATUS_FAILURE;
|
||||
}
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Tests a transitive hard dependency in the validated acyclic service graph.
|
||||
static bool service_depends_on(uint16_t serviceIndex, uint16_t providerIndex) {
|
||||
const INIT_SERVICE_DESCRIPTOR* desc = &gServiceConfig.Services[serviceIndex];
|
||||
for (uint32_t i = 0; i < desc->DependencyCount; ++i) {
|
||||
uint16_t dep = desc->Dependencies[i];
|
||||
if (dep == providerIndex || service_depends_on(dep, providerIndex)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Critical consumers make their provider's startup a critical dependency.
|
||||
static bool critical_service_affected(uint16_t providerIndex) {
|
||||
if (gServices[providerIndex].Descriptor->Critical) return true;
|
||||
for (uint32_t i = 0; i < gServiceConfig.ServiceCount; ++i)
|
||||
if (gServices[i].Descriptor->Critical &&
|
||||
service_depends_on((uint16_t)i, providerIndex)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Advances one completed READY handshake from the existing Port wait event.
|
||||
static KSTATUS complete_starting_service(INIT_SERVICE* service) {
|
||||
KSTATUS status = finish_service_ready(service, 0u);
|
||||
if (KSUCCESS(status)) {
|
||||
debug_print("service: ready id=");
|
||||
debug_print(service->Descriptor->Id);
|
||||
debug_print("\n");
|
||||
} else {
|
||||
debug_print("[init] service startup failed: ");
|
||||
debug_print(service->Descriptor->Id);
|
||||
debug_print(" ");
|
||||
debug_status(status);
|
||||
debug_print("\n");
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
// Uses the original launch timestamp for each independent READY deadline.
|
||||
static uint64_t starting_timeout_ms(uint64_t now) {
|
||||
uint64_t nearest = SYS_WAIT_TIMEOUT_INFINITE;
|
||||
for (uint32_t i = 0; i < gServiceConfig.ServiceCount; ++i) {
|
||||
const INIT_SERVICE* service = &gServices[i];
|
||||
if (service->State != InitServiceStarting) continue;
|
||||
uint64_t elapsed = now >= service->StartedTick ? now - service->StartedTick : 0u;
|
||||
uint64_t limit = ((uint64_t)service->Descriptor->ReadyTimeoutMilliseconds *
|
||||
gTickFrequency + 999u) / 1000u;
|
||||
if (elapsed >= limit) return 0u;
|
||||
uint64_t remaining = limit - elapsed;
|
||||
uint64_t ms = (remaining / gTickFrequency) * 1000u +
|
||||
((remaining % gTickFrequency) * 1000u + gTickFrequency - 1u) / gTickFrequency;
|
||||
if (ms < nearest) nearest = ms;
|
||||
}
|
||||
return nearest;
|
||||
}
|
||||
|
||||
// Starts due restart attempts and reports a failed critical provider.
|
||||
static KSTATUS start_pending_restarts(SYS_HANDLE volume, SYS_HANDLE context) {
|
||||
uint64_t now = SysGetTick();
|
||||
for (uint32_t i = 0; i < gServiceConfig.ServiceCount; ++i) {
|
||||
INIT_SERVICE* service = &gServices[i];
|
||||
if (service->State != InitServiceRestartPending || now < service->RestartTick) continue;
|
||||
KSTATUS status = start_service_process(volume, context, service);
|
||||
if (KSUCCESS(status)) continue;
|
||||
debug_print("[init] service restart failed: ");
|
||||
debug_print(service->Descriptor->Id);
|
||||
debug_print("\n");
|
||||
if (!queue_service_restart(service) && critical_service_affected((uint16_t)i))
|
||||
return NTLSTATUS_FAILURE;
|
||||
}
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static uint64_t restart_timeout_ms(uint64_t now) {
|
||||
uint64_t nearest = SYS_WAIT_TIMEOUT_INFINITE;
|
||||
for (uint32_t i = 0; i < gServiceConfig.ServiceCount; ++i) {
|
||||
INIT_SERVICE* service = &gServices[i];
|
||||
if (service->State != InitServiceRestartPending) continue;
|
||||
if (now >= service->RestartTick) return 0u;
|
||||
uint64_t ticks = service->RestartTick - now;
|
||||
uint64_t ms = (ticks / gTickFrequency) * 1000u +
|
||||
((ticks % gTickFrequency) * 1000u + gTickFrequency - 1u) / gTickFrequency;
|
||||
if (ms < nearest) nearest = ms;
|
||||
}
|
||||
return nearest;
|
||||
}
|
||||
|
||||
// Expires all overdue handshakes even if another endpoint remains busy.
|
||||
static KSTATUS expire_starting_services(void) {
|
||||
uint64_t now = SysGetTick();
|
||||
for (uint32_t i = 0; i < gServiceConfig.ServiceCount; ++i) {
|
||||
INIT_SERVICE* service = &gServices[i];
|
||||
if (service->State != InitServiceStarting) continue;
|
||||
uint64_t elapsed = now >= service->StartedTick ? now - service->StartedTick : 0u;
|
||||
uint64_t limit = ((uint64_t)service->Descriptor->ReadyTimeoutMilliseconds *
|
||||
gTickFrequency + 999u) / 1000u;
|
||||
if (elapsed < limit) continue;
|
||||
debug_print("[init] READY timeout: ");
|
||||
debug_print(service->Descriptor->Id);
|
||||
debug_print("\n");
|
||||
(void)SysKillProcess(service->Process, 1);
|
||||
close_service_handles(service);
|
||||
service->State = InitServiceFailed;
|
||||
if (!queue_service_restart(service) && critical_service_affected((uint16_t)i))
|
||||
return NTLSTATUS_FAILURE;
|
||||
}
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Waits for the automatic graph to settle before the shell starts.
|
||||
static KSTATUS start_automatic_services(SYS_HANDLE volume, SYS_HANDLE context) {
|
||||
for (;;) {
|
||||
KSTATUS status = start_pending_restarts(volume, context);
|
||||
if (KSUCCESS(status)) status = launch_services(false, volume, context);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
SYS_HANDLE waits[INIT_SERVICE_MAX_COUNT];
|
||||
uint16_t indices[INIT_SERVICE_MAX_COUNT];
|
||||
uint32_t count = 0;
|
||||
for (uint32_t i = 0; i < gServiceConfig.ServiceCount; ++i) {
|
||||
if (gServices[i].State != InitServiceStarting) continue;
|
||||
waits[count] = gServices[i].ControlEndpoint;
|
||||
indices[count++] = (uint16_t)i;
|
||||
}
|
||||
if (!count) {
|
||||
uint64_t retry = restart_timeout_ms(SysGetTick());
|
||||
if (retry == SYS_WAIT_TIMEOUT_INFINITE) return NTLSTATUS_SUCCESS;
|
||||
if (retry) (void)SysSleep(retry);
|
||||
continue;
|
||||
}
|
||||
uint32_t selected = 0;
|
||||
SYS_WAIT_MANY_REQUEST request = {0};
|
||||
request.Size = sizeof(request);
|
||||
request.Handles = waits;
|
||||
request.Count = count;
|
||||
request.SignaledIndexOut = &selected;
|
||||
uint64_t now = SysGetTick();
|
||||
uint64_t readyWait = starting_timeout_ms(now);
|
||||
uint64_t retryWait = restart_timeout_ms(now);
|
||||
request.TimeoutMilliseconds = readyWait < retryWait ? readyWait : retryWait;
|
||||
if (request.TimeoutMilliseconds) {
|
||||
status = SysWaitMany(&request);
|
||||
if (status != NTLSTATUS_TIMEOUT && !KSUCCESS(status)) return status;
|
||||
if (KSUCCESS(status)) {
|
||||
INIT_SERVICE* service = &gServices[indices[selected]];
|
||||
if (!KSUCCESS(complete_starting_service(service)) &&
|
||||
!queue_service_restart(service) && critical_service_affected(indices[selected]))
|
||||
return NTLSTATUS_FAILURE;
|
||||
}
|
||||
}
|
||||
status = expire_starting_services();
|
||||
if (!KSUCCESS(status)) return status;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Requests graceful STOP and bounds the fallback termination of any service.
|
||||
static void stop_service(INIT_SERVICE* service) {
|
||||
if (!service || service->Process == SYS_HANDLE_INVALID) return;
|
||||
@@ -482,10 +716,17 @@ static void stop_service(INIT_SERVICE* service) {
|
||||
send.EndpointHandle = service->ControlEndpoint;
|
||||
send.Payload = &message;
|
||||
send.PayloadLength = sizeof(message);
|
||||
uint64_t started = SysGetTick();
|
||||
uint64_t duration = ((uint64_t)service->Descriptor->StopTimeoutMilliseconds *
|
||||
gTickFrequency + 999u) / 1000u;
|
||||
send.TimeoutMilliseconds = service->Descriptor->StopTimeoutMilliseconds;
|
||||
(void)SysSendPort(&send);
|
||||
KSTATUS status = SysWaitSingle(service->Process, SYS_WAIT_FLAG_NONE,
|
||||
service->Descriptor->StopTimeoutMilliseconds);
|
||||
uint64_t elapsed = SysGetTick() - started;
|
||||
uint64_t remaining = elapsed >= duration ? 0u : duration - elapsed;
|
||||
uint64_t remainingMs = gTickFrequency ?
|
||||
(remaining / gTickFrequency) * 1000u +
|
||||
((remaining % gTickFrequency) * 1000u + gTickFrequency - 1u) / gTickFrequency : 0u;
|
||||
KSTATUS status = SysWaitSingle(service->Process, SYS_WAIT_FLAG_NONE, remainingMs);
|
||||
if (!KSUCCESS(status)) {
|
||||
(void)SysKillProcess(service->Process, 1);
|
||||
status = SysWaitSingle(service->Process, SYS_WAIT_FLAG_NONE, 2000u);
|
||||
@@ -501,17 +742,7 @@ static void stop_all_services(void) {
|
||||
stop_service(&gServices[gServiceConfig.StartupOrder[order - 1u]]);
|
||||
}
|
||||
|
||||
// Tests a transitive hard dependency in the validated acyclic service graph.
|
||||
static bool service_depends_on(uint16_t serviceIndex, uint16_t providerIndex) {
|
||||
const INIT_SERVICE_DESCRIPTOR* desc = &gServiceConfig.Services[serviceIndex];
|
||||
for (uint32_t i = 0; i < desc->DependencyCount; ++i) {
|
||||
uint16_t dep = desc->Dependencies[i];
|
||||
if (dep == providerIndex || service_depends_on(dep, providerIndex)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Before restarting a provider, quiesce every transitive consumer in reverse order.
|
||||
// Quiesces dependent services before the provider generation changes.
|
||||
static void stop_service_dependents(uint16_t providerIndex) {
|
||||
for (uint32_t order = gServiceConfig.StartupCount; order > 0; --order) {
|
||||
uint16_t index = gServiceConfig.StartupOrder[order - 1u];
|
||||
@@ -520,19 +751,7 @@ static void stop_service_dependents(uint16_t providerIndex) {
|
||||
}
|
||||
}
|
||||
|
||||
// Starts eligible descendants again using the dependency-first startup order.
|
||||
static KSTATUS restart_service_dependents(uint16_t providerIndex, SYS_HANDLE volume,
|
||||
SYS_HANDLE context) {
|
||||
for (uint32_t order = 0; order < gServiceConfig.StartupCount; ++order) {
|
||||
uint16_t index = gServiceConfig.StartupOrder[order];
|
||||
if (!service_depends_on(index, providerIndex)) continue;
|
||||
KSTATUS status = start_managed_service(volume, context, &gServices[index]);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
}
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Starts the configured native session process from the system-volume capability.
|
||||
// Starts the configured session image from the system volume.
|
||||
static KSTATUS start_configured_child(SYS_HANDLE systemVolume, SYS_STRING_VIEW path, SYS_HANDLE* processOut) {
|
||||
NTL_CREATE_PROCESS_AT_REQUEST create;
|
||||
memset(&create, 0, sizeof(create));
|
||||
@@ -627,47 +846,32 @@ void _start(void) {
|
||||
exit_now(1);
|
||||
}
|
||||
|
||||
SYS_STRING_VIEW childPath = SysGetPeb()->CommandLine;
|
||||
SYS_STRING_VIEW childPath = SysSvFromCstr(gServiceConfig.SessionImage);
|
||||
if (!childPath.Data || childPath.Length == 0) {
|
||||
debug_print("[init] FATAL: no configured session process\n");
|
||||
if (unprivilegedServiceContext != SYS_HANDLE_INVALID) (void)SysCloseHandle(unprivilegedServiceContext);
|
||||
exit_now(1);
|
||||
}
|
||||
|
||||
uint32_t autoCount = gServiceConfig.StartupCount;
|
||||
if (autoCount + 2u > SYS_WAIT_MANY_MAX_HANDLES) {
|
||||
if (gServiceConfig.StartupCount + 2u > SYS_WAIT_MANY_MAX_HANDLES) {
|
||||
debug_print("[init] FATAL: service graph exceeds supervision wait capacity\n");
|
||||
exit_now(1);
|
||||
}
|
||||
for (uint32_t i = 0; i < gServiceConfig.ServiceCount; ++i)
|
||||
initialize_service(&gServices[i], &gServiceConfig.Services[i]);
|
||||
|
||||
for (uint32_t order = 0; order < gServiceConfig.StartupCount; ++order) {
|
||||
uint16_t index = gServiceConfig.StartupOrder[order];
|
||||
INIT_SERVICE* service = &gServices[index];
|
||||
status = start_managed_service(systemVolume, unprivilegedServiceContext, service);
|
||||
debug_print("[init] service ");
|
||||
debug_print(service->Descriptor->Id);
|
||||
if (KSUCCESS(status)) debug_print(" READY\n");
|
||||
else {
|
||||
debug_print(" startup failed: ");
|
||||
debug_status(status);
|
||||
debug_print("\n");
|
||||
if (service->Descriptor->Critical) {
|
||||
stop_all_services();
|
||||
exit_now(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
INIT_RESTART_BUDGET restartBudgets[INIT_SERVICE_MAX_COUNT] = {0};
|
||||
uint64_t tickFrequency = 0;
|
||||
status = SysGetTickFrequency(&tickFrequency);
|
||||
if (!KSUCCESS(status) || tickFrequency == 0u) {
|
||||
status = SysGetTickFrequency(&gTickFrequency);
|
||||
if (!KSUCCESS(status) || gTickFrequency == 0u || gTickFrequency > UINT64_MAX / 600000u) {
|
||||
debug_print("[init] FATAL: monotonic clock unavailable for supervision\n");
|
||||
stop_all_services();
|
||||
exit_now(1);
|
||||
}
|
||||
status = start_automatic_services(systemVolume, unprivilegedServiceContext);
|
||||
if (!KSUCCESS(status)) {
|
||||
debug_print("[init] FATAL: automatic service startup failed\n");
|
||||
stop_all_services();
|
||||
exit_now(1);
|
||||
}
|
||||
for (;;) {
|
||||
SYS_HANDLE child = SYS_HANDLE_INVALID;
|
||||
status = start_configured_child(systemVolume, childPath, &child);
|
||||
@@ -678,10 +882,22 @@ void _start(void) {
|
||||
stop_all_services();
|
||||
exit_now(1);
|
||||
}
|
||||
debug_print("[init] session process started\n");
|
||||
debug_print("init: session started\n");
|
||||
status = launch_services(true, systemVolume, unprivilegedServiceContext);
|
||||
if (!KSUCCESS(status)) {
|
||||
stop_all_services();
|
||||
exit_now(1);
|
||||
}
|
||||
|
||||
bool restartChild = false;
|
||||
for (;;) {
|
||||
status = start_pending_restarts(systemVolume, unprivilegedServiceContext);
|
||||
if (KSUCCESS(status)) status = launch_services(true, systemVolume, unprivilegedServiceContext);
|
||||
if (!KSUCCESS(status) || !KSUCCESS(expire_starting_services())) {
|
||||
debug_print("[init] FATAL: delayed service dependency failed\n");
|
||||
stop_all_services();
|
||||
exit_now(1);
|
||||
}
|
||||
SYS_HANDLE waits[SYS_WAIT_MANY_MAX_HANDLES];
|
||||
uint32_t kinds[SYS_WAIT_MANY_MAX_HANDLES];
|
||||
uint32_t waitCount = 0;
|
||||
@@ -690,9 +906,11 @@ void _start(void) {
|
||||
waits[waitCount] = child;
|
||||
kinds[waitCount++] = UINT32_MAX - 1u;
|
||||
for (uint32_t i = 0; i < gServiceConfig.ServiceCount; ++i) {
|
||||
if (gServices[i].State != InitServiceReady ||
|
||||
gServices[i].Process == SYS_HANDLE_INVALID) continue;
|
||||
waits[waitCount] = gServices[i].Process;
|
||||
if (gServices[i].Process == SYS_HANDLE_INVALID) continue;
|
||||
if (gServices[i].State != InitServiceReady &&
|
||||
gServices[i].State != InitServiceStarting) continue;
|
||||
waits[waitCount] = gServices[i].State == InitServiceStarting ?
|
||||
gServices[i].ControlEndpoint : gServices[i].Process;
|
||||
kinds[waitCount++] = i;
|
||||
}
|
||||
uint32_t signaled = 0;
|
||||
@@ -701,8 +919,12 @@ void _start(void) {
|
||||
wait.Handles = waits;
|
||||
wait.Count = waitCount;
|
||||
wait.SignaledIndexOut = &signaled;
|
||||
wait.TimeoutMilliseconds = SYS_WAIT_TIMEOUT_INFINITE;
|
||||
uint64_t now = SysGetTick();
|
||||
uint64_t readyWait = starting_timeout_ms(now);
|
||||
uint64_t retryWait = restart_timeout_ms(now);
|
||||
wait.TimeoutMilliseconds = readyWait < retryWait ? readyWait : retryWait;
|
||||
status = SysWaitMany(&wait);
|
||||
if (status == NTLSTATUS_TIMEOUT) continue;
|
||||
if (!KSUCCESS(status)) {
|
||||
debug_print("[init] FATAL: service supervision wait failed\n");
|
||||
(void)SysKillProcess(child, 1);
|
||||
@@ -742,6 +964,19 @@ void _start(void) {
|
||||
}
|
||||
uint16_t failedIndex = (uint16_t)kinds[signaled];
|
||||
INIT_SERVICE* failed = &gServices[failedIndex];
|
||||
if (failed->State == InitServiceStarting) {
|
||||
KSTATUS readyStatus = complete_starting_service(failed);
|
||||
if (!KSUCCESS(readyStatus) && !queue_service_restart(failed) &&
|
||||
critical_service_affected(failedIndex)) {
|
||||
debug_print("[init] FATAL: critical service READY failed\n");
|
||||
(void)SysKillProcess(child, 1);
|
||||
(void)SysWait(child, SYS_WAIT_FLAG_NONE);
|
||||
(void)SysCloseHandle(child);
|
||||
stop_all_services();
|
||||
exit_now(1);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
int32_t exitCode = -1;
|
||||
(void)SysGetProcessExitCode(failed->Process, &exitCode);
|
||||
debug_print("[init] service exited: ");
|
||||
@@ -752,21 +987,12 @@ void _start(void) {
|
||||
stop_service_dependents(failedIndex);
|
||||
close_service_handles(failed);
|
||||
failed->State = InitServiceFailed;
|
||||
if (failed->Descriptor->RestartPolicy == InitServiceRestartOnFailure &&
|
||||
InitConsumeRestartBudget(&restartBudgets[failedIndex],
|
||||
SysGetTick(), tickFrequency)) {
|
||||
status = start_managed_service(systemVolume, unprivilegedServiceContext, failed);
|
||||
if (KSUCCESS(status))
|
||||
status = restart_service_dependents(failedIndex, systemVolume, unprivilegedServiceContext);
|
||||
if (KSUCCESS(status)) continue;
|
||||
debug_print("[init] service restart failed\n");
|
||||
}
|
||||
const INIT_SERVICE_RESTART_POLICY restart = failed->Descriptor->RestartPolicy;
|
||||
if ((restart == InitServiceRestartAlways ||
|
||||
(restart == InitServiceRestartOnFailure && exitCode != 0)) &&
|
||||
queue_service_restart(failed)) continue;
|
||||
// Any affected critical service is a fatal dependency loss.
|
||||
bool fatal = failed->Descriptor->Critical;
|
||||
for (uint32_t i = 0; i < gServiceConfig.ServiceCount; ++i)
|
||||
if (gServices[i].Descriptor->Critical && service_depends_on((uint16_t)i, failedIndex))
|
||||
fatal = true;
|
||||
if (fatal) {
|
||||
if (critical_service_affected(failedIndex)) {
|
||||
(void)SysKillProcess(child, 1);
|
||||
(void)SysWait(child, SYS_WAIT_FLAG_NONE);
|
||||
(void)SysCloseHandle(child);
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
!include $(BASEDIR)\makefile.def
|
||||
@@ -1,19 +1,26 @@
|
||||
// Service restart budget: three automatic attempts in any sliding five-minute interval.
|
||||
// A per-service sliding restart budget with explicit count and time window.
|
||||
|
||||
#pragma once
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define INIT_RESTART_MAX_ATTEMPTS 16u
|
||||
|
||||
typedef struct INIT_RESTART_BUDGET {
|
||||
uint64_t Attempts[3];
|
||||
uint64_t Attempts[INIT_RESTART_MAX_ATTEMPTS];
|
||||
uint32_t Count;
|
||||
} INIT_RESTART_BUDGET;
|
||||
|
||||
static inline bool InitConsumeRestartBudget(INIT_RESTART_BUDGET* budget,
|
||||
uint64_t now, uint64_t frequency) {
|
||||
if (!budget || !frequency || budget->Count > 3u ||
|
||||
frequency > UINT64_MAX / 300u) return false;
|
||||
uint64_t window = frequency * 300u;
|
||||
uint64_t now, uint64_t frequency,
|
||||
uint32_t limit, uint32_t windowMilliseconds) {
|
||||
if (!budget || !frequency || !limit || limit > INIT_RESTART_MAX_ATTEMPTS ||
|
||||
!windowMilliseconds || budget->Count > INIT_RESTART_MAX_ATTEMPTS ||
|
||||
frequency > UINT64_MAX / windowMilliseconds)
|
||||
return false;
|
||||
uint64_t window = frequency * windowMilliseconds;
|
||||
window = window / 1000u + (window % 1000u != 0u);
|
||||
if (!window) window = 1;
|
||||
uint32_t keep = 0;
|
||||
for (uint32_t i = 0; i < budget->Count; ++i) {
|
||||
uint64_t started = budget->Attempts[i];
|
||||
@@ -21,7 +28,7 @@ static inline bool InitConsumeRestartBudget(INIT_RESTART_BUDGET* budget,
|
||||
budget->Attempts[keep++] = started;
|
||||
}
|
||||
budget->Count = keep;
|
||||
if (keep == 3u) return false;
|
||||
if (keep >= limit) return false;
|
||||
budget->Attempts[budget->Count++] = now;
|
||||
return true;
|
||||
}
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <string.h>
|
||||
#include <ntlite/status.h>
|
||||
|
||||
#define INIT_SERVICE_KEY_NAME 0x00000001u
|
||||
#define INIT_SERVICE_KEY_IMAGE 0x00000002u
|
||||
#define INIT_SERVICE_KEY_START 0x00000004u
|
||||
#define INIT_SERVICE_KEY_CRITICAL 0x00000008u
|
||||
@@ -16,18 +17,25 @@
|
||||
#define INIT_SERVICE_KEY_REQUIRES 0x00000080u
|
||||
#define INIT_SERVICE_KEY_GRANTS 0x00000100u
|
||||
#define INIT_SERVICE_KEY_EXPORTS 0x00000200u
|
||||
#define INIT_SERVICE_REQUIRED_KEYS 0x000003FEu
|
||||
#define INIT_SERVICE_KEY_AFTER 0x00000400u
|
||||
#define INIT_SERVICE_KEY_PRIVILEGES 0x00000800u
|
||||
#define INIT_SERVICE_KEY_RESTART_LIMIT 0x00001000u
|
||||
#define INIT_SERVICE_KEY_RESTART_WINDOW 0x00002000u
|
||||
#define INIT_SERVICE_KEY_RESTART_DELAY 0x00004000u
|
||||
|
||||
typedef struct INIT_PARSED_SERVICE {
|
||||
INIT_SERVICE_DESCRIPTOR Descriptor;
|
||||
char DependencyIds[INIT_SERVICE_DEPENDENCY_MAX][INIT_SERVICE_ID_MAX + 1u];
|
||||
uint32_t DependencyCount;
|
||||
char AfterIds[INIT_SERVICE_DEPENDENCY_MAX][INIT_SERVICE_ID_MAX + 1u];
|
||||
uint32_t AfterCount;
|
||||
uint32_t PresentKeys;
|
||||
uint32_t SectionLine;
|
||||
} INIT_PARSED_SERVICE;
|
||||
|
||||
static char gServicesIniBuffer[INIT_SERVICES_MAX_FILE_BYTES + 1u];
|
||||
static INIT_PARSED_SERVICE gParsedServices[INIT_SERVICE_MAX_COUNT];
|
||||
static char gSessionImage[SYS_PATH_MAX];
|
||||
|
||||
// Returns whether one byte is horizontal configuration whitespace.
|
||||
static bool is_space(char c) {
|
||||
@@ -129,29 +137,66 @@ static int32_t find_parsed_service(const INIT_PARSED_SERVICE* services,
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Parses the comma-separated hard dependency list for one service.
|
||||
static bool parse_requires(char* value, INIT_PARSED_SERVICE* service) {
|
||||
service->DependencyCount = 0;
|
||||
// Parses distinct service IDs for one dependency edge type.
|
||||
static bool parse_edges(char* value,
|
||||
char ids[INIT_SERVICE_DEPENDENCY_MAX][INIT_SERVICE_ID_MAX + 1u],
|
||||
uint32_t* countOut) {
|
||||
*countOut = 0;
|
||||
if (!*value) return true;
|
||||
|
||||
char* cursor = value;
|
||||
for (;;) {
|
||||
char* comma = strchr(cursor, ',');
|
||||
if (comma) *comma = '\0';
|
||||
char* id = trim_left(cursor);
|
||||
trim_right(id);
|
||||
if (!validate_service_id(id) || service->DependencyCount >= INIT_SERVICE_DEPENDENCY_MAX)
|
||||
if (!validate_service_id(id) || *countOut >= INIT_SERVICE_DEPENDENCY_MAX)
|
||||
return false;
|
||||
|
||||
for (uint32_t i = 0; i < service->DependencyCount; ++i)
|
||||
if (strcmp(service->DependencyIds[i], id) == 0) return false;
|
||||
if (!copy_bounded(service->DependencyIds[service->DependencyCount],
|
||||
sizeof(service->DependencyIds[0]), id))
|
||||
return false;
|
||||
++service->DependencyCount;
|
||||
|
||||
for (uint32_t i = 0; i < *countOut; ++i)
|
||||
if (strcmp(ids[i], id) == 0) return false;
|
||||
if (!copy_bounded(ids[*countOut], sizeof(ids[0]), id)) return false;
|
||||
++*countOut;
|
||||
if (!comma) break;
|
||||
cursor = comma + 1;
|
||||
cursor = comma + 1u;
|
||||
if (!*cursor) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool parse_privileges(char* value, SYS_PRIVILEGES* privilegesOut) {
|
||||
static const struct { const char* Name; SYS_PRIVILEGES Bit; } names[] = {
|
||||
{ "process-open-any", SYS_PRIV_PROCESS_OPEN_ANY },
|
||||
{ "process-enumerate", SYS_PRIV_PROCESS_ENUMERATE },
|
||||
{ "thread-enumerate-any", SYS_PRIV_THREAD_ENUMERATE_ANY },
|
||||
{ "kernel-diagnostics", SYS_PRIV_KERNEL_DIAGNOSTICS },
|
||||
{ "system-power", SYS_PRIV_SYSTEM_POWER },
|
||||
{ "audio-access", SYS_PRIV_AUDIO_ACCESS },
|
||||
{ "audio-master-control", SYS_PRIV_AUDIO_MASTER_CONTROL },
|
||||
{ "console-access", SYS_PRIV_CONSOLE_ACCESS },
|
||||
{ "display-access", SYS_PRIV_DISPLAY_ACCESS },
|
||||
{ "debug-output", SYS_PRIV_DEBUG_OUTPUT },
|
||||
{ "raw-storage", SYS_PRIV_RAW_STORAGE },
|
||||
{ "filesystem-provider", SYS_PRIV_FILESYSTEM_PROVIDER },
|
||||
{ "network-access", SYS_PRIV_NETWORK_ACCESS }
|
||||
};
|
||||
*privilegesOut = 0;
|
||||
if (!*value) return true;
|
||||
char* cursor = value;
|
||||
for (;;) {
|
||||
char* comma = strchr(cursor, ',');
|
||||
if (comma) *comma = '\0';
|
||||
char* name = trim_left(cursor);
|
||||
trim_right(name);
|
||||
bool found = false;
|
||||
for (uint32_t i = 0; i < sizeof(names) / sizeof(names[0]); ++i) {
|
||||
if (strcmp(names[i].Name, name) != 0) continue;
|
||||
if (*privilegesOut & names[i].Bit) return false;
|
||||
*privilegesOut |= names[i].Bit;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
if (!found) return false;
|
||||
if (!comma) break;
|
||||
cursor = comma + 1u;
|
||||
if (!*cursor) return false;
|
||||
}
|
||||
return true;
|
||||
@@ -215,7 +260,11 @@ static bool parse_exports(char* value, INIT_PARSED_SERVICE* service) {
|
||||
static bool parse_service_key(INIT_PARSED_SERVICE* service, char* key, char* value) {
|
||||
uint32_t bit = 0;
|
||||
|
||||
if (strcmp(key, "image") == 0) {
|
||||
if (strcmp(key, "name") == 0) {
|
||||
bit = INIT_SERVICE_KEY_NAME;
|
||||
if (!*value || !copy_bounded(service->Descriptor.Name, sizeof(service->Descriptor.Name), value))
|
||||
return false;
|
||||
} else if (strcmp(key, "image") == 0) {
|
||||
bit = INIT_SERVICE_KEY_IMAGE;
|
||||
if (!validate_image_path(value) ||
|
||||
!copy_bounded(service->Descriptor.ImagePath, sizeof(service->Descriptor.ImagePath), value))
|
||||
@@ -223,6 +272,7 @@ static bool parse_service_key(INIT_PARSED_SERVICE* service, char* key, char* val
|
||||
} else if (strcmp(key, "start") == 0) {
|
||||
bit = INIT_SERVICE_KEY_START;
|
||||
if (strcmp(value, "auto") == 0) service->Descriptor.StartPolicy = InitServiceStartAuto;
|
||||
else if (strcmp(value, "delayed") == 0) service->Descriptor.StartPolicy = InitServiceStartDelayed;
|
||||
else if (strcmp(value, "disabled") == 0) service->Descriptor.StartPolicy = InitServiceStartDisabled;
|
||||
else return false;
|
||||
} else if (strcmp(key, "critical") == 0) {
|
||||
@@ -232,7 +282,17 @@ static bool parse_service_key(INIT_PARSED_SERVICE* service, char* key, char* val
|
||||
bit = INIT_SERVICE_KEY_RESTART;
|
||||
if (strcmp(value, "never") == 0) service->Descriptor.RestartPolicy = InitServiceRestartNever;
|
||||
else if (strcmp(value, "on-failure") == 0) service->Descriptor.RestartPolicy = InitServiceRestartOnFailure;
|
||||
else if (strcmp(value, "always") == 0) service->Descriptor.RestartPolicy = InitServiceRestartAlways;
|
||||
else return false;
|
||||
} else if (strcmp(key, "restart_limit") == 0) {
|
||||
bit = INIT_SERVICE_KEY_RESTART_LIMIT;
|
||||
if (!parse_u32(value, 1u, 16u, &service->Descriptor.RestartLimit)) return false;
|
||||
} else if (strcmp(key, "restart_window_ms") == 0) {
|
||||
bit = INIT_SERVICE_KEY_RESTART_WINDOW;
|
||||
if (!parse_u32(value, 1u, 3600000u, &service->Descriptor.RestartWindowMilliseconds)) return false;
|
||||
} else if (strcmp(key, "restart_delay_ms") == 0) {
|
||||
bit = INIT_SERVICE_KEY_RESTART_DELAY;
|
||||
if (!parse_u32(value, 0u, 600000u, &service->Descriptor.RestartDelayMilliseconds)) return false;
|
||||
} else if (strcmp(key, "ready_timeout_ms") == 0) {
|
||||
bit = INIT_SERVICE_KEY_READY_TIMEOUT;
|
||||
if (!parse_u32(value, 1u, 600000u, &service->Descriptor.ReadyTimeoutMilliseconds)) return false;
|
||||
@@ -241,7 +301,13 @@ static bool parse_service_key(INIT_PARSED_SERVICE* service, char* key, char* val
|
||||
if (!parse_u32(value, 1u, 600000u, &service->Descriptor.StopTimeoutMilliseconds)) return false;
|
||||
} else if (strcmp(key, "requires") == 0) {
|
||||
bit = INIT_SERVICE_KEY_REQUIRES;
|
||||
if (!parse_requires(value, service)) return false;
|
||||
if (!parse_edges(value, service->DependencyIds, &service->DependencyCount)) return false;
|
||||
} else if (strcmp(key, "after") == 0) {
|
||||
bit = INIT_SERVICE_KEY_AFTER;
|
||||
if (!parse_edges(value, service->AfterIds, &service->AfterCount)) return false;
|
||||
} else if (strcmp(key, "privileges") == 0) {
|
||||
bit = INIT_SERVICE_KEY_PRIVILEGES;
|
||||
if (!parse_privileges(value, &service->Descriptor.Privileges)) return false;
|
||||
} else if (strcmp(key, "grants") == 0) {
|
||||
bit = INIT_SERVICE_KEY_GRANTS;
|
||||
if (!parse_grants(value, service)) return false;
|
||||
@@ -261,6 +327,9 @@ static bool parse_service_key(INIT_PARSED_SERVICE* service, char* key, char* val
|
||||
static KSTATUS parse_services_ini(char* buffer, uint32_t* serviceCountOut, uint32_t* errorLineOut) {
|
||||
uint32_t serviceCount = 0;
|
||||
INIT_PARSED_SERVICE* current = NULL;
|
||||
bool inSession = false;
|
||||
bool sessionSeen = false;
|
||||
bool sessionImageSeen = false;
|
||||
uint32_t lineNumber = 0;
|
||||
char* line = buffer;
|
||||
|
||||
@@ -279,14 +348,22 @@ static KSTATUS parse_services_ini(char* buffer, uint32_t* serviceCountOut, uint3
|
||||
// Blank and whole-line comments are the only ignored line forms.
|
||||
} else if (*text == '[') {
|
||||
size_t length = strlen(text);
|
||||
if (length < 11u || text[length - 1u] != ']' || strchr(text + 1, ']') != &text[length - 1u]) {
|
||||
if (length < 3u || text[length - 1u] != ']' || strchr(text + 1, ']') != &text[length - 1u]) {
|
||||
valid = false;
|
||||
} else {
|
||||
text[length - 1u] = '\0';
|
||||
const char prefix[] = "service.";
|
||||
if (strncmp(text + 1, prefix, sizeof(prefix) - 1u) != 0) {
|
||||
if (strcmp(text + 1, "session") == 0) {
|
||||
if (sessionSeen) valid = false;
|
||||
else {
|
||||
sessionSeen = true;
|
||||
inSession = true;
|
||||
current = NULL;
|
||||
}
|
||||
} else if (strncmp(text + 1, prefix, sizeof(prefix) - 1u) != 0) {
|
||||
valid = false;
|
||||
} else {
|
||||
inSession = false;
|
||||
char* id = text + 1u + sizeof(prefix) - 1u;
|
||||
if (!validate_service_id(id) || serviceCount >= INIT_SERVICE_MAX_COUNT ||
|
||||
find_parsed_service(gParsedServices, serviceCount, id) >= 0) {
|
||||
@@ -295,13 +372,20 @@ static KSTATUS parse_services_ini(char* buffer, uint32_t* serviceCountOut, uint3
|
||||
current = &gParsedServices[serviceCount++];
|
||||
memset(current, 0, sizeof(*current));
|
||||
current->SectionLine = lineNumber;
|
||||
if (!copy_bounded(current->Descriptor.Id, sizeof(current->Descriptor.Id), id))
|
||||
current->Descriptor.StartPolicy = InitServiceStartAuto;
|
||||
current->Descriptor.RestartPolicy = InitServiceRestartNever;
|
||||
current->Descriptor.ReadyTimeoutMilliseconds = 5000u;
|
||||
current->Descriptor.StopTimeoutMilliseconds = 5000u;
|
||||
current->Descriptor.RestartLimit = 3u;
|
||||
current->Descriptor.RestartWindowMilliseconds = 300000u;
|
||||
if (!copy_bounded(current->Descriptor.Id, sizeof(current->Descriptor.Id), id) ||
|
||||
!copy_bounded(current->Descriptor.Name, sizeof(current->Descriptor.Name), id))
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!current) {
|
||||
if (!current && !inSession) {
|
||||
valid = false;
|
||||
} else {
|
||||
char* equals = strchr(text, '=');
|
||||
@@ -313,7 +397,14 @@ static KSTATUS parse_services_ini(char* buffer, uint32_t* serviceCountOut, uint3
|
||||
char* value = trim_left(equals + 1);
|
||||
trim_right(key);
|
||||
trim_right(value);
|
||||
if (!*key || !parse_service_key(current, key, value)) valid = false;
|
||||
if (inSession) {
|
||||
if (strcmp(key, "image") != 0 || sessionImageSeen ||
|
||||
!validate_image_path(value) ||
|
||||
!copy_bounded(gSessionImage, sizeof(gSessionImage), value)) valid = false;
|
||||
else sessionImageSeen = true;
|
||||
} else if (!*key || !parse_service_key(current, key, value)) {
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -326,7 +417,7 @@ static KSTATUS parse_services_ini(char* buffer, uint32_t* serviceCountOut, uint3
|
||||
line = saved ? end + 1u : end;
|
||||
}
|
||||
|
||||
if (serviceCount == 0)
|
||||
if (serviceCount == 0 || !sessionImageSeen)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
*serviceCountOut = serviceCount;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
@@ -337,6 +428,7 @@ static KSTATUS validate_services(uint32_t serviceCount,
|
||||
INIT_SERVICE_CONFIG* configOut,
|
||||
uint32_t* errorLineOut) {
|
||||
memset(configOut, 0, sizeof(*configOut));
|
||||
memcpy(configOut->SessionImage, gSessionImage, strlen(gSessionImage) + 1u);
|
||||
|
||||
static const char* const bootGrants[] = {
|
||||
NTL_BOOT_CAP_NAMESPACE_ROOT, NTL_BOOT_CAP_VOLUME_MANAGER, NTL_BOOT_CAP_INPUT_MANAGER,
|
||||
@@ -369,7 +461,7 @@ static KSTATUS validate_services(uint32_t serviceCount,
|
||||
}
|
||||
bool declaredDependency = false;
|
||||
if (provider >= 0 && (uint32_t)provider != i &&
|
||||
gParsedServices[provider].Descriptor.StartPolicy == InitServiceStartAuto) {
|
||||
gParsedServices[provider].Descriptor.StartPolicy != InitServiceStartDisabled) {
|
||||
for (uint32_t d = 0; d < gParsedServices[i].DependencyCount; ++d)
|
||||
if (strcmp(gParsedServices[i].DependencyIds[d],
|
||||
gParsedServices[provider].Descriptor.Id) == 0)
|
||||
@@ -390,9 +482,29 @@ static KSTATUS validate_services(uint32_t serviceCount,
|
||||
}
|
||||
}
|
||||
|
||||
// Exactly one critical automatic service owns the primordial namespace authority.
|
||||
uint32_t namespaceProviders = 0;
|
||||
for (uint32_t i = 0; i < serviceCount; ++i) {
|
||||
const INIT_SERVICE_DESCRIPTOR* descriptor = &gParsedServices[i].Descriptor;
|
||||
bool namespaceRoot = false;
|
||||
bool registration = false;
|
||||
for (uint32_t g = 0; g < descriptor->GrantCount; ++g) {
|
||||
if (strcmp(descriptor->Grants[g], NTL_BOOT_CAP_NAMESPACE_ROOT) == 0) namespaceRoot = true;
|
||||
if (strcmp(descriptor->Grants[g], NTL_BOOT_CAP_RESOURCE_REGISTRATION) == 0) registration = true;
|
||||
}
|
||||
if (!namespaceRoot && !registration) continue;
|
||||
if (!namespaceRoot || !registration || !descriptor->Critical ||
|
||||
descriptor->StartPolicy != InitServiceStartAuto) {
|
||||
if (errorLineOut) *errorLineOut = gParsedServices[i].SectionLine;
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
}
|
||||
++namespaceProviders;
|
||||
}
|
||||
if (namespaceProviders != 1u) return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
for (uint32_t i = 0; i < serviceCount; ++i) {
|
||||
INIT_PARSED_SERVICE* parsed = &gParsedServices[i];
|
||||
if (parsed->PresentKeys != INIT_SERVICE_REQUIRED_KEYS) {
|
||||
if ((parsed->PresentKeys & INIT_SERVICE_KEY_IMAGE) == 0u) {
|
||||
if (errorLineOut) *errorLineOut = parsed->SectionLine;
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
}
|
||||
@@ -415,14 +527,43 @@ static KSTATUS validate_services(uint32_t serviceCount,
|
||||
}
|
||||
descriptor->Dependencies[d] = (uint16_t)dependency;
|
||||
}
|
||||
descriptor->AfterCount = parsed->AfterCount;
|
||||
for (uint32_t d = 0; d < parsed->AfterCount; ++d) {
|
||||
int32_t dependency = find_parsed_service(gParsedServices, serviceCount, parsed->AfterIds[d]);
|
||||
if (dependency < 0 || (uint32_t)dependency == i) {
|
||||
if (errorLineOut) *errorLineOut = parsed->SectionLine;
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
}
|
||||
for (uint32_t r = 0; r < descriptor->DependencyCount; ++r) {
|
||||
if (descriptor->Dependencies[r] == (uint16_t)dependency) {
|
||||
if (errorLineOut) *errorLineOut = parsed->SectionLine;
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
}
|
||||
}
|
||||
descriptor->After[d] = (uint16_t)dependency;
|
||||
}
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < serviceCount; ++i) {
|
||||
INIT_SERVICE_DESCRIPTOR* descriptor = &configOut->Services[i];
|
||||
if (descriptor->StartPolicy != InitServiceStartAuto) continue;
|
||||
if (descriptor->Critical && descriptor->StartPolicy != InitServiceStartAuto) {
|
||||
if (errorLineOut) *errorLineOut = gParsedServices[i].SectionLine;
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
}
|
||||
if (descriptor->StartPolicy == InitServiceStartDisabled) continue;
|
||||
for (uint32_t d = 0; d < descriptor->DependencyCount; ++d) {
|
||||
INIT_SERVICE_DESCRIPTOR* dependency = &configOut->Services[descriptor->Dependencies[d]];
|
||||
if (dependency->StartPolicy == InitServiceStartDisabled) {
|
||||
if (dependency->StartPolicy == InitServiceStartDisabled ||
|
||||
(descriptor->StartPolicy == InitServiceStartAuto &&
|
||||
dependency->StartPolicy != InitServiceStartAuto)) {
|
||||
if (errorLineOut) *errorLineOut = gParsedServices[i].SectionLine;
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
}
|
||||
}
|
||||
for (uint32_t d = 0; d < descriptor->AfterCount; ++d) {
|
||||
INIT_SERVICE_DESCRIPTOR* dependency = &configOut->Services[descriptor->After[d]];
|
||||
if (descriptor->StartPolicy == InitServiceStartAuto &&
|
||||
dependency->StartPolicy == InitServiceStartDelayed) {
|
||||
if (errorLineOut) *errorLineOut = gParsedServices[i].SectionLine;
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
}
|
||||
@@ -432,7 +573,7 @@ static KSTATUS validate_services(uint32_t serviceCount,
|
||||
uint32_t indegree[INIT_SERVICE_MAX_COUNT] = {0};
|
||||
bool emitted[INIT_SERVICE_MAX_COUNT] = {0};
|
||||
for (uint32_t i = 0; i < serviceCount; ++i)
|
||||
indegree[i] = configOut->Services[i].DependencyCount;
|
||||
indegree[i] = configOut->Services[i].DependencyCount + configOut->Services[i].AfterCount;
|
||||
|
||||
uint32_t emittedCount = 0;
|
||||
while (emittedCount < serviceCount) {
|
||||
@@ -456,19 +597,17 @@ static KSTATUS validate_services(uint32_t serviceCount,
|
||||
}
|
||||
|
||||
emitted[next] = true;
|
||||
if (configOut->Services[next].StartPolicy == InitServiceStartAuto)
|
||||
if (configOut->Services[next].StartPolicy != InitServiceStartDisabled)
|
||||
configOut->StartupOrder[configOut->StartupCount++] = (uint16_t)next;
|
||||
++emittedCount;
|
||||
|
||||
for (uint32_t i = 0; i < serviceCount; ++i) {
|
||||
if (emitted[i]) continue;
|
||||
INIT_SERVICE_DESCRIPTOR* descriptor = &configOut->Services[i];
|
||||
for (uint32_t d = 0; d < descriptor->DependencyCount; ++d) {
|
||||
if (descriptor->Dependencies[d] == (uint16_t)next) {
|
||||
--indegree[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (uint32_t d = 0; d < descriptor->DependencyCount; ++d)
|
||||
if (descriptor->Dependencies[d] == (uint16_t)next) --indegree[i];
|
||||
for (uint32_t d = 0; d < descriptor->AfterCount; ++d)
|
||||
if (descriptor->After[d] == (uint16_t)next) --indegree[i];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -532,6 +671,7 @@ KSTATUS InitLoadServiceConfig(SYS_HANDLE systemVolume,
|
||||
if (errorLineOut) *errorLineOut = 0;
|
||||
|
||||
memset(gParsedServices, 0, sizeof(gParsedServices));
|
||||
memset(gSessionImage, 0, sizeof(gSessionImage));
|
||||
size_t size = 0;
|
||||
KSTATUS status = read_services_ini(systemVolume, &size);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <stdint.h>
|
||||
#include <ntlite/syscall.h>
|
||||
#include <ntlite/service_control.h>
|
||||
#include <ntlite/security.h>
|
||||
|
||||
#define INIT_SERVICES_PATH "system/services.ini"
|
||||
#define INIT_SERVICES_MAX_FILE_BYTES (64u * 1024u)
|
||||
@@ -18,24 +19,33 @@
|
||||
|
||||
typedef enum INIT_SERVICE_START_POLICY {
|
||||
InitServiceStartAuto = 0,
|
||||
InitServiceStartDelayed,
|
||||
InitServiceStartDisabled,
|
||||
} INIT_SERVICE_START_POLICY;
|
||||
|
||||
typedef enum INIT_SERVICE_RESTART_POLICY {
|
||||
InitServiceRestartNever = 0,
|
||||
InitServiceRestartOnFailure,
|
||||
InitServiceRestartAlways,
|
||||
} INIT_SERVICE_RESTART_POLICY;
|
||||
|
||||
typedef struct INIT_SERVICE_DESCRIPTOR {
|
||||
char Id[INIT_SERVICE_ID_MAX + 1u];
|
||||
char Name[64];
|
||||
char ImagePath[SYS_PATH_MAX];
|
||||
INIT_SERVICE_START_POLICY StartPolicy;
|
||||
bool Critical;
|
||||
INIT_SERVICE_RESTART_POLICY RestartPolicy;
|
||||
uint32_t ReadyTimeoutMilliseconds;
|
||||
uint32_t StopTimeoutMilliseconds;
|
||||
uint32_t RestartLimit;
|
||||
uint32_t RestartWindowMilliseconds;
|
||||
uint32_t RestartDelayMilliseconds;
|
||||
SYS_PRIVILEGES Privileges;
|
||||
uint32_t DependencyCount;
|
||||
uint16_t Dependencies[INIT_SERVICE_DEPENDENCY_MAX];
|
||||
uint32_t AfterCount;
|
||||
uint16_t After[INIT_SERVICE_DEPENDENCY_MAX];
|
||||
uint32_t GrantCount;
|
||||
char Grants[INIT_SERVICE_GRANT_MAX][INIT_SERVICE_GRANT_NAME_MAX + 1u];
|
||||
uint32_t ExportCount;
|
||||
@@ -43,6 +53,7 @@ typedef struct INIT_SERVICE_DESCRIPTOR {
|
||||
} INIT_SERVICE_DESCRIPTOR;
|
||||
|
||||
typedef struct INIT_SERVICE_CONFIG {
|
||||
char SessionImage[SYS_PATH_MAX];
|
||||
INIT_SERVICE_DESCRIPTOR Services[INIT_SERVICE_MAX_COUNT];
|
||||
uint16_t StartupOrder[INIT_SERVICE_MAX_COUNT];
|
||||
uint32_t ServiceCount;
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
TARGETNAME=init
|
||||
TARGETPATH=obj
|
||||
TARGETTYPE=PROGRAM
|
||||
FLAVOR=USER
|
||||
TARGETLIBS="$(BASEDIR)\bin\syscall_s.lib" "$(BASEDIR)\bin\libc.lib" "$(BASEDIR)\bin\native_runtime.lib"
|
||||
EXTRA_LFLAGS=/ENTRY:_start
|
||||
SOURCES=@/code/user/system/init/init.c @/code/user/system/init/service_config.c
|
||||
@@ -0,0 +1 @@
|
||||
!include $(BASEDIR)\makefile.def
|
||||
@@ -3,7 +3,7 @@
|
||||
#include <ntlite/atomic.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <ntlite/native_heap.h>
|
||||
#include <string.h>
|
||||
#include <intrin.h>
|
||||
#include <ntlite/exception.h>
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
TARGETNAME=posixsrv
|
||||
TARGETPATH=obj
|
||||
TARGETTYPE=PROGRAM
|
||||
FLAVOR=USER
|
||||
TARGETLIBS="$(BASEDIR)\bin\syscall_s.lib" "$(BASEDIR)\bin\libc.lib" "$(BASEDIR)\bin\native_runtime.lib"
|
||||
EXTRA_LFLAGS=/ENTRY:_start
|
||||
SOURCES=@/code/user/system/posixsrv/posixsrv.c
|
||||
@@ -1,7 +1,7 @@
|
||||
// RSSRV native console. One logical console owns text output policy and consumes RSSRV logical input.
|
||||
|
||||
#include "rssrv_internal.h"
|
||||
#include <stdlib.h>
|
||||
#include <ntlite/native_heap.h>
|
||||
#include <string.h>
|
||||
|
||||
#define RSSRV_EVENT_MANUAL_RESET 1u
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include <stdbool.h>
|
||||
#include "rssrv_internal.h"
|
||||
#include <ntlite/port.h>
|
||||
#include <stdlib.h>
|
||||
#include <ntlite/native_heap.h>
|
||||
#include <string.h>
|
||||
|
||||
#define RSSRV_EVENT_MANUAL_RESET 1u
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
!include $(BASEDIR)\makefile.def
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include "rssrv_internal.h"
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <ntlite/native_heap.h>
|
||||
#include <string.h>
|
||||
|
||||
typedef enum RSSRV_NODE_TYPE {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include <stdbool.h>
|
||||
#include "rssrv_internal.h"
|
||||
#include "publication_scope.h"
|
||||
#include <stdlib.h>
|
||||
#include <ntlite/native_heap.h>
|
||||
#include <string.h>
|
||||
|
||||
typedef struct RSSRV_SESSION {
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
TARGETNAME=rssrv
|
||||
TARGETPATH=obj
|
||||
TARGETTYPE=PROGRAM
|
||||
FLAVOR=USER
|
||||
TARGETLIBS="$(BASEDIR)\bin\syscall_s.lib" "$(BASEDIR)\bin\libc.lib" "$(BASEDIR)\bin\native_runtime.lib"
|
||||
EXTRA_LFLAGS=/ENTRY:_start
|
||||
SOURCES=@/code/user/system/rssrv/console.c @/code/user/system/rssrv/display.c @/code/user/system/rssrv/input.c @/code/user/system/rssrv/namespace.c @/code/user/system/rssrv/resource_service.c @/code/user/system/rssrv/rssrv.c \
|
||||
@/code/user/system/rssrv/session.c @/code/user/system/rssrv/volume.c
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "rssrv_internal.h"
|
||||
#include <stdlib.h>
|
||||
#include <ntlite/native_heap.h>
|
||||
#include <string.h>
|
||||
|
||||
struct RSSRV_VOLUME_PUBLICATION {
|
||||
|
||||
@@ -14,13 +14,11 @@ typedef struct TEST_ENTRY {
|
||||
char Name[SYS_DIR_NAME_MAX];
|
||||
} TEST_ENTRY;
|
||||
|
||||
// Writes one NUL-terminated string to the console.
|
||||
static void print(const char* s) {
|
||||
SYS_WRITE_CONSOLE_REQUEST req = { sizeof(SYS_WRITE_CONSOLE_REQUEST), SysSvFromCstr(s) };
|
||||
SysWriteConsole(&req);
|
||||
}
|
||||
|
||||
// Writes one unsigned integer to the console.
|
||||
static void print_u64(uint64_t v) {
|
||||
char buf[32];
|
||||
int i = 0;
|
||||
@@ -38,7 +36,6 @@ static void print_u64(uint64_t v) {
|
||||
}
|
||||
}
|
||||
|
||||
// Writes one signed integer to the console.
|
||||
static void print_i32(int32_t v) {
|
||||
if (v < 0) {
|
||||
print("-");
|
||||
@@ -48,7 +45,6 @@ static void print_i32(int32_t v) {
|
||||
print_u64((uint32_t)v);
|
||||
}
|
||||
|
||||
// Writes one 32-bit value as hexadecimal.
|
||||
static void print_hex32(uint32_t v) {
|
||||
static const char hex[] = "0123456789abcdef";
|
||||
for (int i = 7; i >= 0; i--) {
|
||||
@@ -294,7 +290,6 @@ static uint32_t run_test(const char* path, int32_t* exitCodeOut) {
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Program entry point.
|
||||
int main(void) {
|
||||
print("all_tests: start\n");
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
!include $(BASEDIR)\makefile.def
|
||||
@@ -0,0 +1,7 @@
|
||||
TARGETNAME=all_tests
|
||||
TARGETPATH=obj
|
||||
TARGETTYPE=PROGRAM
|
||||
FLAVOR=USER
|
||||
TARGETLIBS="$(BASEDIR)\bin\syscall.lib" "$(BASEDIR)\bin\libc.lib" "$(BASEDIR)\bin\native_runtime.lib"
|
||||
EXTRA_LFLAGS=/ENTRY:NtlNativeStart
|
||||
SOURCES=@/code/user/tests/all_tests/all_tests.c
|
||||
@@ -18,13 +18,11 @@ 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;
|
||||
@@ -43,12 +41,10 @@ static void print_u64(uint64_t v) {
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
@@ -56,7 +52,6 @@ static void fail(const char* msg) {
|
||||
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);
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
!include $(BASEDIR)\makefile.def
|
||||
@@ -0,0 +1,7 @@
|
||||
TARGETNAME=anonymous_queue_smoke
|
||||
TARGETPATH=obj
|
||||
TARGETTYPE=PROGRAM
|
||||
FLAVOR=USER
|
||||
TARGETLIBS="$(BASEDIR)\bin\syscall.lib" "$(BASEDIR)\bin\libc.lib" "$(BASEDIR)\bin\native_runtime.lib"
|
||||
EXTRA_LFLAGS=/ENTRY:NtlNativeStart
|
||||
SOURCES=@/code/user/tests/anonymous_queue_smoke/anonymous_queue_smoke.c
|
||||
@@ -72,7 +72,6 @@ typedef struct DIRECT_APC_STATE {
|
||||
volatile uint32_t ArgumentsValid;
|
||||
} DIRECT_APC_STATE;
|
||||
|
||||
// Records one directly queued generic user APC.
|
||||
static void direct_apc(void* context, void* argument1, void* argument2) {
|
||||
DIRECT_APC_STATE* state = (DIRECT_APC_STATE*)context;
|
||||
if (!state) return;
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
!include $(BASEDIR)\makefile.def
|
||||
@@ -0,0 +1,7 @@
|
||||
TARGETNAME=apc_return_smoke
|
||||
TARGETPATH=obj
|
||||
TARGETTYPE=PROGRAM
|
||||
FLAVOR=USER
|
||||
TARGETLIBS="$(BASEDIR)\bin\syscall.lib" "$(BASEDIR)\bin\libc.lib" "$(BASEDIR)\bin\native_runtime.lib"
|
||||
EXTRA_LFLAGS=/ENTRY:NtlNativeStart
|
||||
SOURCES=@/code/user/tests/apc_return_smoke/apc_return_smoke.c
|
||||
@@ -0,0 +1 @@
|
||||
!include $(BASEDIR)\makefile.def
|
||||
@@ -0,0 +1,7 @@
|
||||
TARGETNAME=async_apc
|
||||
TARGETPATH=obj
|
||||
TARGETTYPE=PROGRAM
|
||||
FLAVOR=USER
|
||||
TARGETLIBS="$(BASEDIR)\bin\syscall.lib" "$(BASEDIR)\bin\libc.lib" "$(BASEDIR)\bin\native_runtime.lib"
|
||||
EXTRA_LFLAGS=/ENTRY:NtlNativeStart
|
||||
SOURCES=@/code/user/tests/async_apc/async_apc.c
|
||||
@@ -0,0 +1 @@
|
||||
!include $(BASEDIR)\makefile.def
|
||||
@@ -0,0 +1,7 @@
|
||||
TARGETNAME=async_cancel
|
||||
TARGETPATH=obj
|
||||
TARGETTYPE=PROGRAM
|
||||
FLAVOR=USER
|
||||
TARGETLIBS="$(BASEDIR)\bin\syscall.lib" "$(BASEDIR)\bin\libc.lib" "$(BASEDIR)\bin\native_runtime.lib"
|
||||
EXTRA_LFLAGS=/ENTRY:NtlNativeStart
|
||||
SOURCES=@/code/user/tests/async_cancel/async_cancel.c
|
||||
@@ -32,7 +32,6 @@ typedef char TEST_AUDIO_STREAM_INFO_SIZE[(sizeof(SYS_AUDIO_STREAM_INFO) == 128)
|
||||
typedef char TEST_AUDIO_VOLUME_REQUEST_SIZE[(sizeof(SYS_AUDIO_SET_VOLUME_REQUEST) == 24) ? 1 : -1];
|
||||
typedef char TEST_AUDIO_MASTER_VOLUME_REQUEST_SIZE[(sizeof(SYS_AUDIO_SET_MASTER_VOLUME_REQUEST) == 20) ? 1 : -1];
|
||||
|
||||
// Writes one NUL-terminated string to the console.
|
||||
static void print(const char* text) {
|
||||
SYS_WRITE_CONSOLE_REQUEST request = { sizeof(SYS_WRITE_CONSOLE_REQUEST), SysSvFromCstr(text) };
|
||||
(void)SysWriteConsole(&request);
|
||||
@@ -45,7 +44,6 @@ static void trace_stage(const char* message) {
|
||||
print(message);
|
||||
}
|
||||
|
||||
// Writes one unsigned integer to the console.
|
||||
static void print_u64(uint64_t value) {
|
||||
char digits[32];
|
||||
uint32_t count = 0;
|
||||
@@ -73,13 +71,11 @@ static void cleanup_stream(void) {
|
||||
gOpenStream = 0;
|
||||
}
|
||||
|
||||
// Terminates the test process.
|
||||
static void exit_now(int32_t code) {
|
||||
cleanup_stream();
|
||||
while (true) (void)SysExitProcess(code);
|
||||
}
|
||||
|
||||
// Fails the test with one status value.
|
||||
static void fail_status(const char* operation, KSTATUS status) {
|
||||
print("audio_stream_smoke: ");
|
||||
print(operation);
|
||||
@@ -89,7 +85,6 @@ static void fail_status(const char* operation, KSTATUS status) {
|
||||
exit_now(1);
|
||||
}
|
||||
|
||||
// Fails the test with one plain diagnostic.
|
||||
static void fail(const char* message) {
|
||||
print("audio_stream_smoke: ");
|
||||
print(message);
|
||||
@@ -307,7 +302,6 @@ static uint32_t open_stream(uint32_t endpointId) {
|
||||
NTLSTATUS_SUCCESS);
|
||||
}
|
||||
|
||||
// Writes one sample run and returns the accepted frame count.
|
||||
static uint32_t write_samples(uint32_t stream,
|
||||
const int16_t* frames,
|
||||
uint32_t frameCount,
|
||||
@@ -327,7 +321,6 @@ static uint32_t write_samples(uint32_t stream,
|
||||
return written;
|
||||
}
|
||||
|
||||
// Writes one contract-tone range and returns the accepted frame count.
|
||||
static uint32_t write_frames(uint32_t stream,
|
||||
uint32_t frameOffset,
|
||||
uint32_t frameCount,
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
!include $(BASEDIR)\makefile.def
|
||||
@@ -0,0 +1,7 @@
|
||||
TARGETNAME=audio_stream_smoke
|
||||
TARGETPATH=obj
|
||||
TARGETTYPE=PROGRAM
|
||||
FLAVOR=USER
|
||||
TARGETLIBS="$(BASEDIR)\bin\syscall.lib" "$(BASEDIR)\bin\libc.lib" "$(BASEDIR)\bin\native_runtime.lib"
|
||||
EXTRA_LFLAGS=/ENTRY:NtlNativeStart
|
||||
SOURCES=@/code/user/tests/audio_stream_smoke/audio_stream_smoke.c
|
||||
@@ -10,13 +10,11 @@ static void exit_test(int code) {
|
||||
for (;;) SysExitProcess(code);
|
||||
}
|
||||
|
||||
// Writes one diagnostic string through the transitional native console.
|
||||
static void print(const char* text) {
|
||||
SYS_WRITE_CONSOLE_REQUEST request = { sizeof(request), SysSvFromCstr(text) };
|
||||
(void)SysWriteConsole(&request);
|
||||
}
|
||||
|
||||
// Writes one status value in hexadecimal without a formatting runtime.
|
||||
static void print_status(KSTATUS status) {
|
||||
static const char digits[] = "0123456789ABCDEF";
|
||||
char buffer[11] = "0x00000000";
|
||||
|
||||
Loaded 100 of 696 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user