replace ad hoc driver loading with a proper driver manager, new DDK, new PnP manager with device matching, asynchronous startup, resource ownership and removal, add shared PCI capability helpers, replace typed, rigid services with a proper service manager, implement proper system shutdown and restart, shutting down unloads drivers and shuts down services, add simple power button, remove heavyweight boot self-tests and SMP stress runs, add monotonic timestamps to kernel debug output, fruitless USB and PS/2 fixes, make test builds opt in.
This commit is contained in:
1 parent
c0ad63c27f
commit
d36f19509c
160 files changed
+6563
-7366
No files matched your search
Binary file not shown.
+2
-2
@@ -6,9 +6,9 @@ system_volume = mbr/4E544C31/2
|
||||
|
||||
[kernel]
|
||||
; Debug bitmask. Values are defined canonically in ntlite/ddk/debug.h.
|
||||
; 0x001 console output, 0x002 boot self-tests, 0x004 file log.
|
||||
; 0x001 console output, 0x002 explicit kernel diagnostics, 0x004 file log.
|
||||
; 0x008 pool canaries, 0x010 pool poison, 0x020 pool quarantine, 0x040 pool caller tracking.
|
||||
; 0x080 display assertions, 0x100 intrusive-list checks.
|
||||
debug = 0x1ff
|
||||
debug = 0x004
|
||||
[init]
|
||||
commandline = apps/shell_native.exe
|
||||
+90
-85
@@ -1,6 +1,6 @@
|
||||
// UEFI bootloader entry point. This file performs firmware discovery and the minimum fixed-image/raw-file handoff needed to enter the kernel.
|
||||
|
||||
// The loader interprets only the [boot] system_volume bootstrap selector. It scans drivers.ini [boot],
|
||||
// The loader interprets only the [boot] system_volume bootstrap selector. It scans drivers.ini v2,
|
||||
// verifies and maps fixed kernel/HAL images, translates firmware state, and exits boot services.
|
||||
|
||||
#include <efi.h>
|
||||
@@ -853,50 +853,80 @@ static EFI_STATUS BootOpenSystemVolume(EFI_HANDLE imageHandle,
|
||||
return fs->OpenVolume(fs, rootOut);
|
||||
}
|
||||
|
||||
typedef EFI_STATUS (*BOOT_DRIVER_VISITOR)(const char* name, UINTN length, void* context);
|
||||
typedef EFI_STATUS (*BOOT_DRIVER_VISITOR)(const char* id, const char* image, void* context);
|
||||
|
||||
// Visits raw driver filenames from the exact [boot] section without implementing general INI semantics.
|
||||
// The firmware recognizes only the identity, image, and start fields in driver sections.
|
||||
// Graph validation and all other configuration semantics belong to the kernel.
|
||||
static EFI_STATUS BootVisitDriverLines(const char* text, UINTN length,
|
||||
BOOT_DRIVER_VISITOR visitor, void* context) {
|
||||
if (!text || !visitor)
|
||||
if (!text || !visitor || length == 0 || length > 64u * 1024u)
|
||||
return EFI_INVALID_PARAMETER;
|
||||
|
||||
bool inBootSection = false;
|
||||
char id[BOOT_FILE_NAME_MAX] = {0};
|
||||
char image[BOOT_FILE_NAME_MAX] = {0};
|
||||
bool driverSection = false;
|
||||
bool bootPhase = false;
|
||||
UINTN offset = 0;
|
||||
while (offset < length) {
|
||||
while (offset <= length) {
|
||||
UINTN lineStart = offset;
|
||||
while (offset < length && text[offset] != '\n')
|
||||
++offset;
|
||||
UINTN lineEnd = offset;
|
||||
if (offset < length)
|
||||
++offset;
|
||||
|
||||
while (offset < length && text[offset] != '\n') ++offset;
|
||||
const char* begin = text + lineStart;
|
||||
const char* end = text + lineEnd;
|
||||
const char* end = text + offset;
|
||||
if (offset < length) ++offset;
|
||||
else ++offset; // Process the final section exactly once.
|
||||
BootTrimSpan(&begin, &end);
|
||||
if (begin == end || *begin == ';' || *begin == '#')
|
||||
continue;
|
||||
|
||||
if (begin == end || *begin == ';' || *begin == '#') continue;
|
||||
if (*begin == '[') {
|
||||
if (end[-1] != ']')
|
||||
return EFI_INVALID_PARAMETER;
|
||||
const char* sectionBegin = begin + 1;
|
||||
if (driverSection && bootPhase) {
|
||||
if (!id[0] || !image[0]) return EFI_INVALID_PARAMETER;
|
||||
EFI_STATUS status = visitor(id, image, context);
|
||||
if (EFI_ERROR(status)) return status;
|
||||
}
|
||||
driverSection = false;
|
||||
bootPhase = false;
|
||||
id[0] = '\0';
|
||||
image[0] = '\0';
|
||||
if (end[-1] != ']') return EFI_INVALID_PARAMETER;
|
||||
const char* section = begin + 1;
|
||||
const char* sectionEnd = end - 1;
|
||||
BootTrimSpan(§ionBegin, §ionEnd);
|
||||
inBootSection = BootSpanEquals(sectionBegin, sectionEnd, "boot");
|
||||
if ((UINTN)(sectionEnd - section) > 7u &&
|
||||
memcmp(section, "driver.", 7u) == 0) {
|
||||
section += 7;
|
||||
UINTN n = (UINTN)(sectionEnd - section);
|
||||
if (n == 0 || n >= sizeof(id)) return EFI_INVALID_PARAMETER;
|
||||
memcpy(id, section, n);
|
||||
id[n] = '\0';
|
||||
driverSection = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!inBootSection)
|
||||
continue;
|
||||
for (const char* p = begin; p < end; ++p) {
|
||||
if (*p == '=')
|
||||
return EFI_INVALID_PARAMETER;
|
||||
if (!driverSection) continue;
|
||||
const char* equals = begin;
|
||||
while (equals < end && *equals != '=') ++equals;
|
||||
if (equals == end) return EFI_INVALID_PARAMETER;
|
||||
const char* keyEnd = equals;
|
||||
const char* value = equals + 1;
|
||||
BootTrimSpan(&begin, &keyEnd);
|
||||
BootTrimSpan(&value, &end);
|
||||
for (const char* p = value; p < end; ++p) {
|
||||
if (*p == '#' || *p == ';') {
|
||||
end = p;
|
||||
BootTrimSpan(&value, &end);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
EFI_STATUS status = visitor(begin, (UINTN)(end - begin), context);
|
||||
if (EFI_ERROR(status))
|
||||
return status;
|
||||
if (BootSpanEquals(begin, keyEnd, "image")) {
|
||||
UINTN n = (UINTN)(end - value);
|
||||
if (n == 0 || n >= sizeof(image)) return EFI_INVALID_PARAMETER;
|
||||
memcpy(image, value, n);
|
||||
image[n] = '\0';
|
||||
} else if (BootSpanEquals(begin, keyEnd, "start")) {
|
||||
bootPhase = BootSpanEquals(value, end, "boot");
|
||||
}
|
||||
}
|
||||
if (driverSection && bootPhase) {
|
||||
if (!id[0] || !image[0]) return EFI_INVALID_PARAMETER;
|
||||
return visitor(id, image, context);
|
||||
}
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
@@ -905,14 +935,11 @@ typedef struct BOOT_DRIVER_COUNT_CONTEXT {
|
||||
uint32_t Count;
|
||||
} BOOT_DRIVER_COUNT_CONTEXT;
|
||||
|
||||
// Counts one boot-driver filename while checking the 32-bit handoff count for overflow.
|
||||
static EFI_STATUS BootCountDriver(const char* name, UINTN length, void* context) {
|
||||
(void)name;
|
||||
if (!context || length == 0 || length >= BOOT_FILE_NAME_MAX)
|
||||
return EFI_INVALID_PARAMETER;
|
||||
static EFI_STATUS BootCountDriver(const char* id, const char* image, void* context) {
|
||||
(void)id;
|
||||
(void)image;
|
||||
BOOT_DRIVER_COUNT_CONTEXT* count = (BOOT_DRIVER_COUNT_CONTEXT*)context;
|
||||
if (count->Count == UINT32_MAX)
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
if (count->Count == UINT32_MAX) return EFI_OUT_OF_RESOURCES;
|
||||
++count->Count;
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
@@ -923,84 +950,64 @@ typedef struct BOOT_DRIVER_LOAD_CONTEXT {
|
||||
uint32_t Index;
|
||||
} BOOT_DRIVER_LOAD_CONTEXT;
|
||||
|
||||
// Loads one [boot] driver as a page-backed raw file and records its exact allocation.
|
||||
static EFI_STATUS BootLoadDriver(const char* name, UINTN length, void* context) {
|
||||
// Loads one identified boot image into retained physical pages.
|
||||
static EFI_STATUS BootLoadDriver(const char* id, const char* image, void* context) {
|
||||
BOOT_DRIVER_LOAD_CONTEXT* load = (BOOT_DRIVER_LOAD_CONTEXT*)context;
|
||||
if (!load || load->Index >= load->Capacity || length == 0 || length >= BOOT_FILE_NAME_MAX)
|
||||
if (!load || load->Index >= load->Capacity) return EFI_INVALID_PARAMETER;
|
||||
UINTN idLength = strlen(id);
|
||||
UINTN length = strlen(image);
|
||||
if (idLength == 0 || idLength >= BOOT_FILE_NAME_MAX ||
|
||||
length < 5 || length >= BOOT_FILE_NAME_MAX ||
|
||||
memcmp(image + length - 4, ".sys", 4) != 0)
|
||||
return EFI_INVALID_PARAMETER;
|
||||
|
||||
for (UINTN i = 0; i < length; ++i) {
|
||||
unsigned char c = (unsigned char)name[i];
|
||||
if (c < 0x21u || c > 0x7Eu || name[i] == '/' || name[i] == '\\' || name[i] == '=')
|
||||
return EFI_INVALID_PARAMETER;
|
||||
char c = image[i];
|
||||
if (!((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') ||
|
||||
c == '-' || c == '_' || c == '.')) return EFI_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
char asciiName[BOOT_FILE_NAME_MAX];
|
||||
memcpy(asciiName, name, length);
|
||||
asciiName[length] = '\0';
|
||||
|
||||
CHAR16 unicodeName[BOOT_FILE_NAME_MAX];
|
||||
if (AsciiStrToUnicodeStrS(asciiName, unicodeName, BOOT_FILE_NAME_MAX) != 0)
|
||||
if (AsciiStrToUnicodeStrS(image, unicodeName, BOOT_FILE_NAME_MAX) != 0)
|
||||
return EFI_INVALID_PARAMETER;
|
||||
|
||||
CHAR16 path[128];
|
||||
UnicodeSPrint(path, sizeof(path), L"drivers\\%s", unicodeName);
|
||||
|
||||
BOOT_BLOB blob = {0};
|
||||
EFI_STATUS status = BootReadBlobFile(gSystemRoot, path, &blob);
|
||||
if (EFI_ERROR(status))
|
||||
return status;
|
||||
if (EFI_ERROR(status)) return status;
|
||||
if (blob.Length == 0) {
|
||||
gBS->FreePages(blob.PhysicalBase, blob.PageCount);
|
||||
return EFI_LOAD_ERROR;
|
||||
}
|
||||
|
||||
BOOT_FILE* file = &load->Files[load->Index++];
|
||||
AsciiStrCpyS(file->Name, sizeof(file->Name), asciiName);
|
||||
AsciiStrCpyS(file->DriverId, sizeof(file->DriverId), id);
|
||||
AsciiStrCpyS(file->Image, sizeof(file->Image), image);
|
||||
file->PhysicalBase = blob.PhysicalBase;
|
||||
file->Length = blob.Length;
|
||||
file->PageCount = blob.PageCount;
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
// Builds the dynamic raw boot-driver descriptor list from drivers.ini [boot].
|
||||
static EFI_STATUS BootLoadDrivers(const char* text, UINTN length, BOOT_FILE_LIST* listOut) {
|
||||
if (!text || !listOut)
|
||||
return EFI_INVALID_PARAMETER;
|
||||
|
||||
if (!text || !listOut) return EFI_INVALID_PARAMETER;
|
||||
*listOut = (BOOT_FILE_LIST){0};
|
||||
BOOT_DRIVER_COUNT_CONTEXT count = {0};
|
||||
EFI_STATUS status = BootVisitDriverLines(text, length, BootCountDriver, &count);
|
||||
if (EFI_ERROR(status))
|
||||
return status;
|
||||
if (count.Count == 0)
|
||||
return EFI_NOT_FOUND;
|
||||
|
||||
if (EFI_ERROR(status)) return status;
|
||||
if (!count.Count || count.Count > 64u) return EFI_INVALID_PARAMETER;
|
||||
UINTN descriptorBytes = (UINTN)count.Count * sizeof(BOOT_FILE);
|
||||
if (descriptorBytes / sizeof(BOOT_FILE) != count.Count)
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
UINTN pageCount = (descriptorBytes + PAGE_SIZE - 1u) / PAGE_SIZE;
|
||||
if (pageCount == 0 || pageCount > UINT32_MAX)
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
|
||||
EFI_PHYSICAL_ADDRESS physical = 0;
|
||||
status = BootAllocatePages(pageCount, &physical);
|
||||
if (EFI_ERROR(status))
|
||||
return status;
|
||||
|
||||
if (EFI_ERROR(status)) return status;
|
||||
BOOT_DRIVER_LOAD_CONTEXT load = {0};
|
||||
load.Files = (BOOT_FILE*)physical;
|
||||
load.Capacity = count.Count;
|
||||
status = BootVisitDriverLines(text, length, BootLoadDriver, &load);
|
||||
if (EFI_ERROR(status) || load.Index != count.Count) {
|
||||
for (uint32_t i = 0; i < load.Index; ++i) {
|
||||
if (load.Files[i].PhysicalBase && load.Files[i].PageCount)
|
||||
gBS->FreePages(load.Files[i].PhysicalBase, load.Files[i].PageCount);
|
||||
}
|
||||
for (uint32_t i = 0; i < load.Index; ++i)
|
||||
gBS->FreePages(load.Files[i].PhysicalBase, load.Files[i].PageCount);
|
||||
gBS->FreePages(physical, pageCount);
|
||||
return EFI_ERROR(status) ? status : EFI_LOAD_ERROR;
|
||||
}
|
||||
|
||||
listOut->FilesPhysical = (uintptr_t)physical;
|
||||
listOut->Count = count.Count;
|
||||
listOut->PageCount = (uint32_t)pageCount;
|
||||
@@ -1543,17 +1550,15 @@ EFI_STATUS EFIAPI UefiMain(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE* SystemTable
|
||||
return status;
|
||||
}
|
||||
|
||||
UINT8* driversIni = NULL;
|
||||
UINTN driversIniSize = 0;
|
||||
status = BootReadPoolFile(gSystemRoot, L"drivers\\drivers.ini", &driversIni, &driversIniSize);
|
||||
status = BootReadBlobFile(gSystemRoot, L"drivers\\drivers.ini", &info->DriversIni);
|
||||
if (EFI_ERROR(status)) {
|
||||
Print(L"Mandatory drivers.ini could not be loaded: %r\n", status);
|
||||
return status;
|
||||
}
|
||||
status = BootLoadDrivers((const char*)driversIni, driversIniSize, &info->BootDrivers);
|
||||
gBS->FreePool(driversIni);
|
||||
status = BootLoadDrivers((const char*)info->DriversIni.PhysicalBase,
|
||||
info->DriversIni.Length, &info->BootDrivers);
|
||||
if (EFI_ERROR(status)) {
|
||||
Print(L"drivers.ini [boot] is invalid or a boot driver could not be loaded: %r\n", status);
|
||||
Print(L"drivers.ini preload failed: %r\n", status);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
+29
-15
@@ -48,18 +48,20 @@ typedef struct Driver_Target {
|
||||
const char* sources;
|
||||
const char* cflags;
|
||||
const char* libs;
|
||||
const char* entry_flag;
|
||||
} Driver_Target;
|
||||
|
||||
#define DRIVER_TARGET(name_, obj_dir_, sources_) { (name_), (obj_dir_), (sources_), DRIVER_CFLAGS_LITERAL, "" }
|
||||
#define DRIVER_TARGET_LIBS(name_, obj_dir_, sources_, libs_) { (name_), (obj_dir_), (sources_), DRIVER_CFLAGS_LITERAL, (libs_) }
|
||||
#define DRIVER_TARGET_FLAGS(name_, obj_dir_, sources_, cflags_) { (name_), (obj_dir_), (sources_), (cflags_), "" }
|
||||
#define DRIVER_TARGET(name_, obj_dir_, sources_) { (name_), (obj_dir_), (sources_), DRIVER_CFLAGS_LITERAL, "", "/NOENTRY" }
|
||||
#define DRIVER_TARGET_LIBS(name_, obj_dir_, sources_, libs_) { (name_), (obj_dir_), (sources_), DRIVER_CFLAGS_LITERAL, (libs_), "/NOENTRY" }
|
||||
#define DRIVER_TARGET_FLAGS(name_, obj_dir_, sources_, cflags_) { (name_), (obj_dir_), (sources_), (cflags_), "", "/NOENTRY" }
|
||||
#define DRIVER_TARGET_DECLARATIVE(name_, obj_dir_, sources_) { (name_), (obj_dir_), (sources_), DRIVER_CFLAGS_LITERAL, "", "/NOENTRY" }
|
||||
|
||||
static const Driver_Target PCI_BUS_DRIVER =
|
||||
DRIVER_TARGET_FLAGS("pci", "obj\\drivers\\pci", "code\\drivers\\bus\\pci\\pci.c", DRIVER_CFLAGS_LITERAL " /DPCI_SYS_BUILD");
|
||||
|
||||
static const Driver_Target STANDALONE_DRIVERS[] = {
|
||||
DRIVER_TARGET("ps2", "obj\\drivers\\ps2", "code\\drivers\\input\\ps2\\ps2.c code\\drivers\\input\\ps2\\ps2_i8042.c"),
|
||||
DRIVER_TARGET("serial", "obj\\drivers\\serial", "code\\drivers\\input\\serial\\serial.c"),
|
||||
DRIVER_TARGET_DECLARATIVE("serial", "obj\\drivers\\serial", "code\\drivers\\input\\serial\\serial.c"),
|
||||
DRIVER_TARGET_LIBS("hda", "obj\\drivers\\hda", "code\\drivers\\audio\\hda\\hda.c code\\drivers\\audio\\hda\\hda_transport.c code\\drivers\\audio\\hda\\hda_codec.c", "bin\\pci.lib"),
|
||||
DRIVER_TARGET_LIBS("ata", "obj\\drivers\\ata", "code\\drivers\\storage\\ata\\ata.c", "bin\\pci.lib"),
|
||||
DRIVER_TARGET_LIBS("ahci", "obj\\drivers\\ahci", "code\\drivers\\storage\\ahci\\ahci.c code\\drivers\\storage\\ahci\\ahci_port.c", "bin\\pci.lib"),
|
||||
@@ -73,7 +75,7 @@ static const Driver_Target STANDALONE_DRIVERS[] = {
|
||||
// Creates layout.
|
||||
void create_layout() {
|
||||
const char* dirs[] = {
|
||||
"obj", "bin", "obj\\boot", "obj\\kernel", "obj\\lwip", "obj\\ext4", "obj\\ntfs3g", "obj\\hal",
|
||||
"obj", "bin", "obj\\boot", "obj\\ddk", "obj\\kernel", "obj\\lwip", "obj\\ext4", "obj\\ntfs3g", "obj\\hal",
|
||||
"obj\\syscall", "obj\\runtime", "obj\\win32", "obj\\posix", "obj\\drivers", "obj\\init", "obj\\rssrv", "obj\\posixsrv", "obj\\dwm",
|
||||
"obj\\shell_win32", "obj\\shell_posix", "obj\\shell_native",
|
||||
"obj\\async_apc", "obj\\async_cancel", "obj\\apc_return_smoke", "obj\\edit", "obj\\httpget",
|
||||
@@ -114,7 +116,7 @@ bool clean_object_directory(const char* obj_dir) {
|
||||
// Cleans object directories that are linked through wildcards.
|
||||
bool clean_build_object_directories(void) {
|
||||
const char* dirs[] = {
|
||||
"obj\\boot", "obj\\kernel", "obj\\lwip", "obj\\ext4", "obj\\hal",
|
||||
"obj\\boot", "obj\\ddk", "obj\\kernel", "obj\\lwip", "obj\\ext4", "obj\\hal",
|
||||
"obj\\syscall", "obj\\runtime", "obj\\win32", "obj\\posix",
|
||||
"obj\\init", "obj\\rssrv", "obj\\posixsrv", "obj\\dwm", "obj\\shell_win32", "obj\\shell_posix", "obj\\shell_native",
|
||||
"obj\\async_apc", "obj\\async_cancel", "obj\\apc_return_smoke", "obj\\edit", "obj\\httpget",
|
||||
@@ -323,6 +325,11 @@ bool create_disk_vhd_if_missing(void) {
|
||||
// Runs the requested operation.
|
||||
int main(int argc, char** argv) {
|
||||
NOB_GO_REBUILD_URSELF(argc, argv);
|
||||
bool with_tests = argc == 2 && strcmp(argv[1], "--with-tests") == 0;
|
||||
if (argc != 1 && !with_tests) {
|
||||
nob_log(NOB_ERROR, "Usage: build.exe [--with-tests]");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!create_disk_vhd_if_missing()) return 1;
|
||||
if (!create_usbfatroot_if_missing()) return 1;
|
||||
@@ -378,7 +385,7 @@ int main(int argc, char** argv) {
|
||||
nob_temp_sprintf("cl %s /DNTL_HAL_BUILD /MP /c /Foobj\\hal\\ code\\kernel\\hal\\*.c && link %s /DLL /FIXED /DYNAMICBASE:NO /HIGHENTROPYVA:NO /RELEASE /BASE:0xFFFF800010000000 /OUT:bin\\hal.dll /NOENTRY /IGNORE:4216 obj\\printf.obj obj\\libc.obj obj\\hal\\*.obj", KernelCflags, LFLAGS),
|
||||
|
||||
// Kernel.
|
||||
nob_temp_sprintf("cl %s /DKE_DISPLAY_BUILD /MP /c /Foobj\\kernel\\ code\\kernel\\*.c code\\common\\boot_volume_selector.c && link %s /SUBSYSTEM:NATIVE /FIXED /DYNAMICBASE:NO /HIGHENTROPYVA:NO /RELEASE /BASE:0xFFFF800000000000 /ENTRY:KernelMain /OUT:bin\\kernel.exe bin\\hal.lib obj\\printf.obj obj\\libc.obj obj\\kernel\\*.obj", KernelCflags, LFLAGS),
|
||||
nob_temp_sprintf("cl %s /DNTL_KERNEL_BUILD /DKE_DISPLAY_BUILD /MP /c /Foobj\\kernel\\ code\\kernel\\*.c code\\common\\boot_volume_selector.c && link %s /SUBSYSTEM:NATIVE /FIXED /DYNAMICBASE:NO /HIGHENTROPYVA:NO /RELEASE /BASE:0xFFFF800000000000 /ENTRY:KernelMain /MAP:bin\\kernel.map /OUT:bin\\kernel.exe bin\\hal.lib obj\\printf.obj obj\\libc.obj obj\\kernel\\*.obj", KernelCflags, LFLAGS),
|
||||
|
||||
// Subsystem DLLs.
|
||||
"link /nologo /SUBSYSTEM:NATIVE /NODEFAULTLIB /DLL " USER_IMAGE_FLAGS_LITERAL " /OUT:bin\\syscall.dll obj\\syscall\\stubs.obj obj\\syscall\\pe_loader.obj obj\\syscall\\module_loader.obj obj\\syscall\\process_loader.obj obj\\syscall\\resource.obj obj\\syscall\\input_service.obj obj\\syscall\\console_service.obj obj\\syscall\\desktop.obj obj\\syscall\\path.obj obj\\libc.obj",
|
||||
@@ -392,9 +399,14 @@ int main(int argc, char** argv) {
|
||||
if (!run_sync(core_tasks[i])) return 1;
|
||||
}
|
||||
|
||||
// The support library contains only driver-side helpers, never kernel policy.
|
||||
if (!run_sync(nob_temp_sprintf(
|
||||
"cl %s /MP /c /Foobj\\ddk\\ code\\ddk\\*.c && lib /nologo /OUT:bin\\ntlddk.lib obj\\ddk\\*.obj",
|
||||
DRIVER_CFLAGS_LITERAL))) return 1;
|
||||
|
||||
nob_log(NOB_INFO, "--- Building Bus Drivers ---");
|
||||
const char* pci_cmd = nob_temp_sprintf(
|
||||
"cl %s /MP /c /Fo%s\\ %s && link /nologo /SUBSYSTEM:NATIVE /NODEFAULTLIB /DLL /OUT:bin\\%s.sys /IMPLIB:bin\\%s.lib /ENTRY:DriverEntry bin\\kernel.lib obj\\printf.obj obj\\libc.obj %s\\*.obj",
|
||||
"cl %s /MP /c /Fo%s\\ %s && link /nologo /SUBSYSTEM:NATIVE /NODEFAULTLIB /DLL /OUT:bin\\%s.sys /IMPLIB:bin\\%s.lib /NOENTRY bin\\kernel.lib bin\\ntlddk.lib obj\\printf.obj obj\\libc.obj %s\\*.obj",
|
||||
PCI_BUS_DRIVER.cflags, PCI_BUS_DRIVER.obj_dir, PCI_BUS_DRIVER.sources,
|
||||
PCI_BUS_DRIVER.name, PCI_BUS_DRIVER.name, PCI_BUS_DRIVER.obj_dir
|
||||
);
|
||||
@@ -406,9 +418,9 @@ int main(int argc, char** argv) {
|
||||
for (size_t i = 0; i < NOB_ARRAY_LEN(STANDALONE_DRIVERS); i++) {
|
||||
const Driver_Target* driver = &STANDALONE_DRIVERS[i];
|
||||
const char* cmd = nob_temp_sprintf(
|
||||
"cl %s /MP /c /Fo%s\\ %s && link /nologo /SUBSYSTEM:NATIVE /NODEFAULTLIB /DLL /OUT:bin\\%s.sys /ENTRY:DriverEntry bin\\kernel.lib %s obj\\libc.obj %s\\*.obj",
|
||||
"cl %s /MP /c /Fo%s\\ %s && link /nologo /SUBSYSTEM:NATIVE /NODEFAULTLIB /DLL /OUT:bin\\%s.sys %s bin\\kernel.lib %s obj\\libc.obj %s\\*.obj",
|
||||
driver->cflags, driver->obj_dir, driver->sources, driver->name,
|
||||
driver->libs ? driver->libs : "", driver->obj_dir
|
||||
driver->entry_flag, driver->libs ? driver->libs : "", driver->obj_dir
|
||||
);
|
||||
nob_da_append(&procs, run_async(cmd));
|
||||
}
|
||||
@@ -425,7 +437,7 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
}
|
||||
nob_sb_append_null(&lwip_files);
|
||||
nob_da_append(&procs, run_async(nob_temp_sprintf("cl %s /MP /c /Foobj\\lwip\\ %s && link /nologo /SUBSYSTEM:NATIVE /NODEFAULTLIB /DLL /OUT:bin\\lwip.sys /ENTRY:DriverEntry bin\\kernel.lib obj\\libc.obj obj\\lwip\\*.obj", LwipCflags, lwip_files.items)));
|
||||
nob_da_append(&procs, run_async(nob_temp_sprintf("cl %s /MP /c /Foobj\\lwip\\ %s && link /nologo /SUBSYSTEM:NATIVE /NODEFAULTLIB /DLL /OUT:bin\\lwip.sys /NOENTRY bin\\kernel.lib obj\\libc.obj obj\\lwip\\*.obj", LwipCflags, lwip_files.items)));
|
||||
}
|
||||
|
||||
Nob_String_Builder ext4_sb = {0}, ext4_files = {0};
|
||||
@@ -440,7 +452,7 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
}
|
||||
nob_sb_append_null(&ext4_files);
|
||||
nob_da_append(&procs, run_async(nob_temp_sprintf("cl %s /MP /c /Foobj\\ext4\\ %s && link /nologo /SUBSYSTEM:NATIVE /NODEFAULTLIB /DLL /OUT:bin\\ext4.sys /ENTRY:DriverEntry bin\\kernel.lib obj\\libc.obj obj\\ext4\\*.obj", Ext4Cflags, ext4_files.items)));
|
||||
nob_da_append(&procs, run_async(nob_temp_sprintf("cl %s /MP /c /Foobj\\ext4\\ %s && link /nologo /SUBSYSTEM:NATIVE /NODEFAULTLIB /DLL /OUT:bin\\ext4.sys /NOENTRY bin\\kernel.lib obj\\libc.obj obj\\ext4\\*.obj", Ext4Cflags, ext4_files.items)));
|
||||
}
|
||||
|
||||
Nob_String_Builder ntfs3g_sb = {0}, ntfs3g_files = {0};
|
||||
@@ -455,7 +467,7 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
}
|
||||
nob_sb_append_null(&ntfs3g_files);
|
||||
nob_da_append(&procs, run_async(nob_temp_sprintf("cl %s /MP /c /Foobj\\ntfs3g\\ %s && link /nologo /SUBSYSTEM:NATIVE /NODEFAULTLIB /DLL /OUT:bin\\ntfs.sys /ENTRY:DriverEntry bin\\kernel.lib obj\\printf.obj obj\\libc.obj obj\\ntfs3g\\*.obj", Ntfs3gCflags, ntfs3g_files.items)));
|
||||
nob_da_append(&procs, run_async(nob_temp_sprintf("cl %s /MP /c /Foobj\\ntfs3g\\ %s && link /nologo /SUBSYSTEM:NATIVE /NODEFAULTLIB /DLL /OUT:bin\\ntfs.sys /NOENTRY bin\\kernel.lib obj\\printf.obj obj\\libc.obj obj\\ntfs3g\\*.obj", Ntfs3gCflags, ntfs3g_files.items)));
|
||||
}
|
||||
|
||||
|
||||
@@ -537,6 +549,8 @@ int main(int argc, char** argv) {
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < NOB_ARRAY_LEN(apps); i++) {
|
||||
if (!with_tests && strncmp(apps[i][0], "tests\\", 6) == 0)
|
||||
continue;
|
||||
const char* appCflags = strcmp(apps[i][3], "/ENTRY:PosixStart") == 0
|
||||
? PosixUserCflags
|
||||
: UserCflags;
|
||||
@@ -565,7 +579,7 @@ int main(int argc, char** argv) {
|
||||
mmd_dir(VHD_PART2, "apps");
|
||||
mmd_dir(VHD_PART2, "system");
|
||||
mmd_dir(VHD_PART2, "drivers");
|
||||
mmd_dir(VHD_PART2, "tests");
|
||||
if (with_tests) mmd_dir(VHD_PART2, "tests");
|
||||
|
||||
// The ESP is only the boot anchor; the configured system volume carries the OS payload.
|
||||
if (!mcopy_file("bin\\bootx64.efi", VHD_PART1, "EFI/BOOT/bootx64.efi")) return 1;
|
||||
@@ -684,7 +698,7 @@ int main(int argc, char** argv) {
|
||||
"bin\\surface_multithread_commit.exe",
|
||||
"bin\\surface_cross_process_smoke.exe",
|
||||
};
|
||||
if (!mcopy_files_to_dir(VHD_PART2, "tests/", test_files, NOB_ARRAY_LEN(test_files))) return 1;
|
||||
if (with_tests && !mcopy_files_to_dir(VHD_PART2, "tests/", test_files, NOB_ARRAY_LEN(test_files))) return 1;
|
||||
|
||||
const char* optional_app_files[3];
|
||||
size_t optional_app_count = 0;
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
// Pure DDK support library routines; no kernel or module-wide state.
|
||||
|
||||
#include <ntlite/ddk/pci_caps.h>
|
||||
|
||||
KSTATUS NtlPciFindCapability(const NTL_PCI_CONFIG_READER* reader,
|
||||
uint8_t firstOffset,
|
||||
uint8_t capabilityId,
|
||||
uint8_t* outOffset) {
|
||||
if (!reader || !reader->Read32 || !outOffset || !capabilityId)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
*outOffset = 0;
|
||||
uint8_t current = firstOffset;
|
||||
uint64_t visited = 0;
|
||||
|
||||
while (current) {
|
||||
if (current < 0x40u || (current & 3u) != 0u)
|
||||
return NTLSTATUS_DEVICE_ERROR;
|
||||
|
||||
uint64_t bit = 1ull << (current / 4u - 16u);
|
||||
if (visited & bit)
|
||||
return NTLSTATUS_DEVICE_ERROR;
|
||||
visited |= bit;
|
||||
|
||||
uint32_t header = reader->Read32(reader->Context, current);
|
||||
if ((uint8_t)header == capabilityId) {
|
||||
*outOffset = current;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
current = (uint8_t)((header >> 8) & 0xFCu);
|
||||
}
|
||||
|
||||
return NTLSTATUS_NOT_FOUND;
|
||||
}
|
||||
@@ -1,222 +0,0 @@
|
||||
# Native service architecture
|
||||
|
||||
This document defines the intended user-mode service model for nt-lite. It is an architectural contract, not a description of every detail of the current implementation. Code should move toward this model without introducing temporary service boundaries that are not meant to survive.
|
||||
|
||||
## Process roles
|
||||
|
||||
`init.exe` is the kernel-selected bootstrap user process and user-mode service supervisor. Its process identifier is deliberately not an ABI: it happens to be allocated early today, but code, configuration, diagnostics, and personalities must not assume a particular PID for init.
|
||||
|
||||
`init.exe` is not a UNIX init process: it does not imply a kernel process hierarchy, it does not adopt orphaned processes, and it does not provide reparenting semantics. Holding a process handle for supervision does not make the supervised process a child in a UNIX sense.
|
||||
|
||||
The intended user-mode system plane has four roles:
|
||||
|
||||
- `init.exe` owns service lifecycle and authority delegation.
|
||||
- `rssrv.exe` is the critical native system server.
|
||||
- Session-owned infrastructure such as `dwm.exe` is noncritical and owns bulky policy that should not make RSSRV monolithic.
|
||||
- Subsystem-specific personality servers, such as `posixsrv.exe`, are optional and noncritical to the native system.
|
||||
|
||||
Do not split native input, resource naming, display authority, or console ownership into one service process per subsystem merely to isolate components. nt-lite is a hybrid kernel. RSSRV is intentionally a substantial native server, but it is not the desktop compositor: the desktop window manager and composition engine belong in a separate `dwm.exe` process so window-tree, z-order, damage, decoration, and composition policy can evolve without expanding RSSRV into the whole graphical environment.
|
||||
|
||||
## Kernel and RSSRV ownership
|
||||
|
||||
The kernel owns mechanisms that require kernel authority or hardware access. The bootstrap service manager receives explicit manager capabilities for the native resource plane and delegates attenuated input, display, volume, and namespace authority to RSSRV; this authority must not depend on init's PID. This includes:
|
||||
|
||||
- process and thread execution;
|
||||
- virtual memory and sections;
|
||||
- object handles and capabilities;
|
||||
- ports and other IPC primitives;
|
||||
- filesystem and storage drivers;
|
||||
- physical input drivers and raw input-device objects;
|
||||
- display miniports, low-level display devices, framebuffer or VRAM mappings, and hardware presentation primitives;
|
||||
- kernel surfaces and other graphics primitives whose implementation belongs below user-mode policy.
|
||||
|
||||
RSSRV owns shared native user-mode policy and system-visible logical resources. Its intended responsibilities include:
|
||||
|
||||
- the global native object namespace;
|
||||
- namespace directories, bindings, enumeration, and symbolic links;
|
||||
- publication of mounted volumes and other native resources;
|
||||
- logical keyboard and mouse ownership above raw device input;
|
||||
- keyboard state and layout policy where it does not belong in the hardware driver;
|
||||
- logical pointer state, input focus, capture, and routing;
|
||||
- native console objects, console buffers, console input, and active-console policy;
|
||||
- interactive-session state;
|
||||
- the session's display metadata, modeset policy, and transitional console authority;
|
||||
- authority and routing used by the session desktop/window manager, without owning the window tree or compositor.
|
||||
|
||||
RSSRV owns the logical object and its policy; it does not replace the kernel driver or hardware object. For example, a keyboard driver remains a kernel component, while RSSRV consumes normalized key events and owns the logical keyboard state exposed to native user mode.
|
||||
|
||||
RSSRV should remain internally modular even though its native resource facilities share one process and failure domain. Separate namespace, volume, input, console, session, and display components are preferred over separate user-mode services. Desktop/window/composition policy is the exception: it has enough independent bulk and failure behavior to live in `dwm.exe`.
|
||||
|
||||
The implemented RSSRV foundation uses one root `RSSRV_STATE` and passes explicit component state to worker threads instead of relying on subsystem globals. The namespace tree lives in `namespace.c`, mounted-volume publication in `volume.c`, resource protocol/listener handling in `resource_service.c`, Session 0 policy in `session.c`, and `rssrv.c` is limited to bootstrap, service control, and component lifetime. RSSRV receives attenuated kernel input-manager and display-manager capabilities in dedicated input/display component state rather than relying on ambient process privilege.
|
||||
|
||||
Logical input is now an RSSRV-owned facility. The kernel input manager exposes a capability-scoped stream of normalized physical key make/break and relative mouse events. RSSRV consumes that stream, owns key-down/modifier/lock state, performs the initial keyboard-layout character mapping, and owns logical pointer position and mouse-button/wheel state. Session 0 owns routing policy above that logical state: one active target, keyboard focus, ordinary pointer focus, and optional pointer capture. Keyboard and pointer dispatch snapshot the selected target before calling it, so target-specific queues do not share or race the raw logical-input queue. Session 0 owns logical pointer state and routing, while DWM mirrors that authoritative position to the visible hardware cursor through its separately delegated display-manager capability. RSSRV publishes the raw logical listener at `/Services/Input`; normal native applications and the current POSIX/Win32 personality bindings may consume that service through the `NtlInput*` client API, while the console receives a separately routed focused-input queue. The old global input syscalls remain a transitional native ABI for existing callers and contract tests, but they are no longer the canonical interactive input path and should be deleted after those callers migrate.
|
||||
|
||||
RSSRV owns logical typematic timing for the Session 0 input path. Repeated physical make events are treated as idempotent there so PS/2 hardware repeat and USB HID share one logical repeat policy. The legacy kernel-global input path retains its existing kernel repeat timer only for legacy consumers that have not moved to RSSRV routing.
|
||||
|
||||
## Desktop window manager
|
||||
|
||||
`dwm.exe` is the Session 0 desktop window manager and compositor. It is not a personality server and it is not a replacement for RSSRV. RSSRV remains the authority broker for the interactive session: it owns Session 0, logical input routing, console ownership, and the namespace objects through which the session is named. init owns bootstrap delegation of the display-manager capability and splits its rights between RSSRV and DWM.
|
||||
|
||||
DWM owns graphical policy that would otherwise make RSSRV monolithic:
|
||||
|
||||
- the desktop and window tree;
|
||||
- z-order and visibility;
|
||||
- window bounds and decorations;
|
||||
- dirty-region aggregation and composition scheduling;
|
||||
- composition of client surfaces into the desktop surface;
|
||||
- later animation and graphical effects.
|
||||
|
||||
DWM must not receive ambient display privilege. init delegates an attenuated display-manager capability carrying query, physical-present, and cursor rights only; RSSRV receives the disjoint mode-setting and transitional-console authority it still needs. Mode-setting remains outside DWM unless a later design gives it a concrete reason to own that policy. A DWM crash must not take RSSRV down; native namespace, input, console, and session authority remain alive even if the graphical desktop disappears. DWM becomes eligible for `restart=on-failure` only after its session reconstruction protocol is defined, and then it is subject to the same three-restarts-in-any-sliding-five-minute-window rule as every other restartable managed process.
|
||||
|
||||
DWM also owns visible pointer presentation policy independently from RSSRV input routing. The native desktop model must support a free cursor, a cursor confined to a desktop/window rectangle, and a relative-pointer lock suitable for games. Relative lock hides and pins the hardware cursor while preserving RSSRV's relative mouse deltas for the captured input target; it must not synthesize motion by repeatedly warping the logical pointer. Cursor visibility is an explicit DWM policy bit rather than a side effect of focus or capture. RSSRV pointer capture chooses the input recipient, while DWM confinement/relative-lock controls only presentation. The later desktop/window protocol will authorize focused clients to request these transitions without granting them DisplayManager cursor authority.
|
||||
|
||||
## Personality servers
|
||||
|
||||
A personality server exists only for state or policy that is specific to one application personality and must be shared between processes.
|
||||
|
||||
Personality servers are not the owners of generic hardware or native session resources. They consume native facilities exposed by the kernel and RSSRV.
|
||||
|
||||
`posixsrv.exe` may eventually own POSIX-specific shared state such as:
|
||||
|
||||
- POSIX credentials and supplementary groups;
|
||||
- POSIX process-group and session identifiers;
|
||||
- cross-process POSIX signal state and routing;
|
||||
- the synthetic POSIX filesystem metadata overlay;
|
||||
- POSIX FIFO metadata;
|
||||
- passwd and group database state.
|
||||
|
||||
POSIX does not cause the kernel to acquire a UNIX process hierarchy. A POSIX launch relationship may be tracked by the POSIX subsystem when required by an exposed API, but there is no reparenting to a distinguished process and no orphan adoption. In particular, POSIX must not infer special semantics from whatever PID the kernel assigned to `init.exe`.
|
||||
|
||||
A Win32 personality server should not be created merely for symmetry. If shared Win32-only state eventually requires a server, `winsrv.exe` is the preferred role name. Native console, input, session, and display ownership remains in RSSRV rather than moving into a Win32 server.
|
||||
|
||||
## Service discovery
|
||||
|
||||
The numeric kernel system-service registry is bootstrap infrastructure, not the general service namespace.
|
||||
|
||||
`SYS_SERVICE_RESOURCE` exists so processes can find RSSRV before a higher-level namespace is available. New ordinary services should not receive additional numeric kernel service identifiers merely for discovery.
|
||||
|
||||
Once RSSRV is available, named services and logical system resources should be published through the RSSRV namespace. A future namespace may contain entries such as:
|
||||
|
||||
```text
|
||||
/
|
||||
├── Device
|
||||
│ ├── Keyboard0
|
||||
│ ├── Mouse0
|
||||
│ ├── Display0
|
||||
│ └── HarddiskVolume1
|
||||
├── Services
|
||||
│ ├── Input
|
||||
│ ├── Console
|
||||
│ └── Posix
|
||||
├── Sessions
|
||||
│ └── 0
|
||||
│ ├── Console
|
||||
│ └── Desktop
|
||||
├── SystemRoot -> /Device/HarddiskVolume1
|
||||
└── Console -> /Sessions/0/Console
|
||||
```
|
||||
|
||||
The current namespace foundation materializes `/Device`, `/Services`, and `/Sessions` as real RSSRV namespace directories. `/SystemRoot` is a real RSSRV symbolic link to the independently published `/Device/HarddiskVolumeN` object for the bootstrap system volume. `/Device/Display0` is a query-only display-manager child capability: init delegates mode-setting and transitional console authority to RSSRV while physical-present and cursor authority are delegated separately to DWM. `/Services/Input` and `/Services/Console` are bound RSSRV listener capabilities for the raw logical-input and native-console protocols. Session 0 is materialized as `/Sessions/0`, with `/Sessions/0/Desktop` reserved for the userspace desktop/window-manager object and `/Sessions/0/Console` bound to the current console listener. `/Console` is a symbolic link to `/Sessions/0/Console`. Namespace directories are enumerable through the resource protocol; native shell `dir` and tab completion use that enumeration rather than synthesizing names from filesystem paths.
|
||||
|
||||
Names added later are not ABI until code deliberately makes them so. The important rule is that RSSRV is the canonical named namespace after bootstrap.
|
||||
|
||||
## Service configuration and supervision
|
||||
|
||||
`init.exe` owns service lifecycle. The mandatory `system/services.ini` file, packaged next to `settings.ini`, defines stable service policy. The kernel does not parse this file. `init.exe` reads it directly through the bootstrap system-volume capability before RSSRV starts, so locating RSSRV never depends on RSSRV itself.
|
||||
|
||||
The configuration uses case-sensitive `[service.<id>]` sections. Every service section must define all of these keys:
|
||||
|
||||
```ini
|
||||
[service.rssrv]
|
||||
type=resource
|
||||
image=system/rssrv.exe
|
||||
start=auto
|
||||
critical=true
|
||||
restart=never
|
||||
ready_timeout_ms=5000
|
||||
stop_timeout_ms=5000
|
||||
requires=
|
||||
```
|
||||
|
||||
The parser is deliberately strict. Unknown or duplicate keys, duplicate service identifiers, malformed values, missing required keys, missing dependencies, dependency cycles, and an automatic service depending on a disabled service are fatal configuration errors. The file has no includes, quoting language, environment expansion, continuation lines, or implicit fallback descriptor. A missing or malformed `services.ini` is fatal rather than causing init to fall back to a compiled duplicate of service policy.
|
||||
|
||||
`type` selects an audited service adapter whose bootstrap authority remains compiled into init. Configuration is not a capability language. In particular, the resource adapter owns the restricted security context and startup capabilities required by RSSRV; `services.ini` cannot grant arbitrary privileges or handles. The generic adapter is intentionally narrow: the service receives only its private service-control listener, runs under the shared unprivileged service context, exports exactly one public Port listener in READY, and init publishes that listener at `/Services/<id>` through RSSRV. Automatic generic services must be noncritical, `restart=never`, and explicitly depend on RSSRV. `posixsrv.exe` is the first consumer of this adapter.
|
||||
|
||||
Exactly one resource service must be configured. While resource-server reconstruction is unsupported it must be automatic, critical, dependency-free, and `restart=never`. A future implementation may relax only the restart restriction once recovery semantics exist.
|
||||
|
||||
Managed services use the parsed descriptor and a runtime record. The runtime record owns the current process handle, control connection, and lifecycle state. Service dependencies are resolved to descriptor indices and dependency-first startup order is computed while validating the configuration.
|
||||
|
||||
The private service-control READY protocol is generic. A READY notification reports a status and the number of transferred export handles; the meaning and expected count of those handles come from the compiled service adapter. RSSRV exports its init-only administrative resource listener plus the Session 0 control listener delegated onward to DWM. DWM exports one desktop listener for init to publish at `/Sessions/0/Desktop`. A generic service exports one public listener for `/Services/<id>`. The protocol does not encode service-specific READY flags.
|
||||
|
||||
The configured interactive session process is not a managed service. It is launched and observed separately from the service table. `dwm.exe`, by contrast, is an explicit noncritical `[service.dwm]`: it is launched by init in dependency order after RSSRV, receives no ambient security privilege, and exports no machine-global endpoint. Its initial bootstrap owns the visible hardware cursor; later compositor/window protocol work remains session policy inside that same service process.
|
||||
|
||||
The service manager should remain small. Restart accounting, additional startup capabilities, and recovery policy should be added only where they have a concrete consumer.
|
||||
|
||||
A service is expected to move through a small set of states such as:
|
||||
|
||||
```text
|
||||
stopped -> starting -> ready -> stopping -> stopped
|
||||
\-> failed
|
||||
```
|
||||
|
||||
A service becomes ready only after its authoritative state and exported capabilities are usable. Process creation alone is not readiness.
|
||||
|
||||
A normal requested stop is not a crash and must not consume restart budget.
|
||||
|
||||
## Automatic restart policy
|
||||
|
||||
Automatic restart is opt-in for each service. A service must not be marked restartable until its authoritative state, exported capabilities, and dependent clients have a defined recovery model.
|
||||
|
||||
When automatic restart is enabled for a failed or crashed service, `init.exe` applies a sliding five-minute restart window:
|
||||
|
||||
1. Remove recorded automatic-restart timestamps that are at least five minutes old.
|
||||
2. If three automatic restarts remain in the window, suppress another automatic restart and leave the service in the failed state.
|
||||
3. Otherwise record the new restart attempt and restart the service.
|
||||
|
||||
This permits at most three automatic restart attempts in any sliding five-minute interval. It is not a fixed five-minute bucket and it does not reset on wall-clock boundaries.
|
||||
|
||||
A restart attempt is counted when `init.exe` commits to an automatic restart after a failure, not merely when the failed process is observed. Startup failure of the replacement still consumes that attempt.
|
||||
|
||||
Dependency-driven recovery caused by a service failure counts against the failed service's restart budget. A deliberate stop, shutdown, or explicit administrative start does not count as an automatic restart.
|
||||
|
||||
The default policy is no automatic restart until reconstruction semantics are defined. In particular, RSSRV is critical and must not be blindly restarted as though its namespace and session state survived. If RSSRV is ever made restartable, recovery must explicitly rebuild the native service plane and any user session state that cannot survive the loss of RSSRV.
|
||||
|
||||
The three-restarts-in-five-minutes rule is an invariant of `restart=on-failure`, not a configurable knob. The current shipped service set contains no automatically restartable service, so init does not yet need runtime restart accounting. RSSRV is `restart=never`; an unexpected RSSRV exit is a terminal critical-service failure. The interactive session process is not a service and its separate restart behavior is outside this service restart budget.
|
||||
|
||||
## Failure domains
|
||||
|
||||
RSSRV is a critical native service because it is intended to own authoritative namespace and session state. Loss of RSSRV is therefore a native service-plane failure, not an ordinary personality failure. If a critical service reaches an unrecoverable failed state, init emits a fatal diagnostic and exits; the kernel already treats unexpected init termination as fatal.
|
||||
|
||||
Personality servers are noncritical to the native system. Failure of `posixsrv.exe`, for example, may invalidate POSIX shared state and require affected POSIX processes to fail or be terminated, but it must not make native RSSRV facilities unavailable.
|
||||
|
||||
Clients must not silently treat a newly started service instance as the same stateful server unless the protocol explicitly supports reconstruction. A restarted service instance has a new generation of authoritative state unless a future design specifies otherwise.
|
||||
|
||||
## Design constraints
|
||||
|
||||
When extending this model:
|
||||
|
||||
- keep hardware mechanism in the kernel and shared native user-mode policy in RSSRV;
|
||||
- do not create a service merely because a subsystem can be isolated behind IPC;
|
||||
- do not duplicate a native resource owner inside multiple personality servers;
|
||||
- keep the numeric kernel service registry bootstrap-only;
|
||||
- keep service authority capability-based rather than identity checks against process names;
|
||||
- define recovery semantics before enabling automatic restart;
|
||||
- prefer one canonical path to a service or resource and remove transitional aliases once the real model works.
|
||||
|
||||
## Native console ownership
|
||||
|
||||
RSSRV owns the one logical native console used before multi-session support exists. The console is an RSSRV component, not a separate service process. It is published as `/Services/Console` so native clients and personality libraries use one canonical console protocol.
|
||||
|
||||
The console component currently serializes output, cursor movement, fills, explicit glyph drawing, clear operations, and logical keyboard input. It owns a monotonically increasing content generation and a focused key queue populated by Session 0 routing. The input service remains available for applications that need the raw logical keyboard or pointer stream rather than console semantics.
|
||||
|
||||
The kernel text console remains a transitional rendering backend. RSSRV reaches it only through the delegated display-manager capability and `SYS_DISPLAY_MANAGER_RIGHT_CONSOLE`; RSSRV does not receive `SYS_PRIV_CONSOLE_ACCESS`. The capability-scoped console bridge exists so normal user applications can migrate away from ambient console syscalls before the kernel text-buffer implementation is removed or reduced to early-boot/panic use.
|
||||
|
||||
Normal native and personality console code should use `NtlConsole*`. The old `SysWriteConsole`, `SysGetConsoleInfo`, `SysSetConsoleCursor`, fill, clear, and direct glyph syscalls remain supported temporarily for bootstrapping, diagnostics, and regression tests. Their existence is not a commitment to keep two console APIs. Once DWM session/window composition can replace the kernel's interactive text-console path, remaining ordinary callers should move and the stale ambient syscalls should be deleted.
|
||||
|
||||
Session 0 now exists as explicit RSSRV state. The console registers a stable in-process input target with that session and is the initial active target, keyboard focus, and pointer focus. Pointer capture is represented separately so the later userspace window manager can capture pointer delivery without changing the raw input service or console protocol. The console owns its own focused key queue; `/Services/Input` remains a raw logical observer queue rather than competing with console reads for the same records.
|
||||
|
||||
`/Services/Console` names the process-independent console protocol, `/Sessions/0/Console` names the Session 0 instance, and `/Console` is the convenience symbolic link to the current session console. `/Sessions/0/Desktop` is currently only a namespace directory; the later DWM phase will replace that placeholder with the first real desktop policy object.
|
||||
|
||||
Display authority is split explicitly at bootstrap. RSSRV retains mode-setting and transitional console rights, while init delegates physical-present and hardware-cursor rights to the noncritical DWM service. The published `/Device/Display0` capability remains intentionally query-only. Neither RSSRV nor ordinary applications need ambient `SYS_PRIV_DISPLAY_ACCESS` for the normal path.
|
||||
|
||||
Kernel surfaces are now shareable capability-backed pixel storage rather than process-private presentation objects. A surface may have multiple independent VAD mappings in one or more processes; mapping rights distinguish read-only compositor access from client write access. `SysCommitSurface` records clipped damage, increments a content generation, and signals the waitable surface object without touching physical scanout. Surface wakeups are deliberately coalescing: consumers use the generation to detect skipped commits, and `LastDamageBounds` describes only the most recent commit rather than an acknowledged damage queue. The later DWM window protocol will carry the compositor's authoritative dirty-region submission. `SysPresentDisplayManagerSurface` is the capability-scoped physical-present path intended for DWM. The old ambient `SysPresentSurface` API and its handle right are deleted. Ordinary clients can create, map, share, wait on, and commit surfaces but cannot touch physical scanout. `SysPresentDisplayManagerSurface` is the only surface-to-scanout path and requires an explicitly delegated DisplayManager PRESENT capability.
|
||||
@@ -1,10 +1,10 @@
|
||||
#include <ntlite/ddk/module.h>
|
||||
// Intel High Definition Audio PCI attachment and controller lifetime management.
|
||||
|
||||
#include "hda.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
const NTL_DDK* gHdaDdk;
|
||||
static DRIVER_OBJECT gHdaDriver;
|
||||
static HDA_CONTROLLER* gHdaControllers[HDA_MAX_CONTROLLERS];
|
||||
static uint32_t gHdaControllerCount;
|
||||
@@ -157,7 +157,7 @@ static KSTATUS HdaConfigureInterrupt(HDA_CONTROLLER* controller) {
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
gHdaDdk->Debug->Print(
|
||||
DbgPrint(
|
||||
"HDA: MSI setup failed status=0x%x; attempting explicit INTx fallback\n",
|
||||
status);
|
||||
controller->UsingMsi = false;
|
||||
@@ -182,7 +182,7 @@ static KSTATUS HdaWaitForControllerInterruptWorkIdle(HDA_CONTROLLER* controller)
|
||||
if (controller->IsrActive == 0 &&
|
||||
controller->Dpc.Inserted == 0 && controller->DpcActive == 0)
|
||||
return NTLSTATUS_SUCCESS;
|
||||
gHdaDdk->Timer->Stall(10);
|
||||
KeStallExecution(10);
|
||||
}
|
||||
return NTLSTATUS_TIMEOUT;
|
||||
}
|
||||
@@ -268,7 +268,7 @@ static KSTATUS HdaStartDevice(HDA_CONTROLLER* controller) {
|
||||
return status;
|
||||
}
|
||||
if (!KSUCCESS(status)) {
|
||||
gHdaDdk->Debug->Print(
|
||||
DbgPrint(
|
||||
"HDA: no analog endpoint registered status=0x%x\n",
|
||||
status);
|
||||
}
|
||||
@@ -379,14 +379,9 @@ static KSTATUS HdaAddDevice(DRIVER_OBJECT* driver, DEVICE_OBJECT* physicalDevice
|
||||
}
|
||||
|
||||
// Registers the Intel HDA transport driver with the I/O manager.
|
||||
KSTATUS DriverEntry(const NTL_DDK* ddk, NTL_DRIVER_INIT* init) {
|
||||
(void)init;
|
||||
if (!ddk || !ddk->Debug || !ddk->Debug->Print || !ddk->Dma || !ddk->Timer ||
|
||||
!ddk->Timer->QueryTicks || !ddk->Timer->Stall || !ddk->Audio ||
|
||||
!ddk->Scheduler || !ddk->Scheduler->DelayThread)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
static KSTATUS DriverInitialize(NTL_MODULE* module) {
|
||||
(void)module;
|
||||
|
||||
gHdaDdk = ddk;
|
||||
gHdaControllerCount = 0;
|
||||
memset(gHdaControllers, 0, sizeof(gHdaControllers));
|
||||
|
||||
@@ -395,9 +390,8 @@ KSTATUS DriverEntry(const NTL_DDK* ddk, NTL_DRIVER_INIT* init) {
|
||||
gHdaDriver.IdTable = &gHdaIdTable;
|
||||
gHdaDriver.DriverAddDevice = HdaAddDevice;
|
||||
gHdaDriver.MajorFunction[IRP_MJ_PNP] = HdaDispatchPnp;
|
||||
|
||||
KSTATUS status = IoRegisterDriverObject(&gHdaDriver);
|
||||
if (!KSUCCESS(status))
|
||||
gHdaDdk->Debug->Print("HDA: IoRegisterDriverObject failed status=0x%x\n", status);
|
||||
return status;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static DRIVER_OBJECT* const gModuleIoDrivers[] = { &gHdaDriver };
|
||||
NTL_DECLARE_IO_MODULE(DriverInitialize, gModuleIoDrivers);
|
||||
@@ -300,8 +300,6 @@ struct HDA_CONTROLLER {
|
||||
uint64_t ControllerInterruptCount;
|
||||
};
|
||||
|
||||
extern const NTL_DDK* gHdaDdk;
|
||||
|
||||
// Returns one byte from the controller register aperture.
|
||||
uint8_t HdaRead8(const HDA_CONTROLLER* controller, uint32_t offset);
|
||||
|
||||
|
||||
@@ -281,7 +281,7 @@ static KSTATUS HdaCodecPowerD0(HDA_CODEC_STATE* state,
|
||||
if ((powerState & HDA_POWER_ACTUAL_MASK) ==
|
||||
(HDA_POWER_STATE_D0 << HDA_POWER_ACTUAL_SHIFT))
|
||||
return NTLSTATUS_SUCCESS;
|
||||
gHdaDdk->Timer->Stall(10);
|
||||
KeStallExecution(10);
|
||||
}
|
||||
return NTLSTATUS_TIMEOUT;
|
||||
}
|
||||
@@ -583,7 +583,7 @@ static KSTATUS HdaCodecEnumerateAll(HDA_CODEC_STATE* state) {
|
||||
continue;
|
||||
}
|
||||
lastStatus = status;
|
||||
gHdaDdk->Debug->Print(
|
||||
DbgPrint(
|
||||
"HDA: codec %u topology discovery failed status=0x%x\n",
|
||||
codecAddress,
|
||||
status);
|
||||
@@ -1176,7 +1176,7 @@ static void HdaCodecRefreshEndpointsLocked(HDA_CODEC_STATE* state) {
|
||||
if (endpoint->Available != available) {
|
||||
endpoint->Available = available;
|
||||
if (endpoint->KernelEndpoint)
|
||||
gHdaDdk->Audio->SetEndpointAvailable(endpoint->KernelEndpoint,
|
||||
IoAudioSetEndpointAvailable(endpoint->KernelEndpoint,
|
||||
available ? 1u : 0u);
|
||||
}
|
||||
if (!endpoint->Running || endpoint->Starting ||
|
||||
@@ -1210,13 +1210,13 @@ static void HdaCodecRefreshEndpointsLocked(HDA_CODEC_STATE* state) {
|
||||
uintptr_t flags = KeAcquireSpinlock(&state->CallbackLock);
|
||||
endpoint->Running = false;
|
||||
KeReleaseSpinlock(&state->CallbackLock, flags);
|
||||
gHdaDdk->Debug->Print(
|
||||
DbgPrint(
|
||||
"HDA: jack reroute failed kind=%u route=%u status=0x%x\n",
|
||||
endpoint->Kind,
|
||||
selectedRoute,
|
||||
status);
|
||||
if (endpoint->KernelEndpoint) {
|
||||
gHdaDdk->Audio->ReportEndpointFailure(endpoint->KernelEndpoint,
|
||||
IoAudioReportEndpointFailure(endpoint->KernelEndpoint,
|
||||
status,
|
||||
0);
|
||||
}
|
||||
@@ -1306,7 +1306,7 @@ static void HdaCodecWorkThread(uintptr_t argument) {
|
||||
static void HdaCodecPollThread(uintptr_t argument) {
|
||||
HDA_CODEC_STATE* state = (HDA_CODEC_STATE*)argument;
|
||||
while (state->PollRunning) {
|
||||
(void)gHdaDdk->Scheduler->DelayThread(HDA_JACK_POLL_INTERVAL_MS);
|
||||
(void)KeDelayThread(HDA_JACK_POLL_INTERVAL_MS);
|
||||
if (!state->PollRunning)
|
||||
break;
|
||||
uintptr_t flags = KeAcquireSpinlock(&state->CallbackLock);
|
||||
@@ -1355,7 +1355,7 @@ static void HdaCodecPeriodComplete(void* context,
|
||||
endpoint = state->ActiveEndpoint->KernelEndpoint;
|
||||
KeReleaseSpinlock(&state->CallbackLock, flags);
|
||||
if (endpoint) {
|
||||
gHdaDdk->Audio->NotifyPeriod(endpoint,
|
||||
IoAudioNotifyPeriod(endpoint,
|
||||
completedPeriodIndex,
|
||||
presentedFrames,
|
||||
finePositionFrames,
|
||||
@@ -1453,7 +1453,7 @@ static void HdaCodecTransportFailure(void* context, KSTATUS failureStatus) {
|
||||
KeReleaseSpinlock(&state->CallbackLock, flags);
|
||||
for (uint8_t index = 0; index < state->EndpointCount; index++) {
|
||||
if (state->Endpoints[index].KernelEndpoint) {
|
||||
gHdaDdk->Audio->ReportEndpointFailure(
|
||||
IoAudioReportEndpointFailure(
|
||||
state->Endpoints[index].KernelEndpoint,
|
||||
failureStatus,
|
||||
0);
|
||||
@@ -1747,7 +1747,7 @@ static KSTATUS HdaCodecRegisterEndpoints(HDA_CODEC_STATE* state) {
|
||||
}
|
||||
}
|
||||
|
||||
status = gHdaDdk->Audio->RegisterEndpoint(®istration,
|
||||
status = IoAudioRegisterEndpoint(®istration,
|
||||
&endpoint->KernelEndpoint);
|
||||
if (!KSUCCESS(status))
|
||||
return status;
|
||||
@@ -1779,7 +1779,7 @@ static void HdaCodecStopWorkers(HDA_CODEC_STATE* state) {
|
||||
|
||||
// Discovers codec topology and registers usable analog render endpoints.
|
||||
KSTATUS HdaCodecInitialize(HDA_CONTROLLER* controller) {
|
||||
if (!controller || !gHdaDdk || !gHdaDdk->Audio || !gHdaDdk->Scheduler)
|
||||
if (!controller)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
HDA_CODEC_STATE* state = (HDA_CODEC_STATE*)MmAllocatePoolTag(
|
||||
sizeof(HDA_CODEC_STATE),
|
||||
@@ -1801,7 +1801,7 @@ KSTATUS HdaCodecInitialize(HDA_CONTROLLER* controller) {
|
||||
goto failed;
|
||||
status = HdaCodecBuildRoutes(state);
|
||||
if (!KSUCCESS(status)) {
|
||||
gHdaDdk->Debug->Print(
|
||||
DbgPrint(
|
||||
"HDA: no usable 48000 Hz, stereo, 16-bit analog converter-to-pin route\n");
|
||||
goto failed;
|
||||
}
|
||||
@@ -1873,7 +1873,7 @@ void HdaCodecStop(HDA_CONTROLLER* controller, IO_AUDIO_REMOVAL_MODE mode) {
|
||||
HDA_LOGICAL_ENDPOINT* endpoint = &state->Endpoints[index - 1u];
|
||||
if (!endpoint->KernelEndpoint)
|
||||
continue;
|
||||
gHdaDdk->Audio->UnregisterEndpoint(endpoint->KernelEndpoint, mode);
|
||||
IoAudioUnregisterEndpoint(endpoint->KernelEndpoint, mode);
|
||||
endpoint->KernelEndpoint = (IO_AUDIO_ENDPOINT*)0;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,15 +16,12 @@ static __forceinline void HdaMemoryBarrier(void) {
|
||||
|
||||
// Returns the controller's monotonic microsecond counter.
|
||||
static uint64_t HdaQueryTicks(void) {
|
||||
if (!gHdaDdk || !gHdaDdk->Timer || !gHdaDdk->Timer->QueryTicks)
|
||||
return 0;
|
||||
return gHdaDdk->Timer->QueryTicks();
|
||||
return KeQueryTicks();
|
||||
}
|
||||
|
||||
// Delays for a bounded controller settling interval.
|
||||
static void HdaStall(uint32_t microseconds) {
|
||||
if (gHdaDdk && gHdaDdk->Timer && gHdaDdk->Timer->Stall)
|
||||
gHdaDdk->Timer->Stall(microseconds);
|
||||
KeStallExecution(microseconds);
|
||||
}
|
||||
|
||||
// Returns one byte from the controller register aperture.
|
||||
@@ -170,7 +167,7 @@ static uint8_t HdaRingSizeSelector(uint16_t count) {
|
||||
|
||||
// Releases all DMA and synchronization resources owned by one controller.
|
||||
void HdaReleaseDma(HDA_CONTROLLER* controller) {
|
||||
if (!controller || !gHdaDdk || !gHdaDdk->Dma)
|
||||
if (!controller)
|
||||
return;
|
||||
|
||||
if (controller->StreamMutex) {
|
||||
@@ -190,12 +187,12 @@ void HdaReleaseDma(HDA_CONTROLLER* controller) {
|
||||
controller->RecoveryFinished = (EVENT*)0;
|
||||
}
|
||||
|
||||
gHdaDdk->Dma->FreeCommonBuffer(controller->Output.BdlDma);
|
||||
IoDmaFreeCommonBuffer(controller->Output.BdlDma);
|
||||
for (uint32_t ringIndex = 0; ringIndex < HDA_MAX_LOGICAL_ENDPOINTS; ringIndex++)
|
||||
gHdaDdk->Dma->FreeCommonBuffer(controller->Output.Rings[ringIndex].Dma);
|
||||
gHdaDdk->Dma->FreeCommonBuffer(controller->PositionDma);
|
||||
gHdaDdk->Dma->FreeCommonBuffer(controller->RirbDma);
|
||||
gHdaDdk->Dma->FreeCommonBuffer(controller->CorbDma);
|
||||
IoDmaFreeCommonBuffer(controller->Output.Rings[ringIndex].Dma);
|
||||
IoDmaFreeCommonBuffer(controller->PositionDma);
|
||||
IoDmaFreeCommonBuffer(controller->RirbDma);
|
||||
IoDmaFreeCommonBuffer(controller->CorbDma);
|
||||
controller->Output.BdlDma = (IO_DMA_BUFFER*)0;
|
||||
for (uint32_t ringIndex = 0; ringIndex < HDA_MAX_LOGICAL_ENDPOINTS; ringIndex++) {
|
||||
controller->Output.Rings[ringIndex].Dma = (IO_DMA_BUFFER*)0;
|
||||
@@ -211,14 +208,14 @@ void HdaReleaseDma(HDA_CONTROLLER* controller) {
|
||||
controller->Corb = (uint32_t*)0;
|
||||
|
||||
if (controller->DmaAdapter) {
|
||||
gHdaDdk->Dma->PutAdapter(controller->DmaAdapter);
|
||||
IoDmaPutAdapter(controller->DmaAdapter);
|
||||
controller->DmaAdapter = (IO_DMA_ADAPTER*)0;
|
||||
}
|
||||
}
|
||||
|
||||
// Allocates the transport and output DMA resources for one controller.
|
||||
KSTATUS HdaAllocateDma(HDA_CONTROLLER* controller) {
|
||||
if (!controller || !controller->Fdo || !gHdaDdk || !gHdaDdk->Dma)
|
||||
if (!controller || !controller->Fdo)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
IO_DMA_REQUIREMENTS requirements;
|
||||
@@ -229,7 +226,7 @@ KSTATUS HdaAllocateDma(HDA_CONTROLLER* controller) {
|
||||
requirements.Alignment = 128u;
|
||||
requirements.MaximumSegments = 1u;
|
||||
|
||||
KSTATUS status = gHdaDdk->Dma->GetAdapter(controller->Fdo,
|
||||
KSTATUS status = IoDmaGetAdapter(controller->Fdo,
|
||||
&requirements,
|
||||
&controller->DmaAdapter);
|
||||
if (!KSUCCESS(status))
|
||||
@@ -248,7 +245,7 @@ KSTATUS HdaAllocateDma(HDA_CONTROLLER* controller) {
|
||||
return status;
|
||||
}
|
||||
|
||||
status = gHdaDdk->Dma->AllocateCommonBuffer(controller->DmaAdapter,
|
||||
status = IoDmaAllocateCommonBuffer(controller->DmaAdapter,
|
||||
(size_t)controller->CorbEntryCount * sizeof(uint32_t),
|
||||
128u,
|
||||
&controller->CorbDma);
|
||||
@@ -256,7 +253,7 @@ KSTATUS HdaAllocateDma(HDA_CONTROLLER* controller) {
|
||||
HdaReleaseDma(controller);
|
||||
return status;
|
||||
}
|
||||
status = gHdaDdk->Dma->AllocateCommonBuffer(controller->DmaAdapter,
|
||||
status = IoDmaAllocateCommonBuffer(controller->DmaAdapter,
|
||||
(size_t)controller->RirbEntryCount * sizeof(HDA_RIRB_ENTRY),
|
||||
128u,
|
||||
&controller->RirbDma);
|
||||
@@ -266,7 +263,7 @@ KSTATUS HdaAllocateDma(HDA_CONTROLLER* controller) {
|
||||
}
|
||||
|
||||
// The position buffer is optional because LPIB is the supported fallback.
|
||||
status = gHdaDdk->Dma->AllocateCommonBuffer(controller->DmaAdapter,
|
||||
status = IoDmaAllocateCommonBuffer(controller->DmaAdapter,
|
||||
(size_t)controller->TotalStreamCount * sizeof(uint64_t),
|
||||
128u,
|
||||
&controller->PositionDma);
|
||||
@@ -274,7 +271,7 @@ KSTATUS HdaAllocateDma(HDA_CONTROLLER* controller) {
|
||||
controller->PositionDma = (IO_DMA_BUFFER*)0;
|
||||
|
||||
for (uint32_t ringIndex = 0; ringIndex < HDA_MAX_LOGICAL_ENDPOINTS; ringIndex++) {
|
||||
status = gHdaDdk->Dma->AllocateCommonBuffer(
|
||||
status = IoDmaAllocateCommonBuffer(
|
||||
controller->DmaAdapter,
|
||||
HDA_OUTPUT_BUFFER_BYTES,
|
||||
128u,
|
||||
@@ -284,7 +281,7 @@ KSTATUS HdaAllocateDma(HDA_CONTROLLER* controller) {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
status = gHdaDdk->Dma->AllocateCommonBuffer(controller->DmaAdapter,
|
||||
status = IoDmaAllocateCommonBuffer(controller->DmaAdapter,
|
||||
HDA_OUTPUT_PERIOD_COUNT * sizeof(HDA_BDL_ENTRY),
|
||||
128u,
|
||||
&controller->Output.BdlDma);
|
||||
@@ -293,27 +290,27 @@ KSTATUS HdaAllocateDma(HDA_CONTROLLER* controller) {
|
||||
return status;
|
||||
}
|
||||
|
||||
controller->Corb = (uint32_t*)gHdaDdk->Dma->GetCommonBufferAddress(controller->CorbDma);
|
||||
controller->Rirb = (HDA_RIRB_ENTRY*)gHdaDdk->Dma->GetCommonBufferAddress(controller->RirbDma);
|
||||
controller->CorbDeviceAddress = gHdaDdk->Dma->GetCommonBufferDeviceAddress(controller->CorbDma);
|
||||
controller->RirbDeviceAddress = gHdaDdk->Dma->GetCommonBufferDeviceAddress(controller->RirbDma);
|
||||
controller->Corb = (uint32_t*)IoDmaGetCommonBufferAddress(controller->CorbDma);
|
||||
controller->Rirb = (HDA_RIRB_ENTRY*)IoDmaGetCommonBufferAddress(controller->RirbDma);
|
||||
controller->CorbDeviceAddress = IoDmaGetCommonBufferDeviceAddress(controller->CorbDma);
|
||||
controller->RirbDeviceAddress = IoDmaGetCommonBufferDeviceAddress(controller->RirbDma);
|
||||
if (controller->PositionDma) {
|
||||
controller->PositionBuffer = (volatile uint64_t*)
|
||||
gHdaDdk->Dma->GetCommonBufferAddress(controller->PositionDma);
|
||||
IoDmaGetCommonBufferAddress(controller->PositionDma);
|
||||
controller->PositionDeviceAddress =
|
||||
gHdaDdk->Dma->GetCommonBufferDeviceAddress(controller->PositionDma);
|
||||
IoDmaGetCommonBufferDeviceAddress(controller->PositionDma);
|
||||
}
|
||||
for (uint32_t ringIndex = 0; ringIndex < HDA_MAX_LOGICAL_ENDPOINTS; ringIndex++) {
|
||||
controller->Output.Rings[ringIndex].Buffer = (uint8_t*)
|
||||
gHdaDdk->Dma->GetCommonBufferAddress(controller->Output.Rings[ringIndex].Dma);
|
||||
IoDmaGetCommonBufferAddress(controller->Output.Rings[ringIndex].Dma);
|
||||
controller->Output.Rings[ringIndex].DeviceAddress =
|
||||
gHdaDdk->Dma->GetCommonBufferDeviceAddress(
|
||||
IoDmaGetCommonBufferDeviceAddress(
|
||||
controller->Output.Rings[ringIndex].Dma);
|
||||
}
|
||||
controller->Output.Bdl = (HDA_BDL_ENTRY*)
|
||||
gHdaDdk->Dma->GetCommonBufferAddress(controller->Output.BdlDma);
|
||||
IoDmaGetCommonBufferAddress(controller->Output.BdlDma);
|
||||
controller->Output.BdlDeviceAddress =
|
||||
gHdaDdk->Dma->GetCommonBufferDeviceAddress(controller->Output.BdlDma);
|
||||
IoDmaGetCommonBufferDeviceAddress(controller->Output.BdlDma);
|
||||
|
||||
memset(controller->Corb, 0, (size_t)controller->CorbEntryCount * sizeof(uint32_t));
|
||||
memset(controller->Rirb, 0, (size_t)controller->RirbEntryCount * sizeof(HDA_RIRB_ENTRY));
|
||||
@@ -502,7 +499,7 @@ static KSTATUS HdaResetCorbReadPointer(HDA_CONTROLLER* controller) {
|
||||
static KSTATUS HdaReportInitializationFailure(HDA_CONTROLLER* controller,
|
||||
const char* stage,
|
||||
KSTATUS status) {
|
||||
gHdaDdk->Debug->Print(
|
||||
DbgPrint(
|
||||
"HDA: init stage=%s failed status=0x%x GCTL=0x%08x STATESTS=0x%04x "
|
||||
"CORB=%02x/%02x/%04x/%04x RIRB=%02x/%02x/%04x SDCTL=0x%06x\n",
|
||||
stage,
|
||||
@@ -1381,7 +1378,7 @@ static void HdaRecoveryWorker(uintptr_t argument) {
|
||||
if (!KSUCCESS(status) && status != NTLSTATUS_DEVICE_REMOVED) {
|
||||
if (callbacks.TransportFailure)
|
||||
callbacks.TransportFailure(callbackContext, status);
|
||||
gHdaDdk->Debug->Print("HDA: recovery failed status=0x%x\n", status);
|
||||
DbgPrint("HDA: recovery failed status=0x%x\n", status);
|
||||
}
|
||||
if (queueDpc)
|
||||
(void)KeInsertQueueDpc(&controller->Dpc, (void*)0, (void*)0);
|
||||
|
||||
+36
-36
@@ -1,3 +1,4 @@
|
||||
#include <ntlite/ddk/module.h>
|
||||
// PCI bus driver. This file owns PCI enumeration, PDO creation, BAR decoding, and explicit MMIO mappings for function drivers.
|
||||
|
||||
#include "pci.h"
|
||||
@@ -9,11 +10,11 @@
|
||||
#include <common/printf.h>
|
||||
|
||||
#include <ntlite/ddk/ddk.h>
|
||||
#include <ntlite/ddk/pci_caps.h>
|
||||
#include <ntlite/ddi/io_driver.h>
|
||||
|
||||
int _fltused; // MSVC floating-point helper anchor required by printf.obj
|
||||
|
||||
static const NTL_DDK* gDdk;
|
||||
static DRIVER_OBJECT gPciDriver;
|
||||
static uint32_t gPciDeviceCount;
|
||||
|
||||
@@ -65,14 +66,14 @@ static bool PciIsPdo(DEVICE_OBJECT* pdo, PCI_PDO_EXTENSION** outExt) {
|
||||
|
||||
// Implements the raw Read32 operation.
|
||||
static uint32_t PciRawRead32(uint8_t bus, uint8_t dev, uint8_t fn, uint16_t offset) {
|
||||
if (!gDdk || !gDdk->Platform || !gDdk->Platform->PciRead32 || offset > 0xFCu) return 0xFFFFFFFFu;
|
||||
return gDdk->Platform->PciRead32(0, bus, dev, fn, offset & 0xFCu);
|
||||
if (offset > 0xFCu) return 0xFFFFFFFFu;
|
||||
return KePciReadConfig32(0, bus, dev, fn, offset & 0xFCu);
|
||||
}
|
||||
|
||||
// Implements the raw Write32 operation.
|
||||
static void PciRawWrite32(uint8_t bus, uint8_t dev, uint8_t fn, uint16_t offset, uint32_t value) {
|
||||
if (!gDdk || !gDdk->Platform || !gDdk->Platform->PciWrite32 || offset > 0xFCu) return;
|
||||
gDdk->Platform->PciWrite32(0, bus, dev, fn, offset & 0xFCu, value);
|
||||
if (offset > 0xFCu) return;
|
||||
KePciWriteConfig32(0, bus, dev, fn, offset & 0xFCu, value);
|
||||
}
|
||||
|
||||
// Implements the raw Read8 operation.
|
||||
@@ -386,7 +387,7 @@ static KSTATUS PciCreatePdo(uint8_t bus, uint8_t dev, uint8_t fn) {
|
||||
}
|
||||
|
||||
gPciDeviceCount++;
|
||||
gDdk->Debug->Print("pci: %v ven=%04x dev=%04x class=%02x%02x%02x irq=%u\n",
|
||||
DbgPrint("pci: %v ven=%04x dev=%04x class=%02x%02x%02x irq=%u\n",
|
||||
pdo->Name,
|
||||
info.VendorId,
|
||||
info.DeviceId,
|
||||
@@ -411,7 +412,7 @@ static void PciEnumerateBus(void) {
|
||||
for (uint8_t fn = 0; fn < fnLimit; fn++) {
|
||||
KSTATUS st = PciCreatePdo((uint8_t)bus, (uint8_t)dev, fn);
|
||||
if (!KSUCCESS(st) && st != NTLSTATUS_NOT_FOUND) {
|
||||
gDdk->Debug->Print("pci: failed to create PDO for %02x:%02x.%u st=0x%x\n",
|
||||
DbgPrint("pci: failed to create PDO for %02x:%02x.%u st=0x%x\n",
|
||||
bus, dev, fn, st);
|
||||
}
|
||||
}
|
||||
@@ -536,7 +537,7 @@ __declspec(dllexport) KSTATUS PciMapBar(DEVICE_OBJECT* pdo, uint8_t index, uint6
|
||||
return st;
|
||||
|
||||
for (uint64_t off = 0; off < mapLength; off += NTL_DDK_PAGE_SIZE) {
|
||||
st = gDdk->Memory->MapPage(virtBase + (uintptr_t)off, physBase + off, flags, NtlDdkMapFailIfPresent);
|
||||
st = MmDdkMapPage(virtBase + (uintptr_t)off, physBase + off, flags, NtlDdkMapFailIfPresent);
|
||||
if (!KSUCCESS(st))
|
||||
return st;
|
||||
}
|
||||
@@ -578,25 +579,26 @@ __declspec(dllexport) KSTATUS PciGetInterrupt(DEVICE_OBJECT* pdo, uint8_t* outIr
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Returns capability.
|
||||
// Reads configuration through the PCI provider bound to this PDO.
|
||||
static uint32_t PciCapabilityRead32(void* context, uint16_t offset) {
|
||||
const PCI_PDO_EXTENSION* ext = (const PCI_PDO_EXTENSION*)context;
|
||||
return PciRawRead32(ext->Info.Address.Bus,
|
||||
ext->Info.Address.Device,
|
||||
ext->Info.Address.Function,
|
||||
offset);
|
||||
}
|
||||
|
||||
// Finds one PCI capability using the shared DDK parser.
|
||||
static uint8_t PciFindCapability(PCI_PDO_EXTENSION* ext, uint8_t capId) {
|
||||
if (!ext || !ext->CapabilityOffset)
|
||||
return 0;
|
||||
|
||||
uint8_t cap = ext->CapabilityOffset & 0xFCu;
|
||||
for (uint32_t guard = 0; cap && guard < 48u; guard++) {
|
||||
uint32_t hdr = PciRawRead32(ext->Info.Address.Bus,
|
||||
ext->Info.Address.Device,
|
||||
ext->Info.Address.Function,
|
||||
cap);
|
||||
uint8_t id = (uint8_t)(hdr & 0xFFu);
|
||||
uint8_t next = (uint8_t)((hdr >> 8) & 0xFCu);
|
||||
if (id == capId)
|
||||
return cap;
|
||||
cap = next;
|
||||
}
|
||||
|
||||
return 0;
|
||||
const NTL_PCI_CONFIG_READER reader = { ext, PciCapabilityRead32 };
|
||||
uint8_t offset = 0;
|
||||
if (!KSUCCESS(NtlPciFindCapability(&reader, ext->CapabilityOffset & 0xFCu,
|
||||
capId, &offset)))
|
||||
return 0;
|
||||
return offset;
|
||||
}
|
||||
|
||||
// Enables msi.
|
||||
@@ -619,7 +621,7 @@ __declspec(dllexport) KSTATUS PciEnableMsi(DEVICE_OBJECT* pdo, uint8_t irqSlot)
|
||||
bool is64 = (control & PCI_MSI_CTRL_64BIT) != 0;
|
||||
bool pvm = (control & PCI_MSI_CTRL_PVM) != 0;
|
||||
NTL_DDK_MSI_MESSAGE message;
|
||||
KSTATUS messageStatus = gDdk->Interrupt->ComposeMsiMessage(irqSlot, &message);
|
||||
KSTATUS messageStatus = KeIrqComposeMsiMessage(irqSlot, &message);
|
||||
if (!KSUCCESS(messageStatus))
|
||||
return messageStatus;
|
||||
|
||||
@@ -643,7 +645,7 @@ __declspec(dllexport) KSTATUS PciEnableMsi(DEVICE_OBJECT* pdo, uint8_t irqSlot)
|
||||
cmd |= PCI_COMMAND_INTX_DISABLE;
|
||||
PciRawWrite16(bus, dev, fn, PCI_REG_COMMAND_STATUS, cmd);
|
||||
|
||||
gDdk->Debug->Print("pci: MSI enabled %02x:%02x.%u cap=0x%02x vector=0x%02x irqslot=%u %s\n",
|
||||
DbgPrint("pci: MSI enabled %02x:%02x.%u cap=0x%02x vector=0x%02x irqslot=%u %s\n",
|
||||
bus, dev, fn, cap, (unsigned)(message.Data & 0xFFu), irqSlot, is64 ? "64-bit" : "32-bit");
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
@@ -658,7 +660,7 @@ __declspec(dllexport) KSTATUS PciEnableMsiAuto(DEVICE_OBJECT* pdo, uint8_t* outI
|
||||
return NTLSTATUS_NOT_SUPPORTED;
|
||||
|
||||
uint8_t irq = 0xFFu;
|
||||
KSTATUS st = gDdk->Interrupt->AllocateMsiVector(&irq);
|
||||
KSTATUS st = KeIrqAllocateMsiVector(&irq);
|
||||
if (!KSUCCESS(st))
|
||||
return st;
|
||||
|
||||
@@ -670,21 +672,19 @@ __declspec(dllexport) KSTATUS PciEnableMsiAuto(DEVICE_OBJECT* pdo, uint8_t* outI
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Implements the driver Entry operation.
|
||||
KSTATUS DriverEntry(const NTL_DDK* ddk, NTL_DRIVER_INIT* init) {
|
||||
(void)init;
|
||||
if (!ddk || !ddk->Debug || !ddk->Interrupt || !ddk->Memory || !ddk->Platform) return NTLSTATUS_INVALID_PARAMETER;
|
||||
gDdk = ddk;
|
||||
static KSTATUS DriverInitialize(NTL_MODULE* module) {
|
||||
(void)module;
|
||||
|
||||
gPciDeviceCount = 0;
|
||||
|
||||
IoInitializeDriverObject(&gPciDriver, SYS_SV_LITERAL("pci"));
|
||||
gPciDriver.MajorFunction[IRP_MJ_PNP] = PciDispatchPnp;
|
||||
|
||||
KSTATUS st = IoRegisterDriverObject(&gPciDriver);
|
||||
if (!KSUCCESS(st)) return st;
|
||||
|
||||
gDdk->Debug->Print("pci: DriverEntry ABI=%u\n", PciDriverAbiVersion());
|
||||
DbgPrint("pci: module ABI=%u\n", PciDriverAbiVersion());
|
||||
PciEnumerateBus();
|
||||
gDdk->Debug->Print("pci: enumeration complete, devices=%u\n", gPciDeviceCount);
|
||||
DbgPrint("pci: enumeration complete, devices=%u\n", gPciDeviceCount);
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static DRIVER_OBJECT* const gModuleIoDrivers[] = { &gPciDriver };
|
||||
NTL_DECLARE_IO_MODULE(DriverInitialize, gModuleIoDrivers);
|
||||
@@ -1,9 +1,9 @@
|
||||
#include <ntlite/ddk/module.h>
|
||||
// USB driver package entry point. This file registers the USB bus, class, hub, HID, storage, and xHCI drivers.
|
||||
|
||||
#include "usb.h"
|
||||
|
||||
DRIVER_OBJECT gXhciDriver;
|
||||
const NTL_DDK* gUsbDdk;
|
||||
|
||||
static const IO_DEVICE_ID gXhciIds[] = {
|
||||
{ IoBusPci, { "PCI\\CC_0C0330", sizeof("PCI\\CC_0C0330") - 1 } },
|
||||
@@ -31,15 +31,12 @@ static KSTATUS UsbInitializeXhciDriver(void) {
|
||||
gXhciDriver.IdTable = &gXhciIdTable;
|
||||
gXhciDriver.DriverAddDevice = XhciAddDevice;
|
||||
gXhciDriver.MajorFunction[IRP_MJ_PNP] = XhciDispatchPnp;
|
||||
return IoRegisterDriverObject(&gXhciDriver);
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Implements the driver Entry operation.
|
||||
KSTATUS DriverEntry(const NTL_DDK* ddk, NTL_DRIVER_INIT* init) {
|
||||
(void)init;
|
||||
if (!ddk || !ddk->Dma || !ddk->Timer) return NTLSTATUS_INVALID_PARAMETER;
|
||||
DbgPrint("usb: driver loading\n");
|
||||
gUsbDdk = ddk;
|
||||
static KSTATUS DriverInitialize(NTL_MODULE* module) {
|
||||
(void)module;
|
||||
DbgPrint("usb: driver loading phase2-xecp-20261023\n");
|
||||
|
||||
KSTATUS st = UsbInitializeBusModel();
|
||||
if (!KSUCCESS(st)) {
|
||||
@@ -80,3 +77,12 @@ KSTATUS DriverEntry(const NTL_DDK* ddk, NTL_DRIVER_INIT* init) {
|
||||
DbgPrint("usb: driver initialized\n");
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
extern DRIVER_OBJECT gUsbBusDriver;
|
||||
extern DRIVER_OBJECT gUsbCompositeDriver;
|
||||
extern DRIVER_OBJECT gUsbHidDriver;
|
||||
extern DRIVER_OBJECT gUsbStorageDriver;
|
||||
extern DRIVER_OBJECT gUsbHubDriver;
|
||||
|
||||
static DRIVER_OBJECT* const gModuleIoDrivers[] = { &gXhciDriver, &gUsbBusDriver, &gUsbCompositeDriver, &gUsbHidDriver, &gUsbStorageDriver, &gUsbHubDriver };
|
||||
NTL_DECLARE_IO_MODULE(DriverInitialize, gModuleIoDrivers);
|
||||
@@ -112,6 +112,7 @@ static __inline void UsbTrace(const char* fmt, ...) { (void)fmt; }
|
||||
#define XHCI_TRB_TYPE_DISABLE_SLOT 10u
|
||||
#define XHCI_TRB_TYPE_ADDRESS_DEVICE 11u
|
||||
#define XHCI_TRB_TYPE_CONFIGURE_ENDPOINT 12u
|
||||
#define XHCI_TRB_TYPE_STOP_ENDPOINT 15u
|
||||
|
||||
#define XHCI_CC_SUCCESS 1u
|
||||
#define XHCI_CC_SHORT_PACKET 13u
|
||||
@@ -129,6 +130,9 @@ static __inline void UsbTrace(const char* fmt, ...) { (void)fmt; }
|
||||
|
||||
#define XHCI_CONTEXT_ENTRIES(n) ((uint32_t)(n) << 27)
|
||||
#define XHCI_SLOT_SPEED(s) ((uint32_t)(s) << 20)
|
||||
#define XHCI_SLOT_HUB (1u << 26)
|
||||
#define XHCI_SLOT_PORT_COUNT(n) ((uint32_t)(n) << 24)
|
||||
#define XHCI_SLOT_TT_THINK(n) (((uint32_t)(n) & 3u) << 16)
|
||||
#define XHCI_SLOT_ROOT_PORT(p) ((uint32_t)(p) << 16)
|
||||
#define XHCI_EP_TYPE_CONTROL (4u << 3)
|
||||
#define XHCI_EP_TYPE_BULK_OUT (2u << 3)
|
||||
@@ -409,6 +413,8 @@ struct USB_HCD {
|
||||
DEVICE_OBJECT* ControllerDevice;
|
||||
DEVICE_OBJECT* RootHubPdo;
|
||||
void* RootHubContext;
|
||||
SPINLOCK RootHubLock;
|
||||
bool RootScanRequested;
|
||||
uint8_t ControllerIndex;
|
||||
uint8_t MaxRootPorts;
|
||||
};
|
||||
@@ -674,6 +680,8 @@ typedef struct XHCI_DEVICE {
|
||||
uint8_t DeviceProtocol;
|
||||
uint8_t ConfigurationValue;
|
||||
bool Configured;
|
||||
uint8_t HubPortCount;
|
||||
uint16_t HubCharacteristics;
|
||||
USB_DEVICE* UsbDevice;
|
||||
DEVICE_OBJECT* UsbPdo;
|
||||
DEVICE_OBJECT* ParentDeviceObject;
|
||||
@@ -786,7 +794,6 @@ typedef struct XHCI_WAIT_TARGET {
|
||||
|
||||
typedef struct XHCI_CONTROLLER {
|
||||
uint32_t Magic;
|
||||
const NTL_DDK* Ddk;
|
||||
DEVICE_OBJECT* Fdo;
|
||||
DEVICE_OBJECT* Pdo;
|
||||
DEVICE_OBJECT* LowerDevice;
|
||||
@@ -854,6 +861,7 @@ typedef struct XHCI_CONTROLLER {
|
||||
volatile bool InterruptsEnabled;
|
||||
volatile uint32_t InterruptCount;
|
||||
volatile uint32_t EventDpcCount;
|
||||
volatile uint32_t EventDpcBudgetHits;
|
||||
SPINLOCK EventLock;
|
||||
SPINLOCK WaitLock;
|
||||
SPINLOCK CommandLock;
|
||||
@@ -871,7 +879,6 @@ extern DRIVER_OBJECT gUsbCompositeDriver;
|
||||
extern DRIVER_OBJECT gUsbHidDriver;
|
||||
extern DRIVER_OBJECT gUsbHubDriver;
|
||||
extern DRIVER_OBJECT gXhciDriver;
|
||||
extern const NTL_DDK* gUsbDdk;
|
||||
|
||||
KSTATUS UsbInitializeBusModel(void);
|
||||
KSTATUS UsbInitializeCompositeDriver(void);
|
||||
@@ -919,9 +926,9 @@ uint32_t XhciRead32(volatile uint8_t* base, uint32_t reg);
|
||||
void XhciWrite32(volatile uint8_t* base, uint32_t reg, uint32_t value);
|
||||
uint64_t XhciRead64(volatile uint8_t* base, uint32_t reg);
|
||||
void XhciWrite64(volatile uint8_t* base, uint32_t reg, uint64_t value);
|
||||
void UsbDelayUs(const NTL_DDK* ddk, uint32_t us);
|
||||
bool UsbWaitSet32(const NTL_DDK* ddk, volatile uint8_t* base, uint32_t reg, uint32_t mask, uint32_t timeoutUs);
|
||||
bool UsbWaitClear32(const NTL_DDK* ddk, volatile uint8_t* base, uint32_t reg, uint32_t mask, uint32_t timeoutUs);
|
||||
void UsbDelayUs(uint32_t us);
|
||||
bool UsbWaitSet32( volatile uint8_t* base, uint32_t reg, uint32_t mask, uint32_t timeoutUs);
|
||||
bool UsbWaitClear32( volatile uint8_t* base, uint32_t reg, uint32_t mask, uint32_t timeoutUs);
|
||||
void* UsbDmaAllocPages(XHCI_CONTROLLER* xhci,
|
||||
size_t pages,
|
||||
uint64_t* outDeviceAddress,
|
||||
@@ -948,7 +955,6 @@ void XhciLogPortStatus(XHCI_CONTROLLER* xhci);
|
||||
void XhciPowerRootPorts(XHCI_CONTROLLER* xhci);
|
||||
void XhciAckInterrupter0(XHCI_CONTROLLER* xhci);
|
||||
void XhciUpdateErdp(XHCI_CONTROLLER* xhci);
|
||||
bool XhciDrainEventRing(XHCI_CONTROLLER* xhci, XHCI_WAIT_TARGET* target);
|
||||
bool XhciDrainEventRingLocked(XHCI_CONTROLLER* xhci, XHCI_WAIT_TARGET* target);
|
||||
bool XhciWaitTargetComplete(const XHCI_WAIT_TARGET* target);
|
||||
void XhciRegisterWaitTarget(XHCI_CONTROLLER* xhci, XHCI_WAIT_TARGET* target);
|
||||
@@ -993,7 +999,6 @@ void XhciProcessBootKeyboardReport(XHCI_DEVICE* dev, const uint8_t* report, uint
|
||||
void XhciProcessBootMouseReport(XHCI_DEVICE* dev, const uint8_t* report, uint16_t len);
|
||||
KSTATUS XhciArmHidInterruptIn(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev);
|
||||
void XhciArmAllHid(XHCI_CONTROLLER* xhci);
|
||||
void XhciHandleAsyncTransferEvent(XHCI_CONTROLLER* xhci, uint64_t trbDeviceAddress, uint32_t completionCode, uint8_t slotId, uint8_t endpointId, uint32_t residue);
|
||||
KSTATUS XhciStartHidInterrupts(XHCI_CONTROLLER* xhci);
|
||||
KSTATUS XhciSetHidBootProtocol(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev);
|
||||
KSTATUS XhciAllocateHidMemory(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev);
|
||||
@@ -1002,6 +1007,8 @@ KSTATUS UsbHidAddDevice(DRIVER_OBJECT* driver, DEVICE_OBJECT* physicalDeviceObje
|
||||
KSTATUS UsbHidDispatchPnp(DEVICE_OBJECT* device, IRP* irp);
|
||||
void XhciInitialiseTransferRing(XHCI_TRB* ring, uint64_t ringDeviceAddress, uint32_t trbCount, uint8_t cycle);
|
||||
KSTATUS XhciConfigurePipeEndpoint(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev, USB_PIPE* pipe);
|
||||
KSTATUS XhciConfigureHubContext(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev, uint8_t portCount, uint16_t characteristics);
|
||||
void XhciBuildConfigureSlotContext(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev, uint32_t* slot, uint8_t endpointId);
|
||||
KSTATUS XhciBulkTransfer(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev, uint8_t endpointId, XHCI_TRB* ring, uint64_t ringDeviceAddress, uint32_t* enqueue, uint8_t* cycle, uint64_t bufferDeviceAddress, uint32_t length, uint32_t* outActual);
|
||||
KSTATUS UsbStorageBulkIn(USB_STORAGE_EXTENSION* storage, uint64_t deviceAddress, uint32_t length, uint32_t* outActual);
|
||||
KSTATUS UsbStorageBulkOut(USB_STORAGE_EXTENSION* storage, uint64_t deviceAddress, uint32_t length, uint32_t* outActual);
|
||||
|
||||
@@ -69,8 +69,8 @@ void* UsbAllocateDmaBuffer(USB_DEVICE* dev,
|
||||
|
||||
// Frees a DMA buffer allocated for a USB class driver.
|
||||
void UsbFreeDmaBuffer(IO_DMA_BUFFER* buffer) {
|
||||
if (buffer && gUsbDdk && gUsbDdk->Dma)
|
||||
gUsbDdk->Dma->FreeCommonBuffer(buffer);
|
||||
if (buffer)
|
||||
IoDmaFreeCommonBuffer(buffer);
|
||||
}
|
||||
|
||||
// Creates device.
|
||||
@@ -275,9 +275,6 @@ KSTATUS UsbInitializeBusModel(void) {
|
||||
gUsbBusDriver.MajorFunction[IRP_MJ_PNP] = UsbDispatchPdoPnp;
|
||||
gUsbBusDriver.MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = UsbDispatchInternalDeviceControl;
|
||||
|
||||
KSTATUS st = IoRegisterDriverObject(&gUsbBusDriver);
|
||||
if (!KSUCCESS(st)) return st;
|
||||
|
||||
gUsbBusModelReady = true;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
@@ -290,6 +287,7 @@ void UsbInitializeHcd(USB_HCD* hcd,
|
||||
if (!hcd) return;
|
||||
memset(hcd, 0, sizeof(*hcd));
|
||||
hcd->Operations = ops;
|
||||
hcd->RootHubLock = (SPINLOCK)SPINLOCK_INIT;
|
||||
hcd->ControllerContext = controllerContext;
|
||||
hcd->DriverObject = &gUsbBusDriver;
|
||||
hcd->ControllerIndex = controllerIndex ? controllerIndex : ++gUsbNextControllerIndex;
|
||||
|
||||
@@ -152,5 +152,5 @@ KSTATUS UsbInitializeCompositeDriver(void) {
|
||||
gUsbCompositeDriver.DriverAddDevice = UsbCompositeAddDevice;
|
||||
gUsbCompositeDriver.MajorFunction[IRP_MJ_PNP] = UsbCompositeDispatchPnp;
|
||||
gUsbCompositeDriver.MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = UsbDispatchInternalDeviceControl;
|
||||
return IoRegisterDriverObject(&gUsbCompositeDriver);
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
@@ -11,6 +11,8 @@ KSTATUS XhciSetConfiguration(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev,
|
||||
setup.wIndex = 0;
|
||||
setup.wLength = 0;
|
||||
|
||||
DbgPrint("usb-stage: slot=%u SET_CONFIGURATION begin value=%u\n",
|
||||
dev->SlotId, configurationValue);
|
||||
KSTATUS st = XhciControlNoData(xhci, dev, &setup);
|
||||
if (!KSUCCESS(st)) {
|
||||
DbgPrint("usb: SET_CONFIGURATION slot=%u value=%u failed st=0x%x\n",
|
||||
@@ -20,6 +22,8 @@ KSTATUS XhciSetConfiguration(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev,
|
||||
|
||||
dev->ConfigurationValue = configurationValue;
|
||||
dev->Configured = true;
|
||||
DbgPrint("usb-stage: slot=%u SET_CONFIGURATION done value=%u\n",
|
||||
dev->SlotId, configurationValue);
|
||||
UsbTrace("usb: SET_CONFIGURATION slot=%u value=%u complete\n",
|
||||
dev->SlotId, configurationValue);
|
||||
return NTLSTATUS_SUCCESS;
|
||||
@@ -220,6 +224,7 @@ KSTATUS XhciDiscoverAndSetConfiguration(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev,
|
||||
USB_CONFIGURATION_DESCRIPTOR config;
|
||||
memset(&config, 0, sizeof(config));
|
||||
uint16_t totalLength = 0;
|
||||
DbgPrint("usb-stage: slot=%u GET_CONFIGURATION begin\n", dev->SlotId);
|
||||
KSTATUS st = XhciGetConfigurationDescriptor(xhci, dev, 0, &config, &totalLength);
|
||||
if (!KSUCCESS(st)) {
|
||||
DbgPrint("usb: slot=%u configuration descriptor read failed st=0x%x\n",
|
||||
@@ -227,6 +232,8 @@ KSTATUS XhciDiscoverAndSetConfiguration(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev,
|
||||
return st;
|
||||
}
|
||||
|
||||
DbgPrint("usb-stage: slot=%u GET_CONFIGURATION done total=%u value=%u\n",
|
||||
dev->SlotId, totalLength, config.bConfigurationValue);
|
||||
XhciLogConfigurationTree(xhci, dev, &config, totalLength);
|
||||
|
||||
if (config.bConfigurationValue == 0) {
|
||||
@@ -239,6 +246,7 @@ KSTATUS XhciDiscoverAndSetConfiguration(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev,
|
||||
if (!KSUCCESS(st)) return st;
|
||||
|
||||
if (!dev->UsbPdo) {
|
||||
DbgPrint("usb-stage: slot=%u PDO creation begin\n", dev->SlotId);
|
||||
st = UsbCreateDevicePdo(&xhci->Hcd, dev,
|
||||
dev->ParentDeviceObject ? dev->ParentDeviceObject :
|
||||
(xhci->Hcd.RootHubPdo ? xhci->Hcd.RootHubPdo : xhci->Hcd.ControllerDevice));
|
||||
@@ -246,6 +254,7 @@ KSTATUS XhciDiscoverAndSetConfiguration(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev,
|
||||
DbgPrint("usb: create device PDO slot=%u failed st=0x%x\n", dev->SlotId, st);
|
||||
return st;
|
||||
}
|
||||
DbgPrint("usb-stage: slot=%u PDO creation done\n", dev->SlotId);
|
||||
}
|
||||
|
||||
return NTLSTATUS_SUCCESS;
|
||||
@@ -286,8 +295,14 @@ static KSTATUS XhciEnumeratePathEx(XHCI_CONTROLLER* xhci,
|
||||
if (!xhci || !path || path->RootPort == 0)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
DbgPrint("usb-stage: hcd=%02x:%02x.%u root=%u hub-slot=%u hub-port=%u enable-slot begin\n",
|
||||
xhci->Bus, xhci->Dev, xhci->Fn, path->RootPort,
|
||||
path->BehindHub ? path->ParentHubSlot : 0,
|
||||
path->BehindHub ? path->ParentHubPort : 0);
|
||||
uint8_t slotId = 0;
|
||||
KSTATUS st = XhciEnableSlot(xhci, &slotId);
|
||||
DbgPrint("usb-stage: hcd=%02x:%02x.%u enable-slot done st=0x%x slot=%u\n",
|
||||
xhci->Bus, xhci->Dev, xhci->Fn, st, slotId);
|
||||
if (!KSUCCESS(st)) return st;
|
||||
|
||||
XHCI_DEVICE* dev = &xhci->Devices[slotId];
|
||||
@@ -307,7 +322,11 @@ static KSTATUS XhciEnumeratePathEx(XHCI_CONTROLLER* xhci,
|
||||
|
||||
XhciBuildDefaultControlContext(xhci, dev);
|
||||
|
||||
DbgPrint("usb-stage: hcd=%02x:%02x.%u slot=%u address-device begin speed=%u route=%05x\n",
|
||||
xhci->Bus, xhci->Dev, xhci->Fn, slotId, speed, path->RouteString);
|
||||
st = XhciAddressDevice(xhci, dev);
|
||||
DbgPrint("usb-stage: hcd=%02x:%02x.%u slot=%u address-device done st=0x%x\n",
|
||||
xhci->Bus, xhci->Dev, xhci->Fn, slotId, st);
|
||||
if (!KSUCCESS(st)) {
|
||||
XhciRollbackEnumeratedSlot(xhci, slotId);
|
||||
return st;
|
||||
@@ -316,7 +335,11 @@ static KSTATUS XhciEnumeratePathEx(XHCI_CONTROLLER* xhci,
|
||||
|
||||
USB_DEVICE_DESCRIPTOR desc;
|
||||
memset(&desc, 0, sizeof(desc));
|
||||
DbgPrint("usb-stage: hcd=%02x:%02x.%u slot=%u get-device-descriptor begin\n",
|
||||
xhci->Bus, xhci->Dev, xhci->Fn, slotId);
|
||||
st = XhciGetDeviceDescriptorTimeout(xhci, dev, &desc, USB_XHCI_ENUM_CONTROL_TIMEOUT_US);
|
||||
DbgPrint("usb-stage: hcd=%02x:%02x.%u slot=%u get-device-descriptor done st=0x%x\n",
|
||||
xhci->Bus, xhci->Dev, xhci->Fn, slotId, st);
|
||||
if (!KSUCCESS(st)) {
|
||||
DbgPrint("usb: port %u slot %u descriptor read failed st=0x%x\n",
|
||||
logicalPort, slotId, st);
|
||||
|
||||
@@ -213,9 +213,9 @@ KSTATUS XhciAllocateHidMemory(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev) {
|
||||
dev->HidRing = (XHCI_TRB*)UsbDmaAllocPages(xhci, 1, &dev->HidRingDeviceAddress, &dev->HidRingDma);
|
||||
dev->HidBuffer = (uint8_t*)UsbDmaAllocPages(xhci, 1, &dev->HidBufferDeviceAddress, &dev->HidBufferDma);
|
||||
if (!dev->HidRing || !dev->HidBuffer) {
|
||||
if (gUsbDdk && gUsbDdk->Dma) {
|
||||
gUsbDdk->Dma->FreeCommonBuffer(dev->HidBufferDma);
|
||||
gUsbDdk->Dma->FreeCommonBuffer(dev->HidRingDma);
|
||||
{
|
||||
IoDmaFreeCommonBuffer(dev->HidBufferDma);
|
||||
IoDmaFreeCommonBuffer(dev->HidRingDma);
|
||||
}
|
||||
dev->HidBufferDma = (IO_DMA_BUFFER*)0;
|
||||
dev->HidRingDma = (IO_DMA_BUFFER*)0;
|
||||
@@ -261,10 +261,7 @@ KSTATUS XhciConfigureHidEndpoint(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev) {
|
||||
icc[0] = 0;
|
||||
icc[1] = (1u << 0) | (1u << dci);
|
||||
|
||||
slot[0] = XHCI_CONTEXT_ENTRIES(dci) | XHCI_SLOT_SPEED(dev->Speed);
|
||||
slot[1] = XHCI_SLOT_ROOT_PORT(dev->Port);
|
||||
slot[2] = 0;
|
||||
slot[3] = 0;
|
||||
XhciBuildConfigureSlotContext(xhci, dev, slot, dci);
|
||||
|
||||
uint16_t maxPacket = dev->HidMaxPacket & 0x07FFu;
|
||||
if (maxPacket == 0) maxPacket = dev->HidKeyboard ? 8u : 4u;
|
||||
@@ -442,8 +439,10 @@ static void UsbHidInterruptComplete(USB_URB* urb, void* context) {
|
||||
return;
|
||||
|
||||
ext->InterruptPending = false;
|
||||
if (ext->SurpriseRemoved || ext->Removed || !ext->Started)
|
||||
if (ext->SurpriseRemoved || ext->Removed || !ext->Started) {
|
||||
IoReleaseRemoveLock(&ext->RemoveLock, (IRP*)0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (KSUCCESS(urb->Status)) {
|
||||
uint16_t length = (uint16_t)urb->ActualLength;
|
||||
@@ -468,6 +467,7 @@ static void UsbHidInterruptComplete(USB_URB* urb, void* context) {
|
||||
|
||||
if (!ext->SurpriseRemoved && !ext->Removed)
|
||||
(void)UsbHidArmInterrupt(ext);
|
||||
IoReleaseRemoveLock(&ext->RemoveLock, (IRP*)0);
|
||||
}
|
||||
|
||||
// Implements the hid Send Boot Protocol operation.
|
||||
@@ -523,6 +523,11 @@ static KSTATUS UsbHidArmInterrupt(USB_HID_EXTENSION* ext) {
|
||||
if (ext->InterruptPending)
|
||||
return NTLSTATUS_SUCCESS;
|
||||
|
||||
// One in-flight callback owns a remove reference through dispatch or cancel.
|
||||
KSTATUS refStatus = IoAcquireRemoveLock(&ext->RemoveLock, (IRP*)0);
|
||||
if (!KSUCCESS(refStatus))
|
||||
return refStatus;
|
||||
ext->InterruptPending = true;
|
||||
memset(ext->ReportBuffer, 0, ext->ReportLength);
|
||||
memset(&ext->InterruptUrb, 0, sizeof(ext->InterruptUrb));
|
||||
ext->InterruptUrb.Function = UrbFunctionInterruptTransfer;
|
||||
@@ -536,12 +541,13 @@ static KSTATUS UsbHidArmInterrupt(USB_HID_EXTENSION* ext) {
|
||||
ext->InterruptUrb.CompleteContext = ext;
|
||||
|
||||
KSTATUS st = UsbSubmitUrbAsync(ext->LowerDevice, &ext->InterruptUrb);
|
||||
if (st != NTLSTATUS_PENDING && !KSUCCESS(st))
|
||||
return st;
|
||||
if (ext->InterruptUrb.Status != NTLSTATUS_PENDING && ext->InterruptUrb.Status != NTLSTATUS_SUCCESS)
|
||||
return ext->InterruptUrb.Status;
|
||||
|
||||
ext->InterruptPending = true;
|
||||
// The USB PDO returns PENDING even when the HCD rejected the URB and
|
||||
// completed its IRP synchronously. Inspect the URB's actual status.
|
||||
if (st != NTLSTATUS_PENDING || ext->InterruptUrb.Status != NTLSTATUS_PENDING) {
|
||||
ext->InterruptPending = false;
|
||||
IoReleaseRemoveLock(&ext->RemoveLock, (IRP*)0);
|
||||
return st == NTLSTATUS_PENDING ? ext->InterruptUrb.Status : st;
|
||||
}
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -616,8 +622,6 @@ static KSTATUS UsbHidStartDevice(DEVICE_OBJECT* device, USB_HID_EXTENSION* ext)
|
||||
ext->LastModifiers = 0;
|
||||
ext->LastButtons = 0;
|
||||
|
||||
KeInputRegisterDevice(ext->Keyboard ? "usb-hid-keyboard" : (ext->AbsoluteMouse ? "usb-hid-tablet" : "usb-hid-mouse"), 0, &ext->InputDeviceId);
|
||||
|
||||
ext->Started = true;
|
||||
device->PnpState = DevicePnpStarted;
|
||||
if (ext->InterfacePdo)
|
||||
@@ -627,9 +631,21 @@ static KSTATUS UsbHidStartDevice(DEVICE_OBJECT* device, USB_HID_EXTENSION* ext)
|
||||
(XHCI_CONTROLLER*)ext->Device->Hcd->ControllerContext : (XHCI_CONTROLLER*)0;
|
||||
|
||||
st = UsbHidArmInterrupt(ext);
|
||||
if (!KSUCCESS(st))
|
||||
if (!KSUCCESS(st)) {
|
||||
ext->Started = false;
|
||||
device->PnpState = DevicePnpStopped;
|
||||
if (ext->InterfacePdo)
|
||||
ext->InterfacePdo->PnpState = DevicePnpStopped;
|
||||
if (ext->InterruptPipe) {
|
||||
ext->InterruptPipe->Open = false;
|
||||
UsbDereferencePipe(ext->InterruptPipe);
|
||||
ext->InterruptPipe = (USB_PIPE*)0;
|
||||
}
|
||||
UsbHidReleaseDma(ext);
|
||||
return st;
|
||||
}
|
||||
|
||||
KeInputRegisterDevice(ext->Keyboard ? "usb-hid-keyboard" : (ext->AbsoluteMouse ? "usb-hid-tablet" : "usb-hid-mouse"), 0, &ext->InputDeviceId);
|
||||
if (xhci)
|
||||
xhci->HidDeviceCount++;
|
||||
|
||||
@@ -679,8 +695,13 @@ KSTATUS UsbHidDispatchPnp(DEVICE_OBJECT* device, IRP* irp) {
|
||||
if (ext) {
|
||||
ext->SurpriseRemoved = true;
|
||||
ext->Started = false;
|
||||
if (ext->InterruptPending)
|
||||
(void)UsbCancelUrb(&ext->InterruptUrb);
|
||||
if (ext->InterruptPending) {
|
||||
st = UsbCancelUrb(&ext->InterruptUrb);
|
||||
if (!KSUCCESS(st)) {
|
||||
IoCompleteRequest(irp, st, 0);
|
||||
return NTLSTATUS_PENDING;
|
||||
}
|
||||
}
|
||||
}
|
||||
return IoForwardIrpToLower(device, irp);
|
||||
|
||||
@@ -689,13 +710,15 @@ KSTATUS UsbHidDispatchPnp(DEVICE_OBJECT* device, IRP* irp) {
|
||||
if (ext) {
|
||||
ext->Removed = true;
|
||||
ext->Started = false;
|
||||
if (ext->InterruptPending)
|
||||
(void)UsbCancelUrb(&ext->InterruptUrb);
|
||||
if (ext->InterruptPipe) {
|
||||
ext->InterruptPipe->Open = false;
|
||||
UsbDereferencePipe(ext->InterruptPipe);
|
||||
ext->InterruptPipe = (USB_PIPE*)0;
|
||||
if (ext->InterruptPending) {
|
||||
st = UsbCancelUrb(&ext->InterruptUrb);
|
||||
if (!KSUCCESS(st)) {
|
||||
IoCompleteRequest(irp, st, 0);
|
||||
return NTLSTATUS_PENDING;
|
||||
}
|
||||
}
|
||||
if (ext->InterruptPipe)
|
||||
ext->InterruptPipe->Open = false;
|
||||
if (ext->Device && ext->Device->Hcd) {
|
||||
XHCI_CONTROLLER* xhci = (XHCI_CONTROLLER*)ext->Device->Hcd->ControllerContext;
|
||||
if (xhci && xhci->HidDeviceCount)
|
||||
@@ -706,6 +729,10 @@ KSTATUS UsbHidDispatchPnp(DEVICE_OBJECT* device, IRP* irp) {
|
||||
IoDestroyRemoveLock(&ext->RemoveLock);
|
||||
ext->RemoveLockReady = false;
|
||||
}
|
||||
if (ext->InterruptPipe) {
|
||||
UsbDereferencePipe(ext->InterruptPipe);
|
||||
ext->InterruptPipe = (USB_PIPE*)0;
|
||||
}
|
||||
UsbHidReleaseDma(ext);
|
||||
}
|
||||
|
||||
@@ -751,8 +778,16 @@ KSTATUS UsbHidAddDevice(DRIVER_OBJECT* driver, DEVICE_OBJECT* physicalDeviceObje
|
||||
bool bootMouse = pdoExt->InterfaceDescriptor.bInterfaceSubClass == USB_HID_SUBCLASS_BOOT &&
|
||||
pdoExt->InterfaceDescriptor.bInterfaceProtocol == USB_HID_PROTOCOL_MOUSE;
|
||||
bool qemuTablet = UsbHidIsQemuAbsoluteTabletPdo(pdoExt, ep);
|
||||
if (!bootKeyboard && !bootMouse && !qemuTablet)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
if (!bootKeyboard && !bootMouse && !qemuTablet) {
|
||||
// A nonboot HID interface requires its report descriptor to be parsed.
|
||||
// Avoid treating opaque reports as boot mouse packets.
|
||||
DbgPrint("usb: HID slot=%u if=%u subclass=%u protocol=%u requires report parser\n",
|
||||
pdoExt->Device->SlotId,
|
||||
pdoExt->InterfaceDescriptor.bInterfaceNumber,
|
||||
pdoExt->InterfaceDescriptor.bInterfaceSubClass,
|
||||
pdoExt->InterfaceDescriptor.bInterfaceProtocol);
|
||||
return NTLSTATUS_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
DEVICE_OBJECT* fdo = (DEVICE_OBJECT*)0;
|
||||
KSTATUS st = IoCreateDevice(driver, sizeof(USB_HID_EXTENSION), &fdo);
|
||||
@@ -799,5 +834,5 @@ KSTATUS UsbInitializeHidDriver(void) {
|
||||
gUsbHidDriver.IdTable = &gUsbHidIdTable;
|
||||
gUsbHidDriver.DriverAddDevice = UsbHidAddDevice;
|
||||
gUsbHidDriver.MajorFunction[IRP_MJ_PNP] = UsbHidDispatchPnp;
|
||||
return IoRegisterDriverObject(&gUsbHidDriver);
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
+156
-42
@@ -99,7 +99,7 @@ static KSTATUS UsbHubEnumerateRootPortWithRetries(USB_HUB_EXTENSION* hub,
|
||||
|
||||
DbgPrint("usb-hub: root port %u enumeration attempt %u failed st=0x%x; retrying\n",
|
||||
port, attempt, last);
|
||||
UsbDelayUs(xhci->Ddk, 50000);
|
||||
UsbDelayUs(50000);
|
||||
}
|
||||
|
||||
if (UsbHubRootPortConnected(xhci, port)) {
|
||||
@@ -126,7 +126,7 @@ static KSTATUS UsbHubEnumerateConnectedRootPort(USB_HUB_EXTENSION* hub,
|
||||
hub->RootPortSlot[port] = USB_HUB_PORT_ENUMERATING;
|
||||
hub->RootPortState[port] = UsbHubPortConnectedDebounce;
|
||||
|
||||
UsbDelayUs(xhci->Ddk, 30000u);
|
||||
UsbDelayUs(30000u);
|
||||
if (!UsbHubRootPortConnected(xhci, port)) {
|
||||
UsbHubClearRootPortFailure(hub, port);
|
||||
hub->RootPortSlot[port] = 0;
|
||||
@@ -393,13 +393,21 @@ static KSTATUS UsbHubResetExternalPort(USB_HUB_EXTENSION* hub, uint8_t port, uin
|
||||
|
||||
USB_HUB_PORT_STATUS ps;
|
||||
memset(&ps, 0, sizeof(ps));
|
||||
bool resetComplete = false;
|
||||
for (uint32_t i = 0; i < 500u; i++) {
|
||||
UsbDelayUs(((XHCI_CONTROLLER*)hub->Hcd->ControllerContext)->Ddk, 1000u);
|
||||
UsbDelayUs(1000u);
|
||||
st = UsbHubGetPortStatus(hub, port, &ps);
|
||||
if (!KSUCCESS(st))
|
||||
return st;
|
||||
if (ps.Change & USB_HUB_PORT_CHANGE_RESET)
|
||||
if (ps.Change & USB_HUB_PORT_CHANGE_RESET) {
|
||||
resetComplete = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!resetComplete) {
|
||||
DbgPrint("usb-hub: slot=%u port=%u reset timed out status=%04x change=%04x\n",
|
||||
hub->Device ? hub->Device->SlotId : 0, port, ps.Status, ps.Change);
|
||||
return NTLSTATUS_TIMEOUT;
|
||||
}
|
||||
|
||||
(void)UsbHubClearPortFeature(hub, port, USB_HUB_PORT_FEAT_C_RESET);
|
||||
@@ -464,7 +472,7 @@ static void UsbHubHandleExternalPortChange(USB_HUB_EXTENSION* hub, uint8_t port)
|
||||
|
||||
hub->ChildSlot[port] = USB_HUB_PORT_ENUMERATING;
|
||||
hub->ChildPortState[port] = UsbHubPortConnectedDebounce;
|
||||
UsbDelayUs(((XHCI_CONTROLLER*)hub->Hcd->ControllerContext)->Ddk, 30000u);
|
||||
UsbDelayUs(30000u);
|
||||
|
||||
uint8_t speed = 0;
|
||||
hub->ChildPortState[port] = UsbHubPortResetting;
|
||||
@@ -478,8 +486,12 @@ static void UsbHubHandleExternalPortChange(USB_HUB_EXTENSION* hub, uint8_t port)
|
||||
}
|
||||
|
||||
hub->ChildPortState[port] = UsbHubPortEnumerating;
|
||||
DbgPrint("usb-stage: hub-slot=%u port=%u child enumeration begin speed=%u\n",
|
||||
hub->Device ? hub->Device->SlotId : 0, port, speed);
|
||||
uint8_t slot = 0;
|
||||
st = XhciEnumerateHubPortEx((XHCI_CONTROLLER*)hub->Hcd->ControllerContext, hub, port, speed, &slot);
|
||||
DbgPrint("usb-stage: hub-slot=%u port=%u child enumeration done st=0x%x slot=%u\n",
|
||||
hub->Device ? hub->Device->SlotId : 0, port, st, slot);
|
||||
if (KSUCCESS(st)) {
|
||||
hub->ChildSlot[port] = slot;
|
||||
hub->ChildPortState[port] = UsbHubPortChildPresent;
|
||||
@@ -580,6 +592,12 @@ static bool UsbHubTakeInitialRootScan(USB_HUB_EXTENSION* hub) {
|
||||
// Implements the hub Worker operation.
|
||||
static void UsbHubWorker(void* arg) {
|
||||
USB_HUB_EXTENSION* hub = (USB_HUB_EXTENSION*)arg;
|
||||
if (hub && hub->Kind == UsbHubKindRoot && hub->Hcd) {
|
||||
XHCI_CONTROLLER* xhci = (XHCI_CONTROLLER*)hub->Hcd->ControllerContext;
|
||||
if (xhci)
|
||||
DbgPrint("usb-stage: hcd=%02x:%02x.%u root worker entered ports=%u\n",
|
||||
xhci->Bus, xhci->Dev, xhci->Fn, hub->PortCount);
|
||||
}
|
||||
while (hub && hub->WorkerRunning) {
|
||||
KSTATUS st = KeWaitSemaphore(hub->WorkerSemaphore, KE_WAIT_INFINITE);
|
||||
if (!KSUCCESS(st))
|
||||
@@ -588,8 +606,11 @@ static void UsbHubWorker(void* arg) {
|
||||
break;
|
||||
|
||||
if (hub->Kind == UsbHubKindRoot) {
|
||||
if (UsbHubTakeInitialRootScan(hub))
|
||||
(void)UsbHubDoRootPortScan(hub->Hcd);
|
||||
if (UsbHubTakeInitialRootScan(hub)) {
|
||||
KSTATUS scanStatus = UsbHubDoRootPortScan(hub->Hcd);
|
||||
if (!KSUCCESS(scanStatus))
|
||||
DbgPrint("usb-stage: root scan aborted st=0x%x\n", scanStatus);
|
||||
}
|
||||
|
||||
uint8_t port = 0;
|
||||
while (UsbHubPopPendingRootPort(hub, &port))
|
||||
@@ -657,6 +678,12 @@ static KSTATUS UsbHubStartWorker(USB_HUB_EXTENSION* hub) {
|
||||
UsbHubStopWorker(hub);
|
||||
return status;
|
||||
}
|
||||
if (hub->Kind == UsbHubKindRoot && hub->Hcd) {
|
||||
XHCI_CONTROLLER* xhci = (XHCI_CONTROLLER*)hub->Hcd->ControllerContext;
|
||||
if (xhci)
|
||||
DbgPrint("usb-stage: hcd=%02x:%02x.%u root worker created\n",
|
||||
xhci->Bus, xhci->Dev, xhci->Fn);
|
||||
}
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -675,6 +702,8 @@ static KSTATUS UsbHubDoRootPortScan(USB_HCD* hcd) {
|
||||
if (!xhci || !xhci->Op)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
DbgPrint("usb-stage: hcd=%02x:%02x.%u root scan begin ports=%u\n",
|
||||
xhci->Bus, xhci->Dev, xhci->Fn, hub->PortCount);
|
||||
for (uint8_t port = 1; port <= hub->PortCount; port++) {
|
||||
uint32_t portsc = XhciRead32(xhci->Op, XhciPortReg(port));
|
||||
if (!(portsc & XHCI_PORTSC_CCS))
|
||||
@@ -686,9 +715,13 @@ static KSTATUS UsbHubDoRootPortScan(USB_HCD* hcd) {
|
||||
continue;
|
||||
}
|
||||
|
||||
DbgPrint("usb-stage: hcd=%02x:%02x.%u root-port=%u begin portsc=%08x\n",
|
||||
xhci->Bus, xhci->Dev, xhci->Fn, port, portsc);
|
||||
attempted++;
|
||||
uint8_t slot = 0;
|
||||
KSTATUS st = UsbHubEnumerateConnectedRootPort(hub, xhci, port, &slot);
|
||||
DbgPrint("usb-stage: hcd=%02x:%02x.%u root-port=%u done st=0x%x slot=%u\n",
|
||||
xhci->Bus, xhci->Dev, xhci->Fn, port, st, slot);
|
||||
if (KSUCCESS(st)) {
|
||||
hub->RootPortSlot[port] = slot;
|
||||
hub->RootPortState[port] = UsbHubPortChildPresent;
|
||||
@@ -699,27 +732,45 @@ static KSTATUS UsbHubDoRootPortScan(USB_HCD* hcd) {
|
||||
}
|
||||
}
|
||||
|
||||
DbgPrint("usb-hub: root-port scan complete; attempted=%u configured=%u\n", attempted, configured);
|
||||
DbgPrint("usb-hub: hcd=%02x:%02x.%u root-port scan complete; attempted=%u configured=%u\n",
|
||||
xhci->Bus, xhci->Dev, xhci->Fn, attempted, configured);
|
||||
uintptr_t flags = KeAcquireSpinlock(&hub->PendingLock);
|
||||
hub->InitialScanComplete = true;
|
||||
hub->InitialScanInProgress = false;
|
||||
KeReleaseSpinlock(&hub->PendingLock, flags);
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Implements the hub Schedule Root Scan operation.
|
||||
KSTATUS UsbHubScheduleRootScan(USB_HCD* hcd) {
|
||||
if (!hcd || !hcd->RootHubContext)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
USB_HUB_EXTENSION* hub = (USB_HUB_EXTENSION*)hcd->RootHubContext;
|
||||
if (hub->Kind != UsbHubKindRoot || !hub->Started)
|
||||
return NTLSTATUS_OBJECT_DEAD;
|
||||
static SEMAPHORE* UsbHubQueueInitialScan(USB_HUB_EXTENSION* hub) {
|
||||
if (!hub || !hub->Started || !hub->WorkerSemaphore)
|
||||
return (SEMAPHORE*)0;
|
||||
|
||||
uintptr_t flags = KeAcquireSpinlock(&hub->PendingLock);
|
||||
if (!hub->InitialScanComplete && !hub->InitialScanInProgress)
|
||||
bool pending = !hub->InitialScanComplete && !hub->InitialScanInProgress;
|
||||
if (pending)
|
||||
hub->InitialScanPending = true;
|
||||
KeReleaseSpinlock(&hub->PendingLock, flags);
|
||||
|
||||
if (hub->WorkerSemaphore)
|
||||
KeSignalSemaphore(hub->WorkerSemaphore);
|
||||
if (!pending)
|
||||
return (SEMAPHORE*)0;
|
||||
|
||||
ObReferenceObject(&hub->WorkerSemaphore->Header);
|
||||
return hub->WorkerSemaphore;
|
||||
}
|
||||
|
||||
KSTATUS UsbHubScheduleRootScan(USB_HCD* hcd) {
|
||||
if (!hcd || !hcd->RootHubPdo)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
uintptr_t flags = KeAcquireSpinlock(&hcd->RootHubLock);
|
||||
hcd->RootScanRequested = true;
|
||||
SEMAPHORE* wake = UsbHubQueueInitialScan((USB_HUB_EXTENSION*)hcd->RootHubContext);
|
||||
KeReleaseSpinlock(&hcd->RootHubLock, flags);
|
||||
|
||||
if (wake) {
|
||||
KeSignalSemaphore(wake);
|
||||
ObDereferenceObject(&wake->Header);
|
||||
}
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -800,8 +851,10 @@ static void UsbHubExternalChangeComplete(USB_URB* urb, void* context) {
|
||||
return;
|
||||
|
||||
hub->ChangeUrbPending = false;
|
||||
if (!KSUCCESS(urb->Status) || hub->Removed || hub->SurpriseRemoved)
|
||||
if (!KSUCCESS(urb->Status) || hub->Removed || hub->SurpriseRemoved) {
|
||||
IoReleaseRemoveLock(&hub->RemoveLock, (IRP*)0);
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t actual = urb->ActualLength;
|
||||
if (actual > USB_HUB_CHANGE_BITMAP_BYTES)
|
||||
@@ -813,6 +866,7 @@ static void UsbHubExternalChangeComplete(USB_URB* urb, void* context) {
|
||||
if (byteIndex < actual && (hub->ChangeBitmap[byteIndex] & (1u << bit)))
|
||||
UsbHubQueueExternalPortChange(hub, port);
|
||||
}
|
||||
IoReleaseRemoveLock(&hub->RemoveLock, (IRP*)0);
|
||||
}
|
||||
|
||||
// Implements the hub Arm External Interrupt operation.
|
||||
@@ -820,6 +874,10 @@ static void UsbHubArmExternalInterrupt(USB_HUB_EXTENSION* hub) {
|
||||
if (!hub || !hub->InterruptPipe || !hub->ChangeBitmap || hub->ChangeUrbPending)
|
||||
return;
|
||||
|
||||
KSTATUS refStatus = IoAcquireRemoveLock(&hub->RemoveLock, (IRP*)0);
|
||||
if (!KSUCCESS(refStatus))
|
||||
return;
|
||||
hub->ChangeUrbPending = true;
|
||||
memset(hub->ChangeBitmap, 0, USB_HUB_CHANGE_BITMAP_BYTES);
|
||||
memset(&hub->ChangeUrb, 0, sizeof(hub->ChangeUrb));
|
||||
hub->ChangeUrb.Function = UrbFunctionInterruptTransfer;
|
||||
@@ -835,11 +893,12 @@ static void UsbHubArmExternalInterrupt(USB_HUB_EXTENSION* hub) {
|
||||
hub->ChangeUrb.CompleteContext = hub;
|
||||
|
||||
KSTATUS st = UsbSubmitUrbAsync(hub->LowerDevice, &hub->ChangeUrb);
|
||||
if (st == NTLSTATUS_PENDING) {
|
||||
hub->ChangeUrbPending = true;
|
||||
} else if (!KSUCCESS(st)) {
|
||||
if (st != NTLSTATUS_PENDING || hub->ChangeUrb.Status != NTLSTATUS_PENDING) {
|
||||
hub->ChangeUrbPending = false;
|
||||
IoReleaseRemoveLock(&hub->RemoveLock, (IRP*)0);
|
||||
DbgPrint("usb-hub: slot=%u interrupt arm failed st=0x%x\n",
|
||||
hub->Device ? hub->Device->SlotId : 0, st);
|
||||
hub->Device ? hub->Device->SlotId : 0,
|
||||
st == NTLSTATUS_PENDING ? hub->ChangeUrb.Status : st);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -853,12 +912,30 @@ static KSTATUS UsbHubStartRootDevice(DEVICE_OBJECT* device, USB_HUB_EXTENSION* h
|
||||
if (!KSUCCESS(st))
|
||||
return st;
|
||||
|
||||
hub->Started = true;
|
||||
device->PnpState = DevicePnpStarted;
|
||||
if (hub->HubPdo)
|
||||
hub->HubPdo->PnpState = DevicePnpStarted;
|
||||
|
||||
DbgPrint("usb-hub: root hub started ports=%u\n", hub->PortCount);
|
||||
uintptr_t flags = KeAcquireSpinlock(&hub->Hcd->RootHubLock);
|
||||
hub->Started = true;
|
||||
SEMAPHORE* wake = hub->Hcd->RootScanRequested ? UsbHubQueueInitialScan(hub) : (SEMAPHORE*)0;
|
||||
KeReleaseSpinlock(&hub->Hcd->RootHubLock, flags);
|
||||
|
||||
XHCI_CONTROLLER* xhci = (XHCI_CONTROLLER*)hub->Hcd->ControllerContext;
|
||||
if (xhci)
|
||||
DbgPrint("usb-hub: hcd=%02x:%02x.%u root hub started ports=%u scan=%s\n",
|
||||
xhci->Bus, xhci->Dev, xhci->Fn, hub->PortCount,
|
||||
wake ? "queued" : "not-queued");
|
||||
|
||||
// Publish the startup checkpoint before waking enumeration. A bad port or
|
||||
// an interrupt storm can otherwise hide which hub started the work.
|
||||
if (wake) {
|
||||
KeSignalSemaphore(wake);
|
||||
ObDereferenceObject(&wake->Header);
|
||||
if (xhci)
|
||||
DbgPrint("usb-stage: hcd=%02x:%02x.%u initial scan wake returned\n",
|
||||
xhci->Bus, xhci->Dev, xhci->Fn);
|
||||
}
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -888,10 +965,24 @@ static KSTATUS UsbHubStartExternalDevice(DEVICE_OBJECT* device, USB_HUB_EXTENSIO
|
||||
}
|
||||
hub->PowerOnToPowerGoodMs = desc.bPwrOn2PwrGood ? (uint8_t)(desc.bPwrOn2PwrGood * 2u) : 100u;
|
||||
|
||||
// xHCI needs the hub's Slot Context before routing low/full-speed child
|
||||
// transfers through a high-speed transaction translator.
|
||||
XHCI_CONTROLLER* xhci = hub->Hcd ? (XHCI_CONTROLLER*)hub->Hcd->ControllerContext : (XHCI_CONTROLLER*)0;
|
||||
XHCI_DEVICE* xdev = xhci && hub->Device->SlotId <= XHCI_MAX_TRACKED_SLOTS
|
||||
? &xhci->Devices[hub->Device->SlotId] : (XHCI_DEVICE*)0;
|
||||
if (!xdev || !xdev->Allocated) {
|
||||
UsbHubReleaseDma(hub);
|
||||
return NTLSTATUS_DEVICE_ERROR;
|
||||
}
|
||||
st = XhciConfigureHubContext(xhci, xdev, hub->PortCount, desc.wHubCharacteristics);
|
||||
if (!KSUCCESS(st)) {
|
||||
UsbHubReleaseDma(hub);
|
||||
return st;
|
||||
}
|
||||
|
||||
for (uint8_t port = 1; port <= hub->PortCount; port++)
|
||||
(void)UsbHubSetPortFeature(hub, port, USB_HUB_PORT_FEAT_POWER);
|
||||
UsbDelayUs(((XHCI_CONTROLLER*)hub->Hcd->ControllerContext)->Ddk,
|
||||
(uint32_t)hub->PowerOnToPowerGoodMs * 1000u);
|
||||
UsbDelayUs((uint32_t)hub->PowerOnToPowerGoodMs * 1000u);
|
||||
|
||||
st = UsbHubStartWorker(hub);
|
||||
if (!KSUCCESS(st)) {
|
||||
@@ -971,12 +1062,22 @@ KSTATUS UsbHubDispatchPnp(DEVICE_OBJECT* device, IRP* irp) {
|
||||
device->PnpState = DevicePnpSurpriseRemoved;
|
||||
if (hub) {
|
||||
hub->SurpriseRemoved = true;
|
||||
if (hub->Kind == UsbHubKindRoot && hub->Hcd) {
|
||||
uintptr_t flags = KeAcquireSpinlock(&hub->Hcd->RootHubLock);
|
||||
if (hub->Hcd->RootHubContext == hub)
|
||||
hub->Hcd->RootHubContext = (void*)0;
|
||||
hub->Started = false;
|
||||
KeReleaseSpinlock(&hub->Hcd->RootHubLock, flags);
|
||||
}
|
||||
hub->WorkerRunning = false;
|
||||
if (hub->ChangeUrbPending)
|
||||
(void)UsbCancelUrb(&hub->ChangeUrb);
|
||||
if (hub->WorkerSemaphore)
|
||||
KeSignalSemaphore(hub->WorkerSemaphore);
|
||||
UsbHubStopWorker(hub);
|
||||
if (hub->ChangeUrbPending) {
|
||||
st = UsbCancelUrb(&hub->ChangeUrb);
|
||||
if (!KSUCCESS(st)) {
|
||||
IoCompleteRequest(irp, st, 0);
|
||||
return NTLSTATUS_PENDING;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove downstream PDOs owned by this hub FDO. The explicit
|
||||
// slot arrays are still used as the hardware lookup table; the
|
||||
@@ -1000,23 +1101,34 @@ KSTATUS UsbHubDispatchPnp(DEVICE_OBJECT* device, IRP* irp) {
|
||||
device->PnpState = DevicePnpDeleted;
|
||||
if (hub) {
|
||||
hub->Removed = true;
|
||||
hub->WorkerRunning = false;
|
||||
if (hub->ChangeUrbPending)
|
||||
(void)UsbCancelUrb(&hub->ChangeUrb);
|
||||
(void)UsbRemoveDeviceObjectChildren(device, false);
|
||||
if (hub->InterruptPipe) {
|
||||
hub->InterruptPipe->Open = false;
|
||||
UsbDereferencePipe(hub->InterruptPipe);
|
||||
hub->InterruptPipe = (USB_PIPE*)0;
|
||||
if (hub->Kind == UsbHubKindRoot && hub->Hcd) {
|
||||
uintptr_t flags = KeAcquireSpinlock(&hub->Hcd->RootHubLock);
|
||||
if (hub->Hcd->RootHubContext == hub)
|
||||
hub->Hcd->RootHubContext = (void*)0;
|
||||
hub->Started = false;
|
||||
KeReleaseSpinlock(&hub->Hcd->RootHubLock, flags);
|
||||
}
|
||||
hub->WorkerRunning = false;
|
||||
UsbHubStopWorker(hub);
|
||||
if (hub->Kind == UsbHubKindRoot && hub->Hcd && hub->Hcd->RootHubContext == hub)
|
||||
hub->Hcd->RootHubContext = (void*)0;
|
||||
if (hub->ChangeUrbPending) {
|
||||
st = UsbCancelUrb(&hub->ChangeUrb);
|
||||
if (!KSUCCESS(st)) {
|
||||
IoCompleteRequest(irp, st, 0);
|
||||
return NTLSTATUS_PENDING;
|
||||
}
|
||||
}
|
||||
(void)UsbRemoveDeviceObjectChildren(device, false);
|
||||
if (hub->InterruptPipe)
|
||||
hub->InterruptPipe->Open = false;
|
||||
if (hub->RemoveLockReady) {
|
||||
IoReleaseRemoveLockAndWait(&hub->RemoveLock, irp);
|
||||
IoDestroyRemoveLock(&hub->RemoveLock);
|
||||
hub->RemoveLockReady = false;
|
||||
}
|
||||
if (hub->InterruptPipe) {
|
||||
UsbDereferencePipe(hub->InterruptPipe);
|
||||
hub->InterruptPipe = (USB_PIPE*)0;
|
||||
}
|
||||
UsbHubReleaseDma(hub);
|
||||
}
|
||||
|
||||
@@ -1118,7 +1230,9 @@ KSTATUS UsbHubAddDevice(DRIVER_OBJECT* driver, DEVICE_OBJECT* physicalDeviceObje
|
||||
}
|
||||
|
||||
if (kind == UsbHubKindRoot) {
|
||||
uintptr_t flags = KeAcquireSpinlock(&hub->Hcd->RootHubLock);
|
||||
hub->Hcd->RootHubContext = hub;
|
||||
KeReleaseSpinlock(&hub->Hcd->RootHubLock, flags);
|
||||
DbgPrint("usb-hub: root hub FDO attached\n");
|
||||
} else {
|
||||
DbgPrint("usb-hub: external hub FDO attached slot=%u ep=%02x\n",
|
||||
@@ -1135,5 +1249,5 @@ KSTATUS UsbInitializeHubDriver(void) {
|
||||
gUsbHubDriver.IdTable = &gUsbHubIdTable;
|
||||
gUsbHubDriver.DriverAddDevice = UsbHubAddDevice;
|
||||
gUsbHubDriver.MajorFunction[IRP_MJ_PNP] = UsbHubDispatchPnp;
|
||||
return IoRegisterDriverObject(&gUsbHubDriver);
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
@@ -347,7 +347,7 @@ KSTATUS UsbStorageTestUnitReady(USB_STORAGE_EXTENSION* storage) {
|
||||
if (KSUCCESS(st)) return NTLSTATUS_SUCCESS;
|
||||
|
||||
last = st;
|
||||
UsbDelayUs(xhci->Ddk, 10000u);
|
||||
UsbDelayUs(10000u);
|
||||
}
|
||||
|
||||
return last;
|
||||
@@ -468,7 +468,7 @@ void UsbStorageAcquireIo(USB_STORAGE_EXTENSION* storage) {
|
||||
if (!storage) return;
|
||||
XHCI_CONTROLLER* xhci = storage->Device ? (XHCI_CONTROLLER*)storage->Device->Hcd->ControllerContext : (XHCI_CONTROLLER*)0;
|
||||
while (NtlAtomicCompareExchange32(&storage->IoBusy, 1, 0) != 0) {
|
||||
if (xhci) UsbDelayUs(xhci->Ddk, 10);
|
||||
if (xhci) UsbDelayUs(10);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -584,10 +584,10 @@ KSTATUS XhciAllocateMassStorageMemory(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev) {
|
||||
dev->MassOutRing = (XHCI_TRB*)UsbDmaAllocPages(xhci, 1, &dev->MassOutRingDeviceAddress, &dev->MassOutRingDma);
|
||||
dev->MassBuffer = (uint8_t*)UsbDmaAllocPages(xhci, USB_STORAGE_DMA_PAGES, &dev->MassBufferDeviceAddress, &dev->MassBufferDma);
|
||||
if (!dev->MassInRing || !dev->MassOutRing || !dev->MassBuffer) {
|
||||
if (gUsbDdk && gUsbDdk->Dma) {
|
||||
gUsbDdk->Dma->FreeCommonBuffer(dev->MassBufferDma);
|
||||
gUsbDdk->Dma->FreeCommonBuffer(dev->MassOutRingDma);
|
||||
gUsbDdk->Dma->FreeCommonBuffer(dev->MassInRingDma);
|
||||
{
|
||||
IoDmaFreeCommonBuffer(dev->MassBufferDma);
|
||||
IoDmaFreeCommonBuffer(dev->MassOutRingDma);
|
||||
IoDmaFreeCommonBuffer(dev->MassInRingDma);
|
||||
}
|
||||
dev->MassBufferDma = (IO_DMA_BUFFER*)0;
|
||||
dev->MassOutRingDma = (IO_DMA_BUFFER*)0;
|
||||
@@ -1059,5 +1059,5 @@ KSTATUS UsbInitializeStorageDriver(void) {
|
||||
gUsbStorageDriver.MajorFunction[IRP_MJ_WRITE] = UsbStorageDispatchReadWrite;
|
||||
gUsbStorageDriver.MajorFunction[IRP_MJ_FLUSH_BUFFERS] = UsbStorageDispatchFlush;
|
||||
gUsbStorageDriver.MajorFunction[IRP_MJ_PNP] = UsbStorageDispatchPnp;
|
||||
return IoRegisterDriverObject(&gUsbStorageDriver);
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
+293
-104
@@ -48,27 +48,22 @@ void XhciWrite64(volatile uint8_t* base, uint32_t reg, uint64_t value) {
|
||||
}
|
||||
|
||||
// Implements the delay Us operation.
|
||||
void UsbDelayUs(const NTL_DDK* ddk, uint32_t us) {
|
||||
if (ddk && ddk->Timer && ddk->Timer->Stall) ddk->Timer->Stall(us);
|
||||
else {
|
||||
for (uint32_t i = 0; i < us * 32u; i++) _mm_pause();
|
||||
}
|
||||
void UsbDelayUs(uint32_t us) {
|
||||
KeStallExecution(us);
|
||||
}
|
||||
|
||||
bool UsbWaitSet32(const NTL_DDK* ddk, volatile uint8_t* base, uint32_t reg,
|
||||
uint32_t mask, uint32_t timeoutUs) {
|
||||
bool UsbWaitSet32(volatile uint8_t* base, uint32_t reg, uint32_t mask, uint32_t timeoutUs) {
|
||||
for (uint32_t i = 0; i < timeoutUs; i++) {
|
||||
if ((XhciRead32(base, reg) & mask) == mask) return true;
|
||||
UsbDelayUs(ddk, 1);
|
||||
UsbDelayUs(1);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UsbWaitClear32(const NTL_DDK* ddk, volatile uint8_t* base, uint32_t reg,
|
||||
uint32_t mask, uint32_t timeoutUs) {
|
||||
bool UsbWaitClear32(volatile uint8_t* base, uint32_t reg, uint32_t mask, uint32_t timeoutUs) {
|
||||
for (uint32_t i = 0; i < timeoutUs; i++) {
|
||||
if ((XhciRead32(base, reg) & mask) == 0) return true;
|
||||
UsbDelayUs(ddk, 1);
|
||||
UsbDelayUs(1);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -83,7 +78,7 @@ void* UsbDmaAllocPages(XHCI_CONTROLLER* xhci,
|
||||
return (void*)0;
|
||||
|
||||
IO_DMA_BUFFER* buffer = (IO_DMA_BUFFER*)0;
|
||||
KSTATUS status = xhci->Ddk->Dma->AllocateCommonBuffer(xhci->DmaAdapter,
|
||||
KSTATUS status = IoDmaAllocateCommonBuffer(xhci->DmaAdapter,
|
||||
pages * (size_t)PAGE_SIZE,
|
||||
PAGE_SIZE,
|
||||
&buffer);
|
||||
@@ -93,14 +88,14 @@ void* UsbDmaAllocPages(XHCI_CONTROLLER* xhci,
|
||||
return (void*)0;
|
||||
}
|
||||
|
||||
*outDeviceAddress = xhci->Ddk->Dma->GetCommonBufferDeviceAddress(buffer);
|
||||
*outDeviceAddress = IoDmaGetCommonBufferDeviceAddress(buffer);
|
||||
*outBuffer = buffer;
|
||||
return xhci->Ddk->Dma->GetCommonBufferAddress(buffer);
|
||||
return IoDmaGetCommonBufferAddress(buffer);
|
||||
}
|
||||
|
||||
// Releases all DMA allocations owned by one xHCI slot.
|
||||
void XhciReleaseDeviceDma(XHCI_DEVICE* dev) {
|
||||
if (!dev || !gUsbDdk || !gUsbDdk->Dma)
|
||||
if (!dev)
|
||||
return;
|
||||
|
||||
for (uint32_t endpoint = 1; endpoint < 32; endpoint++) {
|
||||
@@ -110,15 +105,15 @@ void XhciReleaseDeviceDma(XHCI_DEVICE* dev) {
|
||||
UsbDereferencePipe(pipe);
|
||||
}
|
||||
|
||||
gUsbDdk->Dma->FreeCommonBuffer(dev->MassBufferDma);
|
||||
gUsbDdk->Dma->FreeCommonBuffer(dev->HidBufferDma);
|
||||
gUsbDdk->Dma->FreeCommonBuffer(dev->DescriptorDma);
|
||||
gUsbDdk->Dma->FreeCommonBuffer(dev->MassOutRingDma);
|
||||
gUsbDdk->Dma->FreeCommonBuffer(dev->MassInRingDma);
|
||||
gUsbDdk->Dma->FreeCommonBuffer(dev->HidRingDma);
|
||||
gUsbDdk->Dma->FreeCommonBuffer(dev->Ep0RingDma);
|
||||
gUsbDdk->Dma->FreeCommonBuffer(dev->DeviceContextDma);
|
||||
gUsbDdk->Dma->FreeCommonBuffer(dev->InputContextDma);
|
||||
IoDmaFreeCommonBuffer(dev->MassBufferDma);
|
||||
IoDmaFreeCommonBuffer(dev->HidBufferDma);
|
||||
IoDmaFreeCommonBuffer(dev->DescriptorDma);
|
||||
IoDmaFreeCommonBuffer(dev->MassOutRingDma);
|
||||
IoDmaFreeCommonBuffer(dev->MassInRingDma);
|
||||
IoDmaFreeCommonBuffer(dev->HidRingDma);
|
||||
IoDmaFreeCommonBuffer(dev->Ep0RingDma);
|
||||
IoDmaFreeCommonBuffer(dev->DeviceContextDma);
|
||||
IoDmaFreeCommonBuffer(dev->InputContextDma);
|
||||
|
||||
dev->MassBufferDma = (IO_DMA_BUFFER*)0;
|
||||
dev->HidBufferDma = (IO_DMA_BUFFER*)0;
|
||||
@@ -143,22 +138,22 @@ void XhciReleaseDeviceDma(XHCI_DEVICE* dev) {
|
||||
|
||||
// Releases controller-wide xHCI DMA allocations.
|
||||
static void XhciReleaseControllerDma(XHCI_CONTROLLER* xhci) {
|
||||
if (!xhci || !xhci->Ddk || !xhci->Ddk->Dma)
|
||||
if (!xhci)
|
||||
return;
|
||||
|
||||
for (uint32_t slot = 1; slot <= XHCI_MAX_TRACKED_SLOTS; slot++)
|
||||
XhciReleaseDeviceDma(&xhci->Devices[slot]);
|
||||
for (uint16_t i = 0; i < XHCI_MAX_SCRATCHPADS; i++) {
|
||||
xhci->Ddk->Dma->FreeCommonBuffer(xhci->ScratchpadDma[i]);
|
||||
IoDmaFreeCommonBuffer(xhci->ScratchpadDma[i]);
|
||||
xhci->ScratchpadDma[i] = (IO_DMA_BUFFER*)0;
|
||||
xhci->ScratchpadDeviceAddress[i] = 0;
|
||||
}
|
||||
|
||||
xhci->Ddk->Dma->FreeCommonBuffer(xhci->ScratchpadArrayDma);
|
||||
xhci->Ddk->Dma->FreeCommonBuffer(xhci->ErstDma);
|
||||
xhci->Ddk->Dma->FreeCommonBuffer(xhci->EventRingDma);
|
||||
xhci->Ddk->Dma->FreeCommonBuffer(xhci->CommandRingDma);
|
||||
xhci->Ddk->Dma->FreeCommonBuffer(xhci->DcbaaDma);
|
||||
IoDmaFreeCommonBuffer(xhci->ScratchpadArrayDma);
|
||||
IoDmaFreeCommonBuffer(xhci->ErstDma);
|
||||
IoDmaFreeCommonBuffer(xhci->EventRingDma);
|
||||
IoDmaFreeCommonBuffer(xhci->CommandRingDma);
|
||||
IoDmaFreeCommonBuffer(xhci->DcbaaDma);
|
||||
xhci->ScratchpadArrayDma = (IO_DMA_BUFFER*)0;
|
||||
xhci->ErstDma = (IO_DMA_BUFFER*)0;
|
||||
xhci->EventRingDma = (IO_DMA_BUFFER*)0;
|
||||
@@ -170,7 +165,7 @@ static void XhciReleaseControllerDma(XHCI_CONTROLLER* xhci) {
|
||||
xhci->CommandRing = (XHCI_TRB*)0;
|
||||
xhci->Dcbaa = (uint64_t*)0;
|
||||
|
||||
xhci->Ddk->Dma->PutAdapter(xhci->DmaAdapter);
|
||||
IoDmaPutAdapter(xhci->DmaAdapter);
|
||||
xhci->DmaAdapter = (IO_DMA_ADAPTER*)0;
|
||||
}
|
||||
|
||||
@@ -220,6 +215,76 @@ uint32_t* XhciInputEndpointContext(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev,
|
||||
return (uint32_t*)(dev->InputContext + (uint32_t)(endpointId + 1u) * xhci->ContextBytes);
|
||||
}
|
||||
|
||||
// Reconstructs the addressed device topology when configuring an endpoint or hub.
|
||||
// Configure Endpoint must not discard route, TT, or hub attributes on the slot.
|
||||
void XhciBuildConfigureSlotContext(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev,
|
||||
uint32_t* slot, uint8_t endpointId) {
|
||||
if (!xhci || !dev || !slot)
|
||||
return;
|
||||
memset(slot, 0, xhci->ContextBytes);
|
||||
|
||||
uint8_t lastContext = endpointId;
|
||||
if (dev->DeviceContext) {
|
||||
uint32_t previous = ((const uint32_t*)dev->DeviceContext)[0];
|
||||
uint8_t previousLast = (uint8_t)((previous >> 27) & 31u);
|
||||
if (previousLast > lastContext)
|
||||
lastContext = previousLast;
|
||||
}
|
||||
uint8_t rootPort = dev->Path.RootPort ? dev->Path.RootPort : dev->Port;
|
||||
slot[0] = XHCI_CONTEXT_ENTRIES(lastContext) | XHCI_SLOT_SPEED(dev->Speed) |
|
||||
(dev->Path.RouteString & 0xfffffu);
|
||||
slot[1] = XHCI_SLOT_ROOT_PORT(rootPort);
|
||||
if (dev->Path.BehindHub && dev->Speed <= 2u && dev->Path.ParentHubSlot) {
|
||||
slot[2] = (uint32_t)dev->Path.ParentHubSlot |
|
||||
((uint32_t)dev->Path.ParentHubPort << 8);
|
||||
}
|
||||
if (dev->HubPortCount) {
|
||||
slot[0] |= XHCI_SLOT_HUB;
|
||||
slot[1] |= XHCI_SLOT_PORT_COUNT(dev->HubPortCount);
|
||||
if (dev->Speed == 3u)
|
||||
slot[2] |= XHCI_SLOT_TT_THINK(dev->HubCharacteristics >> 5);
|
||||
}
|
||||
}
|
||||
|
||||
// Publishes an external hub's topology to xHCI before its child ports are used.
|
||||
KSTATUS XhciConfigureHubContext(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev,
|
||||
uint8_t portCount, uint16_t characteristics) {
|
||||
if (!xhci || !dev || !dev->Addressed || !dev->InputContext ||
|
||||
!dev->DeviceContext || !portCount)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
uint8_t oldPortCount = dev->HubPortCount;
|
||||
uint16_t oldCharacteristics = dev->HubCharacteristics;
|
||||
dev->HubPortCount = portCount;
|
||||
dev->HubCharacteristics = characteristics;
|
||||
|
||||
memset(dev->InputContext, 0, PAGE_SIZE);
|
||||
uint32_t* icc = XhciInputControlContext(xhci, dev);
|
||||
uint32_t* slot = XhciInputSlotContext(xhci, dev);
|
||||
// Update only the slot context; existing endpoint contexts remain active.
|
||||
icc[1] = 1u;
|
||||
XhciBuildConfigureSlotContext(xhci, dev, slot, 1u);
|
||||
|
||||
XHCI_COMMAND_RESULT result;
|
||||
memset(&result, 0, sizeof(result));
|
||||
DbgPrint("usb-stage: hcd=%02x:%02x.%u hub-slot=%u configure-context begin ports=%u tt=%u speed=%u\n",
|
||||
xhci->Bus, xhci->Dev, xhci->Fn, dev->SlotId,
|
||||
portCount, (characteristics >> 5) & 3u, dev->Speed);
|
||||
KSTATUS st = XhciSubmitCommand(xhci, dev->InputContextDeviceAddress, 0,
|
||||
(XHCI_TRB_TYPE_CONFIGURE_ENDPOINT << XHCI_TRB_TYPE_SHIFT) |
|
||||
((uint32_t)dev->SlotId << 24),
|
||||
&result);
|
||||
if (!KSUCCESS(st) || result.CompletionCode != XHCI_CC_SUCCESS) {
|
||||
DbgPrint("usb-stage: hub-slot=%u configure-context failed st=0x%x cc=%u\n",
|
||||
dev->SlotId, st, result.CompletionCode);
|
||||
dev->HubPortCount = oldPortCount;
|
||||
dev->HubCharacteristics = oldCharacteristics;
|
||||
return KSUCCESS(st) ? NTLSTATUS_DEVICE_ERROR : st;
|
||||
}
|
||||
DbgPrint("usb-stage: hub-slot=%u configure-context done\n", dev->SlotId);
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Implements the ep Max Packet operation.
|
||||
uint32_t XhciEpMaxPacket(uint8_t speed) {
|
||||
switch (speed) {
|
||||
@@ -254,6 +319,15 @@ const char* XhciPortSpeedName(uint32_t speed) {
|
||||
}
|
||||
}
|
||||
|
||||
// Advances an xHCI extended-capability offset using the relative DWORD link.
|
||||
static uint32_t XhciNextExtendedCapabilityDword(uint32_t current, uint8_t next,
|
||||
uint32_t mappedBytes) {
|
||||
if (!next) return 0;
|
||||
uint64_t following = (uint64_t)current + next;
|
||||
if (following * 4u + sizeof(uint32_t) > mappedBytes) return 0;
|
||||
return (uint32_t)following;
|
||||
}
|
||||
|
||||
// Parses supported Protocols.
|
||||
void XhciParseSupportedProtocols(XHCI_CONTROLLER* xhci) {
|
||||
memset(xhci->PortProtocolMajor, 0, sizeof(xhci->PortProtocolMajor));
|
||||
@@ -297,8 +371,18 @@ void XhciParseSupportedProtocols(XHCI_CONTROLLER* xhci) {
|
||||
}
|
||||
}
|
||||
|
||||
xecp = next;
|
||||
xecp = XhciNextExtendedCapabilityDword(xecp, next, xhci->MmioBytes);
|
||||
}
|
||||
|
||||
uint32_t usb2Ports = 0;
|
||||
uint32_t usb3Ports = 0;
|
||||
for (uint32_t port = 1; port <= xhci->MaxPorts; port++) {
|
||||
if (xhci->PortProtocolMajor[port] == 2u) usb2Ports++;
|
||||
else if (xhci->PortProtocolMajor[port] >= 3u) usb3Ports++;
|
||||
}
|
||||
DbgPrint("usb: xHCI hcd=%02x:%02x.%u protocol ports usb2=%u usb3=%u unknown=%u\n",
|
||||
xhci->Bus, xhci->Dev, xhci->Fn, usb2Ports, usb3Ports,
|
||||
(uint32_t)xhci->MaxPorts - usb2Ports - usb3Ports);
|
||||
}
|
||||
|
||||
// Implements the port Protocol Major operation.
|
||||
@@ -385,7 +469,7 @@ void XhciPowerRootPorts(XHCI_CONTROLLER* xhci) {
|
||||
}
|
||||
|
||||
if (powered) {
|
||||
UsbDelayUs(xhci->Ddk, 20000u);
|
||||
UsbDelayUs(20000u);
|
||||
DbgPrint("usb: xHCI powered %u previously-off root port(s)\n", powered);
|
||||
}
|
||||
}
|
||||
@@ -465,12 +549,43 @@ static XHCI_WAIT_TARGET* XhciClaimTransferWaiter(XHCI_CONTROLLER* xhci,
|
||||
return w;
|
||||
}
|
||||
|
||||
// Implements the drain Event Ring operation.
|
||||
bool XhciDrainEventRing(XHCI_CONTROLLER* xhci, XHCI_WAIT_TARGET* target) {
|
||||
(void)target;
|
||||
bool processed = false;
|
||||
// A completion leaves EventLock with its class-device rundown reference held.
|
||||
// Client callbacks can rearm transfers or acquire unrelated PnP locks.
|
||||
#define XHCI_COMPLETION_BATCH 16u
|
||||
|
||||
while (true) {
|
||||
typedef struct XHCI_ASYNC_COMPLETION {
|
||||
USB_URB* Urb;
|
||||
IRP* Irp;
|
||||
KSTATUS Status;
|
||||
uint32_t ActualLength;
|
||||
} XHCI_ASYNC_COMPLETION;
|
||||
|
||||
// Claims an asynchronous TD exactly once while the event lock is owned.
|
||||
static bool XhciClaimAsyncCompletion(XHCI_CONTROLLER* xhci, uint64_t trbAddress,
|
||||
uint32_t completionCode, uint8_t slotId,
|
||||
uint8_t endpointId, uint32_t residue,
|
||||
XHCI_ASYNC_COMPLETION* completion);
|
||||
|
||||
// Executes callbacks outside the event lock; class remove locks own their lifetime.
|
||||
static void XhciDispatchAsyncCompletion(const XHCI_ASYNC_COMPLETION* completed) {
|
||||
USB_URB* urb = completed->Urb;
|
||||
if (urb->Complete)
|
||||
urb->Complete(urb, urb->CompleteContext);
|
||||
if (completed->Irp)
|
||||
IoCompleteRequest(completed->Irp, completed->Status,
|
||||
KSUCCESS(completed->Status) ? completed->ActualLength : 0);
|
||||
}
|
||||
|
||||
// Drains a bounded batch while the event lock is held.
|
||||
static bool XhciDrainEventRing(XHCI_CONTROLLER* xhci,
|
||||
XHCI_ASYNC_COMPLETION* completions, uint32_t* completionCount,
|
||||
uint32_t* changedPorts) {
|
||||
bool processed = false;
|
||||
*completionCount = 0;
|
||||
|
||||
for (uint32_t drained = 0; drained < XHCI_EVENT_TRBS; drained++) {
|
||||
if (*completionCount == XHCI_COMPLETION_BATCH)
|
||||
break;
|
||||
XHCI_TRB* ev = &xhci->EventRing[xhci->EventDequeue];
|
||||
uint32_t control = ev->Control;
|
||||
uint8_t cycle = (uint8_t)(control & XHCI_TRB_CYCLE);
|
||||
@@ -495,15 +610,15 @@ bool XhciDrainEventRing(XHCI_CONTROLLER* xhci, XHCI_WAIT_TARGET* target) {
|
||||
UsbTrace("usb: xHCI transfer event trb=0x%016llX cc=%u slot=%u ep=%u residue=%u\n",
|
||||
(unsigned long long)param, cc, slotId, epId, residue);
|
||||
}
|
||||
|
||||
XHCI_WAIT_TARGET* waiter = XhciClaimTransferWaiter(xhci, param, slotId, epId, cc, residue);
|
||||
if (!waiter)
|
||||
XhciHandleAsyncTransferEvent(xhci, param, cc, slotId, epId, residue);
|
||||
if (!waiter && XhciClaimAsyncCompletion(xhci, param, cc, slotId, epId, residue,
|
||||
&completions[*completionCount]))
|
||||
(*completionCount)++;
|
||||
} else if (type == XHCI_TRB_TYPE_PORT_SC) {
|
||||
uint32_t portId = ev->ParameterLo >> 24;
|
||||
UsbTrace("usb: xHCI port-status-change event port=%u cc=%u\n", portId, cc);
|
||||
if (portId && portId <= xhci->MaxPorts)
|
||||
UsbHubQueueRootPortChange(&xhci->Hcd, (uint8_t)portId);
|
||||
if (portId && portId <= xhci->MaxPorts && portId <= USB_ROOT_HUB_MAX_PORTS)
|
||||
changedPorts[portId / 32u] |= 1u << (portId & 31u);
|
||||
} else {
|
||||
DbgPrint("usb: xHCI event type=%u cc=%u p=0x%016llX status=%08x ctrl=%08x\n",
|
||||
type, cc, (unsigned long long)param, ev->Status, ev->Control);
|
||||
@@ -521,12 +636,27 @@ bool XhciDrainEventRing(XHCI_CONTROLLER* xhci, XHCI_WAIT_TARGET* target) {
|
||||
return processed;
|
||||
}
|
||||
|
||||
// Implements the drain Event Ring Locked operation.
|
||||
// Serializes event-ring ownership without calling into class drivers under the lock.
|
||||
bool XhciDrainEventRingLocked(XHCI_CONTROLLER* xhci, XHCI_WAIT_TARGET* target) {
|
||||
(void)target;
|
||||
if (!xhci) return false;
|
||||
XHCI_ASYNC_COMPLETION completions[XHCI_COMPLETION_BATCH];
|
||||
uint32_t changedPorts[8] = { 0 };
|
||||
uint32_t count = 0;
|
||||
uintptr_t flags = KeAcquireSpinlock(&xhci->EventLock);
|
||||
bool processed = XhciDrainEventRing(xhci, target);
|
||||
bool processed = XhciDrainEventRing(xhci, completions, &count, changedPorts);
|
||||
KeReleaseSpinlock(&xhci->EventLock, flags);
|
||||
|
||||
// Hub wakeups can take scheduler locks; keep them outside EventLock too.
|
||||
for (uint32_t word = 0; word < 8; word++) {
|
||||
uint32_t bits = changedPorts[word];
|
||||
for (uint32_t bit = 0; bit < 32; bit++) {
|
||||
if (bits & (1u << bit))
|
||||
UsbHubQueueRootPortChange(&xhci->Hcd, (uint8_t)(word * 32u + bit));
|
||||
}
|
||||
}
|
||||
for (uint32_t i = 0; i < count; i++)
|
||||
XhciDispatchAsyncCompletion(&completions[i]);
|
||||
return processed;
|
||||
}
|
||||
|
||||
@@ -591,7 +721,7 @@ KSTATUS XhciWaitForArmedTarget(XHCI_CONTROLLER* xhci, XHCI_WAIT_TARGET* target,
|
||||
KeInsertQueueDpc(&xhci->EventDpc, (void*)0, (void*)0);
|
||||
}
|
||||
|
||||
UsbDelayUs(xhci->Ddk, 1);
|
||||
UsbDelayUs(1);
|
||||
}
|
||||
|
||||
XhciUnregisterWaitTarget(xhci, target);
|
||||
@@ -617,9 +747,17 @@ void XhciEventDpcRoutine(KDPC* dpc, void* context,
|
||||
return;
|
||||
|
||||
xhci->EventDpcCount++;
|
||||
while (XhciDrainEventRingLocked(xhci, NULL)) {
|
||||
// Keep draining until the software dequeue catches up to the producer.
|
||||
// Processing is bounded even if connected hubs continuously report changes.
|
||||
// Updating ERDP clears EHB; remaining events cause the interrupter to
|
||||
// assert again, allowing other DPCs and scheduled threads to make progress.
|
||||
for (uint32_t pass = 0; pass < 4u; pass++) {
|
||||
if (!XhciDrainEventRingLocked(xhci, NULL))
|
||||
return;
|
||||
}
|
||||
uint32_t hits = ++xhci->EventDpcBudgetHits;
|
||||
if ((hits & (hits - 1u)) == 0u)
|
||||
DbgPrint("usb: xHCI hcd=%02x:%02x.%u event DPC budget reached count=%u\n",
|
||||
xhci->Bus, xhci->Dev, xhci->Fn, hits);
|
||||
}
|
||||
|
||||
#define XHCI_CONTROLLER_MAGIC 0x58484349u // XHCI
|
||||
@@ -700,7 +838,7 @@ KSTATUS XhciBiosHandoff(XHCI_CONTROLLER* xhci) {
|
||||
ok = true;
|
||||
break;
|
||||
}
|
||||
UsbDelayUs(xhci->Ddk, 1);
|
||||
UsbDelayUs(1);
|
||||
}
|
||||
if (!ok) {
|
||||
DbgPrint("usb: xHCI BIOS handoff timed out; continuing anyway\n");
|
||||
@@ -712,7 +850,7 @@ KSTATUS XhciBiosHandoff(XHCI_CONTROLLER* xhci) {
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
xecp = next;
|
||||
xecp = XhciNextExtendedCapabilityDword(xecp, next, xhci->MmioBytes);
|
||||
}
|
||||
|
||||
return NTLSTATUS_SUCCESS;
|
||||
@@ -723,7 +861,7 @@ KSTATUS XhciStopAndReset(XHCI_CONTROLLER* xhci) {
|
||||
uint32_t cmd = XhciRead32(xhci->Op, XHCI_OP_USBCMD);
|
||||
if (cmd & XHCI_USBCMD_RS) {
|
||||
XhciWrite32(xhci->Op, XHCI_OP_USBCMD, cmd & ~XHCI_USBCMD_RS);
|
||||
if (!UsbWaitSet32(xhci->Ddk, xhci->Op, XHCI_OP_USBSTS,
|
||||
if (!UsbWaitSet32(xhci->Op, XHCI_OP_USBSTS,
|
||||
XHCI_USBSTS_HCH, 1000000u)) {
|
||||
DbgPrint("usb: xHCI stop timeout usbsts=%08x\n",
|
||||
XhciRead32(xhci->Op, XHCI_OP_USBSTS));
|
||||
@@ -732,14 +870,14 @@ KSTATUS XhciStopAndReset(XHCI_CONTROLLER* xhci) {
|
||||
}
|
||||
|
||||
XhciWrite32(xhci->Op, XHCI_OP_USBCMD, XHCI_USBCMD_HCRST);
|
||||
if (!UsbWaitClear32(xhci->Ddk, xhci->Op, XHCI_OP_USBCMD,
|
||||
if (!UsbWaitClear32(xhci->Op, XHCI_OP_USBCMD,
|
||||
XHCI_USBCMD_HCRST, 1000000u)) {
|
||||
DbgPrint("usb: xHCI reset timeout usbcmd=%08x\n",
|
||||
XhciRead32(xhci->Op, XHCI_OP_USBCMD));
|
||||
return NTLSTATUS_TIMEOUT;
|
||||
}
|
||||
|
||||
if (!UsbWaitClear32(xhci->Ddk, xhci->Op, XHCI_OP_USBSTS,
|
||||
if (!UsbWaitClear32(xhci->Op, XHCI_OP_USBSTS,
|
||||
XHCI_USBSTS_CNR, 1000000u)) {
|
||||
DbgPrint("usb: xHCI controller-not-ready timeout usbsts=%08x\n",
|
||||
XhciRead32(xhci->Op, XHCI_OP_USBSTS));
|
||||
@@ -769,8 +907,7 @@ static void XhciHaltForDmaRelease(XHCI_CONTROLLER* xhci) {
|
||||
uint32_t command = XhciRead32(xhci->Op, XHCI_OP_USBCMD);
|
||||
command &= ~(XHCI_USBCMD_RS | XHCI_USBCMD_INTE);
|
||||
XhciWrite32(xhci->Op, XHCI_OP_USBCMD, command);
|
||||
if (!UsbWaitSet32(xhci->Ddk,
|
||||
xhci->Op,
|
||||
if (!UsbWaitSet32(xhci->Op,
|
||||
XHCI_OP_USBSTS,
|
||||
XHCI_USBSTS_HCH,
|
||||
1000000u))
|
||||
@@ -1048,7 +1185,7 @@ KSTATUS XhciStart(XHCI_CONTROLLER* xhci) {
|
||||
cmd |= XHCI_USBCMD_RS;
|
||||
XhciWrite32(xhci->Op, XHCI_OP_USBCMD, cmd);
|
||||
|
||||
if (!UsbWaitClear32(xhci->Ddk, xhci->Op, XHCI_OP_USBSTS,
|
||||
if (!UsbWaitClear32(xhci->Op, XHCI_OP_USBSTS,
|
||||
XHCI_USBSTS_HCH, 1000000u)) {
|
||||
DbgPrint("usb: xHCI start timeout usbsts=%08x\n",
|
||||
XhciRead32(xhci->Op, XHCI_OP_USBSTS));
|
||||
@@ -1099,6 +1236,8 @@ KSTATUS XhciResetPort(XHCI_CONTROLLER* xhci, uint8_t port, uint8_t* outSpeed) {
|
||||
|
||||
uint32_t speed = (portsc & XHCI_PORTSC_SPEED_MASK) >> XHCI_PORTSC_SPEED_SHIFT;
|
||||
uint8_t protocol = XhciPortProtocolMajor(xhci, port, portsc);
|
||||
DbgPrint("usb-stage: hcd=%02x:%02x.%u root-port=%u reset begin proto=%u speed=%u portsc=%08x\n",
|
||||
xhci->Bus, xhci->Dev, xhci->Fn, port, protocol, speed, portsc);
|
||||
|
||||
XhciClearPortChanges(xhci, port);
|
||||
portsc = XhciRead32(xhci->Op, off);
|
||||
@@ -1120,7 +1259,7 @@ KSTATUS XhciResetPort(XHCI_CONTROLLER* xhci, uint8_t port, uint8_t* outSpeed) {
|
||||
break;
|
||||
}
|
||||
XhciDrainEventRingLocked(xhci, NULL);
|
||||
UsbDelayUs(xhci->Ddk, 1);
|
||||
UsbDelayUs(1);
|
||||
}
|
||||
|
||||
XhciClearPortChanges(xhci, port);
|
||||
@@ -1133,6 +1272,8 @@ KSTATUS XhciResetPort(XHCI_CONTROLLER* xhci, uint8_t port, uint8_t* outSpeed) {
|
||||
(portsc & XHCI_PORTSC_PED) ? 1u : 0u,
|
||||
protocol, XhciPortSpeedName(speed), speed);
|
||||
|
||||
DbgPrint("usb-stage: hcd=%02x:%02x.%u root-port=%u superspeed ready=%u portsc=%08x\n",
|
||||
xhci->Bus, xhci->Dev, xhci->Fn, port, enabled ? 1u : 0u, portsc);
|
||||
if (!enabled || !(portsc & XHCI_PORTSC_CCS) || !(portsc & XHCI_PORTSC_PED))
|
||||
return NTLSTATUS_DEVICE_ERROR;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
@@ -1149,11 +1290,12 @@ KSTATUS XhciResetPort(XHCI_CONTROLLER* xhci, uint8_t port, uint8_t* outSpeed) {
|
||||
break;
|
||||
}
|
||||
XhciDrainEventRingLocked(xhci, NULL);
|
||||
UsbDelayUs(xhci->Ddk, 1);
|
||||
UsbDelayUs(1);
|
||||
}
|
||||
|
||||
if (!resetDone) {
|
||||
DbgPrint("usb: xHCI port %u reset timeout portsc=%08x\n", port, portsc);
|
||||
DbgPrint("usb: xHCI hcd=%02x:%02x.%u port %u reset timeout portsc=%08x\n",
|
||||
xhci->Bus, xhci->Dev, xhci->Fn, port, portsc);
|
||||
return NTLSTATUS_TIMEOUT;
|
||||
}
|
||||
|
||||
@@ -1167,6 +1309,8 @@ KSTATUS XhciResetPort(XHCI_CONTROLLER* xhci, uint8_t port, uint8_t* outSpeed) {
|
||||
(portsc & XHCI_PORTSC_PED) ? 1u : 0u,
|
||||
protocol, XhciPortSpeedName(speed), speed);
|
||||
|
||||
DbgPrint("usb-stage: hcd=%02x:%02x.%u root-port=%u reset done portsc=%08x speed=%u\n",
|
||||
xhci->Bus, xhci->Dev, xhci->Fn, port, portsc, speed);
|
||||
if (!(portsc & XHCI_PORTSC_CCS) || !(portsc & XHCI_PORTSC_PED))
|
||||
return NTLSTATUS_DEVICE_ERROR;
|
||||
|
||||
@@ -1674,10 +1818,7 @@ KSTATUS XhciConfigurePipeEndpoint(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev, USB_P
|
||||
icc[0] = 0;
|
||||
icc[1] = (1u << 0) | (1u << dci);
|
||||
|
||||
slot[0] = XHCI_CONTEXT_ENTRIES(dci) | XHCI_SLOT_SPEED(dev->Speed);
|
||||
slot[1] = XHCI_SLOT_ROOT_PORT(dev->Port);
|
||||
slot[2] = 0;
|
||||
slot[3] = 0;
|
||||
XhciBuildConfigureSlotContext(xhci, dev, slot, dci);
|
||||
|
||||
uint16_t maxPacket = pipe->MaxPacketSize & 0x07FFu;
|
||||
if (maxPacket == 0) maxPacket = 8u;
|
||||
@@ -1692,7 +1833,7 @@ KSTATUS XhciConfigurePipeEndpoint(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev, USB_P
|
||||
} else if (pipe->Type == UsbPipeBulk) {
|
||||
endpointType = XHCI_EP_TYPE_BULK_OUT;
|
||||
} else {
|
||||
xhci->Ddk->Dma->FreeCommonBuffer(ctx->RingDma);
|
||||
IoDmaFreeCommonBuffer(ctx->RingDma);
|
||||
MmFreePool(ctx);
|
||||
return NTLSTATUS_NOT_SUPPORTED;
|
||||
}
|
||||
@@ -1715,7 +1856,7 @@ KSTATUS XhciConfigurePipeEndpoint(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev, USB_P
|
||||
if (!KSUCCESS(st) || result.CompletionCode != XHCI_CC_SUCCESS) {
|
||||
DbgPrint("usb: Configure pipe endpoint failed slot=%u ep=%u type=%u st=0x%x cc=%u\n",
|
||||
dev->SlotId, dci, pipe->Type, st, result.CompletionCode);
|
||||
xhci->Ddk->Dma->FreeCommonBuffer(ctx->RingDma);
|
||||
IoDmaFreeCommonBuffer(ctx->RingDma);
|
||||
MmFreePool(ctx);
|
||||
return KSUCCESS(st) ? NTLSTATUS_DEVICE_ERROR : st;
|
||||
}
|
||||
@@ -1740,8 +1881,15 @@ static KSTATUS XhciHcdSubmitInterruptUrb(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev
|
||||
XHCI_PIPE_CONTEXT* ctx = (XHCI_PIPE_CONTEXT*)urb->Pipe->HcdPipeContext;
|
||||
if (!ctx || !ctx->Configured)
|
||||
return NTLSTATUS_DEVICE_ERROR;
|
||||
if (ctx->ActiveTransfer)
|
||||
uintptr_t flags = KeAcquireSpinlock(&xhci->EventLock);
|
||||
if (ctx->ActiveTransfer) {
|
||||
KeReleaseSpinlock(&xhci->EventLock, flags);
|
||||
return NTLSTATUS_QUEUE_FULL;
|
||||
}
|
||||
if (urb->Pipe->Halted) {
|
||||
KeReleaseSpinlock(&xhci->EventLock, flags);
|
||||
return NTLSTATUS_OBJECT_DEAD;
|
||||
}
|
||||
|
||||
uint64_t trbDeviceAddress = 0;
|
||||
st = XhciRingAppend(ctx->Ring,
|
||||
@@ -1755,7 +1903,10 @@ static KSTATUS XhciHcdSubmitInterruptUrb(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev
|
||||
XHCI_TRB_ISP |
|
||||
XHCI_TRB_IOC,
|
||||
&trbDeviceAddress);
|
||||
if (!KSUCCESS(st)) return st;
|
||||
if (!KSUCCESS(st)) {
|
||||
KeReleaseSpinlock(&xhci->EventLock, flags);
|
||||
return st;
|
||||
}
|
||||
|
||||
ctx->ActiveTransfer = true;
|
||||
ctx->ActiveTrbDeviceAddress = trbDeviceAddress;
|
||||
@@ -1768,58 +1919,51 @@ static KSTATUS XhciHcdSubmitInterruptUrb(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev
|
||||
|
||||
XhciBarrier();
|
||||
XhciWrite32(xhci->Doorbells, (uint32_t)dev->SlotId * 4u, ctx->EndpointId);
|
||||
KeReleaseSpinlock(&xhci->EventLock, flags);
|
||||
return NTLSTATUS_PENDING;
|
||||
}
|
||||
|
||||
void XhciHandleAsyncTransferEvent(XHCI_CONTROLLER* xhci, uint64_t trbDeviceAddress,
|
||||
uint32_t completionCode, uint8_t slotId,
|
||||
uint8_t endpointId, uint32_t residue) {
|
||||
if (!xhci || slotId == 0 || slotId > XHCI_MAX_TRACKED_SLOTS || endpointId >= 32)
|
||||
return;
|
||||
// Completes ownership of an interrupt URB before its callback is dispatched.
|
||||
static bool XhciClaimAsyncCompletion(XHCI_CONTROLLER* xhci, uint64_t trbDeviceAddress,
|
||||
uint32_t completionCode, uint8_t slotId,
|
||||
uint8_t endpointId, uint32_t residue,
|
||||
XHCI_ASYNC_COMPLETION* completed) {
|
||||
if (!xhci || !completed || slotId == 0 || slotId > XHCI_MAX_TRACKED_SLOTS || endpointId >= 32)
|
||||
return false;
|
||||
|
||||
XHCI_DEVICE* dev = &xhci->Devices[slotId];
|
||||
USB_PIPE* pipe = dev->EndpointPipes[endpointId];
|
||||
if (!pipe || !pipe->HcdPipeContext)
|
||||
return;
|
||||
return false;
|
||||
|
||||
XHCI_PIPE_CONTEXT* ctx = (XHCI_PIPE_CONTEXT*)pipe->HcdPipeContext;
|
||||
if (!ctx->ActiveTransfer || ctx->ActiveTrbDeviceAddress != trbDeviceAddress)
|
||||
return;
|
||||
return false;
|
||||
|
||||
USB_URB* urb = ctx->ActiveUrb;
|
||||
ctx->ActiveTransfer = false;
|
||||
ctx->ActiveTrbDeviceAddress = 0;
|
||||
ctx->ActiveUrb = (USB_URB*)0;
|
||||
|
||||
if (!urb)
|
||||
return;
|
||||
return false;
|
||||
|
||||
KSTATUS st = NTLSTATUS_SUCCESS;
|
||||
if (urb->Cancelled) {
|
||||
if (urb->Cancelled)
|
||||
st = NTLSTATUS_CANCELLED;
|
||||
} else if (completionCode != XHCI_CC_SUCCESS &&
|
||||
completionCode != XHCI_CC_SHORT_PACKET) {
|
||||
else if (completionCode != XHCI_CC_SUCCESS && completionCode != XHCI_CC_SHORT_PACKET)
|
||||
st = NTLSTATUS_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
uint32_t actual = 0;
|
||||
if (KSUCCESS(st)) {
|
||||
actual = (residue <= urb->Length) ? (urb->Length - residue) : 0;
|
||||
}
|
||||
|
||||
uint32_t actual = KSUCCESS(st) && residue <= urb->Length ? urb->Length - residue : 0;
|
||||
urb->Pending = false;
|
||||
urb->Status = st;
|
||||
urb->ActualLength = actual;
|
||||
urb->HcdCompletionCode = completionCode;
|
||||
|
||||
IRP* completeIrp = urb->Irp;
|
||||
completed->Urb = urb;
|
||||
completed->Irp = urb->Irp;
|
||||
completed->Status = st;
|
||||
completed->ActualLength = actual;
|
||||
urb->Irp = (IRP*)0;
|
||||
|
||||
if (urb->Complete)
|
||||
urb->Complete(urb, urb->CompleteContext);
|
||||
|
||||
if (completeIrp)
|
||||
IoCompleteRequest(completeIrp, st, KSUCCESS(st) ? actual : 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Implements the hcd Submit Bulk Urb operation.
|
||||
@@ -1905,13 +2049,59 @@ static KSTATUS XhciHcdSubmitUrb(USB_HCD* hcd, USB_URB* urb) {
|
||||
return st;
|
||||
}
|
||||
|
||||
// Implements the hcd Cancel Urb operation.
|
||||
static KSTATUS XhciHcdCancelUrb(USB_HCD* hcd, USB_URB* urb) {
|
||||
(void)hcd;
|
||||
if (!urb) return NTLSTATUS_INVALID_PARAMETER;
|
||||
if (!hcd || !urb || !urb->Device || !urb->Pipe)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
XHCI_CONTROLLER* xhci = (XHCI_CONTROLLER*)hcd->ControllerContext;
|
||||
XHCI_DEVICE* dev = (XHCI_DEVICE*)urb->Device->HcdDeviceContext;
|
||||
if (!xhci || !dev || dev->SlotId == 0 || !urb->Pipe->HcdPipeContext)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
XHCI_PIPE_CONTEXT* ctx = (XHCI_PIPE_CONTEXT*)urb->Pipe->HcdPipeContext;
|
||||
uintptr_t flags = KeAcquireSpinlock(&xhci->EventLock);
|
||||
urb->Cancelled = true;
|
||||
if (!urb->Pending)
|
||||
urb->Status = NTLSTATUS_CANCELLED;
|
||||
bool active = ctx->ActiveTransfer && ctx->ActiveUrb == urb;
|
||||
KeReleaseSpinlock(&xhci->EventLock, flags);
|
||||
if (!active)
|
||||
return NTLSTATUS_SUCCESS;
|
||||
|
||||
XHCI_COMMAND_RESULT result;
|
||||
memset(&result, 0, sizeof(result));
|
||||
KSTATUS st = XhciSubmitCommand(xhci, 0, 0,
|
||||
(XHCI_TRB_TYPE_STOP_ENDPOINT << XHCI_TRB_TYPE_SHIFT) |
|
||||
((uint32_t)ctx->EndpointId << 16) |
|
||||
((uint32_t)dev->SlotId << 24),
|
||||
&result);
|
||||
|
||||
flags = KeAcquireSpinlock(&xhci->EventLock);
|
||||
if (!ctx->ActiveTransfer || ctx->ActiveUrb != urb) {
|
||||
KeReleaseSpinlock(&xhci->EventLock, flags);
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
if (!KSUCCESS(st) || result.CompletionCode != XHCI_CC_SUCCESS) {
|
||||
KeReleaseSpinlock(&xhci->EventLock, flags);
|
||||
DbgPrint("usb: xHCI STOP_ENDPOINT failed slot=%u ep=%u st=0x%x cc=%u\n",
|
||||
dev->SlotId, ctx->EndpointId, st, result.CompletionCode);
|
||||
return KSUCCESS(st) ? NTLSTATUS_DEVICE_ERROR : st;
|
||||
}
|
||||
|
||||
ctx->ActiveTransfer = false;
|
||||
ctx->ActiveTrbDeviceAddress = 0;
|
||||
ctx->ActiveUrb = (USB_URB*)0;
|
||||
urb->Pipe->Halted = true;
|
||||
urb->Pending = false;
|
||||
urb->Status = NTLSTATUS_CANCELLED;
|
||||
urb->ActualLength = 0;
|
||||
urb->HcdCompletionCode = result.CompletionCode;
|
||||
|
||||
IRP* completeIrp = urb->Irp;
|
||||
urb->Irp = (IRP*)0;
|
||||
KeReleaseSpinlock(&xhci->EventLock, flags);
|
||||
if (urb->Complete)
|
||||
urb->Complete(urb, urb->CompleteContext);
|
||||
if (completeIrp)
|
||||
IoCompleteRequest(completeIrp, NTLSTATUS_CANCELLED, 0);
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -1933,8 +2123,7 @@ static void XhciHcdDestroyPipe(USB_HCD* hcd, USB_PIPE* pipe) {
|
||||
if (!pipe || !pipe->HcdPipeContext)
|
||||
return;
|
||||
XHCI_PIPE_CONTEXT* ctx = (XHCI_PIPE_CONTEXT*)pipe->HcdPipeContext;
|
||||
if (gUsbDdk && gUsbDdk->Dma)
|
||||
gUsbDdk->Dma->FreeCommonBuffer(ctx->RingDma);
|
||||
IoDmaFreeCommonBuffer(ctx->RingDma);
|
||||
ctx->RingDma = (IO_DMA_BUFFER*)0;
|
||||
ctx->Ring = (XHCI_TRB*)0;
|
||||
pipe->HcdPipeContext = (void*)0;
|
||||
@@ -2109,7 +2298,7 @@ KSTATUS XhciStartController(XHCI_CONTROLLER* xhci) {
|
||||
dmaRequirements.Boundary = 0x10000u;
|
||||
dmaRequirements.MaximumSegmentLength = 0x10000u;
|
||||
dmaRequirements.MaximumSegments = XHCI_PIPE_RING_TRBS - 1u;
|
||||
st = xhci->Ddk->Dma->GetAdapter(xhci->Fdo,
|
||||
st = IoDmaGetAdapter(xhci->Fdo,
|
||||
&dmaRequirements,
|
||||
&xhci->DmaAdapter);
|
||||
if (!KSUCCESS(st)) {
|
||||
@@ -2165,7 +2354,7 @@ KSTATUS XhciStartController(XHCI_CONTROLLER* xhci) {
|
||||
if (!KSUCCESS(st))
|
||||
DbgPrint("usb: root hub async scan scheduling failed st=0x%x\n", st);
|
||||
else
|
||||
DbgPrint("usb: root hub initial scan queued asynchronously\n");
|
||||
DbgPrint("usb: root hub initial scan requested\n");
|
||||
|
||||
DbgPrint("usb: xHCI host controller initialised; HID boot input active=%u mass-storage will arrive through hotplug\n",
|
||||
xhci->HidDeviceCount);
|
||||
@@ -2247,7 +2436,7 @@ KSTATUS XhciAddDevice(DRIVER_OBJECT* driver, DEVICE_OBJECT* physicalDeviceObject
|
||||
XHCI_CONTROLLER* xhci = (XHCI_CONTROLLER*)fdo->DeviceExtension;
|
||||
memset(xhci, 0, sizeof(*xhci));
|
||||
xhci->Magic = XHCI_CONTROLLER_MAGIC;
|
||||
xhci->Ddk = gUsbDdk;
|
||||
|
||||
xhci->Fdo = fdo;
|
||||
xhci->Pdo = physicalDeviceObject;
|
||||
xhci->Irq = 0xFFu;
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
#include <ntlite/ddk/module.h>
|
||||
// VMware SVGA-II PCI display provider. This file owns adapter setup, FIFO command submission, framebuffer presentation, and cursor integration.
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "vmwaresvga.h"
|
||||
|
||||
const NTL_DDK* gVmwSvgaDdk;
|
||||
|
||||
static DRIVER_OBJECT gVmwSvgaDriver;
|
||||
|
||||
static const IO_DEVICE_ID gVmwSvgaIds[] = {
|
||||
@@ -1500,8 +1499,10 @@ static KSTATUS VmwSvgaStartDevice(VMWSVGA_DEVICE* v) {
|
||||
|
||||
st = KeDisplaySetPrimary(&v->DisplayDevice);
|
||||
KeDisplaySuspendConsole(false);
|
||||
if (!KSUCCESS(st))
|
||||
if (!KSUCCESS(st)) {
|
||||
(void)KeDisplayUnregisterDevice(&v->DisplayDevice);
|
||||
return st;
|
||||
}
|
||||
|
||||
v->Started = true;
|
||||
v->Fdo->PnpState = DevicePnpStarted;
|
||||
@@ -1534,11 +1535,15 @@ KSTATUS VmwSvgaDispatchPnp(DEVICE_OBJECT* device, IRP* irp) {
|
||||
return NTLSTATUS_SUCCESS;
|
||||
|
||||
case IRP_MN_SURPRISE_REMOVAL:
|
||||
if (v->DisplayDevice.Ops)
|
||||
(void)KeDisplayUnregisterDevice(&v->DisplayDevice);
|
||||
v->SurpriseRemoved = true;
|
||||
device->PnpState = DevicePnpSurpriseRemoved;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
|
||||
case IRP_MN_REMOVE_DEVICE:
|
||||
if (v->DisplayDevice.Ops)
|
||||
(void)KeDisplayUnregisterDevice(&v->DisplayDevice);
|
||||
v->Removed = true;
|
||||
device->PnpState = DevicePnpDeleted;
|
||||
if (v->Backbuffer) {
|
||||
@@ -1588,23 +1593,18 @@ KSTATUS VmwSvgaAddDevice(DRIVER_OBJECT* driver, DEVICE_OBJECT* physicalDeviceObj
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Implements the driver Entry operation.
|
||||
KSTATUS DriverEntry(const NTL_DDK* ddk, NTL_DRIVER_INIT* init) {
|
||||
(void)init;
|
||||
if (!ddk)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
static KSTATUS DriverInitialize(NTL_MODULE* module) {
|
||||
(void)module;
|
||||
|
||||
DbgPrint("VMWSVGA: driver loading...\n");
|
||||
gVmwSvgaDdk = ddk;
|
||||
|
||||
memset(&gVmwSvgaDriver, 0, sizeof(gVmwSvgaDriver));
|
||||
IoInitializeDriverObject(&gVmwSvgaDriver, SYS_SV_LITERAL("vmwaresvga"));
|
||||
gVmwSvgaDriver.IdTable = &gVmwSvgaIdTable;
|
||||
gVmwSvgaDriver.DriverAddDevice = VmwSvgaAddDevice;
|
||||
gVmwSvgaDriver.MajorFunction[IRP_MJ_PNP] = VmwSvgaDispatchPnp;
|
||||
|
||||
KSTATUS st = IoRegisterDriverObject(&gVmwSvgaDriver);
|
||||
if (!KSUCCESS(st))
|
||||
DbgPrint("VMWSVGA: IoRegisterDriverObject failed: 0x%x\n", st);
|
||||
return st;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static DRIVER_OBJECT* const gModuleIoDrivers[] = { &gVmwSvgaDriver };
|
||||
NTL_DECLARE_IO_MODULE(DriverInitialize, gModuleIoDrivers);
|
||||
@@ -210,7 +210,5 @@ typedef struct VMWSVGA_DEVICE {
|
||||
DISPLAY_DEVICE DisplayDevice;
|
||||
} VMWSVGA_DEVICE;
|
||||
|
||||
extern const NTL_DDK* gVmwSvgaDdk;
|
||||
|
||||
KSTATUS VmwSvgaAddDevice(DRIVER_OBJECT* driver, DEVICE_OBJECT* physicalDeviceObject);
|
||||
KSTATUS VmwSvgaDispatchPnp(DEVICE_OBJECT* device, IRP* irp);
|
||||
+89
-16
@@ -1,17 +1,90 @@
|
||||
[boot]
|
||||
pci.sys
|
||||
hda.sys
|
||||
ps2.sys
|
||||
cereal.sys
|
||||
ahci.sys
|
||||
ata.sys
|
||||
usb.sys
|
||||
fat.sys
|
||||
ntfs.sys
|
||||
ext4.sys
|
||||
; drivers.ini
|
||||
|
||||
[runtime]
|
||||
lwip.sys
|
||||
vmwaresvga.sys
|
||||
rtl8139.sys
|
||||
rtl816x.sys
|
||||
[class.bus]
|
||||
|
||||
[class.storage]
|
||||
requires=bus
|
||||
|
||||
[class.filesystem]
|
||||
after=storage
|
||||
|
||||
[class.usb-host]
|
||||
requires=bus
|
||||
|
||||
[class.network-adapter]
|
||||
requires=bus
|
||||
|
||||
[class.network-stack]
|
||||
after=network-adapter
|
||||
|
||||
[driver.pci]
|
||||
image=pci.sys
|
||||
start=boot
|
||||
class=bus
|
||||
critical=true
|
||||
|
||||
[driver.hda]
|
||||
image=hda.sys
|
||||
start=boot
|
||||
requires=pci
|
||||
|
||||
[driver.ps2]
|
||||
image=ps2.sys
|
||||
start=boot
|
||||
|
||||
[driver.cereal]
|
||||
image=cereal.sys
|
||||
start=boot
|
||||
|
||||
[driver.ahci]
|
||||
image=ahci.sys
|
||||
start=boot
|
||||
class=storage
|
||||
|
||||
[driver.ata]
|
||||
image=ata.sys
|
||||
start=boot
|
||||
class=storage
|
||||
|
||||
[driver.usb]
|
||||
image=usb.sys
|
||||
start=boot
|
||||
class=usb-host
|
||||
|
||||
[driver.fat]
|
||||
image=fat.sys
|
||||
start=boot
|
||||
class=filesystem
|
||||
after=usb
|
||||
|
||||
[driver.ntfs]
|
||||
image=ntfs.sys
|
||||
start=boot
|
||||
class=filesystem
|
||||
after=usb
|
||||
|
||||
[driver.ext4]
|
||||
image=ext4.sys
|
||||
start=boot
|
||||
class=filesystem
|
||||
after=usb
|
||||
|
||||
[driver.lwip]
|
||||
image=lwip.sys
|
||||
start=runtime
|
||||
class=network-stack
|
||||
|
||||
[driver.vmwaresvga]
|
||||
image=vmwaresvga.sys
|
||||
start=runtime
|
||||
requires=pci
|
||||
|
||||
[driver.rtl8139]
|
||||
image=rtl8139.sys
|
||||
start=runtime
|
||||
class=network-adapter
|
||||
|
||||
[driver.rtl816x]
|
||||
image=rtl816x.sys
|
||||
start=runtime
|
||||
class=network-adapter
|
||||
@@ -1,3 +1,4 @@
|
||||
#include <ntlite/ddk/module.h>
|
||||
/*
|
||||
* NTLite ext2/ext3/ext4 read/write filesystem driver backed by lwext4.
|
||||
*
|
||||
@@ -1154,12 +1155,9 @@ static void Ext4PartitionChange(const IO_PARTITION_INFO* partition,
|
||||
}
|
||||
}
|
||||
|
||||
KSTATUS DriverEntry(const NTL_DDK* ddk, NTL_DRIVER_INIT* init) {
|
||||
static KSTATUS DriverInitialize(NTL_MODULE* module) {
|
||||
(void)module;
|
||||
DbgPrint("EXT4: Init\n");
|
||||
if (!ddk)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
if (init)
|
||||
init->Name = "ext4";
|
||||
|
||||
Ext4ListInit(&gExt4Mounts);
|
||||
gExt4MountedCount = 0;
|
||||
@@ -1177,3 +1175,5 @@ KSTATUS DriverEntry(const NTL_DDK* ddk, NTL_DRIVER_INIT* init) {
|
||||
DbgPrint("EXT4: Init complete; mounted %u volume(s).\n", gExt4MountedCount);
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
NTL_DECLARE_MODULE(DriverInitialize);
|
||||
@@ -1,3 +1,4 @@
|
||||
#include <ntlite/ddk/module.h>
|
||||
// FAT mount discovery, BPB validation, and driver lifetime.
|
||||
|
||||
#include <stdbool.h>
|
||||
@@ -275,11 +276,10 @@ static void FatPartitionChange(const IO_PARTITION_INFO* partition,
|
||||
}
|
||||
|
||||
// Initializes FAT and subscribes to the authoritative kernel partition stream.
|
||||
KSTATUS DriverEntry(const NTL_DDK* ddk, NTL_DRIVER_INIT* init) {
|
||||
static KSTATUS DriverInitialize(NTL_MODULE* module) {
|
||||
(void)module;
|
||||
DbgPrint("FAT: Init\n");
|
||||
if (!ddk)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
(void)init;
|
||||
|
||||
|
||||
FatCacheInvalidate();
|
||||
LIST_INIT(&gFatMounts);
|
||||
@@ -297,3 +297,5 @@ KSTATUS DriverEntry(const NTL_DDK* ddk, NTL_DRIVER_INIT* init) {
|
||||
DbgPrint("FAT: Init complete; mounted %u volume(s).\n", gMountedCount);
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
NTL_DECLARE_MODULE(DriverInitialize);
|
||||
@@ -1,3 +1,4 @@
|
||||
#include <ntlite/ddk/module.h>
|
||||
// Native read-only NTFS filesystem driver for NTLite.
|
||||
// Portions are based on ntfs-efi by Mark Harmstone (2023), GPLv2-or-later.
|
||||
// The port removes UEFI protocol/header dependencies and targets NTLite's IO_VOLUME_PROVIDER ABI.
|
||||
@@ -1597,11 +1598,10 @@ static void NtfsPartitionChange(const IO_PARTITION_INFO* partition,
|
||||
}
|
||||
|
||||
// Initializes NTFS and subscribes to the authoritative kernel partition stream.
|
||||
KSTATUS DriverEntry(const NTL_DDK* ddk, NTL_DRIVER_INIT* init) {
|
||||
static KSTATUS DriverInitialize(NTL_MODULE* module) {
|
||||
(void)module;
|
||||
DbgPrint("NTFS: Init\n");
|
||||
if (!ddk)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
(void)init;
|
||||
|
||||
|
||||
LIST_INIT(&gNtfsMounts);
|
||||
gMountedCount = 0;
|
||||
@@ -1618,3 +1618,5 @@ KSTATUS DriverEntry(const NTL_DDK* ddk, NTL_DRIVER_INIT* init) {
|
||||
DbgPrint("NTFS: Init complete; mounted %u volume(s).\n", gMountedCount);
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
NTL_DECLARE_MODULE(DriverInitialize);
|
||||
@@ -1,3 +1,4 @@
|
||||
#include <ntlite/ddk/module.h>
|
||||
/* NTFS-3G filesystem provider for NTLite. */
|
||||
|
||||
#include "config.h"
|
||||
@@ -1137,12 +1138,9 @@ static void Ntfs3gPartitionChange(const IO_PARTITION_INFO* partition,
|
||||
}
|
||||
}
|
||||
|
||||
KSTATUS DriverEntry(const NTL_DDK* ddk, NTL_DRIVER_INIT* init) {
|
||||
static KSTATUS DriverInitialize(NTL_MODULE* module) {
|
||||
(void)module;
|
||||
DbgPrint("NTFS3G: Init (libntfs-3g replacement driver)\n");
|
||||
if (!ddk)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
if (init)
|
||||
init->Name = "ntfs";
|
||||
Ntfs3gListInit(&gNtfs3gMounts);
|
||||
gNtfs3gMountedCount = 0;
|
||||
Ntfs3gEnsureMutexes();
|
||||
@@ -1152,3 +1150,5 @@ KSTATUS DriverEntry(const NTL_DDK* ddk, NTL_DRIVER_INIT* init) {
|
||||
DbgPrint("NTFS3G: Init complete; mounted %u volume(s).\n", gNtfs3gMountedCount);
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
NTL_DECLARE_MODULE(DriverInitialize);
|
||||
@@ -1,3 +1,4 @@
|
||||
#include <ntlite/ddk/module.h>
|
||||
// PS/2 input driver package entry point. This file registers keyboard and mouse devices.
|
||||
|
||||
#include "ps2.h"
|
||||
@@ -14,6 +15,14 @@ static volatile int gMousePacketIndex;
|
||||
static PS2_MOUSE_PACKET gMouseDpcQueue[MOUSE_DPC_QUEUE_SIZE];
|
||||
static volatile int gMouseDpcHead;
|
||||
static volatile int gMouseDpcTail;
|
||||
static volatile uint32_t gKeyboardIrqCount;
|
||||
static volatile uint32_t gKeyboardByteCount;
|
||||
static volatile uint32_t gKeyboardEventCount;
|
||||
static volatile uint32_t gMouseIrqCount;
|
||||
static volatile uint32_t gMouseByteCount;
|
||||
static volatile uint32_t gMousePacketCount;
|
||||
static volatile uint32_t gKeyboardForeignIrqCount;
|
||||
static volatile uint32_t gMouseForeignIrqCount;
|
||||
|
||||
// Implements the ps2 Set1 To Key operation.
|
||||
static INPUT_KEY Ps2Set1ToKey(uint8_t make, bool extended) {
|
||||
@@ -137,6 +146,10 @@ static void Ps2KeyboardDpcRoutine(KDPC* dpc, void* context, void* systemArgument
|
||||
INPUT_KEY_EVENT event = gKeyboardDpcQueue[gKeyboardDpcHead];
|
||||
gKeyboardDpcHead = (gKeyboardDpcHead + 1) & (int)KEYBOARD_DPC_QUEUE_MASK;
|
||||
KeInputSubmitKey(&event);
|
||||
uint32_t n = ++gKeyboardEventCount;
|
||||
if ((n & (n - 1u)) == 0u)
|
||||
DbgPrint("ps2: keyboard progress irq=%u raw=%u events=%u foreign=%u\n",
|
||||
gKeyboardIrqCount, gKeyboardByteCount, n, gKeyboardForeignIrqCount);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,7 +175,15 @@ static void Ps2QueueKeyboardEvent(INPUT_KEY key, bool pressed) {
|
||||
|
||||
// Implements the ps2 Keyboard Isr operation.
|
||||
static void Ps2KeyboardIsr(void) {
|
||||
++gKeyboardIrqCount;
|
||||
uint8_t status = __inbyte(PS2_STAT);
|
||||
// The 8042 output buffer is shared; never consume AUX bytes as scancodes.
|
||||
if (!(status & PS2_STAT_OUT_FULL) || (status & PS2_STAT_AUX_DATA)) {
|
||||
++gKeyboardForeignIrqCount;
|
||||
return;
|
||||
}
|
||||
uint8_t sc = __inbyte(PS2_DATA);
|
||||
++gKeyboardByteCount;
|
||||
if (sc == SC1_EXT_PREFIX) {
|
||||
gKeyboardNextExtended = true;
|
||||
return;
|
||||
@@ -197,12 +218,23 @@ static void Ps2MouseDpcRoutine(KDPC* dpc, void* context, void* systemArgument1,
|
||||
.buttons = packet.buttons,
|
||||
};
|
||||
KeInputSubmitMouse(&event);
|
||||
uint32_t n = ++gMousePacketCount;
|
||||
if ((n & (n - 1u)) == 0u)
|
||||
DbgPrint("ps2: mouse progress irq=%u raw=%u packets=%u foreign=%u\n",
|
||||
gMouseIrqCount, gMouseByteCount, n, gMouseForeignIrqCount);
|
||||
}
|
||||
}
|
||||
|
||||
// Implements the ps2 Mouse Isr operation.
|
||||
static void Ps2MouseIsr(void) {
|
||||
++gMouseIrqCount;
|
||||
uint8_t status = __inbyte(PS2_STAT);
|
||||
if (!(status & PS2_STAT_OUT_FULL) || !(status & PS2_STAT_AUX_DATA)) {
|
||||
++gMouseForeignIrqCount;
|
||||
return;
|
||||
}
|
||||
uint8_t data = __inbyte(PS2_DATA);
|
||||
++gMouseByteCount;
|
||||
|
||||
if (gMousePacketIndex == 0 && !(data & 0x08u))
|
||||
return;
|
||||
@@ -281,10 +313,9 @@ static void Ps2MouseInitialize(void) {
|
||||
DbgPrint("ps2: mouse enable did not ACK\n");
|
||||
}
|
||||
|
||||
// Implements the driver Entry operation.
|
||||
KSTATUS DriverEntry(const NTL_DDK* ddk, NTL_DRIVER_INIT* init) {
|
||||
(void)ddk;
|
||||
(void)init;
|
||||
static KSTATUS DriverInitialize(NTL_MODULE* module) {
|
||||
(void)module;
|
||||
|
||||
|
||||
uint32_t keyboardId = 0, mouseId = 0;
|
||||
KeInputRegisterDevice("ps2-keyboard", 0, &keyboardId);
|
||||
@@ -308,3 +339,5 @@ KSTATUS DriverEntry(const NTL_DDK* ddk, NTL_DRIVER_INIT* init) {
|
||||
DbgPrint("ps2: initialized keyboard=%u mouse=%u\n", keyboardId, mouseId);
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
NTL_DECLARE_MODULE(DriverInitialize);
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#define PS2_STAT_OUT_FULL 0x01u
|
||||
#define PS2_STAT_IN_FULL 0x02u
|
||||
#define PS2_STAT_AUX_DATA 0x20u
|
||||
|
||||
#define PS2_IRQ_KEYBOARD 1u
|
||||
#define PS2_IRQ_MOUSE 12u
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include <intrin.h>
|
||||
|
||||
#include <ntlite/status.h>
|
||||
#include <ntlite/ddk/ddk.h>
|
||||
#include <ntlite/ddk/module.h>
|
||||
#include <ntlite/ddi/input_device.h>
|
||||
#include <ntlite/ddk/debug.h>
|
||||
#include <ntlite/ddk/dpc.h>
|
||||
@@ -339,10 +339,9 @@ static KSTATUS SerialConfigureCom1(void) {
|
||||
return NTLSTATUS_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
// DriverEntry registers COM1 as an input source.
|
||||
KSTATUS DriverEntry(const NTL_DDK* ddk, NTL_DRIVER_INIT* init) {
|
||||
(void)ddk;
|
||||
(void)init;
|
||||
// Initializes the optional COM1 input source.
|
||||
static KSTATUS SerialInitialize(NTL_MODULE* module) {
|
||||
(void)module;
|
||||
|
||||
if (!SerialProbeCom1()) {
|
||||
DbgPrint("serial: COM1 not present; input driver disabled\n");
|
||||
@@ -374,3 +373,5 @@ KSTATUS DriverEntry(const NTL_DDK* ddk, NTL_DRIVER_INIT* init) {
|
||||
DbgPrint("serial: initialized COM1 input device=%u irq=%u\n", deviceId, SERIAL_COM1_IRQ);
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
NTL_DECLARE_MODULE(SerialInitialize);
|
||||
@@ -20,6 +20,6 @@ Build notes:
|
||||
|
||||
Load order:
|
||||
|
||||
`drivers.ini` should load `lwip.sys` before NIC drivers so the provider is
|
||||
ready when the NIC registers. The kernel also supports provider-after-device
|
||||
registration, but provider-first keeps boot logs cleaner.
|
||||
`drivers.ini` v2 uses `class.network-stack` with `after=network-adapter`.
|
||||
NIC modules initialize first; lwIP attaches to already registered adapters.
|
||||
The stack also initializes normally when no physical NIC is present.
|
||||
@@ -1,3 +1,4 @@
|
||||
#include <ntlite/ddk/module.h>
|
||||
// lwIP stack provider glue. This file bridges the kernel network-device model into lwIP.
|
||||
|
||||
#include <ntlite/atomic.h>
|
||||
@@ -111,7 +112,6 @@ typedef struct LWIP_PROVIDER_SOCKET {
|
||||
|
||||
static LWIP_BINDING* gBindings[LWIP_MAX_NETIFS];
|
||||
static volatile int gLwipInitialized = 0;
|
||||
static const NTL_DDK* gLwipDdk;
|
||||
|
||||
static KSTATUS LwipProviderAttachDevice(NET_DEVICE* device);
|
||||
static KSTATUS LwipProviderReceiveFrame(NET_DEVICE* device, const void* frame, size_t length);
|
||||
@@ -770,21 +770,15 @@ static void LwipInitDone(void* arg) {
|
||||
|
||||
// Delays the current provider thread through the DDK scheduler table.
|
||||
static KSTATUS LwipDelayThread(uint64_t milliseconds) {
|
||||
if (!gLwipDdk || !gLwipDdk->Scheduler || !gLwipDdk->Scheduler->DelayThread)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
return gLwipDdk->Scheduler->DelayThread(milliseconds);
|
||||
return KeDelayThread(milliseconds);
|
||||
}
|
||||
|
||||
// Implements the stack Initialize operation.
|
||||
static KSTATUS LwipStackInitialize(const NTL_DDK* ddk) {
|
||||
static KSTATUS LwipStackInitialize(void) {
|
||||
if (gLwipInitialized)
|
||||
return NTLSTATUS_SUCCESS;
|
||||
if (!ddk || !ddk->Timer || !ddk->Timer->QueryTicks ||
|
||||
!ddk->Scheduler || !ddk->Scheduler->DelayThread)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
gLwipDdk = ddk;
|
||||
LwipPortSetTimerQuery(ddk->Timer->QueryTicks);
|
||||
LwipPortSetTimerQuery(KeQueryTicks);
|
||||
|
||||
SEMAPHORE* done = 0;
|
||||
KSTATUS st = KeSemaphoreCreate(0, 1, &done);
|
||||
@@ -1822,9 +1816,9 @@ static KSTATUS LwipDispatchInternalDeviceControl(DEVICE_OBJECT* device, IRP* irp
|
||||
return LwipDispatchSocketRequest(request);
|
||||
}
|
||||
|
||||
// Implements the driver Entry operation.
|
||||
KSTATUS DriverEntry(const NTL_DDK* ddk, NTL_DRIVER_INIT* init) {
|
||||
(void)init;
|
||||
static KSTATUS DriverInitialize(NTL_MODULE* module) {
|
||||
(void)module;
|
||||
|
||||
DbgPrint("lwIP: driver loading...\n");
|
||||
|
||||
LWIP_NETIF_CONFIG defaultConfig;
|
||||
@@ -1835,7 +1829,7 @@ KSTATUS DriverEntry(const NTL_DDK* ddk, NTL_DRIVER_INIT* init) {
|
||||
return st;
|
||||
}
|
||||
|
||||
st = LwipStackInitialize(ddk);
|
||||
st = LwipStackInitialize();
|
||||
if (!KSUCCESS(st)) {
|
||||
DbgPrint("lwIP: initialization failed: 0x%x\n", st);
|
||||
return st;
|
||||
@@ -1844,12 +1838,6 @@ KSTATUS DriverEntry(const NTL_DDK* ddk, NTL_DRIVER_INIT* init) {
|
||||
IoInitializeDriverObject(&gLwipDriver, SYS_SV_LITERAL("lwip-stack"));
|
||||
gLwipDriver.MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = LwipDispatchInternalDeviceControl;
|
||||
|
||||
st = IoRegisterDriverObject(&gLwipDriver);
|
||||
if (!KSUCCESS(st)) {
|
||||
DbgPrint("lwIP: driver object registration failed: 0x%x\n", st);
|
||||
return st;
|
||||
}
|
||||
|
||||
st = IoCreateDevice(&gLwipDriver, 0, &gLwipDevice);
|
||||
if (!KSUCCESS(st)) {
|
||||
DbgPrint("lwIP: stack device creation failed: 0x%x\n", st);
|
||||
@@ -1867,3 +1855,6 @@ KSTATUS DriverEntry(const NTL_DDK* ddk, NTL_DRIVER_INIT* init) {
|
||||
DbgPrint("lwIP: driver initialized\n");
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static DRIVER_OBJECT* const gModuleIoDrivers[] = { &gLwipDriver };
|
||||
NTL_DECLARE_IO_MODULE(DriverInitialize, gModuleIoDrivers);
|
||||
@@ -1,8 +1,8 @@
|
||||
#include <ntlite/ddk/module.h>
|
||||
// Realtek RTL8139 network driver. This file owns PCI attachment, MMIO setup, RX/TX rings, and net-device registration.
|
||||
|
||||
#include "rtl8139.h"
|
||||
|
||||
const NTL_DDK* gRtl8139Ddk;
|
||||
static DRIVER_OBJECT gRtl8139Driver;
|
||||
static RTL8139_DEVICE* gRtl8139Devices[RTL8139_MAX_DEVICES];
|
||||
static uint32_t gRtl8139DeviceCount;
|
||||
@@ -69,8 +69,7 @@ static KSTATUS Rtl8139Reset(RTL8139_DEVICE* r) {
|
||||
for (uint32_t i = 0; i < 1000; i++) {
|
||||
if ((Rtl8139Read8(r, RTL8139_REG_CMD) & RTL8139_CMD_RST) == 0)
|
||||
return NTLSTATUS_SUCCESS;
|
||||
if (gRtl8139Ddk->Timer->Stall)
|
||||
gRtl8139Ddk->Timer->Stall(1000);
|
||||
KeStallExecution(1000);
|
||||
}
|
||||
return NTLSTATUS_TIMEOUT;
|
||||
}
|
||||
@@ -100,17 +99,17 @@ static void Rtl8139StopDma(RTL8139_DEVICE* r) {
|
||||
|
||||
// Releases the common buffers and adapter owned by one RTL8139 device.
|
||||
static void Rtl8139ReleaseDma(RTL8139_DEVICE* r) {
|
||||
if (!r || !gRtl8139Ddk || !gRtl8139Ddk->Dma)
|
||||
if (!r)
|
||||
return;
|
||||
gRtl8139Ddk->Dma->FreeCommonBuffer(r->TxDma);
|
||||
gRtl8139Ddk->Dma->FreeCommonBuffer(r->RxDma);
|
||||
IoDmaFreeCommonBuffer(r->TxDma);
|
||||
IoDmaFreeCommonBuffer(r->RxDma);
|
||||
r->TxDma = (IO_DMA_BUFFER*)0;
|
||||
r->RxDma = (IO_DMA_BUFFER*)0;
|
||||
r->TxBuffers = (uint8_t*)0;
|
||||
r->RxBuffer = (uint8_t*)0;
|
||||
r->TxDeviceAddress = 0;
|
||||
r->RxDeviceAddress = 0;
|
||||
gRtl8139Ddk->Dma->PutAdapter(r->DmaAdapter);
|
||||
IoDmaPutAdapter(r->DmaAdapter);
|
||||
r->DmaAdapter = (IO_DMA_ADAPTER*)0;
|
||||
}
|
||||
|
||||
@@ -123,13 +122,13 @@ static KSTATUS Rtl8139AllocateDma(RTL8139_DEVICE* r) {
|
||||
requirements.MaximumSegmentLength = RTL8139_TX_BUF_SIZE;
|
||||
requirements.MaximumSegments = 1;
|
||||
|
||||
KSTATUS status = gRtl8139Ddk->Dma->GetAdapter(r->Fdo,
|
||||
KSTATUS status = IoDmaGetAdapter(r->Fdo,
|
||||
&requirements,
|
||||
&r->DmaAdapter);
|
||||
if (!KSUCCESS(status))
|
||||
return status;
|
||||
|
||||
status = gRtl8139Ddk->Dma->AllocateCommonBuffer(r->DmaAdapter,
|
||||
status = IoDmaAllocateCommonBuffer(r->DmaAdapter,
|
||||
RTL8139_RX_ALLOC_SIZE,
|
||||
PAGE_SIZE,
|
||||
&r->RxDma);
|
||||
@@ -138,7 +137,7 @@ static KSTATUS Rtl8139AllocateDma(RTL8139_DEVICE* r) {
|
||||
return status;
|
||||
}
|
||||
|
||||
status = gRtl8139Ddk->Dma->AllocateCommonBuffer(r->DmaAdapter,
|
||||
status = IoDmaAllocateCommonBuffer(r->DmaAdapter,
|
||||
RTL8139_TX_SLOTS * RTL8139_TX_BUF_SIZE,
|
||||
PAGE_SIZE,
|
||||
&r->TxDma);
|
||||
@@ -147,10 +146,10 @@ static KSTATUS Rtl8139AllocateDma(RTL8139_DEVICE* r) {
|
||||
return status;
|
||||
}
|
||||
|
||||
r->RxBuffer = (uint8_t*)gRtl8139Ddk->Dma->GetCommonBufferAddress(r->RxDma);
|
||||
r->TxBuffers = (uint8_t*)gRtl8139Ddk->Dma->GetCommonBufferAddress(r->TxDma);
|
||||
r->RxDeviceAddress = gRtl8139Ddk->Dma->GetCommonBufferDeviceAddress(r->RxDma);
|
||||
r->TxDeviceAddress = gRtl8139Ddk->Dma->GetCommonBufferDeviceAddress(r->TxDma);
|
||||
r->RxBuffer = (uint8_t*)IoDmaGetCommonBufferAddress(r->RxDma);
|
||||
r->TxBuffers = (uint8_t*)IoDmaGetCommonBufferAddress(r->TxDma);
|
||||
r->RxDeviceAddress = IoDmaGetCommonBufferDeviceAddress(r->RxDma);
|
||||
r->TxDeviceAddress = IoDmaGetCommonBufferDeviceAddress(r->TxDma);
|
||||
|
||||
DbgPrint("RTL8139: rx=0x%016llX tx=0x%016llX\n",
|
||||
(unsigned long long)r->RxDeviceAddress,
|
||||
@@ -603,14 +602,11 @@ KSTATUS Rtl8139AddDevice(DRIVER_OBJECT* driver, DEVICE_OBJECT* physicalDeviceObj
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Implements the driver Entry operation.
|
||||
KSTATUS DriverEntry(const NTL_DDK* ddk, NTL_DRIVER_INIT* init) {
|
||||
(void)init;
|
||||
if (!ddk || !ddk->Dma || !ddk->Timer)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
static KSTATUS DriverInitialize(NTL_MODULE* module) {
|
||||
(void)module;
|
||||
|
||||
DbgPrint("RTL8139: driver loading...\n");
|
||||
gRtl8139Ddk = ddk;
|
||||
|
||||
gRtl8139DeviceCount = 0;
|
||||
|
||||
memset(&gRtl8139Driver, 0, sizeof(gRtl8139Driver));
|
||||
@@ -618,9 +614,8 @@ KSTATUS DriverEntry(const NTL_DDK* ddk, NTL_DRIVER_INIT* init) {
|
||||
gRtl8139Driver.IdTable = &gRtl8139IdTable;
|
||||
gRtl8139Driver.DriverAddDevice = Rtl8139AddDevice;
|
||||
gRtl8139Driver.MajorFunction[IRP_MJ_PNP] = Rtl8139DispatchPnp;
|
||||
|
||||
KSTATUS st = IoRegisterDriverObject(&gRtl8139Driver);
|
||||
if (!KSUCCESS(st))
|
||||
DbgPrint("RTL8139: IoRegisterDriverObject failed: 0x%x\n", st);
|
||||
return st;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static DRIVER_OBJECT* const gModuleIoDrivers[] = { &gRtl8139Driver };
|
||||
NTL_DECLARE_IO_MODULE(DriverInitialize, gModuleIoDrivers);
|
||||
@@ -133,7 +133,5 @@ typedef struct RTL8139_DEVICE {
|
||||
NET_DEVICE NetDevice;
|
||||
} RTL8139_DEVICE;
|
||||
|
||||
extern const NTL_DDK* gRtl8139Ddk;
|
||||
|
||||
KSTATUS Rtl8139AddDevice(DRIVER_OBJECT* driver, DEVICE_OBJECT* physicalDeviceObject);
|
||||
KSTATUS Rtl8139DispatchPnp(DEVICE_OBJECT* device, IRP* irp);
|
||||
@@ -1,8 +1,8 @@
|
||||
#include <ntlite/ddk/module.h>
|
||||
// Realtek RTL816x network driver. This file owns PCI attachment, descriptor rings, and net-device registration.
|
||||
|
||||
#include "rtl816x.h"
|
||||
|
||||
const NTL_DDK* gRtl816xDdk;
|
||||
static DRIVER_OBJECT gRtl816xDriver;
|
||||
static RTL816X_DEVICE* gRtl816xDevices[RTL816X_MAX_DEVICES];
|
||||
static uint32_t gRtl816xDeviceCount;
|
||||
@@ -70,8 +70,7 @@ static KSTATUS RtlReset(RTL816X_DEVICE* r) {
|
||||
for (uint32_t i = 0; i < 1000; i++) {
|
||||
if ((RtlRead8(r, RTL_REG_CHIP_CMD) & RTL_CMD_RESET) == 0)
|
||||
return NTLSTATUS_SUCCESS;
|
||||
if (gRtl816xDdk->Timer->Stall)
|
||||
gRtl816xDdk->Timer->Stall(1000);
|
||||
KeStallExecution(1000);
|
||||
}
|
||||
return NTLSTATUS_TIMEOUT;
|
||||
}
|
||||
@@ -101,12 +100,12 @@ static void RtlStopDma(RTL816X_DEVICE* r) {
|
||||
|
||||
// Releases the common buffers and adapter owned by one RTL816x device.
|
||||
static void RtlReleaseDma(RTL816X_DEVICE* r) {
|
||||
if (!r || !gRtl816xDdk || !gRtl816xDdk->Dma)
|
||||
if (!r)
|
||||
return;
|
||||
gRtl816xDdk->Dma->FreeCommonBuffer(r->TxBuffersDma);
|
||||
gRtl816xDdk->Dma->FreeCommonBuffer(r->RxBuffersDma);
|
||||
gRtl816xDdk->Dma->FreeCommonBuffer(r->TxRingDma);
|
||||
gRtl816xDdk->Dma->FreeCommonBuffer(r->RxRingDma);
|
||||
IoDmaFreeCommonBuffer(r->TxBuffersDma);
|
||||
IoDmaFreeCommonBuffer(r->RxBuffersDma);
|
||||
IoDmaFreeCommonBuffer(r->TxRingDma);
|
||||
IoDmaFreeCommonBuffer(r->RxRingDma);
|
||||
r->TxBuffersDma = (IO_DMA_BUFFER*)0;
|
||||
r->RxBuffersDma = (IO_DMA_BUFFER*)0;
|
||||
r->TxRingDma = (IO_DMA_BUFFER*)0;
|
||||
@@ -119,7 +118,7 @@ static void RtlReleaseDma(RTL816X_DEVICE* r) {
|
||||
r->RxBuffersDeviceAddress = 0;
|
||||
r->TxRingDeviceAddress = 0;
|
||||
r->RxRingDeviceAddress = 0;
|
||||
gRtl816xDdk->Dma->PutAdapter(r->DmaAdapter);
|
||||
IoDmaPutAdapter(r->DmaAdapter);
|
||||
r->DmaAdapter = (IO_DMA_ADAPTER*)0;
|
||||
}
|
||||
|
||||
@@ -132,41 +131,41 @@ static KSTATUS RtlAllocateDma(RTL816X_DEVICE* r) {
|
||||
requirements.MaximumSegmentLength = RTL_BUF_SIZE;
|
||||
requirements.MaximumSegments = RTL_TX_COUNT;
|
||||
|
||||
KSTATUS status = gRtl816xDdk->Dma->GetAdapter(r->Fdo,
|
||||
KSTATUS status = IoDmaGetAdapter(r->Fdo,
|
||||
&requirements,
|
||||
&r->DmaAdapter);
|
||||
if (!KSUCCESS(status))
|
||||
return status;
|
||||
|
||||
status = gRtl816xDdk->Dma->AllocateCommonBuffer(r->DmaAdapter,
|
||||
status = IoDmaAllocateCommonBuffer(r->DmaAdapter,
|
||||
PAGE_SIZE * RTL_RING_PAGES,
|
||||
PAGE_SIZE,
|
||||
&r->RxRingDma);
|
||||
if (!KSUCCESS(status)) goto fail;
|
||||
status = gRtl816xDdk->Dma->AllocateCommonBuffer(r->DmaAdapter,
|
||||
status = IoDmaAllocateCommonBuffer(r->DmaAdapter,
|
||||
PAGE_SIZE * RTL_RING_PAGES,
|
||||
PAGE_SIZE,
|
||||
&r->TxRingDma);
|
||||
if (!KSUCCESS(status)) goto fail;
|
||||
status = gRtl816xDdk->Dma->AllocateCommonBuffer(r->DmaAdapter,
|
||||
status = IoDmaAllocateCommonBuffer(r->DmaAdapter,
|
||||
PAGE_SIZE * RTL_RXBUF_PAGES,
|
||||
PAGE_SIZE,
|
||||
&r->RxBuffersDma);
|
||||
if (!KSUCCESS(status)) goto fail;
|
||||
status = gRtl816xDdk->Dma->AllocateCommonBuffer(r->DmaAdapter,
|
||||
status = IoDmaAllocateCommonBuffer(r->DmaAdapter,
|
||||
PAGE_SIZE * RTL_TXBUF_PAGES,
|
||||
PAGE_SIZE,
|
||||
&r->TxBuffersDma);
|
||||
if (!KSUCCESS(status)) goto fail;
|
||||
|
||||
r->RxRing = (RTL_DESC*)gRtl816xDdk->Dma->GetCommonBufferAddress(r->RxRingDma);
|
||||
r->TxRing = (RTL_DESC*)gRtl816xDdk->Dma->GetCommonBufferAddress(r->TxRingDma);
|
||||
r->RxBuffers = (uint8_t*)gRtl816xDdk->Dma->GetCommonBufferAddress(r->RxBuffersDma);
|
||||
r->TxBuffers = (uint8_t*)gRtl816xDdk->Dma->GetCommonBufferAddress(r->TxBuffersDma);
|
||||
r->RxRingDeviceAddress = gRtl816xDdk->Dma->GetCommonBufferDeviceAddress(r->RxRingDma);
|
||||
r->TxRingDeviceAddress = gRtl816xDdk->Dma->GetCommonBufferDeviceAddress(r->TxRingDma);
|
||||
r->RxBuffersDeviceAddress = gRtl816xDdk->Dma->GetCommonBufferDeviceAddress(r->RxBuffersDma);
|
||||
r->TxBuffersDeviceAddress = gRtl816xDdk->Dma->GetCommonBufferDeviceAddress(r->TxBuffersDma);
|
||||
r->RxRing = (RTL_DESC*)IoDmaGetCommonBufferAddress(r->RxRingDma);
|
||||
r->TxRing = (RTL_DESC*)IoDmaGetCommonBufferAddress(r->TxRingDma);
|
||||
r->RxBuffers = (uint8_t*)IoDmaGetCommonBufferAddress(r->RxBuffersDma);
|
||||
r->TxBuffers = (uint8_t*)IoDmaGetCommonBufferAddress(r->TxBuffersDma);
|
||||
r->RxRingDeviceAddress = IoDmaGetCommonBufferDeviceAddress(r->RxRingDma);
|
||||
r->TxRingDeviceAddress = IoDmaGetCommonBufferDeviceAddress(r->TxRingDma);
|
||||
r->RxBuffersDeviceAddress = IoDmaGetCommonBufferDeviceAddress(r->RxBuffersDma);
|
||||
r->TxBuffersDeviceAddress = IoDmaGetCommonBufferDeviceAddress(r->TxBuffersDma);
|
||||
|
||||
DbgPrint("RTL816X: rxRing=0x%016llX txRing=0x%016llX rxBuf=0x%016llX txBuf=0x%016llX\n",
|
||||
(unsigned long long)r->RxRingDeviceAddress,
|
||||
@@ -667,14 +666,11 @@ KSTATUS Rtl816xAddDevice(DRIVER_OBJECT* driver, DEVICE_OBJECT* physicalDeviceObj
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Implements the driver Entry operation.
|
||||
KSTATUS DriverEntry(const NTL_DDK* ddk, NTL_DRIVER_INIT* init) {
|
||||
(void)init;
|
||||
if (!ddk || !ddk->Dma || !ddk->Timer)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
static KSTATUS DriverInitialize(NTL_MODULE* module) {
|
||||
(void)module;
|
||||
|
||||
DbgPrint("RTL816X: driver loading...\n");
|
||||
gRtl816xDdk = ddk;
|
||||
|
||||
gRtl816xDeviceCount = 0;
|
||||
|
||||
memset(&gRtl816xDriver, 0, sizeof(gRtl816xDriver));
|
||||
@@ -682,9 +678,8 @@ KSTATUS DriverEntry(const NTL_DDK* ddk, NTL_DRIVER_INIT* init) {
|
||||
gRtl816xDriver.IdTable = &gRtl816xIdTable;
|
||||
gRtl816xDriver.DriverAddDevice = Rtl816xAddDevice;
|
||||
gRtl816xDriver.MajorFunction[IRP_MJ_PNP] = Rtl816xDispatchPnp;
|
||||
|
||||
KSTATUS st = IoRegisterDriverObject(&gRtl816xDriver);
|
||||
if (!KSUCCESS(st))
|
||||
DbgPrint("RTL816X: IoRegisterDriverObject failed: 0x%x\n", st);
|
||||
return st;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static DRIVER_OBJECT* const gModuleIoDrivers[] = { &gRtl816xDriver };
|
||||
NTL_DECLARE_IO_MODULE(DriverInitialize, gModuleIoDrivers);
|
||||
@@ -151,7 +151,5 @@ typedef struct RTL816X_DEVICE {
|
||||
NET_DEVICE NetDevice;
|
||||
} RTL816X_DEVICE;
|
||||
|
||||
extern const NTL_DDK* gRtl816xDdk;
|
||||
|
||||
KSTATUS Rtl816xAddDevice(DRIVER_OBJECT* driver, DEVICE_OBJECT* physicalDeviceObject);
|
||||
KSTATUS Rtl816xDispatchPnp(DEVICE_OBJECT* device, IRP* irp);
|
||||
@@ -1,8 +1,8 @@
|
||||
#include <ntlite/ddk/module.h>
|
||||
// AHCI storage driver. This file owns controller attachment, drive discovery, command submission, and worker-driven IRP completion.
|
||||
|
||||
#include "ahci.h"
|
||||
|
||||
const NTL_DDK* gAhciDdk;
|
||||
DRIVER_OBJECT g_driver;
|
||||
|
||||
static AHCI_CONTROLLER* g_controllers[MAX_AHCI_CONTROLLERS];
|
||||
@@ -102,13 +102,13 @@ static void AhciReleaseDriveRuntime(AHCI_DRIVE* drv) {
|
||||
|
||||
// Releases the DMA buffers owned by one AHCI drive.
|
||||
static void AhciReleaseDriveDma(AHCI_DRIVE* drv) {
|
||||
if (!drv || !gAhciDdk || !gAhciDdk->Dma)
|
||||
if (!drv)
|
||||
return;
|
||||
if (drv->port && drv->controller &&
|
||||
!drv->surpriseRemoved && !drv->controller->surpriseRemoved)
|
||||
port_stop(drv->port);
|
||||
gAhciDdk->Dma->FreeCommonBuffer(drv->bounceDma);
|
||||
gAhciDdk->Dma->FreeCommonBuffer(drv->structDma);
|
||||
IoDmaFreeCommonBuffer(drv->bounceDma);
|
||||
IoDmaFreeCommonBuffer(drv->structDma);
|
||||
drv->bounceDma = (IO_DMA_BUFFER*)0;
|
||||
drv->structDma = (IO_DMA_BUFFER*)0;
|
||||
drv->bounceDeviceAddress = 0;
|
||||
@@ -122,16 +122,16 @@ static KSTATUS port_init_memory(AHCI_DRIVE* drv) {
|
||||
if (!drv || !drv->controller || !drv->controller->dmaAdapter)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
KSTATUS status = gAhciDdk->Dma->AllocateCommonBuffer(drv->controller->dmaAdapter,
|
||||
KSTATUS status = IoDmaAllocateCommonBuffer(drv->controller->dmaAdapter,
|
||||
PORT_PAGES_STRUCT * PAGE_SIZE,
|
||||
PAGE_SIZE,
|
||||
&drv->structDma);
|
||||
if (!KSUCCESS(status))
|
||||
return status;
|
||||
drv->structVirt = gAhciDdk->Dma->GetCommonBufferAddress(drv->structDma);
|
||||
drv->structDeviceAddress = gAhciDdk->Dma->GetCommonBufferDeviceAddress(drv->structDma);
|
||||
drv->structVirt = IoDmaGetCommonBufferAddress(drv->structDma);
|
||||
drv->structDeviceAddress = IoDmaGetCommonBufferDeviceAddress(drv->structDma);
|
||||
|
||||
status = gAhciDdk->Dma->AllocateCommonBuffer(drv->controller->dmaAdapter,
|
||||
status = IoDmaAllocateCommonBuffer(drv->controller->dmaAdapter,
|
||||
BOUNCE_BYTES,
|
||||
PAGE_SIZE,
|
||||
&drv->bounceDma);
|
||||
@@ -139,8 +139,8 @@ static KSTATUS port_init_memory(AHCI_DRIVE* drv) {
|
||||
AhciReleaseDriveDma(drv);
|
||||
return status;
|
||||
}
|
||||
drv->bounceVirt = gAhciDdk->Dma->GetCommonBufferAddress(drv->bounceDma);
|
||||
drv->bounceDeviceAddress = gAhciDdk->Dma->GetCommonBufferDeviceAddress(drv->bounceDma);
|
||||
drv->bounceVirt = IoDmaGetCommonBufferAddress(drv->bounceDma);
|
||||
drv->bounceDeviceAddress = IoDmaGetCommonBufferDeviceAddress(drv->bounceDma);
|
||||
|
||||
HBA_PORT* port = drv->port;
|
||||
port_stop(port);
|
||||
@@ -701,7 +701,7 @@ static KSTATUS AhciDispatchControllerPnp(DEVICE_OBJECT* device, IRP* irp) {
|
||||
}
|
||||
}
|
||||
AhciUnregisterController(ctl);
|
||||
gAhciDdk->Dma->PutAdapter(ctl->dmaAdapter);
|
||||
IoDmaPutAdapter(ctl->dmaAdapter);
|
||||
ctl->dmaAdapter = (IO_DMA_ADAPTER*)0;
|
||||
IoDetachDevice(device);
|
||||
IoDeleteDevice(device);
|
||||
@@ -940,7 +940,7 @@ static KSTATUS AhciStartController(AHCI_CONTROLLER* ctl) {
|
||||
dmaRequirements.Alignment = 2;
|
||||
dmaRequirements.MaximumSegmentLength = 1u << 22;
|
||||
dmaRequirements.MaximumSegments = PORT_PRDT_COUNT;
|
||||
st = gAhciDdk->Dma->GetAdapter(ctl->fdo, &dmaRequirements, &ctl->dmaAdapter);
|
||||
st = IoDmaGetAdapter(ctl->fdo, &dmaRequirements, &ctl->dmaAdapter);
|
||||
if (!KSUCCESS(st)) {
|
||||
DbgPrint("AHCI: DMA adapter creation failed (0x%x)\n", st);
|
||||
return st;
|
||||
@@ -949,7 +949,7 @@ static KSTATUS AhciStartController(AHCI_CONTROLLER* ctl) {
|
||||
st = AhciRegisterController(ctl);
|
||||
if (!KSUCCESS(st)) {
|
||||
DbgPrint("AHCI: controller registry full (0x%x)\n", st);
|
||||
gAhciDdk->Dma->PutAdapter(ctl->dmaAdapter);
|
||||
IoDmaPutAdapter(ctl->dmaAdapter);
|
||||
ctl->dmaAdapter = (IO_DMA_ADAPTER*)0;
|
||||
return st;
|
||||
}
|
||||
@@ -1159,14 +1159,11 @@ static KSTATUS AhciAddDevice(DRIVER_OBJECT* driver, DEVICE_OBJECT* physicalDevic
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Implements the driver Entry operation.
|
||||
KSTATUS DriverEntry(const NTL_DDK* ddk, NTL_DRIVER_INIT* init) {
|
||||
(void)init;
|
||||
if (!ddk || !ddk->Memory || !ddk->Memory->PhysToVirt || !ddk->Dma)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
static KSTATUS DriverInitialize(NTL_MODULE* module) {
|
||||
(void)module;
|
||||
|
||||
DbgPrint("AHCI: driver loading...\n");
|
||||
gAhciDdk = ddk;
|
||||
|
||||
g_controllerCount = 0;
|
||||
g_nextDiskIndex = 0;
|
||||
|
||||
@@ -1178,9 +1175,8 @@ KSTATUS DriverEntry(const NTL_DDK* ddk, NTL_DRIVER_INIT* init) {
|
||||
g_driver.MajorFunction[IRP_MJ_WRITE] = AhciDispatchReadWrite;
|
||||
g_driver.MajorFunction[IRP_MJ_FLUSH_BUFFERS] = AhciDispatchFlush;
|
||||
g_driver.MajorFunction[IRP_MJ_PNP] = AhciDispatchPnp;
|
||||
|
||||
KSTATUS st = IoRegisterDriverObject(&g_driver);
|
||||
if (!KSUCCESS(st))
|
||||
DbgPrint("AHCI: IoRegisterDriverObject failed: 0x%x\n", st);
|
||||
return st;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static DRIVER_OBJECT* const gModuleIoDrivers[] = { &g_driver };
|
||||
NTL_DECLARE_IO_MODULE(DriverInitialize, gModuleIoDrivers);
|
||||
@@ -385,7 +385,6 @@ struct AHCI_CONTROLLER {
|
||||
bool removed;
|
||||
};
|
||||
|
||||
extern const NTL_DDK* gAhciDdk;
|
||||
extern DRIVER_OBJECT g_driver;
|
||||
|
||||
KSTATUS ahci_map_mmio(uint64_t physBase, uint64_t length);
|
||||
|
||||
@@ -10,9 +10,9 @@ KSTATUS ahci_map_mmio(uint64_t physBase, uint64_t length) {
|
||||
uint64_t pageEnd = (physBase + length + PAGE_SIZE - 1u) & ~(uint64_t)(PAGE_SIZE - 1u);
|
||||
int pageIndex = 0;
|
||||
for (uint64_t phys = pageBase; phys < pageEnd; phys += PAGE_SIZE, pageIndex++) {
|
||||
uintptr_t virt = (uintptr_t)gAhciDdk->Memory->PhysToVirt(phys);
|
||||
uintptr_t virt = (uintptr_t)MmDdkPhysToVirt(phys);
|
||||
uint64_t flags = PAGE_PRESENT | PAGE_WRITE | PAGE_CACHE_UC;
|
||||
KSTATUS st = gAhciDdk->Memory->MapPage(virt,
|
||||
KSTATUS st = MmDdkMapPage(virt,
|
||||
phys,
|
||||
flags,
|
||||
NtlDdkMapReplaceExisting);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#include <ntlite/ddk/module.h>
|
||||
// ATA compatibility storage driver. This file owns legacy IDE probing and PIO disk access for older virtual hardware.
|
||||
|
||||
#include "ata.h"
|
||||
|
||||
const NTL_DDK* gAtaDdk;
|
||||
static DRIVER_OBJECT gAtaDriver;
|
||||
static ATA_CONTROLLER* gAtaControllers[ATA_MAX_CONTROLLERS];
|
||||
static uint32_t gAtaControllerCount;
|
||||
@@ -56,12 +56,12 @@ static void AtaDelay400ns(ATA_CHANNEL* ch) {
|
||||
|
||||
// Returns the monotonic DDK timer in microseconds.
|
||||
static uint64_t AtaQueryTicks(void) {
|
||||
return gAtaDdk->Timer->QueryTicks();
|
||||
return KeQueryTicks();
|
||||
}
|
||||
|
||||
// Delays briefly between ATA status samples without involving the scheduler.
|
||||
static void AtaPollDelay(void) {
|
||||
gAtaDdk->Timer->Stall(ATA_POLL_INTERVAL_US);
|
||||
KeStallExecution(ATA_POLL_INTERVAL_US);
|
||||
}
|
||||
|
||||
// Returns whether a status byte represents an unimplemented/floating legacy channel.
|
||||
@@ -125,9 +125,9 @@ static KSTATUS AtaResetChannel(ATA_CHANNEL* ch) {
|
||||
DbgPrint("ATA: channel %u reset begin status=0x%02x\n", ch->Index, before);
|
||||
|
||||
AtaDeviceControl(ch, ATA_DEVICE_CONTROL_NIEN | ATA_DEVICE_CONTROL_SRST);
|
||||
gAtaDdk->Timer->Stall(ATA_RESET_ASSERT_US);
|
||||
KeStallExecution(ATA_RESET_ASSERT_US);
|
||||
AtaDeviceControl(ch, ATA_DEVICE_CONTROL_NIEN);
|
||||
gAtaDdk->Timer->Stall(ATA_RESET_SETTLE_US);
|
||||
KeStallExecution(ATA_RESET_SETTLE_US);
|
||||
|
||||
uint8_t finalStatus = AtaAltStatus(ch);
|
||||
if (AtaStatusIsAbsent(finalStatus)) {
|
||||
@@ -922,14 +922,11 @@ KSTATUS AtaAddDevice(DRIVER_OBJECT* driver, DEVICE_OBJECT* physicalDeviceObject)
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Implements the driver Entry operation.
|
||||
KSTATUS DriverEntry(const NTL_DDK* ddk, NTL_DRIVER_INIT* init) {
|
||||
(void)init;
|
||||
if (!ddk || !ddk->Timer || !ddk->Timer->QueryTicks || !ddk->Timer->Stall)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
static KSTATUS DriverInitialize(NTL_MODULE* module) {
|
||||
(void)module;
|
||||
|
||||
DbgPrint("ATA: driver loading...\n");
|
||||
gAtaDdk = ddk;
|
||||
|
||||
gAtaControllerCount = 0;
|
||||
|
||||
memset(&gAtaDriver, 0, sizeof(gAtaDriver));
|
||||
@@ -940,9 +937,8 @@ KSTATUS DriverEntry(const NTL_DDK* ddk, NTL_DRIVER_INIT* init) {
|
||||
gAtaDriver.MajorFunction[IRP_MJ_WRITE] = AtaDispatchReadWrite;
|
||||
gAtaDriver.MajorFunction[IRP_MJ_FLUSH_BUFFERS] = AtaDispatchFlush;
|
||||
gAtaDriver.MajorFunction[IRP_MJ_PNP] = AtaDispatchPnp;
|
||||
|
||||
KSTATUS st = IoRegisterDriverObject(&gAtaDriver);
|
||||
if (!KSUCCESS(st))
|
||||
DbgPrint("ATA: IoRegisterDriverObject failed: 0x%x\n", st);
|
||||
return st;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static DRIVER_OBJECT* const gModuleIoDrivers[] = { &gAtaDriver };
|
||||
NTL_DECLARE_IO_MODULE(DriverInitialize, gModuleIoDrivers);
|
||||
@@ -133,8 +133,6 @@ typedef struct ATA_CONTROLLER {
|
||||
uint8_t DeviceCount;
|
||||
} ATA_CONTROLLER;
|
||||
|
||||
extern const NTL_DDK* gAtaDdk;
|
||||
|
||||
KSTATUS AtaAddDevice(DRIVER_OBJECT* driver, DEVICE_OBJECT* physicalDeviceObject);
|
||||
KSTATUS AtaDispatchPnp(DEVICE_OBJECT* device, IRP* irp);
|
||||
KSTATUS AtaDispatchReadWrite(DEVICE_OBJECT* device, IRP* irp);
|
||||
@@ -0,0 +1,422 @@
|
||||
// Strict drivers.ini v2 parsing, validation and dependency evaluation.
|
||||
|
||||
#include <kernel/dm_config.h>
|
||||
#include <string.h>
|
||||
|
||||
#define DM_DRIVER_IMAGE_SET 1u
|
||||
#define DM_DRIVER_START_SET 2u
|
||||
#define DM_DRIVER_CLASS_SET 4u
|
||||
#define DM_DRIVER_CRITICAL_SET 8u
|
||||
#define DM_DRIVER_REQUIRES_SET 16u
|
||||
#define DM_DRIVER_AFTER_SET 32u
|
||||
#define DM_CLASS_REQUIRES_SET 1u
|
||||
#define DM_CLASS_AFTER_SET 2u
|
||||
|
||||
static bool DmFail(char* error, size_t capacity, const char* message) {
|
||||
if (error && capacity) {
|
||||
size_t i = 0;
|
||||
while (message[i] && i + 1 < capacity) {
|
||||
error[i] = message[i];
|
||||
++i;
|
||||
}
|
||||
error[i] = '\0';
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool DmSpace(char c) {
|
||||
return c == ' ' || c == '\t' || c == '\r';
|
||||
}
|
||||
|
||||
static void DmTrim(const char** begin, const char** end) {
|
||||
while (*begin < *end && DmSpace(**begin)) ++*begin;
|
||||
while (*end > *begin && DmSpace((*end)[-1])) --*end;
|
||||
}
|
||||
|
||||
static bool DmEqual(const char* begin, const char* end, const char* value) {
|
||||
while (begin < end && *value && *begin == *value) {
|
||||
++begin;
|
||||
++value;
|
||||
}
|
||||
return begin == end && !*value;
|
||||
}
|
||||
|
||||
static bool DmIdentifier(const char* begin, const char* end) {
|
||||
if (begin == end || (size_t)(end - begin) >= DM_ID_MAX) return false;
|
||||
for (const char* p = begin; p < end; ++p) {
|
||||
char c = *p;
|
||||
if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
|
||||
(c >= '0' && c <= '9') || c == '.' || c == '-' || c == '_')) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// PE import module names are compared without ASCII case distinctions.
|
||||
static bool DmImageEqual(const char* a, const char* b) {
|
||||
while (*a && *b) {
|
||||
char x = *a++;
|
||||
char y = *b++;
|
||||
if (x >= 'A' && x <= 'Z') x = (char)(x + ('a' - 'A'));
|
||||
if (y >= 'A' && y <= 'Z') y = (char)(y + ('a' - 'A'));
|
||||
if (x != y) return false;
|
||||
}
|
||||
return !*a && !*b;
|
||||
}
|
||||
|
||||
static bool DmCopy(char* out, size_t capacity, const char* begin, const char* end) {
|
||||
size_t size = (size_t)(end - begin);
|
||||
if (!size || size >= capacity) return false;
|
||||
memcpy(out, begin, size);
|
||||
out[size] = '\0';
|
||||
return true;
|
||||
}
|
||||
|
||||
static int DmFindDriver(const DM_CONFIG* config, const char* id) {
|
||||
for (uint32_t i = 0; i < config->DriverCount; ++i)
|
||||
if (!strcmp(config->Drivers[i].Id, id)) return (int)i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int DmFindClass(const DM_CONFIG* config, const char* id) {
|
||||
for (uint32_t i = 0; i < config->ClassCount; ++i)
|
||||
if (!strcmp(config->Classes[i].Id, id)) return (int)i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static bool DmParseEdges(DM_EDGES* edges, const char* begin, const char* end,
|
||||
DM_DEPENDENCY_KIND kind) {
|
||||
if (begin == end) return true;
|
||||
while (begin < end) {
|
||||
const char* finish = begin;
|
||||
while (finish < end && *finish != ',') ++finish;
|
||||
const char* tokenEnd = finish;
|
||||
DmTrim(&begin, &tokenEnd);
|
||||
if (!DmIdentifier(begin, tokenEnd) || edges->Count == DM_MAX_EDGES) return false;
|
||||
for (uint32_t i = 0; i < edges->Count; ++i) {
|
||||
if (edges->Items[i].Kind == kind &&
|
||||
DmEqual(begin, tokenEnd, edges->Items[i].Id)) return false;
|
||||
}
|
||||
DM_DEPENDENCY* edge = &edges->Items[edges->Count++];
|
||||
edge->Kind = kind;
|
||||
if (!DmCopy(edge->Id, sizeof(edge->Id), begin, tokenEnd)) return false;
|
||||
if (finish == end) return true;
|
||||
begin = finish + 1;
|
||||
DmTrim(&begin, &end);
|
||||
if (begin == end) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Validates references and boot-phase satisfiability without flattening class alternatives.
|
||||
static bool DmValidateEdges(const DM_CONFIG* config, const DM_EDGES* edges,
|
||||
const DM_DRIVER_CONFIG* source, bool required) {
|
||||
for (uint32_t i = 0; i < edges->Count; ++i) {
|
||||
const DM_DEPENDENCY* edge = &edges->Items[i];
|
||||
if (edge->Kind == DmDependencyDriver) {
|
||||
int target = DmFindDriver(config, edge->Id);
|
||||
if (target < 0) return false;
|
||||
if (source && source->Phase == DmStartBoot &&
|
||||
config->Drivers[target].Phase != DmStartBoot) return false;
|
||||
} else {
|
||||
if (DmFindClass(config, edge->Id) < 0) return false;
|
||||
uint32_t bootProviders = 0;
|
||||
for (uint32_t j = 0; j < config->DriverCount; ++j) {
|
||||
if (strcmp(config->Drivers[j].Class, edge->Id)) continue;
|
||||
if (config->Drivers[j].Phase == DmStartBoot) ++bootProviders;
|
||||
}
|
||||
if (required && source && source->Phase == DmStartBoot && !bootProviders)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool DmEdgesOverlap(const DM_EDGES* requires, const DM_EDGES* after) {
|
||||
for (uint32_t i = 0; i < requires->Count; ++i)
|
||||
for (uint32_t j = 0; j < after->Count; ++j)
|
||||
if (requires->Items[i].Kind == after->Items[j].Kind &&
|
||||
!strcmp(requires->Items[i].Id, after->Items[j].Id)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool DmVisitClass(uint32_t index, uint32_t count,
|
||||
uint8_t color[DM_MAX_CLASSES],
|
||||
uint8_t graph[DM_MAX_CLASSES][DM_MAX_CLASSES]) {
|
||||
color[index] = 1;
|
||||
for (uint32_t j = 0; j < count; ++j) {
|
||||
if (!graph[index][j]) continue;
|
||||
if (color[j] == 1 ||
|
||||
(color[j] == 0 && !DmVisitClass(j, count, color, graph))) return false;
|
||||
}
|
||||
color[index] = 2;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DmConfigParse(DM_CONFIG* config, const char* bytes, size_t size,
|
||||
char* error, size_t errorCapacity) {
|
||||
if (!config || !bytes || size == 0 || size > DM_CONFIG_MAX_BYTES)
|
||||
return DmFail(error, errorCapacity, "invalid configuration size");
|
||||
memset(config, 0, sizeof(*config));
|
||||
DM_DRIVER_CONFIG* driver = NULL;
|
||||
DM_CLASS_CONFIG* cls = NULL;
|
||||
bool seenDriverSection = false;
|
||||
size_t pos = 0;
|
||||
while (pos < size) {
|
||||
size_t line = pos;
|
||||
while (pos < size && bytes[pos] != '\n') ++pos;
|
||||
const char* begin = bytes + line;
|
||||
const char* end = bytes + pos;
|
||||
if (pos < size) ++pos;
|
||||
DmTrim(&begin, &end);
|
||||
if (begin == end || *begin == ';' || *begin == '#') continue;
|
||||
if (*begin == '[') {
|
||||
if (end[-1] != ']' || (size_t)(end - begin) < 4)
|
||||
return DmFail(error, errorCapacity, "invalid section");
|
||||
const char* name = begin + 1;
|
||||
const char* nameEnd = end - 1;
|
||||
driver = NULL;
|
||||
cls = NULL;
|
||||
if ((size_t)(nameEnd - name) > 7 && !memcmp(name, "driver.", 7)) {
|
||||
name += 7;
|
||||
if (!DmIdentifier(name, nameEnd) || config->DriverCount == DM_MAX_DRIVERS)
|
||||
return DmFail(error, errorCapacity, "invalid driver id or driver limit");
|
||||
driver = &config->Drivers[config->DriverCount++];
|
||||
seenDriverSection = true;
|
||||
if (!DmCopy(driver->Id, sizeof(driver->Id), name, nameEnd)) return false;
|
||||
for (uint32_t i = 0; i + 1 < config->DriverCount; ++i)
|
||||
if (!strcmp(config->Drivers[i].Id, driver->Id))
|
||||
return DmFail(error, errorCapacity, "duplicate driver id");
|
||||
} else if ((size_t)(nameEnd - name) > 6 && !memcmp(name, "class.", 6)) {
|
||||
name += 6;
|
||||
if (!DmIdentifier(name, nameEnd) || config->ClassCount == DM_MAX_CLASSES)
|
||||
return DmFail(error, errorCapacity, "invalid class id or class limit");
|
||||
if (seenDriverSection)
|
||||
return DmFail(error, errorCapacity, "class declared after driver");
|
||||
cls = &config->Classes[config->ClassCount++];
|
||||
if (!DmCopy(cls->Id, sizeof(cls->Id), name, nameEnd)) return false;
|
||||
for (uint32_t i = 0; i + 1 < config->ClassCount; ++i)
|
||||
if (!strcmp(config->Classes[i].Id, cls->Id))
|
||||
return DmFail(error, errorCapacity, "duplicate class id");
|
||||
} else return DmFail(error, errorCapacity, "unknown section");
|
||||
continue;
|
||||
}
|
||||
if (!driver && !cls) return DmFail(error, errorCapacity, "key outside section");
|
||||
const char* equal = begin;
|
||||
while (equal < end && *equal != '=') ++equal;
|
||||
if (equal == end) return DmFail(error, errorCapacity, "missing equals");
|
||||
const char* keyEnd = equal;
|
||||
const char* value = equal + 1;
|
||||
DmTrim(&begin, &keyEnd);
|
||||
DmTrim(&value, &end);
|
||||
for (const char* p = value; p < end; ++p) {
|
||||
if (*p == ';' || *p == '#') {
|
||||
end = p;
|
||||
DmTrim(&value, &end);
|
||||
break;
|
||||
}
|
||||
}
|
||||
uint8_t mask = 0;
|
||||
if (driver) {
|
||||
if (DmEqual(begin, keyEnd, "image")) {
|
||||
mask = DM_DRIVER_IMAGE_SET;
|
||||
if (!DmCopy(driver->Image, sizeof(driver->Image), value, end))
|
||||
return DmFail(error, errorCapacity, "invalid image");
|
||||
for (const char* p = value; p < end; ++p)
|
||||
if (!((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') ||
|
||||
(*p >= '0' && *p <= '9') ||
|
||||
*p == '-' || *p == '_' || *p == '.'))
|
||||
return DmFail(error, errorCapacity, "invalid image filename");
|
||||
if (end - value < 5 || memcmp(end - 4, ".sys", 4))
|
||||
return DmFail(error, errorCapacity, "image must be a .sys file");
|
||||
} else if (DmEqual(begin, keyEnd, "start")) {
|
||||
mask = DM_DRIVER_START_SET;
|
||||
if (DmEqual(value, end, "boot")) driver->Phase = DmStartBoot;
|
||||
else if (DmEqual(value, end, "runtime")) driver->Phase = DmStartRuntime;
|
||||
else if (DmEqual(value, end, "disabled")) {
|
||||
driver->Phase = DmStartDisabled;
|
||||
driver->State = DmRunDisabled;
|
||||
}
|
||||
else return DmFail(error, errorCapacity, "invalid start phase");
|
||||
} else if (DmEqual(begin, keyEnd, "class")) {
|
||||
mask = DM_DRIVER_CLASS_SET;
|
||||
if (value != end && (!DmIdentifier(value, end) ||
|
||||
!DmCopy(driver->Class, sizeof(driver->Class), value, end)))
|
||||
return DmFail(error, errorCapacity, "invalid class");
|
||||
} else if (DmEqual(begin, keyEnd, "requires")) {
|
||||
mask = DM_DRIVER_REQUIRES_SET;
|
||||
if (!DmParseEdges(&driver->Requires, value, end, DmDependencyDriver))
|
||||
return DmFail(error, errorCapacity, "invalid requires");
|
||||
} else if (DmEqual(begin, keyEnd, "after")) {
|
||||
mask = DM_DRIVER_AFTER_SET;
|
||||
if (!DmParseEdges(&driver->After, value, end, DmDependencyDriver))
|
||||
return DmFail(error, errorCapacity, "invalid after");
|
||||
} else if (DmEqual(begin, keyEnd, "critical")) {
|
||||
mask = DM_DRIVER_CRITICAL_SET;
|
||||
if (value == end) driver->Critical = false;
|
||||
else if (DmEqual(value, end, "true")) driver->Critical = true;
|
||||
else if (DmEqual(value, end, "false")) driver->Critical = false;
|
||||
else return DmFail(error, errorCapacity, "invalid critical value");
|
||||
} else return DmFail(error, errorCapacity, "unknown driver key");
|
||||
if (driver->Present & mask) return DmFail(error, errorCapacity, "duplicate driver key");
|
||||
driver->Present |= mask;
|
||||
} else {
|
||||
if (DmEqual(begin, keyEnd, "requires")) {
|
||||
mask = DM_CLASS_REQUIRES_SET;
|
||||
if (!DmParseEdges(&cls->Requires, value, end, DmDependencyClass))
|
||||
return DmFail(error, errorCapacity, "invalid class requires");
|
||||
} else if (DmEqual(begin, keyEnd, "after")) {
|
||||
mask = DM_CLASS_AFTER_SET;
|
||||
if (!DmParseEdges(&cls->After, value, end, DmDependencyClass))
|
||||
return DmFail(error, errorCapacity, "invalid class after");
|
||||
} else return DmFail(error, errorCapacity, "unknown class key");
|
||||
if (cls->Present & mask) return DmFail(error, errorCapacity, "duplicate class key");
|
||||
cls->Present |= mask;
|
||||
}
|
||||
}
|
||||
if (!config->DriverCount) return DmFail(error, errorCapacity, "no drivers");
|
||||
// Class graph is checked independently even when a class has no providers.
|
||||
uint8_t classGraph[DM_MAX_CLASSES][DM_MAX_CLASSES] = {{0}};
|
||||
for (uint32_t i = 0; i < config->ClassCount; ++i) {
|
||||
const DM_CLASS_CONFIG* cls = &config->Classes[i];
|
||||
if (DmEdgesOverlap(&cls->Requires, &cls->After))
|
||||
return DmFail(error, errorCapacity, "conflicting class dependency");
|
||||
const DM_EDGES* lists[] = { &cls->Requires, &cls->After };
|
||||
for (uint32_t k = 0; k < 2; ++k)
|
||||
for (uint32_t j = 0; j < lists[k]->Count; ++j) {
|
||||
int index = DmFindClass(config, lists[k]->Items[j].Id);
|
||||
if (index < 0) return DmFail(error, errorCapacity, "unknown class dependency");
|
||||
classGraph[i][index] = 1;
|
||||
}
|
||||
}
|
||||
uint8_t classColors[DM_MAX_CLASSES] = {0};
|
||||
for (uint32_t i = 0; i < config->ClassCount; ++i)
|
||||
if (!classColors[i] && !DmVisitClass(i, config->ClassCount,
|
||||
classColors, classGraph))
|
||||
return DmFail(error, errorCapacity, "class dependency cycle");
|
||||
for (uint32_t i = 0; i < config->DriverCount; ++i) {
|
||||
DM_DRIVER_CONFIG* d = &config->Drivers[i];
|
||||
if ((d->Present & (DM_DRIVER_IMAGE_SET | DM_DRIVER_START_SET)) !=
|
||||
(DM_DRIVER_IMAGE_SET | DM_DRIVER_START_SET))
|
||||
return DmFail(error, errorCapacity, "driver needs image and start");
|
||||
for (uint32_t j = 0; j < i; ++j)
|
||||
if (d->Phase != DmStartDisabled &&
|
||||
config->Drivers[j].Phase != DmStartDisabled &&
|
||||
DmImageEqual(config->Drivers[j].Image, d->Image))
|
||||
return DmFail(error, errorCapacity, "duplicate image");
|
||||
if (d->Class[0] && DmFindClass(config, d->Class) < 0)
|
||||
return DmFail(error, errorCapacity, "undeclared driver class");
|
||||
if (DmEdgesOverlap(&d->Requires, &d->After))
|
||||
return DmFail(error, errorCapacity, "conflicting driver dependency");
|
||||
if (!DmValidateEdges(config, &d->Requires, d, true) ||
|
||||
!DmValidateEdges(config, &d->After, d, false))
|
||||
return DmFail(error, errorCapacity, "invalid driver dependency or phase");
|
||||
if (d->Class[0] && d->Phase != DmStartDisabled) {
|
||||
DM_CLASS_CONFIG* c = &config->Classes[DmFindClass(config, d->Class)];
|
||||
if (!DmValidateEdges(config, &c->Requires, d, true) ||
|
||||
!DmValidateEdges(config, &c->After, d, false))
|
||||
return DmFail(error, errorCapacity, "invalid class dependency or phase");
|
||||
}
|
||||
}
|
||||
// An optimistic dry run models requires= as an OR over class providers and
|
||||
// after= as a settlement barrier. It detects real combined deadlocks without
|
||||
// rejecting a class merely because one alternative provider depends on it.
|
||||
for (uint32_t phase = DmStartBoot; phase <= DmStartRuntime; ++phase) {
|
||||
config->ActivePhase = (DM_START_PHASE)phase;
|
||||
for (;;) {
|
||||
bool progress = false;
|
||||
for (uint32_t i = 0; i < config->DriverCount; ++i) {
|
||||
DM_DRIVER_CONFIG* d = &config->Drivers[i];
|
||||
if (d->Phase != phase || d->State != DmRunWaiting) continue;
|
||||
if (DmDriverCanStart(config, i)) {
|
||||
d->State = DmRunStarted;
|
||||
progress = true;
|
||||
} else if (DmDriverIsBlocked(config, i)) {
|
||||
d->State = DmRunBlocked;
|
||||
progress = true;
|
||||
}
|
||||
}
|
||||
if (!progress) break;
|
||||
}
|
||||
for (uint32_t i = 0; i < config->DriverCount; ++i)
|
||||
if (config->Drivers[i].Phase == phase &&
|
||||
config->Drivers[i].State == DmRunWaiting)
|
||||
return DmFail(error, errorCapacity, "unschedulable dependency cycle");
|
||||
}
|
||||
for (uint32_t i = 0; i < config->DriverCount; ++i)
|
||||
if (config->Drivers[i].Phase != DmStartDisabled)
|
||||
config->Drivers[i].State = DmRunWaiting;
|
||||
config->ActivePhase = DmStartBoot;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DmClassSettled(const DM_CONFIG* config, const char* classId) {
|
||||
for (uint32_t i = 0; i < config->DriverCount; ++i) {
|
||||
const DM_DRIVER_CONFIG* driver = &config->Drivers[i];
|
||||
if (!strcmp(driver->Class, classId) &&
|
||||
(config->ActivePhase == DmStartRuntime || driver->Phase == DmStartBoot) &&
|
||||
driver->State == DmRunWaiting) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DmClassAvailable(const DM_CONFIG* config, const char* classId) {
|
||||
for (uint32_t i = 0; i < config->DriverCount; ++i) {
|
||||
const DM_DRIVER_CONFIG* driver = &config->Drivers[i];
|
||||
if (!strcmp(driver->Class, classId) && driver->State == DmRunStarted) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool DmEdgesReady(const DM_CONFIG* config, const DM_EDGES* edges, bool required) {
|
||||
for (uint32_t i = 0; i < edges->Count; ++i) {
|
||||
const DM_DEPENDENCY* edge = &edges->Items[i];
|
||||
if (edge->Kind == DmDependencyClass) {
|
||||
if (required ? !DmClassAvailable(config, edge->Id) :
|
||||
!DmClassSettled(config, edge->Id)) return false;
|
||||
} else {
|
||||
int index = DmFindDriver(config, edge->Id);
|
||||
if (index < 0) return false;
|
||||
DM_RUN_STATE state = config->Drivers[index].State;
|
||||
if (required ? state != DmRunStarted : state == DmRunWaiting) return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DmDriverCanStart(const DM_CONFIG* config, uint32_t index) {
|
||||
if (!config || index >= config->DriverCount) return false;
|
||||
const DM_DRIVER_CONFIG* driver = &config->Drivers[index];
|
||||
if (driver->State != DmRunWaiting ||
|
||||
!DmEdgesReady(config, &driver->Requires, true) ||
|
||||
!DmEdgesReady(config, &driver->After, false)) return false;
|
||||
if (!driver->Class[0]) return true;
|
||||
const DM_CLASS_CONFIG* cls = &config->Classes[DmFindClass(config, driver->Class)];
|
||||
return DmEdgesReady(config, &cls->Requires, true) &&
|
||||
DmEdgesReady(config, &cls->After, false);
|
||||
}
|
||||
|
||||
static bool DmEdgesImpossible(const DM_CONFIG* config, const DM_EDGES* edges) {
|
||||
for (uint32_t i = 0; i < edges->Count; ++i) {
|
||||
const DM_DEPENDENCY* edge = &edges->Items[i];
|
||||
if (edge->Kind == DmDependencyClass) {
|
||||
if (DmClassSettled(config, edge->Id) &&
|
||||
!DmClassAvailable(config, edge->Id)) return true;
|
||||
} else {
|
||||
int index = DmFindDriver(config, edge->Id);
|
||||
if (index < 0 || config->Drivers[index].State == DmRunFailed ||
|
||||
config->Drivers[index].State == DmRunBlocked ||
|
||||
config->Drivers[index].State == DmRunDisabled) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DmDriverIsBlocked(const DM_CONFIG* config, uint32_t index) {
|
||||
if (!config || index >= config->DriverCount) return false;
|
||||
const DM_DRIVER_CONFIG* driver = &config->Drivers[index];
|
||||
if (driver->State != DmRunWaiting) return false;
|
||||
if (DmEdgesImpossible(config, &driver->Requires)) return true;
|
||||
if (!driver->Class[0]) return false;
|
||||
return DmEdgesImpossible(config,
|
||||
&config->Classes[DmFindClass(config, driver->Class)].Requires);
|
||||
}
|
||||
@@ -0,0 +1,226 @@
|
||||
// Driver module identity and versioned declarative module exports.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <ntlite/ddi/io_driver.h>
|
||||
#include <ntlite/ddk/module.h>
|
||||
#include <kernel/driver_module.h>
|
||||
#include <kernel/ldr_image.h>
|
||||
#include <kernel/mm_virt.h>
|
||||
#include <kernel/io_pnp.h>
|
||||
#include <kernel/ke_debug.h>
|
||||
|
||||
#define DM_NAME_MAX 64u
|
||||
|
||||
typedef enum DM_MODULE_STATE {
|
||||
DmModuleStarting,
|
||||
DmModuleStarted,
|
||||
DmModuleFailed,
|
||||
DmModuleStopping,
|
||||
DmModuleStopped,
|
||||
} DM_MODULE_STATE;
|
||||
|
||||
struct NTL_MODULE {
|
||||
struct NTL_MODULE* Next;
|
||||
char Name[DM_NAME_MAX];
|
||||
uintptr_t ImageBase;
|
||||
DM_MODULE_STATE State;
|
||||
NTL_MODULE_PREPARE_SHUTDOWN PrepareShutdown;
|
||||
NTL_MODULE_COMMIT_SHUTDOWN CommitShutdown;
|
||||
};
|
||||
|
||||
static NTL_MODULE* gDriverModules;
|
||||
|
||||
static bool DmImageContains(uintptr_t base, size_t imageSize, uintptr_t ptr, size_t bytes) {
|
||||
return ptr >= base && ptr - base <= imageSize && bytes <= imageSize - (ptr - base);
|
||||
}
|
||||
|
||||
__declspec(dllexport) const char* NtlModuleGetName(const NTL_MODULE* module) {
|
||||
return module ? module->Name : (const char*)0;
|
||||
}
|
||||
|
||||
static bool DmValidImageString(uintptr_t base, size_t size, const char* text,
|
||||
size_t maxLength) {
|
||||
if (!DmImageContains(base, size, (uintptr_t)text, 1u)) return false;
|
||||
for (size_t i = 0; i < maxLength; ++i) {
|
||||
if (!DmImageContains(base, size, (uintptr_t)text + i, 1u)) return false;
|
||||
if (!text[i])
|
||||
return i && SysSvIsUtf8Text(SysSvFromParts(text, i));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool DmValidIoDriver(uintptr_t imageBase, size_t imageSize, const DRIVER_OBJECT* driver) {
|
||||
if (!DmImageContains(imageBase, imageSize, (uintptr_t)driver, sizeof(*driver)) ||
|
||||
driver->Header.Type != ObjectTypeDriver ||
|
||||
driver->Header.LifetimeState != ObjectAlive ||
|
||||
!driver->Name.Length ||
|
||||
!DmImageContains(imageBase, imageSize, (uintptr_t)driver->Name.Data,
|
||||
driver->Name.Length) ||
|
||||
!SysSvIsUtf8Text(driver->Name) ||
|
||||
(driver->DriverAddDevice &&
|
||||
!DmImageContains(imageBase, imageSize, (uintptr_t)driver->DriverAddDevice, 1u)) ||
|
||||
(driver->DriverUnload &&
|
||||
!DmImageContains(imageBase, imageSize, (uintptr_t)driver->DriverUnload, 1u)))
|
||||
return false;
|
||||
|
||||
for (uint32_t i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; ++i) {
|
||||
if (driver->MajorFunction[i] &&
|
||||
!DmImageContains(imageBase, imageSize,
|
||||
(uintptr_t)driver->MajorFunction[i], 1u))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!driver->IdTable) return true;
|
||||
if (!driver->DriverAddDevice ||
|
||||
!DmImageContains(imageBase, imageSize, (uintptr_t)driver->IdTable,
|
||||
sizeof(*driver->IdTable)) ||
|
||||
!driver->IdTable->Count || driver->IdTable->Count > 32u ||
|
||||
!DmImageContains(imageBase, imageSize, (uintptr_t)driver->IdTable->Ids,
|
||||
driver->IdTable->Count * sizeof(IO_DEVICE_ID)))
|
||||
return false;
|
||||
|
||||
for (size_t i = 0; i < driver->IdTable->Count; ++i) {
|
||||
const IO_DEVICE_ID* id = &driver->IdTable->Ids[i];
|
||||
if ((id->BusType != IoBusPci && id->BusType != IoBusUsb &&
|
||||
id->BusType != IoBusUnknown) ||
|
||||
!id->Id.Length ||
|
||||
!DmImageContains(imageBase, imageSize, (uintptr_t)id->Id.Data,
|
||||
id->Id.Length) ||
|
||||
!SysSvIsUtf8Text(id->Id))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
KSTATUS DmStartLoadedDriver(const char* name, uintptr_t imageBase) {
|
||||
if (!name || !*name || !imageBase)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
size_t length = 0;
|
||||
while (length < DM_NAME_MAX && name[length])
|
||||
length++;
|
||||
if (!length || length == DM_NAME_MAX)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
for (NTL_MODULE* it = gDriverModules; it; it = it->Next) {
|
||||
if (strcmp(it->Name, name) == 0 || it->ImageBase == imageBase)
|
||||
return NTLSTATUS_ALREADY_EXISTS;
|
||||
}
|
||||
|
||||
size_t imageSize = LdrGetImageSize(imageBase);
|
||||
if (!imageSize)
|
||||
return NTLSTATUS_INVALID_IMAGE_FORMAT;
|
||||
|
||||
const NTL_MODULE_EXPORT* manifest =
|
||||
(const NTL_MODULE_EXPORT*)LdrGetImageExport(imageBase, NTL_DDK_MODULE_EXPORT_NAME);
|
||||
NTL_MODULE_INITIALIZE initialize = (NTL_MODULE_INITIALIZE)0;
|
||||
|
||||
if (!manifest) return NTLSTATUS_INVALID_IMAGE_FORMAT;
|
||||
{
|
||||
if (!DmImageContains(imageBase, imageSize, (uintptr_t)manifest, sizeof(*manifest)) ||
|
||||
manifest->Signature != NTL_DDK_MODULE_SIGNATURE ||
|
||||
manifest->Version != NTL_DDK_MODULE_ABI_VERSION ||
|
||||
manifest->Size != sizeof(*manifest) || manifest->Flags != 0u ||
|
||||
manifest->Reserved != 0u || manifest->Reserved2 != 0u ||
|
||||
(manifest->Initialize && !DmImageContains(imageBase, imageSize,
|
||||
(uintptr_t)manifest->Initialize, 1u)) ||
|
||||
(manifest->PrepareShutdown && !DmImageContains(imageBase, imageSize,
|
||||
(uintptr_t)manifest->PrepareShutdown, 1u)) ||
|
||||
(manifest->CommitShutdown && !DmImageContains(imageBase, imageSize,
|
||||
(uintptr_t)manifest->CommitShutdown, 1u)))
|
||||
return NTLSTATUS_INVALID_IMAGE_FORMAT;
|
||||
if ((manifest->DeviceCount && !manifest->Devices) ||
|
||||
manifest->DeviceCount > 64u ||
|
||||
(manifest->DeviceCount &&
|
||||
!DmImageContains(imageBase, imageSize, (uintptr_t)manifest->Devices,
|
||||
(size_t)manifest->DeviceCount * sizeof(NTL_DEVICE_DESC))))
|
||||
return NTLSTATUS_INVALID_IMAGE_FORMAT;
|
||||
for (uint32_t i = 0; i < manifest->DeviceCount; ++i) {
|
||||
const NTL_DEVICE_DESC* d = &manifest->Devices[i];
|
||||
if (d->Size != sizeof(*d) || d->Flags || !d->Name || !d->Start ||
|
||||
!DmValidImageString(imageBase, imageSize, d->Name, 64u) ||
|
||||
!DmImageContains(imageBase, imageSize, (uintptr_t)d->Start, 1u) ||
|
||||
(d->Stop && !DmImageContains(imageBase, imageSize, (uintptr_t)d->Stop, 1u)) ||
|
||||
(d->CancelStart && !DmImageContains(imageBase, imageSize,
|
||||
(uintptr_t)d->CancelStart, 1u)) ||
|
||||
!d->MatchCount || d->MatchCount > 32u ||
|
||||
!DmImageContains(imageBase, imageSize, (uintptr_t)d->Matches,
|
||||
(size_t)d->MatchCount * sizeof(NTL_DEVICE_MATCH)))
|
||||
return NTLSTATUS_INVALID_IMAGE_FORMAT;
|
||||
for (uint32_t j = 0; j < d->MatchCount; ++j) {
|
||||
const NTL_DEVICE_MATCH* m = &d->Matches[j];
|
||||
if ((m->Bus != NtlBusPci && m->Bus != NtlBusUsb) ||
|
||||
!DmValidImageString(imageBase, imageSize, m->Id, 96u))
|
||||
return NTLSTATUS_INVALID_IMAGE_FORMAT;
|
||||
}
|
||||
}
|
||||
if ((manifest->IoDriverCount && !manifest->IoDrivers) ||
|
||||
manifest->IoDriverCount > 64u ||
|
||||
(manifest->IoDriverCount &&
|
||||
!DmImageContains(imageBase, imageSize, (uintptr_t)manifest->IoDrivers,
|
||||
(size_t)manifest->IoDriverCount * sizeof(*manifest->IoDrivers))))
|
||||
return NTLSTATUS_INVALID_IMAGE_FORMAT;
|
||||
for (uint32_t i = 0; i < manifest->IoDriverCount; ++i) {
|
||||
if (!DmImageContains(imageBase, imageSize,
|
||||
(uintptr_t)manifest->IoDrivers[i], sizeof(DRIVER_OBJECT)))
|
||||
return NTLSTATUS_INVALID_IMAGE_FORMAT;
|
||||
}
|
||||
initialize = manifest->Initialize;
|
||||
}
|
||||
|
||||
NTL_MODULE* module = (NTL_MODULE*)MmAllocatePool(sizeof(*module));
|
||||
if (!module)
|
||||
return NTLSTATUS_NO_MEMORY;
|
||||
|
||||
memset(module, 0, sizeof(*module));
|
||||
memcpy(module->Name, name, length);
|
||||
module->ImageBase = imageBase;
|
||||
module->State = DmModuleStarting;
|
||||
module->PrepareShutdown = manifest->PrepareShutdown;
|
||||
module->CommitShutdown = manifest->CommitShutdown;
|
||||
module->Next = gDriverModules;
|
||||
gDriverModules = module;
|
||||
|
||||
KSTATUS status = initialize ? initialize(module) : NTLSTATUS_SUCCESS;
|
||||
if (!KSUCCESS(status) && !KFAILED(status))
|
||||
status = NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
if (KSUCCESS(status) && manifest) {
|
||||
for (uint32_t i = 0; i < manifest->IoDriverCount; ++i) {
|
||||
if (!DmValidIoDriver(imageBase, imageSize, manifest->IoDrivers[i])) {
|
||||
status = NTLSTATUS_INVALID_IMAGE_FORMAT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (KSUCCESS(status))
|
||||
status = IoPnpPublishModule(module, manifest->Devices, manifest->DeviceCount,
|
||||
manifest->IoDrivers, manifest->IoDriverCount);
|
||||
module->State = KSUCCESS(status) ? DmModuleStarted : DmModuleFailed;
|
||||
return status;
|
||||
}
|
||||
|
||||
KSTATUS DmPrepareShutdown(void) {
|
||||
KSTATUS firstError = NTLSTATUS_SUCCESS;
|
||||
for (NTL_MODULE* module = gDriverModules; module; module = module->Next) {
|
||||
if (module->State != DmModuleStarted) continue;
|
||||
module->State = DmModuleStopping;
|
||||
if (!module->PrepareShutdown) continue;
|
||||
KSTATUS st = module->PrepareShutdown(module);
|
||||
if (!KSUCCESS(st)) {
|
||||
DbgPrint("shutdown: prepare %s failed 0x%x\n", module->Name, st);
|
||||
if (KSUCCESS(firstError)) firstError = st;
|
||||
}
|
||||
}
|
||||
return firstError;
|
||||
}
|
||||
|
||||
void DmCommitShutdown(void) {
|
||||
for (NTL_MODULE* module = gDriverModules; module; module = module->Next) {
|
||||
if (module->State != DmModuleStopping) continue;
|
||||
if (module->CommitShutdown) module->CommitShutdown(module);
|
||||
module->State = DmModuleStopped;
|
||||
}
|
||||
}
|
||||
+77
-12
@@ -5,6 +5,7 @@
|
||||
#include <kernel/hal/hal_api.h>
|
||||
#include <kernel/hal/hal_timer.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <intrin.h>
|
||||
|
||||
|
||||
@@ -34,6 +35,12 @@ static volatile uint32_t* gIoapic = (volatile uint32_t*)0;
|
||||
static uint32_t gBspLapicId = 0;
|
||||
static uint8_t gX2ApicSupported = 0;
|
||||
static uint8_t gX2ApicEnabled = 0;
|
||||
static uint32_t gIoapicGsiBase = 0;
|
||||
static uint32_t gIoapicRedirectionCount = 0;
|
||||
static uint32_t gIsaGsi[16];
|
||||
static uint16_t gIsaFlags[16];
|
||||
static uint16_t gSciIrq = UINT16_MAX;
|
||||
static bool gIsaOverridesInitialized = false;
|
||||
#define IOAPIC_IDX 0x00
|
||||
#define IOAPIC_DATA 0x10
|
||||
|
||||
@@ -188,29 +195,86 @@ KSTATUS HalApicComposeMsi(uint8_t vector, HAL_MSI_MESSAGE* messageOut) {
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Initializes the identity ISA map before processing MADT source overrides.
|
||||
static void HalApicInitializeIsaMap(void) {
|
||||
if (gIsaOverridesInitialized) return;
|
||||
for (uint32_t irq = 0; irq < 16; ++irq) {
|
||||
gIsaGsi[irq] = irq;
|
||||
gIsaFlags[irq] = 0;
|
||||
}
|
||||
gIsaOverridesInitialized = true;
|
||||
}
|
||||
|
||||
// Records the global-system-interrupt base of the currently supported IOAPIC.
|
||||
void HalApicSetGsiBase(uint32_t gsiBase) { gIoapicGsiBase = gsiBase; }
|
||||
|
||||
// Preserves MADT interrupt-source overrides for legacy ISA devices.
|
||||
void HalApicSetIsaOverride(uint8_t irq, uint32_t gsi, uint16_t flags) {
|
||||
if (irq >= 16) return;
|
||||
HalApicInitializeIsaMap();
|
||||
gIsaGsi[irq] = gsi;
|
||||
gIsaFlags[irq] = flags;
|
||||
}
|
||||
|
||||
// Marks the FADT SCI as a low-active, level-triggered interrupt by default.
|
||||
void HalApicSetSciIrq(uint16_t irq) { gSciIrq = irq; }
|
||||
|
||||
// Resolves one logical IRQ to a physical pin on the selected IOAPIC.
|
||||
static bool HalApicResolveIrq(uint8_t irq, uint32_t* pinOut, uint32_t* flagsOut) {
|
||||
HalApicInitializeIsaMap();
|
||||
uint32_t gsi = irq < 16 ? gIsaGsi[irq] : irq;
|
||||
if (gsi < gIoapicGsiBase || gsi - gIoapicGsiBase >= gIoapicRedirectionCount)
|
||||
return false;
|
||||
uint32_t flags = 0;
|
||||
if (irq == gSciIrq) flags = (1u << 13) | (1u << 15);
|
||||
if (irq < 16) {
|
||||
uint16_t override = gIsaFlags[irq];
|
||||
// MADT 00 is bus-conformant; 11 selects active low or level triggered.
|
||||
if ((override & 3u) == 3u) flags |= (1u << 13);
|
||||
else if ((override & 3u) == 1u) flags &= ~(1u << 13);
|
||||
if (((override >> 2) & 3u) == 3u) flags |= (1u << 15);
|
||||
else if (((override >> 2) & 3u) == 1u) flags &= ~(1u << 15);
|
||||
}
|
||||
*pinOut = gsi - gIoapicGsiBase;
|
||||
*flagsOut = flags;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Reports whether a platform IRQ can be routed by the current IOAPIC.
|
||||
bool HalApicCanRouteIrq(uint8_t irq) {
|
||||
uint32_t pin, flags;
|
||||
return irq < 24 && HalApicResolveIrq(irq, &pin, &flags);
|
||||
}
|
||||
|
||||
// Implements the apic Route Irq operation.
|
||||
void HalApicRouteIrq(uint8_t irq) {
|
||||
// Full initial setup of the redirection entry.
|
||||
// Sets the vector and clears the mask bit in one write.
|
||||
uint32_t vector = IRQ_VECTOR_BASE + irq; // e.g. IRQ 1 -> vector 0x21
|
||||
IoapicWrite(IOREDTBL_HI(irq), (gBspLapicId & 0xFFu) << 24); // external IRQs target the BSP
|
||||
IoapicWrite(IOREDTBL_LO(irq), vector); // unmasked (bit 16 = 0), edge, fixed
|
||||
uint32_t pin, flags;
|
||||
if (!HalApicResolveIrq(irq, &pin, &flags)) return;
|
||||
uint32_t vector = IRQ_VECTOR_BASE + irq;
|
||||
IoapicWrite(IOREDTBL_HI(pin), (gBspLapicId & 0xFFu) << 24);
|
||||
IoapicWrite(IOREDTBL_LO(pin), vector | flags);
|
||||
}
|
||||
|
||||
// Implements the apic Mask Irq operation.
|
||||
void HalApicMaskIrq(uint8_t irq) {
|
||||
uint32_t lo = IoapicRead(IOREDTBL_LO(irq));
|
||||
uint32_t pin, flags;
|
||||
if (!HalApicResolveIrq(irq, &pin, &flags)) return;
|
||||
uint32_t lo = IoapicRead(IOREDTBL_LO(pin));
|
||||
lo |= (1 << 16);
|
||||
IoapicWrite(IOREDTBL_LO(irq), lo);
|
||||
IoapicWrite(IOREDTBL_LO(pin), lo);
|
||||
}
|
||||
|
||||
// Implements the apic Unmask Irq operation.
|
||||
void HalApicUnmaskIrq(uint8_t irq) {
|
||||
uint32_t lo = IoapicRead(IOREDTBL_LO(irq));
|
||||
lo &= ~(1 << 16); // clear mask bit
|
||||
lo = (lo & ~0xFF) | (IRQ_VECTOR_BASE + irq);
|
||||
IoapicWrite(IOREDTBL_HI(irq), (gBspLapicId & 0xFFu) << 24);
|
||||
IoapicWrite(IOREDTBL_LO(irq), lo);
|
||||
uint32_t pin, flags;
|
||||
if (!HalApicResolveIrq(irq, &pin, &flags)) return;
|
||||
uint32_t lo = IoapicRead(IOREDTBL_LO(pin));
|
||||
lo = (lo & ~(0xFFu | (1u << 16) | (1u << 13) | (1u << 15))) |
|
||||
(IRQ_VECTOR_BASE + irq) | flags;
|
||||
IoapicWrite(IOREDTBL_HI(pin), (gBspLapicId & 0xFFu) << 24);
|
||||
IoapicWrite(IOREDTBL_LO(pin), lo);
|
||||
}
|
||||
|
||||
// Implements the apic Send Eoi operation.
|
||||
@@ -240,8 +304,9 @@ void HalApicInitialize(uintptr_t lapicPhys, uintptr_t ioapicPhys) {
|
||||
|
||||
// Mask all IOAPIC lines initially.
|
||||
uint32_t maxRedir = (IoapicRead(1) >> 16) & 0xFF;
|
||||
for (uint8_t i = 0; i <= maxRedir; i++) {
|
||||
IoapicWrite(IOREDTBL_LO(i), (1 << 16) | (IRQ_VECTOR_BASE + i));
|
||||
gIoapicRedirectionCount = maxRedir + 1u;
|
||||
for (uint32_t i = 0; i <= maxRedir; i++) {
|
||||
IoapicWrite(IOREDTBL_LO(i), (1u << 16) | IRQ_VECTOR_BASE);
|
||||
IoapicWrite(IOREDTBL_HI(i), (gBspLapicId & 0xFFu) << 24);
|
||||
}
|
||||
}
|
||||
@@ -122,6 +122,12 @@ typedef struct ACPI_FIXED_REGISTER {
|
||||
|
||||
static ACPI_FIXED_REGISTER gPm1aCnt;
|
||||
static ACPI_FIXED_REGISTER gPm1bCnt;
|
||||
static ACPI_FIXED_REGISTER gPm1aStatus;
|
||||
static ACPI_FIXED_REGISTER gPm1bStatus;
|
||||
static ACPI_FIXED_REGISTER gPm1aEnable;
|
||||
static ACPI_FIXED_REGISTER gPm1bEnable;
|
||||
static uint16_t gSciIrq = 0;
|
||||
static bool gFixedPowerButtonArmed = false;
|
||||
static ACPI_FIXED_REGISTER gSleepControl;
|
||||
static uint32_t gLegacyPm1aPort = 0;
|
||||
static uint32_t gLegacyPm1bPort = 0;
|
||||
@@ -172,6 +178,14 @@ void* HalPhysicalToVirtual(uint64_t phys) {
|
||||
// FADT (Fixed ACPI Description Table) offsets used here. ACPI 2.0+
|
||||
// systems may put the real DSDT and PM1 control registers in the extended
|
||||
// X_* fields, so do not rely only on the legacy 32-bit fields.
|
||||
#define FADT_SCI_INT_OFFSET 46u
|
||||
#define FADT_PM1A_EVT_BLK_OFFSET 56u
|
||||
#define FADT_PM1B_EVT_BLK_OFFSET 60u
|
||||
#define FADT_PM1_EVT_LEN_OFFSET 88u
|
||||
#define FADT_X_PM1A_EVT_BLK_OFFSET 148u
|
||||
#define FADT_X_PM1B_EVT_BLK_OFFSET 160u
|
||||
#define FADT_FLAG_POWER_BUTTON (1u << 4)
|
||||
#define ACPI_PM1_POWER_BUTTON_BIT (1u << 8)
|
||||
#define FADT_DSDT_OFFSET 40u
|
||||
#define FADT_SMI_CMD_OFFSET 48u
|
||||
#define FADT_ACPI_ENABLE_OFFSET 52u
|
||||
@@ -184,6 +198,8 @@ void* HalPhysicalToVirtual(uint64_t phys) {
|
||||
#define FADT_X_PM1A_CNT_BLK_OFFSET 172u
|
||||
#define FADT_X_PM1B_CNT_BLK_OFFSET 184u
|
||||
#define FADT_SLEEP_CONTROL_OFFSET 244u
|
||||
#define FADT_RESET_REG_OFFSET 116u
|
||||
#define FADT_RESET_VALUE_OFFSET 128u
|
||||
#define FADT_FLAG_HW_REDUCED_ACPI (1u << 20)
|
||||
#define INTEL_PCH_SMI_EN_OFFSET 0x30u
|
||||
#define INTEL_PCH_SMI_ON_SLP_EN (1u << 4)
|
||||
@@ -198,6 +214,9 @@ typedef struct {
|
||||
} ACPI_GAS;
|
||||
#pragma pack(pop)
|
||||
|
||||
static ACPI_FIXED_REGISTER gResetRegister;
|
||||
static uint8_t gResetValue;
|
||||
|
||||
// Implements the acpi Read U32 operation.
|
||||
static uint32_t AcpiReadU32(const void* table, uint32_t tableLen, uint32_t off) {
|
||||
uint32_t value = 0;
|
||||
@@ -291,6 +310,77 @@ static void AcpiWriteFixedRegister(const ACPI_FIXED_REGISTER* reg, uint32_t valu
|
||||
else *(volatile uint32_t*)address = value;
|
||||
}
|
||||
|
||||
// Selects a PM1 event block as two adjacent equal-width status and enable registers.
|
||||
static void AcpiConfigurePm1EventBlock(const void* fadt, uint32_t len,
|
||||
uint32_t legacyOffset, uint32_t gasOffset,
|
||||
uint8_t halfLength, ACPI_FIXED_REGISTER* status,
|
||||
ACPI_FIXED_REGISTER* enable) {
|
||||
ACPI_FIXED_REGISTER block = {0};
|
||||
uint32_t legacy = AcpiReadU32(fadt, len, legacyOffset);
|
||||
if (legacy && legacy <= 0xFFFFu) {
|
||||
block.AddressSpaceId = 1;
|
||||
block.Address = legacy;
|
||||
}
|
||||
ACPI_FIXED_REGISTER extended = {0};
|
||||
if (AcpiReadGasRegister(fadt, len, gasOffset, &extended)) block = extended;
|
||||
if (!block.Address || (block.AddressSpaceId == 1 &&
|
||||
block.Address + halfLength * 2u - 1u > 0xFFFFu)) return;
|
||||
block.WidthBytes = halfLength;
|
||||
*status = block;
|
||||
block.Address += halfLength;
|
||||
*enable = block;
|
||||
}
|
||||
|
||||
// Reports the fixed-feature power-button interrupt supported by this HAL.
|
||||
static KSTATUS AcpiQueryFixedButtonIrq(uint8_t* irqOut) {
|
||||
if (!irqOut) return NTLSTATUS_INVALID_PARAMETER;
|
||||
// A FADT control-method power button requires AML evaluation, not PM1 polling.
|
||||
if (gHwReducedAcpi || (gFadtFlags & FADT_FLAG_POWER_BUTTON) ||
|
||||
!gSciIrq || gSciIrq >= 16u || !gPm1aStatus.Address || !gPm1aEnable.Address ||
|
||||
!HalApicCanRouteIrq((uint8_t)gSciIrq))
|
||||
return NTLSTATUS_NOT_SUPPORTED;
|
||||
*irqOut = (uint8_t)gSciIrq;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Discards any stale press then enables the PM1 fixed power-button SCI.
|
||||
static KSTATUS AcpiArmFixedButton(void) {
|
||||
uint8_t irq;
|
||||
KSTATUS status = AcpiQueryFixedButtonIrq(&irq);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
// No SCI_EN means firmware still owns ACPI events. Never steal ownership
|
||||
// through SMI_CMD just to install a power-button handler during boot.
|
||||
if (!(AcpiReadFixedRegister(&gPm1aCnt) & 1u))
|
||||
return NTLSTATUS_NOT_SUPPORTED;
|
||||
AcpiWriteFixedRegister(&gPm1aStatus, ACPI_PM1_POWER_BUTTON_BIT);
|
||||
if (gPm1bStatus.Address)
|
||||
AcpiWriteFixedRegister(&gPm1bStatus, ACPI_PM1_POWER_BUTTON_BIT);
|
||||
uint32_t enabled = AcpiReadFixedRegister(&gPm1aEnable);
|
||||
AcpiWriteFixedRegister(&gPm1aEnable, enabled | ACPI_PM1_POWER_BUTTON_BIT);
|
||||
if (gPm1bEnable.Address) {
|
||||
enabled = AcpiReadFixedRegister(&gPm1bEnable);
|
||||
AcpiWriteFixedRegister(&gPm1bEnable, enabled | ACPI_PM1_POWER_BUTTON_BIT);
|
||||
}
|
||||
gFixedPowerButtonArmed = true;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Acknowledges the fixed-button source only; the SCI may carry unrelated events.
|
||||
static bool AcpiConsumeFixedButton(void) {
|
||||
if (!gFixedPowerButtonArmed) return false;
|
||||
bool pressed = false;
|
||||
if (AcpiReadFixedRegister(&gPm1aStatus) & ACPI_PM1_POWER_BUTTON_BIT) {
|
||||
AcpiWriteFixedRegister(&gPm1aStatus, ACPI_PM1_POWER_BUTTON_BIT);
|
||||
pressed = true;
|
||||
}
|
||||
if (gPm1bStatus.Address &&
|
||||
(AcpiReadFixedRegister(&gPm1bStatus) & ACPI_PM1_POWER_BUTTON_BIT)) {
|
||||
AcpiWriteFixedRegister(&gPm1bStatus, ACPI_PM1_POWER_BUTTON_BIT);
|
||||
pressed = true;
|
||||
}
|
||||
return pressed;
|
||||
}
|
||||
|
||||
// Detects whether PM1 control is using the standard Intel PCH PMBASE layout.
|
||||
static void AcpiDetectIntelPchPmBase(void) {
|
||||
gIntelPchPmBase = 0;
|
||||
@@ -490,10 +580,20 @@ static void ParseFadt(const ACPISDTHeader* header) {
|
||||
|
||||
memset(&gPm1aCnt, 0, sizeof(gPm1aCnt));
|
||||
memset(&gPm1bCnt, 0, sizeof(gPm1bCnt));
|
||||
memset(&gPm1aStatus, 0, sizeof(gPm1aStatus));
|
||||
memset(&gPm1bStatus, 0, sizeof(gPm1bStatus));
|
||||
memset(&gPm1aEnable, 0, sizeof(gPm1aEnable));
|
||||
memset(&gPm1bEnable, 0, sizeof(gPm1bEnable));
|
||||
gSciIrq = 0;
|
||||
if (len >= FADT_SCI_INT_OFFSET + sizeof(gSciIrq))
|
||||
memcpy(&gSciIrq, (const uint8_t*)fadt + FADT_SCI_INT_OFFSET, sizeof(gSciIrq));
|
||||
memset(&gSleepControl, 0, sizeof(gSleepControl));
|
||||
gLegacyPm1aPort = 0;
|
||||
gLegacyPm1bPort = 0;
|
||||
gFadtFlags = AcpiReadU32(fadt, len, FADT_FLAGS_OFFSET);
|
||||
if (AcpiReadByteGasRegister(fadt, len, FADT_RESET_REG_OFFSET, &gResetRegister) &&
|
||||
FADT_RESET_VALUE_OFFSET < len)
|
||||
gResetValue = *((const uint8_t*)fadt + FADT_RESET_VALUE_OFFSET);
|
||||
gHwReducedAcpi = (gFadtFlags & FADT_FLAG_HW_REDUCED_ACPI) != 0;
|
||||
|
||||
uint8_t pm1CntLength = FADT_PM1_CNT_LEN_OFFSET < len
|
||||
@@ -528,6 +628,16 @@ static void ParseFadt(const ACPISDTHeader* header) {
|
||||
if (gHwReducedAcpi)
|
||||
(void)AcpiReadByteGasRegister(fadt, len, FADT_SLEEP_CONTROL_OFFSET, &gSleepControl);
|
||||
|
||||
uint8_t eventLength = FADT_PM1_EVT_LEN_OFFSET < len ?
|
||||
*((const uint8_t*)fadt + FADT_PM1_EVT_LEN_OFFSET) : 0u;
|
||||
if (!gHwReducedAcpi && (eventLength == 4u || eventLength == 8u)) {
|
||||
uint8_t half = eventLength / 2u;
|
||||
AcpiConfigurePm1EventBlock(fadt, len, FADT_PM1A_EVT_BLK_OFFSET,
|
||||
FADT_X_PM1A_EVT_BLK_OFFSET, half, &gPm1aStatus, &gPm1aEnable);
|
||||
AcpiConfigurePm1EventBlock(fadt, len, FADT_PM1B_EVT_BLK_OFFSET,
|
||||
FADT_X_PM1B_EVT_BLK_OFFSET, half, &gPm1bStatus, &gPm1bEnable);
|
||||
}
|
||||
|
||||
gSmiCommandPort = AcpiReadU32(fadt, len, FADT_SMI_CMD_OFFSET);
|
||||
gAcpiEnableValue = FADT_ACPI_ENABLE_OFFSET < len
|
||||
? *((const uint8_t*)fadt + FADT_ACPI_ENABLE_OFFSET)
|
||||
@@ -599,6 +709,15 @@ static void ParseMadt(MADT* madt) {
|
||||
HalAddCpuRecord(uid, lapicId, flags);
|
||||
} else if (type == 1 && len >= 12) { // I/O APIC entry
|
||||
gIoapicPhys = *(uint32_t*)(p + 4);
|
||||
uint32_t base;
|
||||
memcpy(&base, p + 8, sizeof(base));
|
||||
HalApicSetGsiBase(base);
|
||||
} else if (type == 2 && len >= 10 && p[2] == 0) { // ISA source override
|
||||
uint32_t gsi;
|
||||
uint16_t flags;
|
||||
memcpy(&gsi, p + 4, sizeof(gsi));
|
||||
memcpy(&flags, p + 8, sizeof(flags));
|
||||
HalApicSetIsaOverride(p[3], gsi, flags);
|
||||
} else if (type == 5 && len >= 12) { // Local APIC Address Override
|
||||
gLapicPhys = (uintptr_t)*(uint64_t*)(p + 4);
|
||||
} else if (type == 9 && len >= 16) { // Processor Local x2APIC
|
||||
@@ -796,6 +915,19 @@ static void HalShutdown(void) {
|
||||
HalStopProcessor();
|
||||
}
|
||||
|
||||
static void HalReset(void) {
|
||||
(void)HalDisableInterrupts();
|
||||
if (gResetRegister.Address && gResetValue)
|
||||
AcpiWriteFixedRegister(&gResetRegister, gResetValue);
|
||||
if (gApi.Timer.Stall) gApi.Timer.Stall(100000u);
|
||||
__outbyte(0xCF9u, 0x06u);
|
||||
if (gApi.Timer.Stall) gApi.Timer.Stall(100000u);
|
||||
__outbyte(0x64u, 0xFEu);
|
||||
if (gApi.Timer.Stall) gApi.Timer.Stall(100000u);
|
||||
HalDbgPrint("HAL: reset failed\n");
|
||||
HalStopProcessor();
|
||||
}
|
||||
|
||||
static KSTATUS HalQueryProcessorTopology(HAL_PROCESSOR_TOPOLOGY* out) {
|
||||
if (!out) return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
@@ -863,6 +995,7 @@ HAL_ENTRY KSTATUS HalInitialize(const HAL_INIT_PARAMS* initParams) {
|
||||
HalDbgPrint("HAL: LAPIC=%016llx IOAPIC=%016llx HPET=%016llx\n",
|
||||
gLapicPhys, gIoapicPhys, gHpetPhys);
|
||||
|
||||
HalApicSetSciIrq(gSciIrq);
|
||||
HalApicInitialize(gLapicPhys, gIoapicPhys);
|
||||
HalDbgPrint("HAL: local APIC mode=%s BSP_ID=%u\n",
|
||||
HalApicUsingX2Apic() ? "x2APIC" : "xAPIC",
|
||||
@@ -874,6 +1007,10 @@ HAL_ENTRY KSTATUS HalInitialize(const HAL_INIT_PARAMS* initParams) {
|
||||
HalRtcInitialize(&gApi.Rtc);
|
||||
HalPciInitialize(&gApi.Pci);
|
||||
gApi.Power.Shutdown = HalShutdown;
|
||||
gApi.Power.Reset = HalReset;
|
||||
gApi.Power.QueryFixedButtonIrq = AcpiQueryFixedButtonIrq;
|
||||
gApi.Power.ArmFixedButton = AcpiArmFixedButton;
|
||||
gApi.Power.ConsumeFixedButton = AcpiConsumeFixedButton;
|
||||
|
||||
AcpiDetectIntelPchPmBase();
|
||||
gInitialised = 1;
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
// Kernel driver configuration and dependency graph contract.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define DM_MAX_DRIVERS 64u
|
||||
#define DM_MAX_CLASSES 32u
|
||||
#define DM_MAX_EDGES 16u
|
||||
#define DM_ID_MAX 64u
|
||||
#define DM_IMAGE_MAX 64u
|
||||
#define DM_CONFIG_MAX_BYTES (64u * 1024u)
|
||||
|
||||
typedef enum DM_START_PHASE {
|
||||
DmStartBoot,
|
||||
DmStartRuntime,
|
||||
DmStartDisabled,
|
||||
} DM_START_PHASE;
|
||||
|
||||
typedef enum DM_DEPENDENCY_KIND {
|
||||
DmDependencyClass,
|
||||
DmDependencyDriver,
|
||||
} DM_DEPENDENCY_KIND;
|
||||
|
||||
typedef struct DM_DEPENDENCY {
|
||||
DM_DEPENDENCY_KIND Kind;
|
||||
char Id[DM_ID_MAX];
|
||||
} DM_DEPENDENCY;
|
||||
|
||||
typedef struct DM_EDGES {
|
||||
DM_DEPENDENCY Items[DM_MAX_EDGES];
|
||||
uint32_t Count;
|
||||
} DM_EDGES;
|
||||
|
||||
typedef enum DM_RUN_STATE {
|
||||
DmRunWaiting,
|
||||
DmRunStarted,
|
||||
DmRunFailed,
|
||||
DmRunBlocked,
|
||||
DmRunDisabled,
|
||||
} DM_RUN_STATE;
|
||||
|
||||
typedef struct DM_DRIVER_CONFIG {
|
||||
char Id[DM_ID_MAX];
|
||||
char Image[DM_IMAGE_MAX];
|
||||
char Class[DM_ID_MAX];
|
||||
DM_EDGES Requires;
|
||||
DM_EDGES After;
|
||||
DM_START_PHASE Phase;
|
||||
DM_RUN_STATE State;
|
||||
bool Critical;
|
||||
uint8_t Present;
|
||||
} DM_DRIVER_CONFIG;
|
||||
|
||||
typedef struct DM_CLASS_CONFIG {
|
||||
char Id[DM_ID_MAX];
|
||||
DM_EDGES Requires;
|
||||
DM_EDGES After;
|
||||
uint8_t Present;
|
||||
} DM_CLASS_CONFIG;
|
||||
|
||||
typedef struct DM_CONFIG {
|
||||
DM_DRIVER_CONFIG Drivers[DM_MAX_DRIVERS];
|
||||
DM_CLASS_CONFIG Classes[DM_MAX_CLASSES];
|
||||
uint32_t DriverCount;
|
||||
uint32_t ClassCount;
|
||||
DM_START_PHASE ActivePhase;
|
||||
} DM_CONFIG;
|
||||
|
||||
// Parses and validates a complete immutable configuration before any driver starts.
|
||||
// Empty optional values and omitted optional keys have identical semantics.
|
||||
bool DmConfigParse(DM_CONFIG* config, const char* bytes, size_t size,
|
||||
char* error, size_t errorCapacity);
|
||||
|
||||
// Returns true once every configured provider in this class has reached a terminal state.
|
||||
bool DmClassSettled(const DM_CONFIG* config, const char* classId);
|
||||
bool DmClassAvailable(const DM_CONFIG* config, const char* classId);
|
||||
|
||||
// Tests the current requirements/ordering gates for a single waiting module.
|
||||
bool DmDriverCanStart(const DM_CONFIG* config, uint32_t index);
|
||||
|
||||
// Returns true once a waiting driver's required dependency cannot become available.
|
||||
bool DmDriverIsBlocked(const DM_CONFIG* config, uint32_t index);
|
||||
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <ntlite/status.h>
|
||||
|
||||
|
||||
KSTATUS DmStartLoadedDriver(const char* name, uintptr_t imageBase);
|
||||
KSTATUS DmPrepareShutdown(void);
|
||||
void DmCommitShutdown(void);
|
||||
@@ -185,9 +185,14 @@ typedef struct HAL_INTERRUPT_CONTROLLER {
|
||||
} HAL_INTERRUPT_CONTROLLER;
|
||||
|
||||
typedef struct HAL_POWER {
|
||||
// ACPI fixed-feature button events. Control-method devices require an AML owner.
|
||||
KSTATUS (*QueryFixedButtonIrq)(uint8_t* irqOut);
|
||||
KSTATUS (*ArmFixedButton)(void);
|
||||
bool (*ConsumeFixedButton)(void);
|
||||
// ACPI S5 soft-off. Disables interrupts, writes SLP_EN to PM1a/b_CNT,
|
||||
// and halts if firmware doesn't respond. Does not return.
|
||||
void (*Shutdown)(void);
|
||||
void (*Reset)(void);
|
||||
} HAL_POWER;
|
||||
|
||||
typedef struct HAL_API {
|
||||
@@ -226,7 +231,7 @@ typedef struct HAL_INIT_PARAMS {
|
||||
} HAL_INIT_PARAMS;
|
||||
|
||||
// Increment whenever the kernel/HAL table ABI changes.
|
||||
#define HAL_API_VERSION 2u
|
||||
#define HAL_API_VERSION 4u
|
||||
|
||||
// Returns the HAL table. Processor interrupt-state operations are valid before HalInitialize().
|
||||
HAL_ENTRY const HAL_API* HalGetApi(void);
|
||||
|
||||
@@ -7,6 +7,10 @@
|
||||
#include <kernel/hal/hal_api.h>
|
||||
|
||||
void HalApicRouteIrq(uint8_t irq);
|
||||
void HalApicSetGsiBase(uint32_t gsiBase);
|
||||
void HalApicSetIsaOverride(uint8_t irq, uint32_t gsi, uint16_t flags);
|
||||
void HalApicSetSciIrq(uint16_t irq);
|
||||
bool HalApicCanRouteIrq(uint8_t irq);
|
||||
void HalApicMaskIrq(uint8_t irq);
|
||||
void HalApicUnmaskIrq(uint8_t irq);
|
||||
void HalApicSendEoi(void);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Kernel DDK-table provider declarations.
|
||||
// Kernel driver service initialization.
|
||||
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
@@ -8,8 +8,5 @@
|
||||
struct HAL_API;
|
||||
typedef struct HAL_API HAL_API;
|
||||
|
||||
// Initializes the kernel-owned DDK table from platform services.
|
||||
void IoInitializeDdk(const HAL_API* hal, uintptr_t physicalWindowBase);
|
||||
|
||||
// Returns the kernel-owned DDK table passed to driver entrypoints.
|
||||
const NTL_DDK* IoGetDdk(void);
|
||||
void IoInitializeDdk(const HAL_API* hal, uintptr_t physicalWindowBase);
|
||||
@@ -4,6 +4,85 @@
|
||||
|
||||
#include <ntlite/ddk/dma.h>
|
||||
|
||||
typedef struct NTL_DDK_DMA {
|
||||
// Returns the one adapter and requirement set owned by a device object.
|
||||
KSTATUS (*GetAdapter)(struct DEVICE_OBJECT* device,
|
||||
const IO_DMA_REQUIREMENTS* requirements,
|
||||
IO_DMA_ADAPTER** outAdapter);
|
||||
|
||||
// Releases one caller-owned adapter reference.
|
||||
void (*PutAdapter)(IO_DMA_ADAPTER* adapter);
|
||||
|
||||
// Allocates coherent memory with stable CPU and base device addresses. The
|
||||
// caller must quiesce every device reference before releasing the buffer.
|
||||
KSTATUS (*AllocateCommonBuffer)(IO_DMA_ADAPTER* adapter,
|
||||
size_t size,
|
||||
size_t alignment,
|
||||
IO_DMA_BUFFER** outBuffer);
|
||||
|
||||
// Releases a coherent common buffer.
|
||||
void (*FreeCommonBuffer)(IO_DMA_BUFFER* buffer);
|
||||
|
||||
// Returns the stable CPU address of a common buffer.
|
||||
void* (*GetCommonBufferAddress)(const IO_DMA_BUFFER* buffer);
|
||||
|
||||
// Returns the stable device address of a common buffer.
|
||||
uint64_t (*GetCommonBufferDeviceAddress)(const IO_DMA_BUFFER* buffer);
|
||||
|
||||
// Returns the caller-requested size of a common buffer.
|
||||
size_t (*GetCommonBufferSize)(const IO_DMA_BUFFER* buffer);
|
||||
|
||||
// Stabilizes a kernel range for one or more streaming mappings. Pinning
|
||||
// preserves the pages, but does not transfer ownership to a device.
|
||||
KSTATUS (*PinKernelMemory)(void* address,
|
||||
size_t size,
|
||||
IO_DMA_DIRECTION direction,
|
||||
MM_PINNED_MEMORY** outMemory);
|
||||
|
||||
// Stabilizes a process-owned user range for one or more mappings. The
|
||||
// descriptor retains the process and range until the final unpin.
|
||||
KSTATUS (*PinUserMemory)(struct PROCESS* process,
|
||||
void* address,
|
||||
size_t size,
|
||||
IO_DMA_DIRECTION direction,
|
||||
MM_PINNED_MEMORY** outMemory);
|
||||
|
||||
// Releases one pinned-memory reference after all mappings leave.
|
||||
void (*UnpinMemory)(MM_PINNED_MEMORY* memory);
|
||||
|
||||
// Maps a pinned subrange and creates a constrained segment list. A new
|
||||
// mapping begins CPU-owned and requires external operation serialization.
|
||||
KSTATUS (*MapPinnedMemory)(IO_DMA_ADAPTER* adapter,
|
||||
MM_PINNED_MEMORY* memory,
|
||||
size_t offset,
|
||||
size_t length,
|
||||
IO_DMA_DIRECTION direction,
|
||||
uint32_t flags,
|
||||
IO_DMA_MAPPING** outMapping);
|
||||
|
||||
// Releases a mapping and performs a final receive-side bounce copy only
|
||||
// when ownership was transferred to the device and not returned to the CPU.
|
||||
void (*UnmapMemory)(IO_DMA_MAPPING* mapping);
|
||||
|
||||
// Transfers mapping ownership from the CPU to the device. Streaming DMA
|
||||
// must not begin before this operation succeeds.
|
||||
KSTATUS (*SynchronizeForDevice)(IO_DMA_MAPPING* mapping);
|
||||
|
||||
// Transfers mapping ownership from the device to the CPU. CPU readers must
|
||||
// not consume receive data before this operation succeeds.
|
||||
KSTATUS (*SynchronizeForCpu)(IO_DMA_MAPPING* mapping);
|
||||
|
||||
// Returns the immutable segment count owned by a mapping.
|
||||
uint32_t (*GetSegmentCount)(const IO_DMA_MAPPING* mapping);
|
||||
|
||||
// Returns the immutable segment array owned by a mapping.
|
||||
const IO_DMA_SEGMENT* (*GetSegments)(const IO_DMA_MAPPING* mapping);
|
||||
|
||||
// Copies current DMA counters into caller-owned storage.
|
||||
void (*QueryStats)(IO_DMA_STATS* outStats);
|
||||
} NTL_DDK_DMA;
|
||||
|
||||
|
||||
struct DEVICE_OBJECT;
|
||||
|
||||
// Returns the kernel-owned DDK DMA table.
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <ntlite/status.h>
|
||||
#include <ntlite/ddk/device.h>
|
||||
|
||||
struct DEVICE_OBJECT;
|
||||
struct NTL_MODULE;
|
||||
struct DRIVER_OBJECT;
|
||||
|
||||
KSTATUS IoPnpInitialize(void);
|
||||
KSTATUS IoPnpPublishModule(struct NTL_MODULE* module,
|
||||
const NTL_DEVICE_DESC* descriptors, uint32_t count,
|
||||
struct DRIVER_OBJECT* const* ioDrivers, uint32_t ioCount);
|
||||
KSTATUS IoPnpPhysicalDeviceArrived(struct DEVICE_OBJECT* pdo);
|
||||
void IoPnpPhysicalDevicePublished(struct DEVICE_OBJECT* pdo);
|
||||
void IoPnpPhysicalDeviceRemoved(struct DEVICE_OBJECT* pdo);
|
||||
KSTATUS IoPnpWaitForIdle(uint64_t timeoutMs);
|
||||
KSTATUS IoPnpShutdown(uint64_t timeoutMs);
|
||||
@@ -5,6 +5,34 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <ntlite/ddk/audio.h>
|
||||
|
||||
typedef struct NTL_DDK_AUDIO {
|
||||
// Registers one endpoint and returns a provider-owned reference valid until unregister returns.
|
||||
KSTATUS (*RegisterEndpoint)(const IO_AUDIO_ENDPOINT_REGISTRATION* registration,
|
||||
IO_AUDIO_ENDPOINT** outEndpoint);
|
||||
|
||||
// Stops the endpoint, removes it from enumeration, and consumes the provider-owned reference.
|
||||
void (*UnregisterEndpoint)(IO_AUDIO_ENDPOINT* endpoint, IO_AUDIO_REMOVAL_MODE mode);
|
||||
|
||||
// Reports one completed hardware period from ISR/DPC completion processing.
|
||||
// presentedFrames, hardwarePositionFrames, and interruptCount must be monotonic for the
|
||||
// endpoint lifetime. timestampTicks uses the DDK monotonic microsecond source.
|
||||
void (*NotifyPeriod)(IO_AUDIO_ENDPOINT* endpoint,
|
||||
uint32_t completedPeriodIndex,
|
||||
uint64_t presentedFrames,
|
||||
uint64_t hardwarePositionFrames,
|
||||
uint64_t interruptCount,
|
||||
uint64_t timestampTicks);
|
||||
|
||||
// Updates jack-derived endpoint availability without changing endpoint identity.
|
||||
void (*SetEndpointAvailable)(IO_AUDIO_ENDPOINT* endpoint, bool available);
|
||||
|
||||
// Fails an endpoint and wakes every stream owner.
|
||||
void (*ReportEndpointFailure)(IO_AUDIO_ENDPOINT* endpoint,
|
||||
KSTATUS status,
|
||||
uint32_t flags);
|
||||
} NTL_DDK_AUDIO;
|
||||
|
||||
#include <kernel/ob_object.h>
|
||||
#include <kernel/ke_event.h>
|
||||
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
// Named queue registry hooks shared by process handle cleanup and syscalls.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <ntlite/handle.h>
|
||||
|
||||
struct PROCESS;
|
||||
struct OBJECT;
|
||||
|
||||
// Removes registry entries owned by the given process handle/object pair.
|
||||
void KeNamedQueueUnpublishHandle(struct PROCESS* process, SYS_HANDLE handle, struct OBJECT* object);
|
||||
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
#include <ntlite/status.h>
|
||||
#include <stdint.h>
|
||||
|
||||
struct PROCESS;
|
||||
struct EVENT;
|
||||
|
||||
struct HAL_API;
|
||||
KSTATUS KePowerInitialize(void);
|
||||
void KePowerInitializeButton(const struct HAL_API* hal);
|
||||
void KePowerSetInitProcess(struct PROCESS* init);
|
||||
KSTATUS KePowerRequest(uint32_t action);
|
||||
KSTATUS KePowerRequestHardware(uint32_t action);
|
||||
KSTATUS KePowerGetRequest(struct PROCESS* caller, uint32_t* actionOut);
|
||||
KSTATUS KePowerComplete(struct PROCESS* caller, uint32_t action);
|
||||
struct EVENT* KePowerRequestEvent(struct PROCESS* caller);
|
||||
struct EVENT* KePowerCompletionEvent(void);
|
||||
uint32_t KePowerCommittedAction(void);
|
||||
@@ -1,10 +1,6 @@
|
||||
// Kernel ke selftest declarations shared by implementation files and kernel clients.
|
||||
// Debug-only bootstrap checks and focused kernel diagnostics.
|
||||
|
||||
#pragma once
|
||||
// ke_selftest.h - kernel bring-up and regression self-tests.
|
||||
|
||||
#include <kernel/hal/hal_api.h>
|
||||
|
||||
void KiRunCoreHardeningSelfTests(void);
|
||||
void KiRunEarlyKernelSelfTests(void);
|
||||
void KiRunKernelSelfTests(const HAL_API* hal);
|
||||
void KiCheckBootState(void);
|
||||
void KiRunKernelSelfTests(void);
|
||||
@@ -19,8 +19,3 @@ KSTATUS KeAdmitApplicationProcessorsToScheduler(void);
|
||||
// KeStartApplicationProcessors calls this after MADT enumeration; it is also
|
||||
// safe to call again after failed AP startup disables a CPU record.
|
||||
void KeBuildProcessorTopology(void);
|
||||
|
||||
// Run explicit AP-affine kernel workers that exercise the global scheduler,
|
||||
// reschedule IPIs, yield, and event wait/wake. Normal kernel/user/driver
|
||||
// threads remain boot-processor-affine unless their affinity is explicitly widened.
|
||||
KSTATUS KeRunApplicationProcessorSchedulerSelfTests(void);
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
#include <ntlite/service.h>
|
||||
#include <ntlite/status.h>
|
||||
#include <ntlite/security.h>
|
||||
#include <ntlite/ddk/object.h>
|
||||
|
||||
struct PORT;
|
||||
|
||||
@@ -20,5 +20,9 @@ KSTATUS KeSystemServiceReference(SYS_SERVICE_ID serviceId, struct PORT** listene
|
||||
// Removes every service entry that refers to a listener being destroyed.
|
||||
void KeSystemServiceUnregisterListener(struct PORT* listener);
|
||||
|
||||
// Returns the security-context claim required to register one service identity.
|
||||
SYS_SERVICE_CLAIMS KeSystemServiceRequiredClaim(SYS_SERVICE_ID serviceId);
|
||||
// Creates one kernel-issued, typed authority for a numeric registry identity.
|
||||
// Caller owns the returned object reference and may issue attenuated handles.
|
||||
KSTATUS KeSystemServiceCreateRegistrationAuthority(SYS_SERVICE_ID serviceId, OBJECT** authorityOut);
|
||||
|
||||
// Reads the bound identity from a referenced, type-checked authority.
|
||||
KSTATUS KeSystemServiceRegistrationIdentity(const OBJECT* authority, SYS_SERVICE_ID* serviceIdOut);
|
||||
@@ -11,25 +11,31 @@
|
||||
|
||||
#define SYS_PEB_MAGIC 0x30424550u // "PEB0"
|
||||
#define SYS_TEB_MAGIC 0x30424554u // "TEB0"
|
||||
#define SYS_ENV_VERSION 4u
|
||||
#define SYS_ENV_VERSION 5u
|
||||
|
||||
#ifndef SYS_USER_ENV_TYPES_DEFINED
|
||||
#define SYS_USER_ENV_TYPES_DEFINED 1
|
||||
|
||||
#define SYS_STARTUP_CAP_SYSTEM_VOLUME 1u
|
||||
#define SYS_STARTUP_CAP_SERVICE_CONTROL 2u
|
||||
#define SYS_STARTUP_CAP_SYSTEM_VOLUME_NAMESPACE 3u
|
||||
#define SYS_STARTUP_CAP_VOLUME_MANAGER 4u
|
||||
#define SYS_STARTUP_CAP_INPUT_MANAGER 5u
|
||||
#define SYS_STARTUP_CAP_DISPLAY_MANAGER 6u
|
||||
#define SYS_STARTUP_CAP_SESSION_CONTROL 7u
|
||||
#define SYS_STARTUP_CAPABILITY_MAX 32u
|
||||
|
||||
typedef struct SYS_STARTUP_CAPABILITY {
|
||||
uint32_t Tag;
|
||||
char Name[SYS_STARTUP_CAPABILITY_NAME_MAX + 1u];
|
||||
SYS_HANDLE Handle;
|
||||
} SYS_STARTUP_CAPABILITY;
|
||||
|
||||
// Kernel-side validation of canonical process startup capability names.
|
||||
static inline int SysStartupCapabilityNameValid(const char name[SYS_STARTUP_CAPABILITY_NAME_MAX + 1u]) {
|
||||
if (!name || !name[0]) return 0;
|
||||
int terminated = 0;
|
||||
for (uint32_t i = 0; i <= SYS_STARTUP_CAPABILITY_NAME_MAX; ++i) {
|
||||
char c = name[i];
|
||||
if (!c) { terminated = 1; continue; }
|
||||
if (terminated || !((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
|
||||
(c >= '0' && c <= '9') || c == '_' || c == '-' || c == '.')) return 0;
|
||||
}
|
||||
return terminated;
|
||||
}
|
||||
|
||||
typedef struct SYS_PEB {
|
||||
uint32_t Magic;
|
||||
uint32_t Version;
|
||||
|
||||
@@ -20,6 +20,9 @@ KSTATUS LdrLoadImageFileForProcess(struct PROCESS* process, struct FILE_OBJECT*
|
||||
size_t LdrGetImageSize(uintptr_t base);
|
||||
size_t LdrGetMappedImageSize(struct PROCESS* process, uintptr_t base);
|
||||
|
||||
// Look up a named export in one already-mapped image.
|
||||
void* LdrGetImageExport(uintptr_t imageBase, const char* functionName);
|
||||
|
||||
// Look up a named export in a loaded module.
|
||||
// 'moduleName' must match a registered kernel module name such as "hal.dll".
|
||||
void* LdrGetExport(const char* moduleName, const char* functionName);
|
||||
@@ -40,4 +43,5 @@ KSTATUS LdrProtectImageForProcess(struct PROCESS* process, uintptr_t imageBase,
|
||||
struct BOOT_INFO; // forward declaration
|
||||
KSTATUS LdrRegisterModulesFromBootInfo(const struct BOOT_INFO* bootInfo);
|
||||
KSTATUS LdrRegisterModule(const char* name, uintptr_t base);
|
||||
KSTATUS LdrSetModuleExportsAvailable(const char* name, bool available);
|
||||
const char* LdrFindKernelModuleByAddress(uintptr_t address, uintptr_t* baseOut, size_t* sizeOut);
|
||||
@@ -72,6 +72,7 @@ KSTATUS MmFileCacheWrite(FILE_OBJECT* file, uint64_t offset, const void* buf,
|
||||
|
||||
// Flushes dirty cached pages for one file.
|
||||
KSTATUS MmFileCacheFlushFile(FILE_OBJECT* file);
|
||||
KSTATUS MmFileCacheFlushAll(void);
|
||||
|
||||
// Drops cached clean pages overlapping the byte range. Dirty pages are flushed
|
||||
// by callers before destructive metadata operations.
|
||||
|
||||
@@ -13,8 +13,6 @@ typedef struct SECURITY_CONTEXT {
|
||||
uint32_t SecurityId;
|
||||
SYS_PRIVILEGES EffectivePrivileges;
|
||||
SYS_PRIVILEGES DelegablePrivileges;
|
||||
SYS_SERVICE_CLAIMS ServiceClaims;
|
||||
SYS_SERVICE_CLAIMS DelegableServiceClaims;
|
||||
uint32_t Flags;
|
||||
} SECURITY_CONTEXT;
|
||||
|
||||
@@ -24,19 +22,14 @@ KSTATUS SeInitializeSecurity(void);
|
||||
// Returns the bootstrap root context. The returned pointer is owned by the security subsystem.
|
||||
SECURITY_CONTEXT* SeGetRootSecurityContext(void);
|
||||
|
||||
// Derives one immutable child context without allowing privilege or service-claim amplification.
|
||||
// Derives one immutable child context without allowing ambient privilege amplification.
|
||||
KSTATUS SeDeriveSecurityContext(SECURITY_CONTEXT* source,
|
||||
SYS_PRIVILEGES effectivePrivileges,
|
||||
SYS_PRIVILEGES delegablePrivileges,
|
||||
SYS_SERVICE_CLAIMS serviceClaims,
|
||||
SYS_SERVICE_CLAIMS delegableServiceClaims,
|
||||
SECURITY_CONTEXT** contextOut);
|
||||
|
||||
// Returns whether one process currently carries all requested ambient privileges.
|
||||
bool SeProcessHasPrivilege(const struct PROCESS* process, SYS_PRIVILEGES privileges);
|
||||
|
||||
// Returns whether one security context carries all requested service claims.
|
||||
bool SeContextHasServiceClaims(const SECURITY_CONTEXT* context, SYS_SERVICE_CLAIMS claims);
|
||||
|
||||
// Copies one immutable security context into the public query layout.
|
||||
void SeQuerySecurityContext(const SECURITY_CONTEXT* context, SYS_SECURITY_CONTEXT_INFO* infoOut);
|
||||
@@ -193,4 +193,9 @@
|
||||
#define SYS_GET_SOCKET_OPTION 160
|
||||
#define SYS_POLL 161
|
||||
|
||||
#define SYS_MAX 162
|
||||
#define SYS_OPEN_POWER_EVENT 162
|
||||
#define SYS_REQUEST_SYSTEM_POWER 163
|
||||
#define SYS_QUERY_SYSTEM_POWER 164
|
||||
#define SYS_COMPLETE_SYSTEM_POWER 165
|
||||
|
||||
#define SYS_MAX 166
|
||||
+115
-108
@@ -1,135 +1,142 @@
|
||||
// Kernel DDK-table provider implementation.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <ntlite/ddk/ddk.h>
|
||||
#include <kernel/hal/hal_api.h>
|
||||
#include <kernel/io_ddk.h>
|
||||
#include <kernel/io_dma.h>
|
||||
#include <kernel/ke_audio.h>
|
||||
#include <kernel/ke_debug.h>
|
||||
#include <kernel/ke_irq.h>
|
||||
#include <kernel/ke_sched.h>
|
||||
#include <kernel/mm_pte.h>
|
||||
|
||||
static const HAL_API* gIoDdkHal;
|
||||
static uintptr_t gIoDdkPhysicalWindowBase;
|
||||
static const HAL_API* gHal;
|
||||
static uintptr_t gPhysicalWindowBase;
|
||||
|
||||
// Returns the kernel direct-map alias for a physical address.
|
||||
static void* IoDdkPhysToVirt(uint64_t physicalAddress) {
|
||||
if (!gIoDdkPhysicalWindowBase || physicalAddress > UINTPTR_MAX - gIoDdkPhysicalWindowBase)
|
||||
return (void*)0;
|
||||
return (void*)(gIoDdkPhysicalWindowBase + (uintptr_t)physicalAddress);
|
||||
void IoInitializeDdk(const HAL_API* hal, uintptr_t physicalWindowBase) {
|
||||
gHal = hal;
|
||||
gPhysicalWindowBase = physicalWindowBase;
|
||||
}
|
||||
|
||||
// Maps one kernel page through the DDK memory surface.
|
||||
static KSTATUS IoDdkMapPage(uintptr_t virtualAddress,
|
||||
uint64_t physicalAddress,
|
||||
uint64_t pageFlags,
|
||||
NTL_DDK_MAP_MODE mode) {
|
||||
MM_PTE_MAP_MODE kernelMode = MmPteMapFailIfPresent;
|
||||
if (mode == NtlDdkMapReplaceExisting)
|
||||
kernelMode = MmPteMapReplaceExisting;
|
||||
return MmPteMapPage(virtualAddress, physicalAddress, pageFlags, kernelMode);
|
||||
__declspec(dllexport) uint64_t KeQueryTicks(void) {
|
||||
return gHal && gHal->Timer.QueryTicks ? gHal->Timer.QueryTicks() : 0;
|
||||
}
|
||||
|
||||
// Reads one aligned PCI configuration DWORD through the platform HAL.
|
||||
static uint32_t IoDdkPciRead32(uint16_t segment,
|
||||
uint8_t bus,
|
||||
uint8_t device,
|
||||
uint8_t function,
|
||||
uint16_t offset) {
|
||||
if (!gIoDdkHal || !gIoDdkHal->Pci.Read32)
|
||||
return 0xFFFFFFFFu;
|
||||
return gIoDdkHal->Pci.Read32(segment, bus, device, function, offset);
|
||||
__declspec(dllexport) void KeStallExecution(uint32_t microseconds) {
|
||||
if (gHal && gHal->Timer.Stall) gHal->Timer.Stall(microseconds);
|
||||
}
|
||||
|
||||
// Writes one aligned PCI configuration DWORD through the platform HAL.
|
||||
static void IoDdkPciWrite32(uint16_t segment,
|
||||
uint8_t bus,
|
||||
uint8_t device,
|
||||
uint8_t function,
|
||||
uint16_t offset,
|
||||
uint32_t value) {
|
||||
if (!gIoDdkHal || !gIoDdkHal->Pci.Write32)
|
||||
return;
|
||||
gIoDdkHal->Pci.Write32(segment, bus, device, function, offset, value);
|
||||
__declspec(dllexport) uint32_t KePciReadConfig32(uint16_t segment, uint8_t bus, uint8_t device, uint8_t function, uint16_t offset) {
|
||||
return gHal && gHal->Pci.Read32 ? gHal->Pci.Read32(segment, bus, device, function, offset) : UINT32_MAX;
|
||||
}
|
||||
|
||||
// Allocates one kernel IRQ slot for MSI delivery.
|
||||
static KSTATUS IoDdkAllocateMsiVector(uint8_t* outIrq) {
|
||||
return KeIrqAllocateMsiVector(outIrq);
|
||||
__declspec(dllexport) void KePciWriteConfig32(uint16_t segment, uint8_t bus, uint8_t device, uint8_t function, uint16_t offset, uint32_t value) {
|
||||
if (gHal && gHal->Pci.Write32) gHal->Pci.Write32(segment, bus, device, function, offset, value);
|
||||
}
|
||||
|
||||
|
||||
// Composes one platform MSI message for an allocated nt-lite IRQ slot.
|
||||
static KSTATUS IoDdkComposeMsiMessage(uint8_t irq, NTL_DDK_MSI_MESSAGE* messageOut) {
|
||||
if (!gIoDdkHal || !messageOut || irq >= 24u)
|
||||
__declspec(dllexport) KSTATUS KeIrqComposeMsiMessage(uint8_t irq, NTL_DDK_MSI_MESSAGE* out) {
|
||||
if (!gHal || !out || irq >= 24u || !gHal->InterruptController.ComposeMsi)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
HAL_MSI_MESSAGE message;
|
||||
KSTATUS status = gIoDdkHal->InterruptController.ComposeMsi((uint8_t)(0x20u + irq), &message);
|
||||
if (!KSUCCESS(status))
|
||||
return status;
|
||||
|
||||
messageOut->Address = message.Address;
|
||||
messageOut->Data = message.Data;
|
||||
messageOut->Reserved = 0;
|
||||
KSTATUS status = gHal->InterruptController.ComposeMsi((uint8_t)(0x20u + irq), &message);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
out->Address = message.Address;
|
||||
out->Data = message.Data;
|
||||
out->Reserved = 0;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static const NTL_DDK_DEBUG gIoDdkDebug = {
|
||||
.Print = DbgPrint,
|
||||
.GetFlags = KeGetDebugFlags,
|
||||
};
|
||||
|
||||
static const NTL_DDK_INTERRUPT gIoDdkInterrupt = {
|
||||
.AllocateMsiVector = IoDdkAllocateMsiVector,
|
||||
.ComposeMsiMessage = IoDdkComposeMsiMessage,
|
||||
};
|
||||
|
||||
static const NTL_DDK_MEMORY gIoDdkMemory = {
|
||||
.PhysToVirt = IoDdkPhysToVirt,
|
||||
.MapPage = IoDdkMapPage,
|
||||
};
|
||||
|
||||
static const NTL_DDK_PLATFORM gIoDdkPlatform = {
|
||||
.PciRead32 = IoDdkPciRead32,
|
||||
.PciWrite32 = IoDdkPciWrite32,
|
||||
};
|
||||
|
||||
static const NTL_DDK_SCHEDULER gIoDdkScheduler = {
|
||||
.DelayThread = KeDelayThread,
|
||||
.Yield = KeYield,
|
||||
};
|
||||
|
||||
static NTL_DDK_TIMER gIoDdkTimer = {
|
||||
.QueryTicks = 0,
|
||||
.Stall = 0,
|
||||
};
|
||||
|
||||
static NTL_DDK gIoDdk = {
|
||||
.Version = NTL_DDK_VERSION,
|
||||
.Debug = &gIoDdkDebug,
|
||||
.Audio = (const NTL_DDK_AUDIO*)0,
|
||||
.Dma = (const NTL_DDK_DMA*)0,
|
||||
.Interrupt = &gIoDdkInterrupt,
|
||||
.Memory = &gIoDdkMemory,
|
||||
.Platform = &gIoDdkPlatform,
|
||||
.Scheduler = &gIoDdkScheduler,
|
||||
.Timer = &gIoDdkTimer,
|
||||
};
|
||||
|
||||
// Initializes the kernel-owned DDK table from platform services.
|
||||
void IoInitializeDdk(const HAL_API* hal, uintptr_t physicalWindowBase) {
|
||||
gIoDdkHal = hal;
|
||||
gIoDdkPhysicalWindowBase = physicalWindowBase;
|
||||
gIoDdk.Audio = KeAudioGetDdk();
|
||||
gIoDdk.Dma = IoGetDmaDdk();
|
||||
gIoDdkTimer.QueryTicks = hal ? hal->Timer.QueryTicks : 0;
|
||||
gIoDdkTimer.Stall = hal ? hal->Timer.Stall : 0;
|
||||
__declspec(dllexport) void* MmDdkPhysToVirt(uint64_t physicalAddress) {
|
||||
if (!gPhysicalWindowBase || physicalAddress > UINTPTR_MAX - gPhysicalWindowBase)
|
||||
return (void*)0;
|
||||
return (void*)(gPhysicalWindowBase + (uintptr_t)physicalAddress);
|
||||
}
|
||||
|
||||
// Returns the kernel-owned DDK table passed to driver entrypoints.
|
||||
const NTL_DDK* IoGetDdk(void) {
|
||||
return &gIoDdk;
|
||||
__declspec(dllexport) KSTATUS MmDdkMapPage(uintptr_t virtualAddress, uint64_t physicalAddress, uint64_t flags, NTL_DDK_MAP_MODE mode) {
|
||||
return MmPteMapPage(virtualAddress, physicalAddress, flags,
|
||||
mode == NtlDdkMapReplaceExisting ? MmPteMapReplaceExisting : MmPteMapFailIfPresent);
|
||||
}
|
||||
|
||||
__declspec(dllexport) KSTATUS IoDmaGetAdapter(struct DEVICE_OBJECT* device, const IO_DMA_REQUIREMENTS* requirements, IO_DMA_ADAPTER** outAdapter) {
|
||||
return IoGetDmaDdk()->GetAdapter(device, requirements, outAdapter);
|
||||
}
|
||||
|
||||
__declspec(dllexport) void IoDmaPutAdapter(IO_DMA_ADAPTER* adapter) {
|
||||
IoGetDmaDdk()->PutAdapter(adapter);
|
||||
}
|
||||
|
||||
__declspec(dllexport) KSTATUS IoDmaAllocateCommonBuffer(IO_DMA_ADAPTER* adapter, size_t size, size_t alignment, IO_DMA_BUFFER** outBuffer) {
|
||||
return IoGetDmaDdk()->AllocateCommonBuffer(adapter, size, alignment, outBuffer);
|
||||
}
|
||||
|
||||
__declspec(dllexport) void IoDmaFreeCommonBuffer(IO_DMA_BUFFER* buffer) {
|
||||
IoGetDmaDdk()->FreeCommonBuffer(buffer);
|
||||
}
|
||||
|
||||
__declspec(dllexport) void* IoDmaGetCommonBufferAddress(const IO_DMA_BUFFER* buffer) {
|
||||
return IoGetDmaDdk()->GetCommonBufferAddress(buffer);
|
||||
}
|
||||
|
||||
__declspec(dllexport) uint64_t IoDmaGetCommonBufferDeviceAddress(const IO_DMA_BUFFER* buffer) {
|
||||
return IoGetDmaDdk()->GetCommonBufferDeviceAddress(buffer);
|
||||
}
|
||||
|
||||
__declspec(dllexport) size_t IoDmaGetCommonBufferSize(const IO_DMA_BUFFER* buffer) {
|
||||
return IoGetDmaDdk()->GetCommonBufferSize(buffer);
|
||||
}
|
||||
|
||||
__declspec(dllexport) KSTATUS IoDmaPinKernelMemory(void* address, size_t size, IO_DMA_DIRECTION direction, MM_PINNED_MEMORY** outMemory) {
|
||||
return IoGetDmaDdk()->PinKernelMemory(address, size, direction, outMemory);
|
||||
}
|
||||
|
||||
__declspec(dllexport) KSTATUS IoDmaPinUserMemory(struct PROCESS* process, void* address, size_t size, IO_DMA_DIRECTION direction, MM_PINNED_MEMORY** outMemory) {
|
||||
return IoGetDmaDdk()->PinUserMemory(process, address, size, direction, outMemory);
|
||||
}
|
||||
|
||||
__declspec(dllexport) void IoDmaUnpinMemory(MM_PINNED_MEMORY* memory) {
|
||||
IoGetDmaDdk()->UnpinMemory(memory);
|
||||
}
|
||||
|
||||
__declspec(dllexport) KSTATUS IoDmaMapPinnedMemory(IO_DMA_ADAPTER* adapter, MM_PINNED_MEMORY* memory, size_t offset, size_t length, IO_DMA_DIRECTION direction, uint32_t flags, IO_DMA_MAPPING** outMapping) {
|
||||
return IoGetDmaDdk()->MapPinnedMemory(adapter, memory, offset, length, direction, flags, outMapping);
|
||||
}
|
||||
|
||||
__declspec(dllexport) void IoDmaUnmapMemory(IO_DMA_MAPPING* mapping) {
|
||||
IoGetDmaDdk()->UnmapMemory(mapping);
|
||||
}
|
||||
|
||||
__declspec(dllexport) KSTATUS IoDmaSynchronizeForDevice(IO_DMA_MAPPING* mapping) {
|
||||
return IoGetDmaDdk()->SynchronizeForDevice(mapping);
|
||||
}
|
||||
|
||||
__declspec(dllexport) KSTATUS IoDmaSynchronizeForCpu(IO_DMA_MAPPING* mapping) {
|
||||
return IoGetDmaDdk()->SynchronizeForCpu(mapping);
|
||||
}
|
||||
|
||||
__declspec(dllexport) uint32_t IoDmaGetSegmentCount(const IO_DMA_MAPPING* mapping) {
|
||||
return IoGetDmaDdk()->GetSegmentCount(mapping);
|
||||
}
|
||||
|
||||
__declspec(dllexport) const IO_DMA_SEGMENT* IoDmaGetSegments(const IO_DMA_MAPPING* mapping) {
|
||||
return IoGetDmaDdk()->GetSegments(mapping);
|
||||
}
|
||||
|
||||
__declspec(dllexport) void IoDmaQueryStats(IO_DMA_STATS* outStats) {
|
||||
IoGetDmaDdk()->QueryStats(outStats);
|
||||
}
|
||||
|
||||
__declspec(dllexport) KSTATUS IoAudioRegisterEndpoint(const IO_AUDIO_ENDPOINT_REGISTRATION* registration, IO_AUDIO_ENDPOINT** outEndpoint) {
|
||||
return KeAudioGetDdk()->RegisterEndpoint(registration, outEndpoint);
|
||||
}
|
||||
|
||||
__declspec(dllexport) void IoAudioUnregisterEndpoint(IO_AUDIO_ENDPOINT* endpoint, IO_AUDIO_REMOVAL_MODE mode) {
|
||||
KeAudioGetDdk()->UnregisterEndpoint(endpoint, mode);
|
||||
}
|
||||
|
||||
__declspec(dllexport) void IoAudioNotifyPeriod(IO_AUDIO_ENDPOINT* endpoint, uint32_t completedPeriodIndex, uint64_t presentedFrames, uint64_t hardwarePositionFrames, uint64_t interruptCount, uint64_t timestampTicks) {
|
||||
KeAudioGetDdk()->NotifyPeriod(endpoint, completedPeriodIndex, presentedFrames, hardwarePositionFrames, interruptCount, timestampTicks);
|
||||
}
|
||||
|
||||
__declspec(dllexport) void IoAudioSetEndpointAvailable(IO_AUDIO_ENDPOINT* endpoint, bool available) {
|
||||
KeAudioGetDdk()->SetEndpointAvailable(endpoint, available);
|
||||
}
|
||||
|
||||
__declspec(dllexport) void IoAudioReportEndpointFailure(IO_AUDIO_ENDPOINT* endpoint, KSTATUS status, uint32_t flags) {
|
||||
KeAudioGetDdk()->ReportEndpointFailure(endpoint, status, flags);
|
||||
}
|
||||
+8
-135
@@ -11,72 +11,7 @@
|
||||
#include <kernel/ke_debug.h>
|
||||
#include <kernel/ob_object.h>
|
||||
#include <kernel/ke_sched.h>
|
||||
|
||||
#define MAX_DRIVER_OBJECTS 64
|
||||
#define MAX_PDO_OBJECTS 128
|
||||
|
||||
// Implements the sv Or Unnamed operation.
|
||||
static SYS_STRING_VIEW IoSvOrUnnamed(SYS_STRING_VIEW name) {
|
||||
return SysSvIsEmpty(name) ? SYS_SV_LITERAL("<unnamed>") : name;
|
||||
}
|
||||
|
||||
// Implements the bus Type Name operation.
|
||||
static const char* IoBusTypeName(IO_BUS_TYPE busType) {
|
||||
switch (busType) {
|
||||
case IoBusPci: return "pci";
|
||||
case IoBusUsb: return "usb";
|
||||
default: return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
// Driver object registry / PDO AddDevice binding.
|
||||
|
||||
static DRIVER_OBJECT* gDriverObjects[MAX_DRIVER_OBJECTS];
|
||||
static uint32_t gDriverObjectCount;
|
||||
static DEVICE_OBJECT* gPhysicalDevices[MAX_PDO_OBJECTS];
|
||||
static uint32_t gPhysicalDeviceCount;
|
||||
|
||||
// Implements the driver Matches Pdo operation.
|
||||
static bool IoDriverMatchesPdo(const DRIVER_OBJECT* driver, const DEVICE_OBJECT* pdo) {
|
||||
if (!driver || !driver->IdTable || !pdo || pdo->Type != DeviceObjectPhysical)
|
||||
return false;
|
||||
|
||||
for (size_t d = 0; d < driver->IdTable->Count; d++) {
|
||||
const IO_DEVICE_ID* want = &driver->IdTable->Ids[d];
|
||||
if (want->BusType != IoBusUnknown && want->BusType != pdo->u.Pdo.BusType)
|
||||
continue;
|
||||
for (uint8_t p = 0; p < pdo->u.Pdo.IdCount; p++) {
|
||||
if (SysSvEqual(want->Id, pdo->u.Pdo.Ids[p].Id))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Implements the try Add Device For Driver operation.
|
||||
static bool IoTryAddDeviceForDriver(DRIVER_OBJECT* driver, DEVICE_OBJECT* pdo) {
|
||||
if (!driver || !pdo || !driver->DriverAddDevice)
|
||||
return false;
|
||||
if (!IoDriverMatchesPdo(driver, pdo))
|
||||
return false;
|
||||
|
||||
KSTATUS st = driver->DriverAddDevice(driver, pdo);
|
||||
if (KSUCCESS(st)) {
|
||||
DbgPrint("io: AddDevice driver=%v pdo=%v bus=%s\n",
|
||||
IoSvOrUnnamed(driver->Name), IoSvOrUnnamed(pdo->Name), IoBusTypeName(pdo->u.Pdo.BusType));
|
||||
|
||||
KSTATUS start = IoSendPnpIrpSync(pdo, IRP_MN_START_DEVICE);
|
||||
if (!KSUCCESS(start) && start != NTLSTATUS_NOT_SUPPORTED) {
|
||||
DbgPrint("io: START_DEVICE failed driver=%v pdo=%v st=0x%x\n",
|
||||
IoSvOrUnnamed(driver->Name), IoSvOrUnnamed(pdo->Name), start);
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
DbgPrint("io: AddDevice failed driver=%v pdo=%v st=0x%x\n",
|
||||
IoSvOrUnnamed(driver->Name), IoSvOrUnnamed(pdo->Name), st);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
#include <kernel/io_pnp.h>
|
||||
|
||||
__declspec(dllexport) void IoInitializeDriverObject(DRIVER_OBJECT* driver,
|
||||
SYS_STRING_VIEW name) {
|
||||
@@ -86,42 +21,12 @@ __declspec(dllexport) void IoInitializeDriverObject(DRIVER_OBJECT* driver,
|
||||
driver->Name = name;
|
||||
}
|
||||
|
||||
// Registers driver Object.
|
||||
__declspec(dllexport) KSTATUS IoRegisterDriverObject(DRIVER_OBJECT* driver) {
|
||||
if (!driver) return NTLSTATUS_INVALID_PARAMETER;
|
||||
if (!SysSvIsUtf8Text(driver->Name)) return NTLSTATUS_INVALID_PARAMETER;
|
||||
if (gDriverObjectCount >= MAX_DRIVER_OBJECTS)
|
||||
return NTLSTATUS_NO_MEMORY;
|
||||
|
||||
if (driver->Header.Type != ObjectTypeDriver ||
|
||||
driver->Header.LifetimeState != ObjectAlive) {
|
||||
ObInitializeObjectHeader(&driver->Header, ObjectTypeDriver);
|
||||
}
|
||||
|
||||
gDriverObjects[gDriverObjectCount++] = driver;
|
||||
DbgPrint("io: registered driver object %v\n", IoSvOrUnnamed(driver->Name));
|
||||
|
||||
for (uint32_t i = 0; i < gPhysicalDeviceCount; i++)
|
||||
(void)IoTryAddDeviceForDriver(driver, gPhysicalDevices[i]);
|
||||
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Device object lifetime.
|
||||
|
||||
static void IoDeviceDestroy(OBJECT* object) {
|
||||
DEVICE_OBJECT* dev = (DEVICE_OBJECT*)object;
|
||||
IoDmaDetachDevice(dev);
|
||||
|
||||
for (uint32_t i = 0; i < gPhysicalDeviceCount; i++) {
|
||||
if (gPhysicalDevices[i] == dev) {
|
||||
gPhysicalDevices[i] = gPhysicalDevices[gPhysicalDeviceCount - 1u];
|
||||
gPhysicalDevices[gPhysicalDeviceCount - 1u] = (DEVICE_OBJECT*)0;
|
||||
gPhysicalDeviceCount--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (dev->LowerDevice)
|
||||
IoDetachDevice(dev);
|
||||
|
||||
@@ -232,27 +137,11 @@ __declspec(dllexport) KSTATUS IoRegisterPhysicalDevice(DEVICE_OBJECT* pdo) {
|
||||
if (!pdo || pdo->Type != DeviceObjectPhysical)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
for (uint32_t i = 0; i < gPhysicalDeviceCount; i++) {
|
||||
if (gPhysicalDevices[i] == pdo)
|
||||
return NTLSTATUS_ALREADY_EXISTS;
|
||||
}
|
||||
KSTATUS status = IoPnpPhysicalDeviceArrived(pdo);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
|
||||
if (gPhysicalDeviceCount >= MAX_PDO_OBJECTS)
|
||||
return NTLSTATUS_NO_MEMORY;
|
||||
IoPnpPhysicalDevicePublished(pdo);
|
||||
|
||||
gPhysicalDevices[gPhysicalDeviceCount++] = pdo;
|
||||
|
||||
bool anyMatched = false;
|
||||
for (uint32_t i = 0; i < gDriverObjectCount; i++) {
|
||||
if (IoTryAddDeviceForDriver(gDriverObjects[i], pdo))
|
||||
anyMatched = true;
|
||||
}
|
||||
|
||||
if (!anyMatched && pdo->u.Pdo.BusType == IoBusUsb) {
|
||||
SYS_STRING_VIEW id = pdo->u.Pdo.IdCount ? pdo->u.Pdo.Ids[0].Id : SYS_SV_LITERAL("<no-id>");
|
||||
DbgPrint("io: no function driver for pdo=%v id=%v bus=%s\n",
|
||||
IoSvOrUnnamed(pdo->Name), id, IoBusTypeName(pdo->u.Pdo.BusType));
|
||||
}
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -280,24 +169,6 @@ __declspec(dllexport) void IoDereferenceDeviceObject(DEVICE_OBJECT* device) {
|
||||
if (device) ObDereferenceObject(&device->Header);
|
||||
}
|
||||
|
||||
// Releases physical Device Registry Entry.
|
||||
static void IoRemovePhysicalDeviceRegistryEntry(DEVICE_OBJECT* device) {
|
||||
if (!device || device->Type != DeviceObjectPhysical)
|
||||
return;
|
||||
|
||||
for (uint32_t i = 0; i < gPhysicalDeviceCount; i++) {
|
||||
if (gPhysicalDevices[i] == device) {
|
||||
for (uint32_t j = i + 1; j < gPhysicalDeviceCount; j++)
|
||||
gPhysicalDevices[j - 1] = gPhysicalDevices[j];
|
||||
gPhysicalDeviceCount--;
|
||||
if (gPhysicalDeviceCount < MAX_PDO_OBJECTS)
|
||||
gPhysicalDevices[gPhysicalDeviceCount] = (DEVICE_OBJECT*)0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Implements the unlink Device From Parent operation.
|
||||
static void IoUnlinkDeviceFromParent(DEVICE_OBJECT* device) {
|
||||
if (!device || !device->Parent)
|
||||
return;
|
||||
@@ -333,9 +204,11 @@ __declspec(dllexport) void IoDeleteDevice(DEVICE_OBJECT* device) {
|
||||
device->PnpState = DevicePnpDeleted;
|
||||
KeReleaseSpinlock(&device->Lock, flags);
|
||||
|
||||
IoDmaDetachDevice(device);
|
||||
if (device->Type == DeviceObjectPhysical)
|
||||
IoPnpPhysicalDeviceRemoved(device);
|
||||
else
|
||||
IoDmaDetachDevice(device);
|
||||
IoUnlinkDeviceFromParent(device);
|
||||
IoRemovePhysicalDeviceRegistryEntry(device);
|
||||
IoDereferenceDeviceObject(device);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,594 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <ntlite/ddi/io_driver.h>
|
||||
#include <ntlite/ddk/device.h>
|
||||
#include <kernel/io_pnp.h>
|
||||
#include <kernel/ke_event.h>
|
||||
#include <kernel/ke_thread.h>
|
||||
#include <kernel/ke_spinlock.h>
|
||||
#include <kernel/ke_work.h>
|
||||
#include <kernel/ke_time.h>
|
||||
#include <kernel/mm_virt.h>
|
||||
#include <kernel/ke_debug.h>
|
||||
|
||||
#define PNP_MAX_DESCRIPTORS 64u
|
||||
#define PNP_MAX_DEVICES 128u
|
||||
#define PNP_WORKERS 2u
|
||||
|
||||
typedef enum PNP_BIND_STATE {
|
||||
PnpUnbound,
|
||||
PnpReserved,
|
||||
PnpStarting,
|
||||
PnpPending,
|
||||
PnpCancelling,
|
||||
PnpStarted,
|
||||
PnpFailed,
|
||||
PnpCleaning,
|
||||
PnpStopping,
|
||||
} PNP_BIND_STATE;
|
||||
|
||||
typedef struct PNP_DRIVER {
|
||||
const NTL_DEVICE_DESC* Descriptor;
|
||||
DRIVER_OBJECT* IoDriver;
|
||||
struct NTL_MODULE* Module;
|
||||
} PNP_DRIVER;
|
||||
|
||||
typedef struct PNP_NODE {
|
||||
DEVICE_OBJECT* Pdo;
|
||||
DEVICE_OBJECT* ParentPdo;
|
||||
NTL_DEVICE* Binding;
|
||||
const PNP_DRIVER* Driver;
|
||||
PNP_BIND_STATE State;
|
||||
bool Removed;
|
||||
bool Published;
|
||||
bool CancelIssued;
|
||||
} PNP_NODE;
|
||||
|
||||
struct NTL_DEVICE_RESOURCE {
|
||||
NTL_DEVICE_RESOURCE* Next;
|
||||
NTL_DEVICE* Owner;
|
||||
NTL_DEVICE_RESOURCE_RELEASE Release;
|
||||
void* Context;
|
||||
};
|
||||
|
||||
struct NTL_DEVICE {
|
||||
SPINLOCK Lock;
|
||||
PNP_NODE* Node;
|
||||
void* Context;
|
||||
NTL_DEVICE_RESOURCE* Resources;
|
||||
KSTATUS CompletionStatus;
|
||||
bool CompletionSet;
|
||||
bool Closing;
|
||||
};
|
||||
|
||||
static SPINLOCK gPnpLock = SPINLOCK_INIT;
|
||||
static PNP_DRIVER gPnpDrivers[PNP_MAX_DESCRIPTORS];
|
||||
static uint32_t gPnpDriverCount;
|
||||
static PNP_NODE* gPnpNodes[PNP_MAX_DEVICES];
|
||||
static uint32_t gPnpNodeCount;
|
||||
static EVENT* gPnpReady;
|
||||
static EVENT* gPnpChanged;
|
||||
static bool gPnpStarted;
|
||||
static bool gPnpShutdown;
|
||||
static KWORK_ITEM gPnpWakeItem;
|
||||
|
||||
static void IoPnpSignal(void* unused) {
|
||||
(void)unused;
|
||||
for (uint32_t i = 0; i < PNP_WORKERS; ++i)
|
||||
KeSignalEvent(gPnpReady);
|
||||
KeSignalEvent(gPnpChanged);
|
||||
}
|
||||
|
||||
static void IoPnpWake(void) {
|
||||
if (gPnpStarted)
|
||||
(void)KeQueueWorkItem(&gPnpWakeItem);
|
||||
}
|
||||
|
||||
static bool IoPnpMatches(const NTL_DEVICE_DESC* descriptor, const DEVICE_OBJECT* pdo) {
|
||||
for (uint32_t i = 0; i < descriptor->MatchCount; ++i) {
|
||||
const NTL_DEVICE_MATCH* match = &descriptor->Matches[i];
|
||||
if ((uint32_t)match->Bus != (uint32_t)pdo->u.Pdo.BusType)
|
||||
continue;
|
||||
size_t length = strlen(match->Id);
|
||||
for (uint8_t j = 0; j < pdo->u.Pdo.IdCount; ++j) {
|
||||
SYS_STRING_VIEW id = pdo->u.Pdo.Ids[j].Id;
|
||||
if (id.Length == length && !memcmp(id.Data, match->Id, length))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool IoPnpMatchesIoDriver(const DRIVER_OBJECT* driver, const DEVICE_OBJECT* pdo) {
|
||||
if (!driver->IdTable || !driver->DriverAddDevice) return false;
|
||||
for (size_t i = 0; i < driver->IdTable->Count; ++i) {
|
||||
const IO_DEVICE_ID* wanted = &driver->IdTable->Ids[i];
|
||||
if (wanted->BusType != IoBusUnknown && wanted->BusType != pdo->u.Pdo.BusType)
|
||||
continue;
|
||||
for (uint8_t j = 0; j < pdo->u.Pdo.IdCount; ++j)
|
||||
if (SysSvEqual(wanted->Id, pdo->u.Pdo.Ids[j].Id)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static const PNP_DRIVER* IoPnpFindDriver(const DEVICE_OBJECT* pdo) {
|
||||
for (uint32_t i = 0; i < gPnpDriverCount; ++i) {
|
||||
if ((gPnpDrivers[i].Descriptor && IoPnpMatches(gPnpDrivers[i].Descriptor, pdo)) ||
|
||||
(gPnpDrivers[i].IoDriver && IoPnpMatchesIoDriver(gPnpDrivers[i].IoDriver, pdo)))
|
||||
return &gPnpDrivers[i];
|
||||
}
|
||||
return (const PNP_DRIVER*)0;
|
||||
}
|
||||
|
||||
static PNP_NODE* IoPnpFindNode(const DEVICE_OBJECT* pdo) {
|
||||
for (uint32_t i = 0; i < gPnpNodeCount; ++i)
|
||||
if (gPnpNodes[i]->Pdo == pdo)
|
||||
return gPnpNodes[i];
|
||||
return (PNP_NODE*)0;
|
||||
}
|
||||
|
||||
static DEVICE_OBJECT* IoPnpParentPdo(const DEVICE_OBJECT* pdo) {
|
||||
const DEVICE_OBJECT* parent = pdo->Parent;
|
||||
for (uint32_t i = 0; parent && i < 64u; ++i) {
|
||||
if (parent->Type == DeviceObjectPhysical)
|
||||
return (DEVICE_OBJECT*)parent;
|
||||
parent = parent->LowerDevice ? parent->LowerDevice : parent->Parent;
|
||||
}
|
||||
return (DEVICE_OBJECT*)0;
|
||||
}
|
||||
|
||||
static bool IoPnpHasChildren(const DEVICE_OBJECT* parent) {
|
||||
for (uint32_t i = 0; i < gPnpNodeCount; ++i) {
|
||||
const DEVICE_OBJECT* ancestor = gPnpNodes[i]->ParentPdo;
|
||||
for (uint32_t depth = 0; ancestor && depth < PNP_MAX_DEVICES; ++depth) {
|
||||
if (ancestor == parent) return true;
|
||||
const PNP_NODE* node = IoPnpFindNode(ancestor);
|
||||
ancestor = node ? node->ParentPdo : (const DEVICE_OBJECT*)0;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void IoPnpReleaseResources(NTL_DEVICE* device) {
|
||||
uintptr_t flags = KeAcquireSpinlock(&device->Lock);
|
||||
device->Closing = true;
|
||||
NTL_DEVICE_RESOURCE* resource = device->Resources;
|
||||
device->Resources = (NTL_DEVICE_RESOURCE*)0;
|
||||
KeReleaseSpinlock(&device->Lock, flags);
|
||||
|
||||
while (resource) {
|
||||
NTL_DEVICE_RESOURCE* next = resource->Next;
|
||||
resource->Owner = (NTL_DEVICE*)0;
|
||||
resource->Release(resource->Context);
|
||||
MmFreePool(resource);
|
||||
resource = next;
|
||||
}
|
||||
}
|
||||
|
||||
static void IoPnpDispose(PNP_NODE* node) {
|
||||
NTL_DEVICE* device = node->Binding;
|
||||
if (device) {
|
||||
if (node->State == PnpStopping) {
|
||||
if (node->Driver->Descriptor && node->Driver->Descriptor->Stop)
|
||||
node->Driver->Descriptor->Stop(device);
|
||||
else if (node->Driver->IoDriver && !node->Pdo->DeletePending)
|
||||
(void)IoSendPnpIrpSync(node->Pdo, IRP_MN_REMOVE_DEVICE);
|
||||
}
|
||||
IoPnpReleaseResources(device);
|
||||
MmFreePool(device);
|
||||
}
|
||||
|
||||
uintptr_t flags = KeAcquireSpinlock(&gPnpLock);
|
||||
for (uint32_t i = 0; i < gPnpNodeCount; ++i) {
|
||||
if (gPnpNodes[i] == node) {
|
||||
gPnpNodes[i] = gPnpNodes[--gPnpNodeCount];
|
||||
gPnpNodes[gPnpNodeCount] = (PNP_NODE*)0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
KeReleaseSpinlock(&gPnpLock, flags);
|
||||
IoDereferenceDeviceObject(node->Pdo);
|
||||
MmFreePool(node);
|
||||
IoPnpWake();
|
||||
}
|
||||
|
||||
static void IoPnpStart(PNP_NODE* node) {
|
||||
const NTL_DEVICE_DESC* desc = node->Driver->Descriptor;
|
||||
size_t contextSize = desc ? desc->ContextSize : 0;
|
||||
NTL_DEVICE* device = (NTL_DEVICE*)MmAllocatePool(sizeof(*device) + contextSize);
|
||||
if (!device) {
|
||||
uintptr_t flags = KeAcquireSpinlock(&gPnpLock);
|
||||
node->State = PnpFailed;
|
||||
KeReleaseSpinlock(&gPnpLock, flags);
|
||||
IoPnpWake();
|
||||
return;
|
||||
}
|
||||
memset(device, 0, sizeof(*device) + contextSize);
|
||||
device->Lock = (SPINLOCK)SPINLOCK_INIT;
|
||||
device->Node = node;
|
||||
device->Context = (void*)(device + 1);
|
||||
|
||||
uintptr_t flags = KeAcquireSpinlock(&gPnpLock);
|
||||
node->Binding = device;
|
||||
node->State = PnpStarting;
|
||||
KeReleaseSpinlock(&gPnpLock, flags);
|
||||
|
||||
KSTATUS result;
|
||||
if (desc) {
|
||||
result = desc->Start(device);
|
||||
} else {
|
||||
DRIVER_OBJECT* io = node->Driver->IoDriver;
|
||||
result = io->DriverAddDevice(io, node->Pdo);
|
||||
if (KSUCCESS(result)) {
|
||||
result = IoSendPnpIrpSync(node->Pdo, IRP_MN_START_DEVICE);
|
||||
if (result == NTLSTATUS_NOT_SUPPORTED) result = NTLSTATUS_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
flags = KeAcquireSpinlock(&gPnpLock);
|
||||
if (device->CompletionSet)
|
||||
result = device->CompletionStatus;
|
||||
node->State = result == NTLSTATUS_PENDING ? PnpPending :
|
||||
KSUCCESS(result) ? PnpStarted : PnpFailed;
|
||||
KeReleaseSpinlock(&gPnpLock, flags);
|
||||
if (KFAILED(result))
|
||||
DbgPrint("pnp: start failed (0x%x)\n", result);
|
||||
IoPnpWake();
|
||||
}
|
||||
|
||||
static void IoPnpWorker(void* ignored) {
|
||||
(void)ignored;
|
||||
for (;;) {
|
||||
(void)KeWaitForEvent(gPnpReady, false);
|
||||
for (;;) {
|
||||
PNP_NODE* node = (PNP_NODE*)0;
|
||||
bool stopping = false;
|
||||
bool cleanup = false;
|
||||
bool cancel = false;
|
||||
uintptr_t flags = KeAcquireSpinlock(&gPnpLock);
|
||||
for (uint32_t i = 0; i < gPnpNodeCount; ++i) {
|
||||
PNP_NODE* candidate = gPnpNodes[i];
|
||||
if (candidate->State == PnpPending && candidate->Removed &&
|
||||
!candidate->CancelIssued && (candidate->Driver->Descriptor && candidate->Driver->Descriptor->CancelStart)) {
|
||||
candidate->CancelIssued = true;
|
||||
candidate->State = PnpCancelling;
|
||||
node = candidate;
|
||||
cancel = true;
|
||||
break;
|
||||
}
|
||||
if (candidate->State == PnpFailed && candidate->Binding && !candidate->Removed) {
|
||||
candidate->State = PnpCleaning;
|
||||
node = candidate;
|
||||
cleanup = true;
|
||||
break;
|
||||
}
|
||||
if (gPnpShutdown && candidate->Removed &&
|
||||
IoPnpHasChildren(candidate->Pdo))
|
||||
continue;
|
||||
if (candidate->Removed && candidate->State != PnpReserved &&
|
||||
candidate->State != PnpStarting && candidate->State != PnpPending &&
|
||||
candidate->State != PnpCancelling && candidate->State != PnpStopping &&
|
||||
candidate->State != PnpCleaning) {
|
||||
candidate->State = PnpStopping;
|
||||
node = candidate;
|
||||
stopping = true;
|
||||
break;
|
||||
}
|
||||
if (candidate->State != PnpUnbound || candidate->Removed ||
|
||||
gPnpShutdown || !candidate->Published)
|
||||
continue;
|
||||
const PNP_DRIVER* driver = IoPnpFindDriver(candidate->Pdo);
|
||||
if (driver) {
|
||||
candidate->Driver = driver;
|
||||
candidate->State = PnpReserved;
|
||||
node = candidate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
KeReleaseSpinlock(&gPnpLock, flags);
|
||||
if (!node) break;
|
||||
KeSignalEvent(gPnpReady);
|
||||
if (stopping) {
|
||||
IoPnpDispose(node);
|
||||
} else if (cancel) {
|
||||
node->Driver->Descriptor->CancelStart(node->Binding);
|
||||
uintptr_t update = KeAcquireSpinlock(&gPnpLock);
|
||||
node->State = node->Binding->CompletionSet ?
|
||||
(KSUCCESS(node->Binding->CompletionStatus) ? PnpStarted : PnpFailed) :
|
||||
PnpPending;
|
||||
KeReleaseSpinlock(&gPnpLock, update);
|
||||
IoPnpWake();
|
||||
} else if (cleanup) {
|
||||
NTL_DEVICE* device = node->Binding;
|
||||
if (node->Driver->Descriptor && node->Driver->Descriptor->Stop)
|
||||
node->Driver->Descriptor->Stop(device);
|
||||
else if (node->Driver->IoDriver && node->Pdo->AttachedDevice)
|
||||
(void)IoSendPnpIrpSync(node->Pdo, IRP_MN_REMOVE_DEVICE);
|
||||
IoPnpReleaseResources(device);
|
||||
uintptr_t update = KeAcquireSpinlock(&gPnpLock);
|
||||
node->Binding = (NTL_DEVICE*)0;
|
||||
node->State = PnpFailed;
|
||||
KeReleaseSpinlock(&gPnpLock, update);
|
||||
MmFreePool(device);
|
||||
IoPnpWake();
|
||||
} else {
|
||||
IoPnpStart(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
KSTATUS IoPnpInitialize(void) {
|
||||
if (gPnpStarted) return NTLSTATUS_ALREADY_EXISTS;
|
||||
KSTATUS st = KeEventCreate(EventAutoReset, false, &gPnpReady);
|
||||
if (!KSUCCESS(st)) return st;
|
||||
st = KeEventCreate(EventAutoReset, false, &gPnpChanged);
|
||||
if (!KSUCCESS(st)) return st;
|
||||
|
||||
for (uint32_t i = 0; i < PNP_WORKERS; ++i) {
|
||||
const KE_THREAD_CREATE_PARAMETERS params =
|
||||
KE_THREAD_CREATE_NAMED_NORMAL(PriorityNormal, "pnp.device");
|
||||
st = KeCreateKernelThread((uintptr_t)IoPnpWorker, 0, ¶ms, (THREAD**)0);
|
||||
if (!KSUCCESS(st)) return st;
|
||||
}
|
||||
KeInitializeWorkItem(&gPnpWakeItem, IoPnpSignal, (void*)0);
|
||||
gPnpStarted = true;
|
||||
IoPnpWake();
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
KSTATUS IoPnpPublishModule(struct NTL_MODULE* module,
|
||||
const NTL_DEVICE_DESC* descriptors, uint32_t count,
|
||||
DRIVER_OBJECT* const* ioDrivers, uint32_t ioCount) {
|
||||
if (!gPnpStarted || !module || (count && !descriptors) || (ioCount && !ioDrivers))
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
uintptr_t flags = KeAcquireSpinlock(&gPnpLock);
|
||||
if (gPnpShutdown) {
|
||||
KeReleaseSpinlock(&gPnpLock, flags);
|
||||
return NTLSTATUS_DEVICE_REMOVED;
|
||||
}
|
||||
if (count > PNP_MAX_DESCRIPTORS - gPnpDriverCount ||
|
||||
ioCount > PNP_MAX_DESCRIPTORS - gPnpDriverCount - count) {
|
||||
KeReleaseSpinlock(&gPnpLock, flags);
|
||||
return NTLSTATUS_NO_MEMORY;
|
||||
}
|
||||
for (uint32_t i = 0; i < count; ++i) {
|
||||
for (uint32_t j = 0; j < gPnpDriverCount; ++j) {
|
||||
SYS_STRING_VIEW existing = gPnpDrivers[j].IoDriver ?
|
||||
gPnpDrivers[j].IoDriver->Name :
|
||||
SysSvFromParts(gPnpDrivers[j].Descriptor->Name,
|
||||
strlen(gPnpDrivers[j].Descriptor->Name));
|
||||
SYS_STRING_VIEW incoming = SysSvFromParts(descriptors[i].Name,
|
||||
strlen(descriptors[i].Name));
|
||||
if (SysSvEqual(existing, incoming)) {
|
||||
KeReleaseSpinlock(&gPnpLock, flags);
|
||||
return NTLSTATUS_ALREADY_EXISTS;
|
||||
}
|
||||
}
|
||||
for (uint32_t j = i + 1; j < count; ++j) {
|
||||
if (!strcmp(descriptors[i].Name, descriptors[j].Name)) {
|
||||
KeReleaseSpinlock(&gPnpLock, flags);
|
||||
return NTLSTATUS_ALREADY_EXISTS;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (uint32_t i = 0; i < ioCount; ++i) {
|
||||
DRIVER_OBJECT* driver = ioDrivers[i];
|
||||
for (uint32_t j = 0; j < gPnpDriverCount; ++j) {
|
||||
SYS_STRING_VIEW existing = gPnpDrivers[j].IoDriver ?
|
||||
gPnpDrivers[j].IoDriver->Name :
|
||||
SysSvFromParts(gPnpDrivers[j].Descriptor->Name,
|
||||
strlen(gPnpDrivers[j].Descriptor->Name));
|
||||
if (SysSvEqual(driver->Name, existing)) {
|
||||
KeReleaseSpinlock(&gPnpLock, flags);
|
||||
return NTLSTATUS_ALREADY_EXISTS;
|
||||
}
|
||||
}
|
||||
for (uint32_t j = i + 1; j < ioCount; ++j) {
|
||||
if (SysSvEqual(driver->Name, ioDrivers[j]->Name)) {
|
||||
KeReleaseSpinlock(&gPnpLock, flags);
|
||||
return NTLSTATUS_ALREADY_EXISTS;
|
||||
}
|
||||
}
|
||||
for (uint32_t j = 0; j < count; ++j) {
|
||||
if (SysSvEqual(driver->Name, SysSvFromParts(descriptors[j].Name,
|
||||
strlen(descriptors[j].Name)))) {
|
||||
KeReleaseSpinlock(&gPnpLock, flags);
|
||||
return NTLSTATUS_ALREADY_EXISTS;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (uint32_t i = 0; i < count; ++i) {
|
||||
gPnpDrivers[gPnpDriverCount].Descriptor = &descriptors[i];
|
||||
gPnpDrivers[gPnpDriverCount].IoDriver = (DRIVER_OBJECT*)0;
|
||||
gPnpDrivers[gPnpDriverCount].Module = module;
|
||||
++gPnpDriverCount;
|
||||
}
|
||||
for (uint32_t i = 0; i < ioCount; ++i) {
|
||||
gPnpDrivers[gPnpDriverCount].Descriptor = (const NTL_DEVICE_DESC*)0;
|
||||
gPnpDrivers[gPnpDriverCount].IoDriver = ioDrivers[i];
|
||||
gPnpDrivers[gPnpDriverCount].Module = module;
|
||||
++gPnpDriverCount;
|
||||
}
|
||||
KeReleaseSpinlock(&gPnpLock, flags);
|
||||
IoPnpWake();
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
KSTATUS IoPnpPhysicalDeviceArrived(DEVICE_OBJECT* pdo) {
|
||||
if (!pdo || pdo->Type != DeviceObjectPhysical) return NTLSTATUS_INVALID_PARAMETER;
|
||||
if (!gPnpStarted) return NTLSTATUS_SUCCESS;
|
||||
PNP_NODE* node = (PNP_NODE*)MmAllocatePool(sizeof(*node));
|
||||
if (!node) return NTLSTATUS_NO_MEMORY;
|
||||
memset(node, 0, sizeof(*node));
|
||||
node->Pdo = pdo;
|
||||
node->ParentPdo = IoPnpParentPdo(pdo);
|
||||
IoReferenceDeviceObject(pdo);
|
||||
uintptr_t flags = KeAcquireSpinlock(&gPnpLock);
|
||||
if (gPnpShutdown) {
|
||||
KeReleaseSpinlock(&gPnpLock, flags);
|
||||
IoDereferenceDeviceObject(pdo);
|
||||
MmFreePool(node);
|
||||
return NTLSTATUS_DEVICE_REMOVED;
|
||||
}
|
||||
for (uint32_t i = 0; i < gPnpNodeCount; ++i) {
|
||||
if (gPnpNodes[i]->Pdo == pdo) {
|
||||
KeReleaseSpinlock(&gPnpLock, flags);
|
||||
IoDereferenceDeviceObject(pdo);
|
||||
MmFreePool(node);
|
||||
return NTLSTATUS_ALREADY_EXISTS;
|
||||
}
|
||||
}
|
||||
if (gPnpNodeCount == PNP_MAX_DEVICES) {
|
||||
KeReleaseSpinlock(&gPnpLock, flags);
|
||||
IoDereferenceDeviceObject(pdo);
|
||||
MmFreePool(node);
|
||||
return NTLSTATUS_NO_MEMORY;
|
||||
}
|
||||
gPnpNodes[gPnpNodeCount++] = node;
|
||||
KeReleaseSpinlock(&gPnpLock, flags);
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
void IoPnpPhysicalDevicePublished(DEVICE_OBJECT* pdo) {
|
||||
if (!pdo || !gPnpStarted) return;
|
||||
uintptr_t flags = KeAcquireSpinlock(&gPnpLock);
|
||||
for (uint32_t i = 0; i < gPnpNodeCount; ++i) {
|
||||
if (gPnpNodes[i]->Pdo == pdo) {
|
||||
gPnpNodes[i]->Published = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
KeReleaseSpinlock(&gPnpLock, flags);
|
||||
IoPnpWake();
|
||||
}
|
||||
|
||||
void IoPnpPhysicalDeviceRemoved(DEVICE_OBJECT* pdo) {
|
||||
if (!pdo || !gPnpStarted) return;
|
||||
uintptr_t flags = KeAcquireSpinlock(&gPnpLock);
|
||||
for (uint32_t i = 0; i < gPnpNodeCount; ++i) {
|
||||
if (gPnpNodes[i]->Pdo == pdo) {
|
||||
gPnpNodes[i]->Removed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
KeReleaseSpinlock(&gPnpLock, flags);
|
||||
IoPnpWake();
|
||||
}
|
||||
|
||||
KSTATUS IoPnpWaitForIdle(uint64_t timeoutMs) {
|
||||
if (!gPnpStarted) return NTLSTATUS_INVALID_PARAMETER;
|
||||
uint64_t deadline = KeMonotonicDeadlineAfterMilliseconds(timeoutMs);
|
||||
for (;;) {
|
||||
bool idle = true;
|
||||
uintptr_t flags = KeAcquireSpinlock(&gPnpLock);
|
||||
for (uint32_t i = 0; i < gPnpNodeCount; ++i) {
|
||||
PNP_NODE* node = gPnpNodes[i];
|
||||
if (node->State == PnpReserved || node->State == PnpStarting ||
|
||||
node->State == PnpPending || node->State == PnpCancelling ||
|
||||
node->State == PnpCleaning ||
|
||||
node->State == PnpStopping ||
|
||||
(node->State == PnpFailed && node->Binding) ||
|
||||
(node->Removed && node->State != PnpPending) ||
|
||||
(!gPnpShutdown && node->State == PnpUnbound && node->Published &&
|
||||
IoPnpFindDriver(node->Pdo))) {
|
||||
idle = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
KeReleaseSpinlock(&gPnpLock, flags);
|
||||
if (idle) return NTLSTATUS_SUCCESS;
|
||||
uint64_t remaining = KeMonotonicMillisecondsUntil(deadline);
|
||||
if (!remaining) return NTLSTATUS_TIMEOUT;
|
||||
uint32_t index = 0;
|
||||
KSTATUS st = KeWaitForMultipleEvents(&gPnpChanged, 1, false, remaining, &index);
|
||||
if (st == NTLSTATUS_TIMEOUT) return NTLSTATUS_TIMEOUT;
|
||||
if (!KSUCCESS(st)) return st;
|
||||
}
|
||||
}
|
||||
|
||||
KSTATUS IoPnpShutdown(uint64_t timeoutMs) {
|
||||
if (!gPnpStarted) return NTLSTATUS_INVALID_PARAMETER;
|
||||
uintptr_t flags = KeAcquireSpinlock(&gPnpLock);
|
||||
gPnpShutdown = true;
|
||||
for (uint32_t i = 0; i < gPnpNodeCount; ++i)
|
||||
gPnpNodes[i]->Removed = true;
|
||||
KeReleaseSpinlock(&gPnpLock, flags);
|
||||
IoPnpWake();
|
||||
return IoPnpWaitForIdle(timeoutMs);
|
||||
}
|
||||
|
||||
__declspec(dllexport) void* NtlDeviceContext(NTL_DEVICE* device) {
|
||||
return device ? device->Context : (void*)0;
|
||||
}
|
||||
|
||||
__declspec(dllexport) const char* NtlDeviceId(NTL_DEVICE* device, uint32_t index) {
|
||||
if (!device || index >= device->Node->Pdo->u.Pdo.IdCount) return (const char*)0;
|
||||
return device->Node->Pdo->u.Pdo.Ids[index].Id.Data;
|
||||
}
|
||||
|
||||
__declspec(dllexport) NTL_BUS_TYPE NtlDeviceBus(NTL_DEVICE* device) {
|
||||
return device ? (NTL_BUS_TYPE)device->Node->Pdo->u.Pdo.BusType : (NTL_BUS_TYPE)0;
|
||||
}
|
||||
|
||||
__declspec(dllexport) KSTATUS NtlDeviceCompleteStart(NTL_DEVICE* device, KSTATUS result) {
|
||||
if (!device || result == NTLSTATUS_PENDING || !device->Node) return NTLSTATUS_INVALID_PARAMETER;
|
||||
uintptr_t flags = KeAcquireSpinlock(&gPnpLock);
|
||||
PNP_NODE* node = device->Node;
|
||||
if (node->Binding != device || device->CompletionSet ||
|
||||
(node->State != PnpStarting && node->State != PnpPending &&
|
||||
node->State != PnpCancelling)) {
|
||||
KeReleaseSpinlock(&gPnpLock, flags);
|
||||
return NTLSTATUS_OBJECT_DEAD;
|
||||
}
|
||||
device->CompletionSet = true;
|
||||
device->CompletionStatus = result;
|
||||
if (node->State == PnpPending) {
|
||||
node->State = KSUCCESS(result) ? PnpStarted : PnpFailed;
|
||||
}
|
||||
KeReleaseSpinlock(&gPnpLock, flags);
|
||||
IoPnpWake();
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
__declspec(dllexport) KSTATUS NtlDeviceOwnResource(NTL_DEVICE* device, void* context,
|
||||
NTL_DEVICE_RESOURCE_RELEASE release,
|
||||
NTL_DEVICE_RESOURCE** out) {
|
||||
if (!device || !release || !out) return NTLSTATUS_INVALID_PARAMETER;
|
||||
*out = (NTL_DEVICE_RESOURCE*)0;
|
||||
NTL_DEVICE_RESOURCE* resource = (NTL_DEVICE_RESOURCE*)MmAllocatePool(sizeof(*resource));
|
||||
if (!resource) return NTLSTATUS_NO_MEMORY;
|
||||
resource->Owner = device;
|
||||
resource->Context = context;
|
||||
resource->Release = release;
|
||||
uintptr_t flags = KeAcquireSpinlock(&device->Lock);
|
||||
if (device->Closing) {
|
||||
KeReleaseSpinlock(&device->Lock, flags);
|
||||
MmFreePool(resource);
|
||||
return NTLSTATUS_OBJECT_DEAD;
|
||||
}
|
||||
resource->Next = device->Resources;
|
||||
device->Resources = resource;
|
||||
KeReleaseSpinlock(&device->Lock, flags);
|
||||
*out = resource;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
__declspec(dllexport) KSTATUS NtlDeviceReleaseResource(NTL_DEVICE_RESOURCE* resource) {
|
||||
if (!resource || !resource->Owner) return NTLSTATUS_INVALID_PARAMETER;
|
||||
NTL_DEVICE* device = resource->Owner;
|
||||
uintptr_t flags = KeAcquireSpinlock(&device->Lock);
|
||||
NTL_DEVICE_RESOURCE** link = &device->Resources;
|
||||
while (*link && *link != resource) link = &(*link)->Next;
|
||||
if (*link != resource) {
|
||||
KeReleaseSpinlock(&device->Lock, flags);
|
||||
return NTLSTATUS_OBJECT_DEAD;
|
||||
}
|
||||
*link = resource->Next;
|
||||
resource->Owner = (NTL_DEVICE*)0;
|
||||
KeReleaseSpinlock(&device->Lock, flags);
|
||||
resource->Release(resource->Context);
|
||||
MmFreePool(resource);
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
+65
-10
@@ -38,6 +38,10 @@ static size_t gDebugLogSize;
|
||||
static size_t gDebugLogWrite;
|
||||
static size_t gDebugLogUsed;
|
||||
static volatile int32_t gDebugLogFlushing;
|
||||
static bool gDebugAtLineStart = true;
|
||||
static bool gDebugClockReady;
|
||||
static uint64_t gDebugClockOrigin;
|
||||
static uint64_t gDebugLastTick;
|
||||
|
||||
// Appends bytes to the in-memory debug log while its lock is held.
|
||||
static void KiDebugLogAppendLocked(const char* text, size_t len) {
|
||||
@@ -185,6 +189,30 @@ uint32_t KeGetDebugFlags(void) {
|
||||
return gDebugFlags;
|
||||
}
|
||||
|
||||
// Prefixes each logical debug line with elapsed monotonic seconds when available.
|
||||
static size_t KiDebugTimestamp(char* buffer, size_t capacity) {
|
||||
if (!gDebugHal || !gDebugHal->Timer.QueryTicks)
|
||||
return 0;
|
||||
|
||||
uint64_t tick = gDebugHal->Timer.QueryTicks();
|
||||
if (tick == 0)
|
||||
return 0;
|
||||
if (!gDebugClockReady) {
|
||||
gDebugClockOrigin = tick;
|
||||
gDebugLastTick = tick;
|
||||
gDebugClockReady = true;
|
||||
}
|
||||
if (tick < gDebugLastTick)
|
||||
tick = gDebugLastTick;
|
||||
gDebugLastTick = tick;
|
||||
|
||||
uint64_t elapsed = tick - gDebugClockOrigin;
|
||||
int count = snprintf_(buffer, capacity, "[%6llu.%06llu] ",
|
||||
(unsigned long long)(elapsed / 1000000ull),
|
||||
(unsigned long long)(elapsed % 1000000ull));
|
||||
return count > 0 && (size_t)count < capacity ? (size_t)count : 0;
|
||||
}
|
||||
|
||||
// Writes one formatted kernel diagnostic.
|
||||
__declspec(dllexport) void DbgPrint(const char* fmt, ...) {
|
||||
if (!gDebugEnabled || !fmt)
|
||||
@@ -202,23 +230,50 @@ __declspec(dllexport) void DbgPrint(const char* fmt, ...) {
|
||||
if (length >= sizeof(line))
|
||||
length = sizeof(line) - 1u;
|
||||
}
|
||||
if (length == 0)
|
||||
return;
|
||||
|
||||
bool panic = KiPanicIsActive();
|
||||
if (panic && !KiPanicCurrentCpuOwnsReport())
|
||||
KiPanicSecondaryHalt();
|
||||
|
||||
if (!panic) {
|
||||
uintptr_t lockFlags = KeAcquireSpinlock(&gDebugPrintLock);
|
||||
if (!NtlAtomicLoad32(&gDebugLogFlushing))
|
||||
KiDebugLogAppendLocked(line, length);
|
||||
KeReleaseSpinlock(&gDebugPrintLock, lockFlags);
|
||||
uint32_t stream = panic ? HAL_OUTPUT_STREAM_PANIC : HAL_OUTPUT_STREAM_DEBUG;
|
||||
uint32_t flags = panic ? HAL_OUTPUT_WRITE_EMERGENCY : 0;
|
||||
uint32_t foreground = panic ? 0x00FF2222u : 0x00CCCCCCu;
|
||||
uint32_t background = panic ? 0x00000000u : 0x00012456u;
|
||||
|
||||
uintptr_t lockFlags = 0;
|
||||
if (!panic)
|
||||
lockFlags = KeAcquireSpinlock(&gDebugPrintLock);
|
||||
|
||||
size_t offset = 0;
|
||||
while (offset < length) {
|
||||
size_t end = offset;
|
||||
while (end < length && line[end] != '\n')
|
||||
end++;
|
||||
if (end < length)
|
||||
end++;
|
||||
|
||||
if (panic || gDebugAtLineStart) {
|
||||
char prefix[48];
|
||||
size_t prefixLength = KiDebugTimestamp(prefix, sizeof(prefix));
|
||||
if (prefixLength) {
|
||||
if (!panic && !NtlAtomicLoad32(&gDebugLogFlushing))
|
||||
KiDebugLogAppendLocked(prefix, prefixLength);
|
||||
KiOutputWrite(stream, flags, foreground, background, prefix, prefixLength);
|
||||
}
|
||||
}
|
||||
|
||||
if (!panic && !NtlAtomicLoad32(&gDebugLogFlushing))
|
||||
KiDebugLogAppendLocked(line + offset, end - offset);
|
||||
KiOutputWrite(stream, flags, foreground, background, line + offset, end - offset);
|
||||
if (!panic)
|
||||
gDebugAtLineStart = line[end - 1u] == '\n';
|
||||
offset = end;
|
||||
}
|
||||
|
||||
KiOutputWrite(panic ? HAL_OUTPUT_STREAM_PANIC : HAL_OUTPUT_STREAM_DEBUG,
|
||||
panic ? HAL_OUTPUT_WRITE_EMERGENCY : 0,
|
||||
panic ? 0x00FF2222u : 0x00CCCCCCu,
|
||||
panic ? 0x00000000u : 0x00012456u,
|
||||
line, length);
|
||||
if (!panic)
|
||||
KeReleaseSpinlock(&gDebugPrintLock, lockFlags);
|
||||
}
|
||||
|
||||
// Writes one debug-log segment and rejects successful zero-progress writes.
|
||||
|
||||
+45
-11
@@ -1516,6 +1516,45 @@ DISPLAY_API KSTATUS KeDisplayRegisterDevice(DISPLAY_DEVICE* device) {
|
||||
return result;
|
||||
}
|
||||
|
||||
DISPLAY_API KSTATUS KeDisplayUnregisterDevice(DISPLAY_DEVICE* device) {
|
||||
if (!device || device == &gBootFbDevice)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
bool providerHeld = false;
|
||||
KSTATUS status = KiDisplayAcquireProviderMutex(&providerHeld);
|
||||
if (!KSUCCESS(status))
|
||||
return status;
|
||||
|
||||
uintptr_t flags = KeAcquireSpinlock(&gDisplayLock);
|
||||
DISPLAY_DEVICE** link = &gDisplayList;
|
||||
while (*link && *link != device)
|
||||
link = &(*link)->Next;
|
||||
|
||||
if (!*link) {
|
||||
KeReleaseSpinlock(&gDisplayLock, flags);
|
||||
KiDisplayReleaseProviderMutex(providerHeld);
|
||||
return NTLSTATUS_NOT_FOUND;
|
||||
}
|
||||
|
||||
*link = device->Next;
|
||||
device->Next = (DISPLAY_DEVICE*)0;
|
||||
if (gPrimaryDisplay == device) {
|
||||
gPrimaryDisplay = (DISPLAY_DEVICE*)0;
|
||||
gDisplayGeneration++;
|
||||
gCursorDrawn = false;
|
||||
gCursorUpdatePending = false;
|
||||
gCursorAppliedGeneration = gCursorRequestGeneration;
|
||||
gCursorCommittedX = DISPLAY_CURSOR_POS_INVALID;
|
||||
gCursorCommittedY = DISPLAY_CURSOR_POS_INVALID;
|
||||
gDirtyAll = false;
|
||||
gDirtyRectCount = 0;
|
||||
gDrawDepth = 0;
|
||||
}
|
||||
KeReleaseSpinlock(&gDisplayLock, flags);
|
||||
KiDisplayReleaseProviderMutex(providerHeld);
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Implements the display Seed New Primary operation.
|
||||
static KSTATUS KiDisplaySeedNewPrimary(DISPLAY_DEVICE* oldDevice, DISPLAY_DEVICE* newDevice) {
|
||||
if (!oldDevice || !newDevice || oldDevice == newDevice)
|
||||
@@ -1570,6 +1609,11 @@ DISPLAY_API KSTATUS KeDisplaySetPrimary(DISPLAY_DEVICE* device) {
|
||||
|
||||
const char* name = device->Name ? device->Name : "?";
|
||||
|
||||
bool providerHeld = false;
|
||||
KSTATUS result = KiDisplayAcquireProviderMutex(&providerHeld);
|
||||
if (!KSUCCESS(result))
|
||||
return result;
|
||||
|
||||
uintptr_t flags = KeAcquireSpinlock(&gDisplayLock);
|
||||
bool found = false;
|
||||
for (DISPLAY_DEVICE* it = gDisplayList; it; it = it->Next) {
|
||||
@@ -1580,6 +1624,7 @@ DISPLAY_API KSTATUS KeDisplaySetPrimary(DISPLAY_DEVICE* device) {
|
||||
}
|
||||
if (!found) {
|
||||
KeReleaseSpinlock(&gDisplayLock, flags);
|
||||
KiDisplayReleaseProviderMutex(providerHeld);
|
||||
return NTLSTATUS_NOT_FOUND;
|
||||
}
|
||||
|
||||
@@ -1590,17 +1635,6 @@ DISPLAY_API KSTATUS KeDisplaySetPrimary(DISPLAY_DEVICE* device) {
|
||||
gCursorUpdatePending = gCursorVisible;
|
||||
KeReleaseSpinlock(&gDisplayLock, flags);
|
||||
|
||||
bool providerHeld = false;
|
||||
KSTATUS result = KiDisplayAcquireProviderMutex(&providerHeld);
|
||||
if (!KSUCCESS(result)) {
|
||||
flags = KeAcquireSpinlock(&gDisplayLock);
|
||||
gDisplayTransitionInProgress = false;
|
||||
gDisplayGeneration++;
|
||||
KeReleaseSpinlock(&gDisplayLock, flags);
|
||||
KiDisplayQueueCursorWork();
|
||||
return result;
|
||||
}
|
||||
|
||||
if (oldDevice && oldCursorDrawn && oldDevice->Ops && oldDevice->Ops->ShowCursor)
|
||||
(void)oldDevice->Ops->ShowCursor(oldDevice, false);
|
||||
|
||||
|
||||
@@ -13,6 +13,9 @@ static SPINLOCK gDpcLock;
|
||||
static volatile int32_t gDpcInitialized = 0;
|
||||
static volatile int32_t gDpcActive = 0;
|
||||
|
||||
// Bound a single interrupt-exit dispatch so self-requeuing devices yield the CPU.
|
||||
#define KE_DPC_DISPATCH_BUDGET 64u
|
||||
|
||||
// Initializes dpc System.
|
||||
void KeInitializeDpcSystem(void) {
|
||||
LIST_INIT(&gDpcQueue);
|
||||
@@ -83,7 +86,10 @@ void KiDrainDpcQueue(void) {
|
||||
if (oldIrql < DISPATCH_LEVEL)
|
||||
KeRaiseIrql(DISPATCH_LEVEL);
|
||||
|
||||
while (true) {
|
||||
// The xHCI event routine already bounds its own work, but an IRQ stream
|
||||
// can requeue it after every dispatch. Limit the whole drain, too. The
|
||||
// pending queue is picked up at the next timer or device interrupt exit.
|
||||
for (uint32_t dispatched = 0; dispatched < KE_DPC_DISPATCH_BUDGET; dispatched++) {
|
||||
uintptr_t flags = KeAcquireSpinlock(&gDpcLock);
|
||||
LIST_ENTRY* node = KeListPopHeadChecked(&gDpcQueue);
|
||||
if (!node) {
|
||||
|
||||
@@ -301,6 +301,9 @@ SYS_RIGHTS KeGetValidHandleRights(OBJECT_TYPE type) {
|
||||
return management | SYS_RIGHT_QUERY | SYS_DISPLAY_MANAGER_RIGHT_ENUMERATE |
|
||||
SYS_DISPLAY_MANAGER_RIGHT_MODESET | SYS_DISPLAY_MANAGER_RIGHT_PRESENT |
|
||||
SYS_DISPLAY_MANAGER_RIGHT_CURSOR | SYS_DISPLAY_MANAGER_RIGHT_CONSOLE;
|
||||
case ObjectTypeServiceRegistration:
|
||||
return management | SYS_RIGHT_QUERY |
|
||||
SYS_SERVICE_REGISTRATION_RIGHT_REGISTER | SYS_SERVICE_REGISTRATION_RIGHT_UNREGISTER;
|
||||
case ObjectTypeDevice:
|
||||
case ObjectTypeDriver:
|
||||
case ObjectTypeUsbDevice:
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
#include <ntlite/atomic.h>
|
||||
#include <ntlite/syscall.h>
|
||||
#include <kernel/ke_power.h>
|
||||
#include <kernel/ke_process.h>
|
||||
#include <kernel/ke_event.h>
|
||||
#include <kernel/ke_spinlock.h>
|
||||
#include <kernel/ke_irq.h>
|
||||
#include <kernel/ke_dpc.h>
|
||||
#include <kernel/ke_settings.h>
|
||||
#include <kernel/ke_debug.h>
|
||||
#include <kernel/hal/hal_api.h>
|
||||
#include <string.h>
|
||||
|
||||
static SPINLOCK gPowerLock = SPINLOCK_INIT;
|
||||
static EVENT* gPowerRequest;
|
||||
static EVENT* gPowerComplete;
|
||||
static PROCESS* gInit;
|
||||
static uint32_t gAction;
|
||||
static bool gCommitted;
|
||||
static const HAL_API* gButtonHal;
|
||||
static uint32_t gButtonAction;
|
||||
static KDPC gPowerButtonDpc;
|
||||
|
||||
KSTATUS KePowerInitialize(void) {
|
||||
if (gPowerRequest) return NTLSTATUS_ALREADY_EXISTS;
|
||||
KSTATUS st = KeEventCreate(EventManualReset, false, &gPowerRequest);
|
||||
if (!KSUCCESS(st)) return st;
|
||||
st = KeEventCreate(EventManualReset, false, &gPowerComplete);
|
||||
return st;
|
||||
}
|
||||
|
||||
// Delivers the physical action after interrupt-exit processing.
|
||||
static void KiPowerButtonDpc(KDPC* dpc, void* context, void* arg1, void* arg2) {
|
||||
(void)dpc;
|
||||
(void)context;
|
||||
(void)arg1;
|
||||
(void)arg2;
|
||||
(void)KePowerRequestHardware(gButtonAction);
|
||||
}
|
||||
|
||||
// The ACPI SCI handler only acknowledges the fixed event and queues normal work.
|
||||
static void KiPowerButtonIsr(void) {
|
||||
if (!gButtonHal || !gButtonHal->Power.ConsumeFixedButton ||
|
||||
!gButtonHal->Power.ConsumeFixedButton()) return;
|
||||
(void)KeInsertQueueDpc(&gPowerButtonDpc, NULL, NULL);
|
||||
}
|
||||
|
||||
// Reads immutable boot-time button policy and attaches it to the fixed ACPI SCI.
|
||||
void KePowerInitializeButton(const HAL_API* hal) {
|
||||
char mode[24];
|
||||
KSTATUS status = KeGetSettingString("power", "button", mode, sizeof(mode));
|
||||
if (!KSUCCESS(status))
|
||||
KePanic(KE_PANIC_CODE_INIT, "power: missing [power] button, status=0x%x\n", status);
|
||||
if (strcmp(mode, "ignore") == 0) {
|
||||
DbgPrint("power: ACPI power button ignored by policy\n");
|
||||
return;
|
||||
}
|
||||
if (strcmp(mode, "shutdown") == 0) gButtonAction = SYS_POWER_ACTION_SHUTDOWN;
|
||||
else if (strcmp(mode, "restart") == 0) gButtonAction = SYS_POWER_ACTION_RESTART;
|
||||
else KePanic(KE_PANIC_CODE_INIT, "power: invalid [power] button=%s\n", mode);
|
||||
|
||||
if (!hal || !hal->Power.QueryFixedButtonIrq || !hal->Power.ArmFixedButton ||
|
||||
!hal->Power.ConsumeFixedButton) {
|
||||
DbgPrint("power: no ACPI fixed power button HAL support\n");
|
||||
return;
|
||||
}
|
||||
uint8_t irq = 0;
|
||||
status = hal->Power.QueryFixedButtonIrq(&irq);
|
||||
if (!KSUCCESS(status)) {
|
||||
DbgPrint("power: ACPI fixed button unavailable (0x%x); AML button not yet supported\n", status);
|
||||
return;
|
||||
}
|
||||
// The IOAPIC is still masked for this IRQ. Arm the firmware source first,
|
||||
// then install the kernel handler and finally unmask via KeIrqRegister.
|
||||
status = hal->Power.ArmFixedButton();
|
||||
if (!KSUCCESS(status)) {
|
||||
DbgPrint("power: cannot arm fixed button IRQ %u, status=0x%x\n", irq, status);
|
||||
return;
|
||||
}
|
||||
KeInitializeDpc(&gPowerButtonDpc, KiPowerButtonDpc, NULL);
|
||||
gButtonHal = hal;
|
||||
KeIrqRegister(irq, KiPowerButtonIsr);
|
||||
DbgPrint("power: ACPI fixed button armed on IRQ %u, action=%s\n", irq, mode);
|
||||
}
|
||||
|
||||
void KePowerSetInitProcess(PROCESS* init) {
|
||||
uintptr_t flags = KeAcquireSpinlock(&gPowerLock);
|
||||
gInit = init;
|
||||
KeReleaseSpinlock(&gPowerLock, flags);
|
||||
}
|
||||
|
||||
// Records one power transition under the coordinator lock. The hardware event
|
||||
// may arrive before init exists; syscall-originated requests require init.
|
||||
static KSTATUS KiPowerQueueRequest(uint32_t action, bool requireInit) {
|
||||
if (action != SYS_POWER_ACTION_SHUTDOWN && action != SYS_POWER_ACTION_RESTART)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
uintptr_t flags = KeAcquireSpinlock(&gPowerLock);
|
||||
KSTATUS status = NTLSTATUS_SUCCESS;
|
||||
if (!gPowerRequest || (requireInit && !gInit)) status = NTLSTATUS_NOT_SUPPORTED;
|
||||
else if (gAction) status = NTLSTATUS_ALREADY_EXISTS;
|
||||
else gAction = action;
|
||||
KeReleaseSpinlock(&gPowerLock, flags);
|
||||
if (KSUCCESS(status)) KeSignalEvent(gPowerRequest);
|
||||
return status;
|
||||
}
|
||||
|
||||
// Accepts a privileged explicit power request only after init exists.
|
||||
KSTATUS KePowerRequest(uint32_t action) { return KiPowerQueueRequest(action, true); }
|
||||
|
||||
// Latches a physical power-button request even during init startup.
|
||||
KSTATUS KePowerRequestHardware(uint32_t action) { return KiPowerQueueRequest(action, false); }
|
||||
|
||||
EVENT* KePowerRequestEvent(PROCESS* caller) {
|
||||
uintptr_t flags = KeAcquireSpinlock(&gPowerLock);
|
||||
EVENT* event = caller && caller == gInit ? gPowerRequest : (EVENT*)0;
|
||||
KeReleaseSpinlock(&gPowerLock, flags);
|
||||
return event;
|
||||
}
|
||||
|
||||
KSTATUS KePowerGetRequest(PROCESS* caller, uint32_t* actionOut) {
|
||||
if (!actionOut) return NTLSTATUS_INVALID_PARAMETER;
|
||||
uintptr_t flags = KeAcquireSpinlock(&gPowerLock);
|
||||
KSTATUS st = caller && caller == gInit ? NTLSTATUS_SUCCESS : NTLSTATUS_ACCESS_DENIED;
|
||||
if (KSUCCESS(st)) *actionOut = gAction;
|
||||
KeReleaseSpinlock(&gPowerLock, flags);
|
||||
return st;
|
||||
}
|
||||
|
||||
KSTATUS KePowerComplete(PROCESS* caller, uint32_t action) {
|
||||
uintptr_t flags = KeAcquireSpinlock(&gPowerLock);
|
||||
KSTATUS st = caller && caller == gInit ? NTLSTATUS_SUCCESS : NTLSTATUS_ACCESS_DENIED;
|
||||
if (KSUCCESS(st) && (!gAction || action != gAction || gCommitted))
|
||||
st = NTLSTATUS_INVALID_PARAMETER;
|
||||
if (KSUCCESS(st)) gCommitted = true;
|
||||
KeReleaseSpinlock(&gPowerLock, flags);
|
||||
if (KSUCCESS(st)) KeSignalEvent(gPowerComplete);
|
||||
return st;
|
||||
}
|
||||
|
||||
EVENT* KePowerCompletionEvent(void) { return gPowerComplete; }
|
||||
|
||||
uint32_t KePowerCommittedAction(void) {
|
||||
uintptr_t flags = KeAcquireSpinlock(&gPowerLock);
|
||||
uint32_t result = gCommitted ? gAction : 0;
|
||||
KeReleaseSpinlock(&gPowerLock, flags);
|
||||
return result;
|
||||
}
|
||||
@@ -629,7 +629,8 @@ KSTATUS KeExitProcess(PROCESS* process, int32_t exitCode) {
|
||||
// Begins termination of one process and wakes every sibling thread.
|
||||
KSTATUS KeTerminateProcess(PROCESS* process, int32_t exitCode) {
|
||||
if (!process) return NTLSTATUS_INVALID_PARAMETER;
|
||||
if (KeIsProcessTerminationProtected(process)) return NTLSTATUS_ACCESS_DENIED;
|
||||
if (KeIsProcessTerminationProtected(process) && KeGetCurrentProcess() != process)
|
||||
return NTLSTATUS_ACCESS_DENIED;
|
||||
|
||||
process->ExitCode = exitCode;
|
||||
NtlAtomicExchange32(&process->Terminating, 1);
|
||||
|
||||
+16
-1333
File diff suppressed because it is too large.
Load diff
@@ -863,218 +863,3 @@ KSTATUS KeAdmitApplicationProcessorsToScheduler(void) {
|
||||
DbgPrint("SMP: scheduler admitted %u APs\n", admitted);
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
#define SMP_AP_WORKER_ROUNDS 64u
|
||||
#define SMP_AP_WORKER_TIMEOUT_US 2000000ull
|
||||
#define SMP_AP_TEST_MAGIC 0x524B575041504D53ULL // 'SMPAPWKR'
|
||||
|
||||
typedef struct SMP_AP_WORK_TEST SMP_AP_WORK_TEST;
|
||||
|
||||
typedef struct SMP_AP_WORKER_ARG {
|
||||
uint64_t Magic;
|
||||
uint32_t CpuIndex;
|
||||
uint32_t WorkerIndex;
|
||||
struct SMP_AP_WORK_TEST* Test;
|
||||
EVENT* StepEvent;
|
||||
volatile int32_t Ready;
|
||||
volatile int32_t WaitArmed;
|
||||
volatile int32_t Progress;
|
||||
volatile int32_t Done;
|
||||
volatile int32_t Failure;
|
||||
uint32_t WrongCpu;
|
||||
uint32_t WrongRunningCpu;
|
||||
} SMP_AP_WORKER_ARG;
|
||||
|
||||
struct SMP_AP_WORK_TEST {
|
||||
uint64_t Magic;
|
||||
uint32_t WorkerCount;
|
||||
volatile int32_t ReadyCount;
|
||||
volatile int32_t DoneCount;
|
||||
volatile int32_t FailureCount;
|
||||
volatile int32_t CpuHits[KE_MAX_CPUS];
|
||||
SMP_AP_WORKER_ARG Workers[KE_MAX_CPUS];
|
||||
};
|
||||
|
||||
// Implements the smp Ap Controlled Worker operation.
|
||||
static void KiSmpApControlledWorker(void* context) {
|
||||
SMP_AP_WORKER_ARG* arg = (SMP_AP_WORKER_ARG*)context;
|
||||
if (!arg || arg->Magic != SMP_AP_TEST_MAGIC || !arg->StepEvent) {
|
||||
KePanic(KE_PANIC_CODE_SELFTEST, "SMP AP worker received bad context 0x%016llX\n", (uint64_t)arg);
|
||||
}
|
||||
|
||||
NtlAtomicExchange32(&arg->Ready, 1);
|
||||
NtlAtomicIncrement32(&arg->Test->ReadyCount);
|
||||
|
||||
for (uint32_t round = 0; round < SMP_AP_WORKER_ROUNDS; round++) {
|
||||
NtlAtomicIncrement32(&arg->WaitArmed);
|
||||
KSTATUS waitStatus = KeWaitForEvent(arg->StepEvent, 0);
|
||||
if (!KSUCCESS(waitStatus)) {
|
||||
NtlAtomicExchange32(&arg->Failure, 1);
|
||||
break;
|
||||
}
|
||||
|
||||
KCPU* cpu = KeGetCurrentCpu();
|
||||
THREAD* self = KeGetCurrentThread();
|
||||
if (!cpu || cpu->CpuIndex != arg->CpuIndex) {
|
||||
arg->WrongCpu = cpu ? cpu->CpuIndex : KE_INVALID_CPU;
|
||||
NtlAtomicExchange32(&arg->Failure, 1);
|
||||
break;
|
||||
}
|
||||
if (!self || self->RunningCpu != cpu->CpuIndex) {
|
||||
arg->WrongRunningCpu = self ? self->RunningCpu : KE_INVALID_CPU;
|
||||
NtlAtomicExchange32(&arg->Failure, 1);
|
||||
break;
|
||||
}
|
||||
if (self && (self->AffinityMask & (1ULL << cpu->CpuIndex)) == 0) {
|
||||
arg->WrongCpu = cpu->CpuIndex;
|
||||
NtlAtomicExchange32(&arg->Failure, 1);
|
||||
break;
|
||||
}
|
||||
|
||||
if (cpu->CpuIndex < KE_MAX_CPUS)
|
||||
NtlAtomicIncrement32(&arg->Test->CpuHits[cpu->CpuIndex]);
|
||||
|
||||
NtlAtomicIncrement32(&arg->Progress);
|
||||
if ((round & 3u) == 0)
|
||||
KeYield();
|
||||
}
|
||||
|
||||
if (NtlAtomicLoad32(&arg->Failure) != 0) {
|
||||
NtlAtomicIncrement32(&arg->Test->FailureCount);
|
||||
}
|
||||
|
||||
NtlAtomicExchange32(&arg->Done, 1);
|
||||
NtlAtomicIncrement32(&arg->Test->DoneCount);
|
||||
}
|
||||
|
||||
// Waits for for Worker Counter.
|
||||
static bool KiWaitForWorkerCounter(volatile int32_t* counter, int32_t expected, const char* what) {
|
||||
uint64_t deadline = KiSmpDeadlineAfter(SMP_AP_WORKER_TIMEOUT_US);
|
||||
for (;;) {
|
||||
if (NtlAtomicLoad32(counter) >= expected)
|
||||
return true;
|
||||
if (KiSmpQueryTicks() >= deadline)
|
||||
break;
|
||||
KeYield();
|
||||
}
|
||||
DbgPrint("SMP scheduler selftest timeout waiting for %s expected=%d got=%ld\n",
|
||||
what ? what : "counter", expected, NtlAtomicLoad32(counter));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Runs application Processor Scheduler Self Tests.
|
||||
KSTATUS KeRunApplicationProcessorSchedulerSelfTests(void) {
|
||||
if (gCpuCount <= 1) {
|
||||
DbgPrint("SMP scheduler selftest: SKIP, no APs\n");
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
DbgPrint("SMP scheduler selftest: begin AP-affine workers\n");
|
||||
|
||||
SMP_AP_WORK_TEST* test = (SMP_AP_WORK_TEST*)MmAllocatePool(sizeof(SMP_AP_WORK_TEST));
|
||||
if (!test) return NTLSTATUS_NO_MEMORY;
|
||||
memset(test, 0, sizeof(*test));
|
||||
test->Magic = SMP_AP_TEST_MAGIC;
|
||||
|
||||
uint32_t workerCount = 0;
|
||||
for (uint32_t cpuIndex = 1; cpuIndex < gCpuCount; cpuIndex++) {
|
||||
KCPU* cpu = &gCpus[cpuIndex];
|
||||
if (cpu->State != KeCpuOnline)
|
||||
continue;
|
||||
if (cpuIndex >= 64)
|
||||
continue;
|
||||
|
||||
SMP_AP_WORKER_ARG* arg = &test->Workers[workerCount];
|
||||
arg->Magic = SMP_AP_TEST_MAGIC;
|
||||
arg->CpuIndex = cpuIndex;
|
||||
arg->WorkerIndex = workerCount;
|
||||
arg->Test = test;
|
||||
|
||||
KSTATUS st = KeEventCreate(EventAutoReset, 0, &arg->StepEvent);
|
||||
if (!KSUCCESS(st)) {
|
||||
KePanic(KE_PANIC_CODE_SELFTEST,
|
||||
"SMP scheduler selftest: AP%u step event create failed status=0x%x\n",
|
||||
cpuIndex, st);
|
||||
}
|
||||
|
||||
KE_THREAD_CREATE_PARAMETERS workerParameters =
|
||||
KE_THREAD_CREATE_NAMED_NORMAL(PriorityNormal, "selftest.smp");
|
||||
workerParameters.AffinityMask = 1ULL << cpuIndex;
|
||||
st = KeCreateKernelThread((uintptr_t)KiSmpApControlledWorker,
|
||||
(uintptr_t)arg,
|
||||
&workerParameters,
|
||||
(THREAD**)0);
|
||||
if (!KSUCCESS(st)) {
|
||||
KePanic(KE_PANIC_CODE_SELFTEST,
|
||||
"SMP scheduler selftest: AP%u worker create failed status=0x%x\n",
|
||||
cpuIndex, st);
|
||||
}
|
||||
|
||||
workerCount++;
|
||||
}
|
||||
test->WorkerCount = workerCount;
|
||||
|
||||
if (workerCount == 0) {
|
||||
MmFreePool(test);
|
||||
DbgPrint("SMP scheduler selftest: SKIP, no online AP workers\n");
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
if (!KiWaitForWorkerCounter(&test->ReadyCount, (int32_t)workerCount, "AP worker ready")) {
|
||||
KePanic(KE_PANIC_CODE_SELFTEST, "SMP scheduler selftest: workers failed to become ready ready=%ld workers=%u\n",
|
||||
test->ReadyCount, workerCount);
|
||||
}
|
||||
|
||||
for (uint32_t round = 0; round < SMP_AP_WORKER_ROUNDS; round++) {
|
||||
for (uint32_t w = 0; w < workerCount; w++) {
|
||||
SMP_AP_WORKER_ARG* arg = &test->Workers[w];
|
||||
if (!KiWaitForWorkerCounter(&arg->WaitArmed, (int32_t)(round + 1), "AP worker wait-armed")) {
|
||||
KePanic(KE_PANIC_CODE_SELFTEST,
|
||||
"SMP scheduler selftest: AP%u did not arm wait for round %u armed=%ld progress=%ld\n",
|
||||
arg->CpuIndex, round, arg->WaitArmed, arg->Progress);
|
||||
}
|
||||
KeSignalEvent(arg->StepEvent);
|
||||
}
|
||||
|
||||
for (uint32_t w = 0; w < workerCount; w++) {
|
||||
SMP_AP_WORKER_ARG* arg = &test->Workers[w];
|
||||
if (!KiWaitForWorkerCounter(&arg->Progress, (int32_t)(round + 1), "AP worker progress")) {
|
||||
KePanic(KE_PANIC_CODE_SELFTEST,
|
||||
"SMP scheduler selftest: AP%u no progress round=%u progress=%ld failure=%ld wrongCpu=%u wrongRunningCpu=%u\n",
|
||||
arg->CpuIndex, round, arg->Progress, arg->Failure,
|
||||
arg->WrongCpu, arg->WrongRunningCpu);
|
||||
}
|
||||
if (NtlAtomicLoad32(&arg->Failure) != 0) {
|
||||
KePanic(KE_PANIC_CODE_SELFTEST,
|
||||
"SMP scheduler selftest: AP%u worker failed round=%u wrongCpu=%u wrongRunningCpu=%u progress=%ld\n",
|
||||
arg->CpuIndex, round, arg->WrongCpu, arg->WrongRunningCpu, arg->Progress);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!KiWaitForWorkerCounter(&test->DoneCount, (int32_t)workerCount, "AP worker done")) {
|
||||
KePanic(KE_PANIC_CODE_SELFTEST, "SMP scheduler selftest: workers did not finish done=%d workers=%u\n",
|
||||
test->DoneCount, workerCount);
|
||||
}
|
||||
|
||||
if (NtlAtomicLoad32(&test->FailureCount) != 0) {
|
||||
KePanic(KE_PANIC_CODE_SELFTEST, "SMP scheduler selftest: failure count=%d\n",
|
||||
test->FailureCount);
|
||||
}
|
||||
|
||||
for (uint32_t w = 0; w < workerCount; w++) {
|
||||
SMP_AP_WORKER_ARG* arg = &test->Workers[w];
|
||||
int32_t hits = (arg->CpuIndex < KE_MAX_CPUS) ? test->CpuHits[arg->CpuIndex] : 0;
|
||||
if (hits <= 0) {
|
||||
KePanic(KE_PANIC_CODE_SELFTEST,
|
||||
"SMP scheduler selftest: AP%u recorded no CPU hits\n", arg->CpuIndex);
|
||||
}
|
||||
if (arg->StepEvent)
|
||||
ObDereferenceObject(&arg->StepEvent->Header);
|
||||
}
|
||||
|
||||
DbgPrint("SMP scheduler selftest: PASS workers=%u rounds=%u\n",
|
||||
workerCount, SMP_AP_WORKER_ROUNDS);
|
||||
MmFreePool(test);
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
+69
-32
@@ -33,6 +33,7 @@
|
||||
#include <kernel/ke_cpu.h>
|
||||
#include <kernel/ke_sched.h>
|
||||
#include <kernel/ke_process.h>
|
||||
#include <kernel/ke_power.h>
|
||||
#include <kernel/se_security.h>
|
||||
#include <kernel/ke_mutex.h>
|
||||
#include <kernel/ke_semaphore.h>
|
||||
@@ -317,6 +318,40 @@ static size_t KiAlignUpSize(size_t value, size_t alignment) {
|
||||
|
||||
// Individual syscall handlers.
|
||||
|
||||
static uint64_t SyscallOpenPowerEvent(SYS_HANDLE* handleOut) {
|
||||
if (!handleOut) return NTLSTATUS_INVALID_PARAMETER;
|
||||
KSTATUS st = MmProbeUserWrite(handleOut, sizeof(*handleOut));
|
||||
if (!KSUCCESS(st)) return st;
|
||||
PROCESS* caller = KeGetCurrentProcess();
|
||||
EVENT* event = KePowerRequestEvent(caller);
|
||||
if (!event) return NTLSTATUS_ACCESS_DENIED;
|
||||
SYS_HANDLE handle = SYS_HANDLE_INVALID;
|
||||
st = KiAllocHandleForCurrentWithRights(&event->Header,
|
||||
SYS_RIGHT_WAIT | SYS_RIGHT_QUERY, &handle);
|
||||
if (!KSUCCESS(st)) return st;
|
||||
st = KiCopyHandleToUser(handleOut, handle);
|
||||
if (!KSUCCESS(st)) (void)KeCloseHandle(caller, handle);
|
||||
return st;
|
||||
}
|
||||
|
||||
static uint64_t SyscallRequestSystemPower(uint32_t action) {
|
||||
KSTATUS st = KiRequirePrivilege(SYS_PRIV_SYSTEM_POWER);
|
||||
return KSUCCESS(st) ? KePowerRequest(action) : st;
|
||||
}
|
||||
|
||||
static uint64_t SyscallQuerySystemPower(uint32_t* actionOut) {
|
||||
if (!actionOut) return NTLSTATUS_INVALID_PARAMETER;
|
||||
KSTATUS st = MmProbeUserWrite(actionOut, sizeof(*actionOut));
|
||||
if (!KSUCCESS(st)) return st;
|
||||
uint32_t action = 0;
|
||||
st = KePowerGetRequest(KeGetCurrentProcess(), &action);
|
||||
return KSUCCESS(st) ? KiCopyU32ToUser(actionOut, action) : st;
|
||||
}
|
||||
|
||||
static uint64_t SyscallCompleteSystemPower(uint32_t action) {
|
||||
return KePowerComplete(KeGetCurrentProcess(), action);
|
||||
}
|
||||
|
||||
// Implements SYS_EXIT_PROCESS.
|
||||
static uint64_t SyscallExitProcess(int32_t exitCode) {
|
||||
THREAD* t = KeGetCurrentThread();
|
||||
@@ -2873,12 +2908,12 @@ static uint64_t SyscallStartProcess(const SYS_START_PROCESS_REQUEST* request) {
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < req.StartupCapabilityCount && KSUCCESS(status); ++i) {
|
||||
if (!capabilities[i].Tag || capabilities[i].Handle == SYS_HANDLE_INVALID) {
|
||||
if (!SysStartupCapabilityNameValid(capabilities[i].Name) || capabilities[i].Handle == SYS_HANDLE_INVALID) {
|
||||
status = NTLSTATUS_INVALID_PARAMETER;
|
||||
break;
|
||||
}
|
||||
for (uint32_t j = 0; j < i; ++j) {
|
||||
if (capabilities[j].Tag == capabilities[i].Tag) {
|
||||
if (strcmp(capabilities[j].Name, capabilities[i].Name) == 0) {
|
||||
status = NTLSTATUS_OBJECT_NAME_COLLISION;
|
||||
break;
|
||||
}
|
||||
@@ -3414,48 +3449,48 @@ static uint64_t SyscallOpenPortPeerProcess(SYS_HANDLE endpointHandle,
|
||||
return status;
|
||||
}
|
||||
|
||||
// Implements SYS_REGISTER_SYSTEM_SERVICE.
|
||||
static uint64_t SyscallRegisterSystemService(SYS_SERVICE_ID serviceId, SYS_HANDLE listenerHandle) {
|
||||
if (serviceId == SYS_SERVICE_INVALID || serviceId > SYS_SERVICE_MAX)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
KSTATUS status = KiRequirePrivilege(SYS_PRIV_SYSTEM_SERVICE_REGISTER);
|
||||
// Implements SYS_REGISTER_SYSTEM_SERVICE using a typed, attenuable registration authority.
|
||||
static uint64_t SyscallRegisterSystemService(SYS_HANDLE registrationHandle, SYS_HANDLE listenerHandle) {
|
||||
PROCESS* process = KeGetCurrentProcess();
|
||||
if (!process) return NTLSTATUS_FAILURE;
|
||||
|
||||
OBJECT* authority = (OBJECT*)0;
|
||||
KSTATUS status = KeReferenceHandle(process, registrationHandle, ObjectTypeServiceRegistration,
|
||||
SYS_SERVICE_REGISTRATION_RIGHT_REGISTER, &authority);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
|
||||
PROCESS* process = KeGetCurrentProcess();
|
||||
SYS_SERVICE_CLAIMS claim = KeSystemServiceRequiredClaim(serviceId);
|
||||
if (!process || !process->SecurityContext || claim == 0 ||
|
||||
!SeContextHasServiceClaims(process->SecurityContext, claim))
|
||||
return NTLSTATUS_ACCESS_DENIED;
|
||||
SYS_SERVICE_ID serviceId = SYS_SERVICE_INVALID;
|
||||
status = KeSystemServiceRegistrationIdentity(authority, &serviceId);
|
||||
ObDereferenceObject(authority);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
|
||||
KSTATUS handleStatus = NTLSTATUS_SUCCESS;
|
||||
PORT* listener = (PORT*)KiHandleToObject(listenerHandle,
|
||||
ObjectTypePort,
|
||||
SYS_PORT_RIGHT_ACCEPT,
|
||||
&handleStatus);
|
||||
PORT* listener = (PORT*)KiHandleToObject(listenerHandle, ObjectTypePort,
|
||||
SYS_PORT_RIGHT_ACCEPT, &handleStatus);
|
||||
if (!listener) return handleStatus;
|
||||
status = KeSystemServiceRegister(serviceId, listener);
|
||||
ObDereferenceObject((OBJECT*)listener);
|
||||
return status;
|
||||
}
|
||||
|
||||
// Implements SYS_UNREGISTER_SYSTEM_SERVICE.
|
||||
static uint64_t SyscallUnregisterSystemService(SYS_SERVICE_ID serviceId, SYS_HANDLE listenerHandle) {
|
||||
if (serviceId == SYS_SERVICE_INVALID || serviceId > SYS_SERVICE_MAX)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
KSTATUS status = KiRequirePrivilege(SYS_PRIV_SYSTEM_SERVICE_REGISTER);
|
||||
// Implements SYS_UNREGISTER_SYSTEM_SERVICE using the same typed authority.
|
||||
static uint64_t SyscallUnregisterSystemService(SYS_HANDLE registrationHandle, SYS_HANDLE listenerHandle) {
|
||||
PROCESS* process = KeGetCurrentProcess();
|
||||
if (!process) return NTLSTATUS_FAILURE;
|
||||
|
||||
OBJECT* authority = (OBJECT*)0;
|
||||
KSTATUS status = KeReferenceHandle(process, registrationHandle, ObjectTypeServiceRegistration,
|
||||
SYS_SERVICE_REGISTRATION_RIGHT_UNREGISTER, &authority);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
|
||||
PROCESS* process = KeGetCurrentProcess();
|
||||
SYS_SERVICE_CLAIMS claim = KeSystemServiceRequiredClaim(serviceId);
|
||||
if (!process || !process->SecurityContext || claim == 0 ||
|
||||
!SeContextHasServiceClaims(process->SecurityContext, claim))
|
||||
return NTLSTATUS_ACCESS_DENIED;
|
||||
SYS_SERVICE_ID serviceId = SYS_SERVICE_INVALID;
|
||||
status = KeSystemServiceRegistrationIdentity(authority, &serviceId);
|
||||
ObDereferenceObject(authority);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
|
||||
KSTATUS handleStatus = NTLSTATUS_SUCCESS;
|
||||
PORT* listener = (PORT*)KiHandleToObject(listenerHandle,
|
||||
ObjectTypePort,
|
||||
SYS_PORT_RIGHT_ACCEPT,
|
||||
&handleStatus);
|
||||
PORT* listener = (PORT*)KiHandleToObject(listenerHandle, ObjectTypePort,
|
||||
SYS_PORT_RIGHT_ACCEPT, &handleStatus);
|
||||
if (!listener) return handleStatus;
|
||||
status = KeSystemServiceUnregister(serviceId, listener);
|
||||
ObDereferenceObject((OBJECT*)listener);
|
||||
@@ -3700,8 +3735,6 @@ static uint64_t SyscallDeriveSecurityContext(const SYS_DERIVE_SECURITY_CONTEXT_R
|
||||
status = SeDeriveSecurityContext(source,
|
||||
req.EffectivePrivileges,
|
||||
req.DelegablePrivileges,
|
||||
req.ServiceClaims,
|
||||
req.DelegableServiceClaims,
|
||||
&derived);
|
||||
ObDereferenceObject(&source->Header);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
@@ -6197,6 +6230,10 @@ typedef struct SYSCALL_ENTRY {
|
||||
|
||||
static const SYSCALL_ENTRY gSyscallTable[SYS_MAX] = {
|
||||
// Process, threads, waits, APC, and exceptions.
|
||||
[SYS_OPEN_POWER_EVENT] = SYSCALL_ENTRY1(SyscallOpenPowerEvent),
|
||||
[SYS_REQUEST_SYSTEM_POWER] = SYSCALL_ENTRY1(SyscallRequestSystemPower),
|
||||
[SYS_QUERY_SYSTEM_POWER] = SYSCALL_ENTRY1(SyscallQuerySystemPower),
|
||||
[SYS_COMPLETE_SYSTEM_POWER] = SYSCALL_ENTRY1(SyscallCompleteSystemPower),
|
||||
[SYS_EXIT_PROCESS] = SYSCALL_ENTRY1(SyscallExitProcess),
|
||||
[SYS_EXIT_THREAD] = SYSCALL_ENTRY1(SyscallExitThread),
|
||||
[SYS_CREATE_PROCESS_OBJECT] = SYSCALL_ENTRY1(SyscallCreateProcessObject),
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include <kernel/ke_port.h>
|
||||
#include <kernel/ke_spinlock.h>
|
||||
#include <kernel/ob_object.h>
|
||||
#include <ntlite/security.h>
|
||||
#include <kernel/ob_object.h>
|
||||
|
||||
static SPINLOCK gSystemServiceLock = SPINLOCK_INIT;
|
||||
static PORT* gSystemServices[SYS_SERVICE_MAX + 1u];
|
||||
@@ -14,14 +14,35 @@ static bool KiSystemServiceIdValid(SYS_SERVICE_ID serviceId) {
|
||||
return serviceId > SYS_SERVICE_INVALID && serviceId <= SYS_SERVICE_MAX;
|
||||
}
|
||||
|
||||
// Returns the security claim that authorizes publication of one well-known service.
|
||||
SYS_SERVICE_CLAIMS KeSystemServiceRequiredClaim(SYS_SERVICE_ID serviceId) {
|
||||
switch (serviceId) {
|
||||
case SYS_SERVICE_RESOURCE:
|
||||
return SYS_SERVICE_CLAIM_RESOURCE;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
// Authority is an object capability, never a process privilege or service role.
|
||||
typedef struct KE_SERVICE_REGISTRATION_AUTHORITY {
|
||||
OBJECT Header;
|
||||
SYS_SERVICE_ID ServiceId;
|
||||
} KE_SERVICE_REGISTRATION_AUTHORITY;
|
||||
|
||||
// Creates a capability to register one numeric service identity.
|
||||
KSTATUS KeSystemServiceCreateRegistrationAuthority(SYS_SERVICE_ID serviceId, OBJECT** authorityOut) {
|
||||
if (!authorityOut) return NTLSTATUS_INVALID_PARAMETER;
|
||||
*authorityOut = (OBJECT*)0;
|
||||
if (!KiSystemServiceIdValid(serviceId)) return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
KE_SERVICE_REGISTRATION_AUTHORITY* authority =
|
||||
(KE_SERVICE_REGISTRATION_AUTHORITY*)ObAllocateObject(ObjectTypeServiceRegistration,
|
||||
sizeof(*authority));
|
||||
if (!authority) return NTLSTATUS_NO_MEMORY;
|
||||
authority->ServiceId = serviceId;
|
||||
*authorityOut = &authority->Header;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Resolves an authority's registry identity without consulting process identity.
|
||||
KSTATUS KeSystemServiceRegistrationIdentity(const OBJECT* object, SYS_SERVICE_ID* serviceIdOut) {
|
||||
if (!object || !serviceIdOut || object->Type != ObjectTypeServiceRegistration)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
const KE_SERVICE_REGISTRATION_AUTHORITY* authority = (const KE_SERVICE_REGISTRATION_AUTHORITY*)object;
|
||||
if (!KiSystemServiceIdValid(authority->ServiceId)) return NTLSTATUS_INVALID_PARAMETER;
|
||||
*serviceIdOut = authority->ServiceId;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Registers one weak listener reference without creating a string or pathname namespace.
|
||||
|
||||
+243
-216
@@ -2,10 +2,12 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <ntlite/bootstrap_capabilities.h>
|
||||
#include <string.h>
|
||||
#include <intrin.h>
|
||||
#pragma intrinsic(_disable)
|
||||
#include <ntlite/boot_info.h>
|
||||
#include <ntlite/syscall.h>
|
||||
#include <kernel/ke_hal.h>
|
||||
#include <kernel/mm_phys.h>
|
||||
#include <kernel/mm_pfn.h>
|
||||
@@ -13,6 +15,8 @@
|
||||
#include <kernel/mm_swap.h>
|
||||
#include <kernel/ob_object.h>
|
||||
#include <kernel/ke_process.h>
|
||||
#include <kernel/ke_power.h>
|
||||
#include <kernel/mm_file_cache.h>
|
||||
#include <kernel/ke_thread.h>
|
||||
#include <kernel/ke_sched.h>
|
||||
#include <kernel/ke_cpu.h>
|
||||
@@ -23,6 +27,9 @@
|
||||
#include <ntlite/ddi/net_device.h>
|
||||
#include <kernel/ke_debug.h>
|
||||
#include <kernel/ldr_image.h>
|
||||
#include <kernel/driver_module.h>
|
||||
#include <kernel/io_pnp.h>
|
||||
#include <kernel/dm_config.h>
|
||||
#include <kernel/hal/hal_api.h>
|
||||
#include <kernel/io_file.h>
|
||||
#include <kernel/io_volume.h>
|
||||
@@ -38,6 +45,7 @@
|
||||
#include <kernel/ke_work.h>
|
||||
#include <kernel/ke_system_root.h>
|
||||
#include <kernel/se_security.h>
|
||||
#include <kernel/ke_system_service.h>
|
||||
#include <kernel/ke_boot_config.h>
|
||||
|
||||
typedef struct {
|
||||
@@ -123,8 +131,6 @@ static void KiInitializeSwapFromSettings(void) {
|
||||
}
|
||||
}
|
||||
|
||||
typedef KSTATUS (*DRIVER_ENTRY_FN)(const NTL_DDK* ddk, NTL_DRIVER_INIT* init);
|
||||
|
||||
// HAL pointer cached at boot for subsystems that still need platform services.
|
||||
static const HAL_API* gKmainHal;
|
||||
|
||||
@@ -168,231 +174,179 @@ static void KiReclaimBootloaderMemory(const BOOT_INFO* bootInfo) {
|
||||
KiReclaimBootBlob(bootInfo, list->FilesPhysical, list->PageCount);
|
||||
if (bootInfo->BootIni.PhysicalBase && bootInfo->BootIni.PageCount)
|
||||
KiReclaimBootBlob(bootInfo, bootInfo->BootIni.PhysicalBase, bootInfo->BootIni.PageCount);
|
||||
if (bootInfo->DriversIni.PhysicalBase && bootInfo->DriversIni.PageCount)
|
||||
KiReclaimBootBlob(bootInfo, bootInfo->DriversIni.PhysicalBase, bootInfo->DriversIni.PageCount);
|
||||
}
|
||||
|
||||
// Returns whether one drivers.ini line character is horizontal whitespace.
|
||||
static bool KiDriverConfigSpace(char c) {
|
||||
return c == ' ' || c == '\t' || c == '\r';
|
||||
}
|
||||
|
||||
// Trims horizontal whitespace from one immutable drivers.ini line span.
|
||||
static void KiDriverConfigTrim(const char** begin, const char** end) {
|
||||
while (*begin < *end && KiDriverConfigSpace(**begin))
|
||||
++*begin;
|
||||
while (*end > *begin && KiDriverConfigSpace((*end)[-1]))
|
||||
--*end;
|
||||
}
|
||||
|
||||
// Returns whether one immutable drivers.ini span exactly matches an ASCII string.
|
||||
static bool KiDriverConfigSpanEquals(const char* begin, const char* end, const char* text) {
|
||||
while (begin < end && *text) {
|
||||
if (*begin++ != *text++)
|
||||
return false;
|
||||
}
|
||||
return begin == end && *text == '\0';
|
||||
}
|
||||
|
||||
// Loads one runtime driver filename from the system-root drivers directory.
|
||||
static KSTATUS KiLoadRuntimeDriverByName(const char* name, const NTL_DDK* ddk) {
|
||||
char relative[512];
|
||||
size_t length = strlen(name);
|
||||
if (length + 9u > sizeof(relative))
|
||||
return NTLSTATUS_BUFFER_TOO_SMALL;
|
||||
|
||||
// Loads a runtime image from the selected system root, keeping PE identity separate
|
||||
// from the configured module id used for lifecycle and dependency accounting.
|
||||
static KSTATUS KiLoadRuntimeDriver(const DM_DRIVER_CONFIG* driver) {
|
||||
char relative[DM_IMAGE_MAX + 8u];
|
||||
size_t length = strlen(driver->Image);
|
||||
memcpy(relative, "drivers/", 8);
|
||||
memcpy(relative + 8, name, length + 1);
|
||||
memcpy(relative + 8, driver->Image, length + 1);
|
||||
FILE_OBJECT* file = NULL;
|
||||
KSTATUS status = KeOpenSystemRootFile(relative, &file);
|
||||
if (!KSUCCESS(status))
|
||||
return status;
|
||||
if (!KSUCCESS(status)) return status;
|
||||
|
||||
uint64_t driverSize = IoGetFileSize(file);
|
||||
if (driverSize == 0 || driverSize > SIZE_MAX) {
|
||||
uint64_t size = IoGetFileSize(file);
|
||||
if (!size || size > SIZE_MAX) {
|
||||
IoCloseFile(file);
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
void* driverMemory = MmAllocatePool((size_t)driverSize);
|
||||
if (!driverMemory) {
|
||||
void* memory = MmAllocatePool((size_t)size);
|
||||
if (!memory) {
|
||||
IoCloseFile(file);
|
||||
return NTLSTATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
size_t got = 0;
|
||||
status = IoReadFile(file, driverMemory, (size_t)driverSize, &got);
|
||||
status = IoReadFile(file, memory, (size_t)size, &got);
|
||||
IoCloseFile(file);
|
||||
if (!KSUCCESS(status) || got != (size_t)driverSize) {
|
||||
MmFreePool(driverMemory);
|
||||
if (!KSUCCESS(status) || got != (size_t)size) {
|
||||
MmFreePool(memory);
|
||||
return KSUCCESS(status) ? NTLSTATUS_FAILURE : status;
|
||||
}
|
||||
|
||||
uintptr_t mappedBase = 0;
|
||||
status = LdrLoadKernelImage(driverMemory, (size_t)driverSize, &mappedBase);
|
||||
MmFreePool(driverMemory);
|
||||
if (!KSUCCESS(status))
|
||||
return status;
|
||||
|
||||
status = LdrResolveImports(mappedBase);
|
||||
if (!KSUCCESS(status))
|
||||
return status;
|
||||
|
||||
status = LdrRegisterModule(name, mappedBase);
|
||||
if (!KSUCCESS(status))
|
||||
return status;
|
||||
|
||||
DRIVER_ENTRY_FN entry = (DRIVER_ENTRY_FN)LdrGetEntryPoint(mappedBase);
|
||||
if (!entry)
|
||||
return NTLSTATUS_FAILURE;
|
||||
|
||||
NTL_DRIVER_INIT init = {0};
|
||||
return entry(ddk, &init);
|
||||
uintptr_t base = 0;
|
||||
status = LdrLoadKernelImage(memory, (size_t)size, &base);
|
||||
MmFreePool(memory);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
status = LdrResolveImports(base);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
status = LdrRegisterModule(driver->Image, base);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
status = DmStartLoadedDriver(driver->Id, base);
|
||||
if (KSUCCESS(status))
|
||||
status = LdrSetModuleExportsAvailable(driver->Image, true);
|
||||
return status;
|
||||
}
|
||||
|
||||
// Scans only the [runtime] section of drivers.ini and loads each filename immediately.
|
||||
static KSTATUS KiLoadRuntimeDriversFromConfig(const char* text, size_t length, const NTL_DDK* ddk) {
|
||||
int inRuntime = 0;
|
||||
size_t offset = 0;
|
||||
|
||||
while (offset < length) {
|
||||
size_t lineStart = offset;
|
||||
while (offset < length && text[offset] != '\n')
|
||||
++offset;
|
||||
size_t lineEnd = offset;
|
||||
if (offset < length)
|
||||
++offset;
|
||||
|
||||
const char* begin = text + lineStart;
|
||||
const char* end = text + lineEnd;
|
||||
KiDriverConfigTrim(&begin, &end);
|
||||
if (begin == end || *begin == ';' || *begin == '#')
|
||||
continue;
|
||||
|
||||
if (*begin == '[') {
|
||||
if (end[-1] != ']')
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
const char* sectionBegin = begin + 1;
|
||||
const char* sectionEnd = end - 1;
|
||||
KiDriverConfigTrim(§ionBegin, §ionEnd);
|
||||
inRuntime = KiDriverConfigSpanEquals(sectionBegin, sectionEnd, "runtime");
|
||||
continue;
|
||||
// Starts a preloaded boot driver by its configuration identity, not array position.
|
||||
static KSTATUS KiLoadBootDriver(const BOOT_INFO* bootInfo,
|
||||
const DM_DRIVER_CONFIG* driver) {
|
||||
const BOOT_FILE* files = (const BOOT_FILE*)(bootInfo->PhysicalMapBase +
|
||||
bootInfo->BootDrivers.FilesPhysical);
|
||||
const BOOT_FILE* found = NULL;
|
||||
for (uint32_t i = 0; i < bootInfo->BootDrivers.Count; ++i) {
|
||||
if (!strcmp(files[i].DriverId, driver->Id)) {
|
||||
found = &files[i];
|
||||
break;
|
||||
}
|
||||
|
||||
if (!inRuntime)
|
||||
continue;
|
||||
|
||||
size_t nameLength = (size_t)(end - begin);
|
||||
if (nameLength == 0 || nameLength >= BOOT_FILE_NAME_MAX)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
for (const char* p = begin; p < end; ++p) {
|
||||
if (*p == '=' || *p == '/' || *p == '\\')
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
char name[BOOT_FILE_NAME_MAX];
|
||||
memcpy(name, begin, nameLength);
|
||||
name[nameLength] = '\0';
|
||||
DbgPrint("Found runtime driver: %s\n", name);
|
||||
|
||||
KSTATUS status = KiLoadRuntimeDriverByName(name, ddk);
|
||||
if (!KSUCCESS(status))
|
||||
DbgPrint("KiInitializeDrivers: runtime driver %s failed, status=0x%x\n", name, status);
|
||||
}
|
||||
|
||||
return NTLSTATUS_SUCCESS;
|
||||
if (!found || strcmp(found->Image, driver->Image)) return NTLSTATUS_INVALID_PARAMETER;
|
||||
uintptr_t base = 0;
|
||||
KSTATUS status = LdrLoadKernelImage(
|
||||
(const void*)(bootInfo->PhysicalMapBase + found->PhysicalBase), found->Length, &base);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
status = LdrResolveImports(base);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
status = LdrRegisterModule(driver->Image, base);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
status = DmStartLoadedDriver(driver->Id, base);
|
||||
if (KSUCCESS(status))
|
||||
status = LdrSetModuleExportsAvailable(driver->Image, true);
|
||||
return status;
|
||||
}
|
||||
|
||||
// Initializes drivers.
|
||||
// Validates that the firmware preloaded exactly the configured boot modules.
|
||||
static bool KiCheckBootDriverHandoff(const BOOT_INFO* bootInfo, const DM_CONFIG* config) {
|
||||
const BOOT_FILE* files = (const BOOT_FILE*)(bootInfo->PhysicalMapBase +
|
||||
bootInfo->BootDrivers.FilesPhysical);
|
||||
uint32_t expected = 0;
|
||||
for (uint32_t i = 0; i < config->DriverCount; ++i) {
|
||||
const DM_DRIVER_CONFIG* driver = &config->Drivers[i];
|
||||
if (driver->Phase != DmStartBoot) continue;
|
||||
++expected;
|
||||
uint32_t occurrences = 0;
|
||||
for (uint32_t j = 0; j < bootInfo->BootDrivers.Count; ++j) {
|
||||
if (!strcmp(files[j].DriverId, driver->Id) &&
|
||||
!strcmp(files[j].Image, driver->Image)) ++occurrences;
|
||||
}
|
||||
if (occurrences != 1) return false;
|
||||
}
|
||||
return expected == bootInfo->BootDrivers.Count;
|
||||
}
|
||||
|
||||
// Starts the lexicographically first eligible driver, independent of INI order.
|
||||
// Failed providers block requires= dependents, while after= waits for settlement.
|
||||
static void KiRunDriverPhase(DM_CONFIG* config, const BOOT_INFO* bootInfo,
|
||||
DM_START_PHASE phase) {
|
||||
config->ActivePhase = phase;
|
||||
for (;;) {
|
||||
int selected = -1;
|
||||
for (uint32_t i = 0; i < config->DriverCount; ++i) {
|
||||
const DM_DRIVER_CONFIG* d = &config->Drivers[i];
|
||||
if (d->Phase != phase || !DmDriverCanStart(config, i)) continue;
|
||||
if (selected < 0 || strcmp(d->Id, config->Drivers[selected].Id) < 0)
|
||||
selected = (int)i;
|
||||
}
|
||||
if (selected >= 0) {
|
||||
DM_DRIVER_CONFIG* d = &config->Drivers[selected];
|
||||
DbgPrint("Driver manager: starting %s (%s)\n", d->Id, d->Image);
|
||||
KSTATUS status = phase == DmStartBoot
|
||||
? KiLoadBootDriver(bootInfo, d) : KiLoadRuntimeDriver(d);
|
||||
d->State = KSUCCESS(status) ? DmRunStarted : DmRunFailed;
|
||||
if (!KSUCCESS(status)) {
|
||||
DbgPrint("Driver manager: failed %s, status=0x%x\n", d->Id, status);
|
||||
if (d->Critical)
|
||||
KePanic(KE_PANIC_CODE_INIT,
|
||||
"Critical driver %s failed (0x%x)\n", d->Id, status);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
bool changed = false;
|
||||
uint32_t waiting = 0;
|
||||
for (uint32_t i = 0; i < config->DriverCount; ++i) {
|
||||
DM_DRIVER_CONFIG* d = &config->Drivers[i];
|
||||
if (d->Phase != phase || d->State != DmRunWaiting) continue;
|
||||
++waiting;
|
||||
if (DmDriverIsBlocked(config, i)) {
|
||||
d->State = DmRunBlocked;
|
||||
DbgPrint("Driver manager: blocked %s by unsatisfied dependency\n", d->Id);
|
||||
if (d->Critical)
|
||||
KePanic(KE_PANIC_CODE_INIT, "Critical driver %s blocked\n", d->Id);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
if (changed) continue;
|
||||
if (waiting)
|
||||
KePanic(KE_PANIC_CODE_INIT,
|
||||
"Driver manager: no eligible drivers in phase %u (waiting=%u)\n",
|
||||
(unsigned)phase, waiting);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static DM_CONFIG* gDriverConfig;
|
||||
|
||||
static void KiInitializeDrivers(BOOT_INFO* bootInfo, const HAL_API* hal) {
|
||||
(void)hal;
|
||||
const NTL_DDK* ddk = IoGetDdk();
|
||||
DbgPrint("KiInitializeDrivers: Initializing drivers...\n");
|
||||
|
||||
// Boot drivers are page-backed raw files supplied by the bootloader.
|
||||
const BOOT_FILE* bootFiles = bootInfo->BootDrivers.FilesPhysical
|
||||
? (const BOOT_FILE*)(bootInfo->PhysicalMapBase + bootInfo->BootDrivers.FilesPhysical)
|
||||
: NULL;
|
||||
DM_CONFIG* config = (DM_CONFIG*)MmAllocatePool(sizeof(*config));
|
||||
if (!config)
|
||||
KePanic(KE_PANIC_CODE_INIT, "Driver manager: configuration allocation failed\n");
|
||||
const char* text = (const char*)(bootInfo->PhysicalMapBase +
|
||||
bootInfo->DriversIni.PhysicalBase);
|
||||
char error[96] = {0};
|
||||
if (!DmConfigParse(config, text, bootInfo->DriversIni.Length, error, sizeof(error)))
|
||||
KePanic(KE_PANIC_CODE_INIT, "Driver manager: invalid drivers.ini: %s\n", error);
|
||||
if (!KiCheckBootDriverHandoff(bootInfo, config))
|
||||
KePanic(KE_PANIC_CODE_INIT, "Driver manager: boot preloads do not match drivers.ini\n");
|
||||
|
||||
for (uint32_t i = 0; bootFiles && i < bootInfo->BootDrivers.Count; ++i) {
|
||||
const BOOT_FILE* file = &bootFiles[i];
|
||||
if (!file->PhysicalBase || !file->Length) {
|
||||
DbgPrint("KiInitializeDrivers: invalid boot driver descriptor at index %u\n", i);
|
||||
continue;
|
||||
}
|
||||
KSTATUS pnpStatus = IoPnpInitialize();
|
||||
if (!KSUCCESS(pnpStatus))
|
||||
KePanic(KE_PANIC_CODE_INIT, "PnP initialization failed (0x%x)\n", pnpStatus);
|
||||
|
||||
DbgPrint("KiInitializeDrivers: Loading boot driver %s\n", file->Name);
|
||||
void* rawPeBuffer = (void*)(bootInfo->PhysicalMapBase + file->PhysicalBase);
|
||||
KiRunDriverPhase(config, bootInfo, DmStartBoot);
|
||||
pnpStatus = IoPnpWaitForIdle(30000u);
|
||||
if (!KSUCCESS(pnpStatus))
|
||||
KePanic(KE_PANIC_CODE_INIT, "PnP boot startup did not settle (0x%x)\n", pnpStatus);
|
||||
|
||||
uintptr_t mappedBase = 0;
|
||||
KSTATUS st = LdrLoadKernelImage(rawPeBuffer, file->Length, &mappedBase);
|
||||
if (!KSUCCESS(st)) {
|
||||
DbgPrint("ERROR: Failed to map driver %s (0x%X)\n", file->Name, st);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!KSUCCESS(LdrResolveImports(mappedBase))) {
|
||||
DbgPrint("ERROR: Failed to resolve imports for %s\n", file->Name);
|
||||
continue;
|
||||
}
|
||||
|
||||
KSTATUS registerStatus = LdrRegisterModule(file->Name, mappedBase);
|
||||
if (!KSUCCESS(registerStatus)) {
|
||||
DbgPrint("ERROR: Failed to register driver %s (0x%X)\n", file->Name, registerStatus);
|
||||
continue;
|
||||
}
|
||||
|
||||
DRIVER_ENTRY_FN entry = (DRIVER_ENTRY_FN)LdrGetEntryPoint(mappedBase);
|
||||
if (entry) {
|
||||
NTL_DRIVER_INIT init = {0};
|
||||
KSTATUS ds = entry(ddk, &init);
|
||||
if (!KSUCCESS(ds))
|
||||
DbgPrint("WARNING: Driver %s returned failure status 0x%X\n", file->Name, ds);
|
||||
}
|
||||
}
|
||||
|
||||
// runtime drivers.
|
||||
KSTATUS s;
|
||||
FILE_OBJECT* f;
|
||||
|
||||
s = KeInitializeSystemRootVolume();
|
||||
if (s != NTLSTATUS_SUCCESS) {
|
||||
KePanic(KE_PANIC_CODE_INIT, "KiInitializeDrivers: Failed to find system root volume (status %d)\n", s);
|
||||
}
|
||||
|
||||
|
||||
s = KeOpenSystemRootFile("drivers/drivers.ini", &f);
|
||||
if (s != NTLSTATUS_SUCCESS) {
|
||||
KePanic(KE_PANIC_CODE_INIT, "KiInitializeDrivers: Failed to open drivers.ini on system root (status %d)\n", s);
|
||||
}
|
||||
|
||||
uint64_t fileSize = IoGetFileSize(f);
|
||||
if (fileSize == 0 || fileSize >= SIZE_MAX) {
|
||||
IoCloseFile(f);
|
||||
KePanic(KE_PANIC_CODE_INIT, "KiInitializeDrivers: drivers.ini has an invalid size.\n");
|
||||
}
|
||||
|
||||
size_t size = (size_t)fileSize;
|
||||
char* mem = (char*)MmAllocatePool(size + 1u);
|
||||
if (!mem) {
|
||||
IoCloseFile(f);
|
||||
KePanic(KE_PANIC_CODE_INIT, "KiInitializeDrivers: Failed to allocate drivers.ini buffer.\n");
|
||||
}
|
||||
|
||||
size_t got = 0;
|
||||
KSTATUS readStatus = IoReadFile(f, mem, size, &got);
|
||||
IoCloseFile(f);
|
||||
if (!KSUCCESS(readStatus) || got != size) {
|
||||
MmFreePool(mem);
|
||||
KePanic(KE_PANIC_CODE_INIT, "KiInitializeDrivers: Failed to read drivers.ini.\n");
|
||||
}
|
||||
mem[got] = '\0';
|
||||
|
||||
KSTATUS runtimeStatus = KiLoadRuntimeDriversFromConfig(mem, got, ddk);
|
||||
MmFreePool(mem);
|
||||
if (!KSUCCESS(runtimeStatus)) {
|
||||
KePanic(KE_PANIC_CODE_INIT,
|
||||
"KiInitializeDrivers: drivers.ini [runtime] is malformed, status=0x%x\n",
|
||||
runtimeStatus);
|
||||
}
|
||||
// Root volume readiness depends on real devices, not the driver module state.
|
||||
KSTATUS status = KeInitializeSystemRootVolume();
|
||||
if (!KSUCCESS(status))
|
||||
KePanic(KE_PANIC_CODE_INIT, "Driver manager: no system root (0x%x)\n", status);
|
||||
KiRunDriverPhase(config, bootInfo, DmStartRuntime);
|
||||
// Retain the authoritative graph and module states for the shutdown slice.
|
||||
gDriverConfig = config;
|
||||
}
|
||||
|
||||
// Phase Helper: Load and launch a process from the filesystem.
|
||||
@@ -442,6 +396,7 @@ static PROCESS* KiLaunchUserProcess(const char* filePath, const char* args) {
|
||||
SYS_HANDLE volumeManagerHandle = SYS_HANDLE_INVALID;
|
||||
SYS_HANDLE inputManagerHandle = SYS_HANDLE_INVALID;
|
||||
SYS_HANDLE displayManagerHandle = SYS_HANDLE_INVALID;
|
||||
SYS_HANDLE registrationAuthorityHandle = SYS_HANDLE_INVALID;
|
||||
s = KeReferenceSystemRootVolume(&systemVolume);
|
||||
if (KSUCCESS(s)) {
|
||||
s = KeCreateHandle(proc, &systemVolume->Header,
|
||||
@@ -493,20 +448,33 @@ static PROCESS* KiLaunchUserProcess(const char* filePath, const char* args) {
|
||||
ObDereferenceObject(&displayManager->Header);
|
||||
}
|
||||
}
|
||||
if (KSUCCESS(s)) {
|
||||
OBJECT* authority = (OBJECT*)0;
|
||||
s = KeSystemServiceCreateRegistrationAuthority(SYS_SERVICE_RESOURCE, &authority);
|
||||
if (KSUCCESS(s)) {
|
||||
s = KeCreateHandle(proc, authority,
|
||||
SYS_RIGHT_DUPLICATE | SYS_RIGHT_DERIVE | SYS_RIGHT_TRANSFER |
|
||||
SYS_RIGHT_QUERY | SYS_SERVICE_REGISTRATION_RIGHT_REGISTER |
|
||||
SYS_SERVICE_REGISTRATION_RIGHT_UNREGISTER,
|
||||
®istrationAuthorityHandle);
|
||||
ObDereferenceObject(authority);
|
||||
}
|
||||
}
|
||||
if (!KSUCCESS(s)) {
|
||||
DbgPrint("KiLaunchUserProcess: Failed to create bootstrap startup capabilities. Status: %d\n", s);
|
||||
IoCloseFile(f);
|
||||
ObDereferenceObject(&proc->Header);
|
||||
return 0;
|
||||
}
|
||||
SYS_STARTUP_CAPABILITY startupCapabilities[5] = {
|
||||
{ SYS_STARTUP_CAP_SYSTEM_VOLUME, systemVolumeHandle },
|
||||
{ SYS_STARTUP_CAP_SYSTEM_VOLUME_NAMESPACE, namespaceVolumeHandle },
|
||||
{ SYS_STARTUP_CAP_VOLUME_MANAGER, volumeManagerHandle },
|
||||
{ SYS_STARTUP_CAP_INPUT_MANAGER, inputManagerHandle },
|
||||
{ SYS_STARTUP_CAP_DISPLAY_MANAGER, displayManagerHandle },
|
||||
SYS_STARTUP_CAPABILITY startupCapabilities[6] = {
|
||||
{ NTL_BOOT_CAP_SYSTEM_VOLUME, systemVolumeHandle },
|
||||
{ NTL_BOOT_CAP_NAMESPACE_ROOT, namespaceVolumeHandle },
|
||||
{ NTL_BOOT_CAP_VOLUME_MANAGER, volumeManagerHandle },
|
||||
{ NTL_BOOT_CAP_INPUT_MANAGER, inputManagerHandle },
|
||||
{ NTL_BOOT_CAP_DISPLAY_POLICY, displayManagerHandle },
|
||||
{ NTL_BOOT_CAP_RESOURCE_REGISTRATION, registrationAuthorityHandle },
|
||||
};
|
||||
s = KeSetProcessStartupCapabilities(proc, startupCapabilities, 5);
|
||||
s = KeSetProcessStartupCapabilities(proc, startupCapabilities, 6);
|
||||
if (!KSUCCESS(s)) {
|
||||
DbgPrint("KiLaunchUserProcess: Failed to install startup capabilities. Status: %d\n", s);
|
||||
IoCloseFile(f);
|
||||
@@ -538,16 +506,20 @@ static PROCESS* KiLaunchUserProcess(const char* filePath, const char* args) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
proc->Flags |= KE_PROCESS_FLAG_TERMINATION_PROTECTED;
|
||||
proc->ImageBase = imageBase;
|
||||
proc->ImageSize = LdrGetMappedImageSize(proc, imageBase);
|
||||
|
||||
uintptr_t entryPoint = LdrGetEntryPoint(imageBase);
|
||||
|
||||
KePowerSetInitProcess(proc);
|
||||
|
||||
// Create the main thread last. KeCreateThread queues it before returning.
|
||||
const KE_THREAD_CREATE_PARAMETERS threadParameters =
|
||||
KE_THREAD_CREATE_NAMED_NORMAL(PriorityNormal, proc->Name);
|
||||
s = KeCreateThread(proc, (uintptr_t)entryPoint, 0, &threadParameters, &thread);
|
||||
if (!KSUCCESS(s)) {
|
||||
KePowerSetInitProcess((PROCESS*)0);
|
||||
DbgPrint("KiLaunchUserProcess: Failed to create thread. Status: %d\n", s);
|
||||
proc->ImageBase = 0;
|
||||
proc->ImageSize = 0;
|
||||
@@ -562,16 +534,33 @@ static PROCESS* KiLaunchUserProcess(const char* filePath, const char* args) {
|
||||
return proc;
|
||||
}
|
||||
|
||||
static void KiFlushRegisteredDisk(uint8_t disk, DEVICE_OBJECT* device, void* context) {
|
||||
(void)device;
|
||||
const char* phase = (const char*)context;
|
||||
KSTATUS st = IoFlushDisk(disk);
|
||||
if (!KSUCCESS(st) && st != NTLSTATUS_NOT_FOUND)
|
||||
DbgPrint("shutdown: %s disk %u flush status 0x%x\n", phase, (unsigned)disk, st);
|
||||
}
|
||||
|
||||
static void KiFlushRegisteredDisks(const char* phase) {
|
||||
KSTATUS st = IoEnumerateDiskDevices(KiFlushRegisteredDisk, (void*)phase);
|
||||
if (!KSUCCESS(st))
|
||||
DbgPrint("shutdown: %s disk enumeration failed 0x%x\n", phase, st);
|
||||
}
|
||||
|
||||
// Implements the system Init Thread operation.
|
||||
void SystemInitThread(void* arg) {
|
||||
SYSTEM_INIT_ARGS* args = (SYSTEM_INIT_ARGS*)arg;
|
||||
|
||||
DbgPrint("SystemInitThread: Thread started. Igniting the system...\n");
|
||||
|
||||
KiRunEarlyKernelSelfTests();
|
||||
|
||||
KiInitializeDrivers(args->BootInfo, args->Hal);
|
||||
|
||||
KSTATUS powerStatus = KePowerInitialize();
|
||||
if (!KSUCCESS(powerStatus))
|
||||
KePanic(KE_PANIC_CODE_INIT, "Power coordinator failed (0x%x)\n", powerStatus);
|
||||
|
||||
KePowerInitializeButton(args->Hal);
|
||||
KeTimeReloadSettings();
|
||||
|
||||
KiInitializeSwapFromSettings();
|
||||
@@ -579,7 +568,7 @@ void SystemInitThread(void* arg) {
|
||||
KiReclaimBootloaderMemory(args->BootInfo);
|
||||
|
||||
if (args->BootConfig.Debug & KE_DEBUG_SELFTEST)
|
||||
KiRunKernelSelfTests(args->Hal);
|
||||
KiRunKernelSelfTests();
|
||||
|
||||
// Launch the bootstrap service manager. Its allocated PID is not a system contract.
|
||||
PROCESS* initProcess = KiLaunchUserProcess("system/init.exe", args->BootConfig.CommandLine);
|
||||
@@ -588,12 +577,23 @@ void SystemInitThread(void* arg) {
|
||||
KePanic(KE_PANIC_CODE_INIT, "SystemInitThread: Failed to start system/init.exe.\n");
|
||||
}
|
||||
|
||||
KeWaitForEvent(initProcess->ExitEvent, 0);
|
||||
EVENT* exits[2] = { KePowerCompletionEvent(), initProcess->ExitEvent };
|
||||
uint32_t signaled = 0;
|
||||
KSTATUS waitStatus = KeWaitForMultipleEvents(exits, 2u, false,
|
||||
SYS_WAIT_TIMEOUT_INFINITE, &signaled);
|
||||
if (!KSUCCESS(waitStatus) || signaled != 0u)
|
||||
KePanic(KE_PANIC_CODE_INIT, "SystemInitThread: init exited without power commit\n");
|
||||
(void)KeWaitForEvent(initProcess->ExitEvent, false);
|
||||
int exitCode = initProcess->ExitCode;
|
||||
if (exitCode != 0) {
|
||||
KePanic(KE_PANIC_CODE_INIT, "SystemInitThread: init.exe exited with code %d.\n", exitCode);
|
||||
}
|
||||
DbgPrint("SystemInitThread: init.exe exited with code %d.\n", exitCode);
|
||||
DbgPrint("SystemInitThread: power commit accepted; init exited with code %d.\n", exitCode);
|
||||
uint32_t action = KePowerCommittedAction();
|
||||
if (!action) KePanic(KE_PANIC_CODE_INIT, "SystemInitThread: missing power action\n");
|
||||
KSTATUS flush = MmFileCacheFlushAll();
|
||||
if (!KSUCCESS(flush)) DbgPrint("shutdown: cache flush status 0x%x\n", flush);
|
||||
KiFlushRegisteredDisks("initial");
|
||||
ObDereferenceObject(&initProcess->Header);
|
||||
|
||||
if (args->BootConfig.Debug & (KE_DEBUG_CONSOLE | KE_DEBUG_FILE_LOG)) {
|
||||
@@ -601,6 +601,20 @@ void SystemInitThread(void* arg) {
|
||||
if (!KSUCCESS(logStatus))
|
||||
DbgPrint("debuglog: shutdown flush failed st=0x%x\n", logStatus);
|
||||
}
|
||||
flush = MmFileCacheFlushAll();
|
||||
if (!KSUCCESS(flush)) DbgPrint("shutdown: final cache flush status 0x%x\n", flush);
|
||||
KiFlushRegisteredDisks("final");
|
||||
KSTATUS prepare = DmPrepareShutdown();
|
||||
if (!KSUCCESS(prepare)) DbgPrint("shutdown: prepare status 0x%x\n", prepare);
|
||||
KSTATUS pnpShutdown = IoPnpShutdown(10000u);
|
||||
if (!KSUCCESS(pnpShutdown))
|
||||
DbgPrint("shutdown: device stop status 0x%x\n", pnpShutdown);
|
||||
DmCommitShutdown();
|
||||
if (action == SYS_POWER_ACTION_RESTART) {
|
||||
if (!gKmainHal->Power.Reset)
|
||||
KePanic(KE_PANIC_CODE_INIT, "shutdown: reset HAL callback unavailable\n");
|
||||
gKmainHal->Power.Reset();
|
||||
}
|
||||
gKmainHal->Power.Shutdown();
|
||||
MmFreePool(args);
|
||||
}
|
||||
@@ -620,9 +634,11 @@ static bool KiValidateBootFile(const BOOT_FILE* file) {
|
||||
return false;
|
||||
|
||||
uint64_t capacity = (uint64_t)file->PageCount * PAGE_SIZE;
|
||||
if ((uint64_t)file->Length + 1u > capacity || file->Name[0] == '\0')
|
||||
if ((uint64_t)file->Length + 1u > capacity)
|
||||
return false;
|
||||
return memchr(file->Name, '\0', sizeof(file->Name)) != NULL;
|
||||
return file->DriverId[0] != '\0' && file->Image[0] != '\0' &&
|
||||
memchr(file->DriverId, '\0', sizeof(file->DriverId)) &&
|
||||
memchr(file->Image, '\0', sizeof(file->Image));
|
||||
}
|
||||
|
||||
// Returns whether one fixed core-image descriptor fits inside its page-backed physical allocation.
|
||||
@@ -657,10 +673,20 @@ static bool KiValidateBootInfo(const BOOT_INFO* bootInfo) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t driverIniCapacity = (uint64_t)bootInfo->DriversIni.PageCount * PAGE_SIZE;
|
||||
if (!KiValidateBootPageRange(bootInfo->DriversIni.PhysicalBase,
|
||||
bootInfo->DriversIni.PageCount) ||
|
||||
bootInfo->DriversIni.Length == 0 ||
|
||||
bootInfo->DriversIni.Length > DM_CONFIG_MAX_BYTES ||
|
||||
(uint64_t)bootInfo->DriversIni.Length + 1u > driverIniCapacity ||
|
||||
bootInfo->DriversIni.PhysicalBase > UINTPTR_MAX - bootInfo->PhysicalMapBase)
|
||||
return false;
|
||||
|
||||
uint64_t driverListCapacity = (uint64_t)bootInfo->BootDrivers.PageCount * PAGE_SIZE;
|
||||
uint64_t driverListBytes = (uint64_t)bootInfo->BootDrivers.Count * sizeof(BOOT_FILE);
|
||||
if (!KiValidateBootPageRange(bootInfo->BootDrivers.FilesPhysical, bootInfo->BootDrivers.PageCount) ||
|
||||
bootInfo->BootDrivers.Count == 0 || driverListBytes > driverListCapacity ||
|
||||
bootInfo->BootDrivers.Count == 0 || bootInfo->BootDrivers.Count > DM_MAX_DRIVERS ||
|
||||
driverListBytes > driverListCapacity ||
|
||||
bootInfo->BootDrivers.FilesPhysical > UINTPTR_MAX - bootInfo->PhysicalMapBase) {
|
||||
return false;
|
||||
}
|
||||
@@ -825,7 +851,8 @@ extern void KernelMain(BOOT_INFO* bootInfo) {
|
||||
KePanic(KE_PANIC_CODE_INIT, "SMP: failed to admit APs to scheduler, status=0x%x\n", smpSchedStatus);
|
||||
}
|
||||
|
||||
KiRunCoreHardeningSelfTests();
|
||||
if (bootConfig.Debug & KE_DEBUG_SELFTEST)
|
||||
KiCheckBootState();
|
||||
|
||||
// Pack the arguments for the init thread.
|
||||
SYSTEM_INIT_ARGS* initArgs = MmAllocatePool(sizeof(SYSTEM_INIT_ARGS));
|
||||
|
||||
+26
-5
@@ -170,6 +170,7 @@ typedef struct MODULE_ENTRY {
|
||||
char Name[LDR_KERNEL_MODULE_NAME_MAX];
|
||||
uintptr_t Base;
|
||||
size_t Size;
|
||||
bool ExportsAvailable;
|
||||
struct MODULE_ENTRY* Next;
|
||||
} MODULE_ENTRY;
|
||||
|
||||
@@ -182,7 +183,8 @@ static KSTATUS KiEnsureUserImageWrite(PROCESS* process, bool user, uintptr_t add
|
||||
}
|
||||
|
||||
// Allocates and inserts one global kernel-module registry entry.
|
||||
static KSTATUS KiRegisterGlobalModule(const char* name, uintptr_t base, size_t size) {
|
||||
static KSTATUS KiRegisterGlobalModule(const char* name, uintptr_t base, size_t size,
|
||||
bool exportsAvailable) {
|
||||
if (!name || !*name || !base || !size)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
@@ -201,6 +203,7 @@ static KSTATUS KiRegisterGlobalModule(const char* name, uintptr_t base, size_t s
|
||||
entry->Name[nameLength] = '\0';
|
||||
entry->Base = base;
|
||||
entry->Size = size;
|
||||
entry->ExportsAvailable = exportsAvailable;
|
||||
entry->Next = gModules;
|
||||
gModules = entry;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
@@ -213,19 +216,32 @@ KSTATUS LdrRegisterModulesFromBootInfo(const BOOT_INFO* bootInfo) {
|
||||
|
||||
KSTATUS status = KiRegisterGlobalModule("kernel.exe",
|
||||
bootInfo->KernelImage.VirtualBase,
|
||||
bootInfo->KernelImage.ImageSize);
|
||||
bootInfo->KernelImage.ImageSize, true);
|
||||
if (!KSUCCESS(status))
|
||||
return status;
|
||||
|
||||
return KiRegisterGlobalModule("hal.dll",
|
||||
bootInfo->HalImage.VirtualBase,
|
||||
bootInfo->HalImage.ImageSize);
|
||||
bootInfo->HalImage.ImageSize, true);
|
||||
}
|
||||
|
||||
// Registers one loaded kernel module without imposing a fixed registry capacity.
|
||||
KSTATUS LdrRegisterModule(const char* name, uintptr_t base) {
|
||||
size_t size = base ? LdrGetImageSize(base) : 0;
|
||||
return KiRegisterGlobalModule(name, base, size);
|
||||
return KiRegisterGlobalModule(name, base, size, false);
|
||||
}
|
||||
|
||||
// Makes an initialized module's exports visible to future import resolution.
|
||||
// Failed images remain mapped for diagnostics but must not provide exports.
|
||||
KSTATUS LdrSetModuleExportsAvailable(const char* name, bool available) {
|
||||
if (!name) return NTLSTATUS_INVALID_PARAMETER;
|
||||
for (MODULE_ENTRY* entry = gModules; entry; entry = entry->Next) {
|
||||
if (_stricmp(entry->Name, name) == 0) {
|
||||
entry->ExportsAvailable = available;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
}
|
||||
return NTLSTATUS_NOT_FOUND;
|
||||
}
|
||||
|
||||
// Finds the loaded kernel module that owns an address.
|
||||
@@ -251,7 +267,7 @@ const char* LdrFindKernelModuleByAddress(uintptr_t address, uintptr_t* baseOut,
|
||||
static uintptr_t KiFindGlobalModuleBase(const char* name) {
|
||||
if (!name) return 0;
|
||||
for (MODULE_ENTRY* module = gModules; module; module = module->Next) {
|
||||
if (_stricmp(module->Name, name) == 0)
|
||||
if (module->ExportsAvailable && _stricmp(module->Name, name) == 0)
|
||||
return module->Base;
|
||||
}
|
||||
return 0;
|
||||
@@ -286,6 +302,11 @@ static void* KiGetExportAtBase(uintptr_t base, const char* functionName) {
|
||||
return (void*)0;
|
||||
}
|
||||
|
||||
// Resolves a named export without depending on the image's registration name.
|
||||
void* LdrGetImageExport(uintptr_t imageBase, const char* functionName) {
|
||||
return functionName ? KiGetExportAtBase(imageBase, functionName) : (void*)0;
|
||||
}
|
||||
|
||||
// Export lookup.
|
||||
void* LdrGetExport(const char* moduleName, const char* functionName) {
|
||||
uintptr_t base = KiFindGlobalModuleBase(moduleName);
|
||||
|
||||
@@ -813,6 +813,25 @@ KSTATUS MmFileCacheWrite(FILE_OBJECT* file, uint64_t offset, const void* buf,
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
KSTATUS MmFileCacheFlushAll(void) {
|
||||
if (!KeCanWait()) return NTLSTATUS_INVALID_IRQL;
|
||||
for (uint32_t i = 0; i < 65536u; ++i) {
|
||||
int didWork = 0;
|
||||
KSTATUS st = KiFileCacheFlushOneDirtyPage((FILE_OBJECT*)0, &didWork);
|
||||
if (st == NTLSTATUS_NOT_FOUND && !didWork) {
|
||||
uintptr_t flags = KeAcquireSpinlock(&gFileCacheLock);
|
||||
bool clean = gFileCacheDirtyPages == 0;
|
||||
KeReleaseSpinlock(&gFileCacheLock, flags);
|
||||
if (clean) return NTLSTATUS_SUCCESS;
|
||||
KeYield();
|
||||
continue;
|
||||
}
|
||||
if (!KSUCCESS(st)) return st;
|
||||
if (!didWork) return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
return NTLSTATUS_LIMIT_REACHED;
|
||||
}
|
||||
|
||||
KSTATUS MmFileCacheFlushFile(FILE_OBJECT* file) {
|
||||
if (!file)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
+15
-6
@@ -794,12 +794,14 @@ void MmPfnQueryStats(MM_PFN_STATS* out) {
|
||||
MM_PFN_STATS stats;
|
||||
memset(&stats, 0, sizeof(stats));
|
||||
|
||||
uintptr_t flags = KeAcquireSpinlock(&gPfnLock);
|
||||
stats.TotalFrames = (uint64_t)gTotalFrames;
|
||||
stats.FreeFrames = (uint64_t)gFreeFrames;
|
||||
stats.UsedFrames = stats.TotalFrames - stats.FreeFrames;
|
||||
|
||||
for (size_t pfn = 0; pfn < gMaxPfn; pfn++) {
|
||||
// Detailed statistics are diagnostic snapshots. Scan bounded PFN batches
|
||||
// so an ordinary query cannot monopolize the global allocator lock on
|
||||
// machines with millions of physical frames. Counters are sampled at end.
|
||||
const size_t pfnCount = gMaxPfn;
|
||||
for (size_t base = 0; base < pfnCount;) {
|
||||
size_t end = pfnCount - base < 1024u ? pfnCount : base + 1024u;
|
||||
uintptr_t flags = KeAcquireSpinlock(&gPfnLock);
|
||||
for (size_t pfn = base; pfn < end; ++pfn) {
|
||||
const MM_PFN* entry = &gPfnDatabase[pfn];
|
||||
if (entry->State == MmPfnStateInvalid)
|
||||
continue;
|
||||
@@ -847,8 +849,15 @@ void MmPfnQueryStats(MM_PFN_STATS* out) {
|
||||
case MmPfnUseMmio: stats.MmioFrames++; break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
KeReleaseSpinlock(&gPfnLock, flags);
|
||||
base = end;
|
||||
}
|
||||
|
||||
uintptr_t flags = KeAcquireSpinlock(&gPfnLock);
|
||||
stats.TotalFrames = (uint64_t)gTotalFrames;
|
||||
stats.FreeFrames = (uint64_t)gFreeFrames;
|
||||
stats.UsedFrames = stats.TotalFrames - stats.FreeFrames;
|
||||
stats.AvailableFrames = stats.FreeFrames + stats.ReclaimableFrames;
|
||||
|
||||
stats.AllocationCount = gPfnAllocations;
|
||||
|
||||
@@ -39,6 +39,7 @@ static uint32_t KiObjectPoolTag(OBJECT_TYPE type) {
|
||||
case ObjectTypeInputManager: return MM_POOL_TAG('i', 'm', 'g', 'r');
|
||||
case ObjectTypeDisplayManager: return MM_POOL_TAG('d', 'm', 'g', 'r');
|
||||
case ObjectTypePipeEndpoint: return MM_POOL_TAG('p', 'i', 'p', 'e');
|
||||
case ObjectTypeServiceRegistration: return MM_POOL_TAG('s', 'r', 'e', 'g');
|
||||
default: return MM_POOL_TAG('o', 'b', 'j', ' ');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,20 +17,15 @@ static void KiSecurityContextDestroy(OBJECT* object) {
|
||||
// Allocates and initializes one immutable security context object.
|
||||
static KSTATUS KiCreateSecurityContext(SYS_PRIVILEGES effectivePrivileges,
|
||||
SYS_PRIVILEGES delegablePrivileges,
|
||||
SYS_SERVICE_CLAIMS serviceClaims,
|
||||
SYS_SERVICE_CLAIMS delegableServiceClaims,
|
||||
SECURITY_CONTEXT** contextOut) {
|
||||
if (!contextOut)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
*contextOut = (SECURITY_CONTEXT*)0;
|
||||
|
||||
if ((effectivePrivileges & ~SYS_PRIV_ALL) != 0 ||
|
||||
(delegablePrivileges & ~SYS_PRIV_ALL) != 0 ||
|
||||
(serviceClaims & ~SYS_SERVICE_CLAIM_ALL) != 0 ||
|
||||
(delegableServiceClaims & ~SYS_SERVICE_CLAIM_ALL) != 0)
|
||||
(delegablePrivileges & ~SYS_PRIV_ALL) != 0)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
if ((delegablePrivileges & ~effectivePrivileges) != 0 ||
|
||||
(delegableServiceClaims & ~serviceClaims) != 0)
|
||||
if ((delegablePrivileges & ~effectivePrivileges) != 0)
|
||||
return NTLSTATUS_ACCESS_DENIED;
|
||||
|
||||
SECURITY_CONTEXT* context = (SECURITY_CONTEXT*)ObAllocateObject(ObjectTypeSecurityContext,
|
||||
@@ -47,8 +42,6 @@ static KSTATUS KiCreateSecurityContext(SYS_PRIVILEGES effectivePrivileges,
|
||||
|
||||
context->EffectivePrivileges = effectivePrivileges;
|
||||
context->DelegablePrivileges = delegablePrivileges;
|
||||
context->ServiceClaims = serviceClaims;
|
||||
context->DelegableServiceClaims = delegableServiceClaims;
|
||||
context->Flags = SYS_SECURITY_CONTEXT_FLAG_NONE;
|
||||
*contextOut = context;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
@@ -62,8 +55,6 @@ KSTATUS SeInitializeSecurity(void) {
|
||||
SECURITY_CONTEXT* context = (SECURITY_CONTEXT*)0;
|
||||
KSTATUS status = KiCreateSecurityContext(SYS_PRIV_ALL,
|
||||
SYS_PRIV_ALL,
|
||||
SYS_SERVICE_CLAIM_ALL,
|
||||
SYS_SERVICE_CLAIM_ALL,
|
||||
&context);
|
||||
if (!KSUCCESS(status))
|
||||
return status;
|
||||
@@ -81,22 +72,16 @@ SECURITY_CONTEXT* SeGetRootSecurityContext(void) {
|
||||
KSTATUS SeDeriveSecurityContext(SECURITY_CONTEXT* source,
|
||||
SYS_PRIVILEGES effectivePrivileges,
|
||||
SYS_PRIVILEGES delegablePrivileges,
|
||||
SYS_SERVICE_CLAIMS serviceClaims,
|
||||
SYS_SERVICE_CLAIMS delegableServiceClaims,
|
||||
SECURITY_CONTEXT** contextOut) {
|
||||
if (!source || !contextOut)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
if ((effectivePrivileges & ~source->DelegablePrivileges) != 0 ||
|
||||
(delegablePrivileges & ~source->DelegablePrivileges) != 0 ||
|
||||
(serviceClaims & ~source->DelegableServiceClaims) != 0 ||
|
||||
(delegableServiceClaims & ~source->DelegableServiceClaims) != 0)
|
||||
(delegablePrivileges & ~source->DelegablePrivileges) != 0)
|
||||
return NTLSTATUS_ACCESS_DENIED;
|
||||
|
||||
return KiCreateSecurityContext(effectivePrivileges,
|
||||
delegablePrivileges,
|
||||
serviceClaims,
|
||||
delegableServiceClaims,
|
||||
contextOut);
|
||||
}
|
||||
|
||||
@@ -107,13 +92,6 @@ bool SeProcessHasPrivilege(const struct PROCESS* process, SYS_PRIVILEGES privile
|
||||
return (process->SecurityContext->EffectivePrivileges & privileges) == privileges;
|
||||
}
|
||||
|
||||
// Tests one security context's effective service-claim mask.
|
||||
bool SeContextHasServiceClaims(const SECURITY_CONTEXT* context, SYS_SERVICE_CLAIMS claims) {
|
||||
if (!context)
|
||||
return false;
|
||||
return (context->ServiceClaims & claims) == claims;
|
||||
}
|
||||
|
||||
// Exports immutable context metadata to one public structure.
|
||||
void SeQuerySecurityContext(const SECURITY_CONTEXT* context, SYS_SECURITY_CONTEXT_INFO* infoOut) {
|
||||
if (!context || !infoOut)
|
||||
@@ -124,7 +102,5 @@ void SeQuerySecurityContext(const SECURITY_CONTEXT* context, SYS_SECURITY_CONTEX
|
||||
infoOut->SecurityId = context->SecurityId;
|
||||
infoOut->EffectivePrivileges = context->EffectivePrivileges;
|
||||
infoOut->DelegablePrivileges = context->DelegablePrivileges;
|
||||
infoOut->ServiceClaims = context->ServiceClaims;
|
||||
infoOut->DelegableServiceClaims = context->DelegableServiceClaims;
|
||||
infoOut->Flags = context->Flags;
|
||||
}
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
// Encodes the ASCII tag 'BOOTINFO'.
|
||||
#define BOOT_INFO_MAGIC 0x424F4F54494E464FULL
|
||||
#define BOOT_INFO_VERSION 3u
|
||||
#define BOOT_INFO_VERSION 4u
|
||||
|
||||
#define BOOT_FILE_NAME_MAX 64u
|
||||
|
||||
@@ -42,13 +42,14 @@ typedef struct BOOT_BLOB {
|
||||
|
||||
// Describes a boot driver raw file passed to the kernel without mapping it as an image.
|
||||
typedef struct BOOT_FILE {
|
||||
char Name[BOOT_FILE_NAME_MAX];
|
||||
char DriverId[BOOT_FILE_NAME_MAX];
|
||||
char Image[BOOT_FILE_NAME_MAX];
|
||||
uintptr_t PhysicalBase;
|
||||
uint32_t Length;
|
||||
uint32_t PageCount;
|
||||
} BOOT_FILE;
|
||||
|
||||
// Describes the page-backed BOOT_FILE array produced from drivers.ini [boot].
|
||||
// Describes the page-backed, identified boot-driver array.
|
||||
typedef struct BOOT_FILE_LIST {
|
||||
uintptr_t FilesPhysical;
|
||||
uint32_t Count;
|
||||
@@ -84,6 +85,7 @@ typedef struct BOOT_INFO {
|
||||
BOOT_IMAGE KernelImage;
|
||||
BOOT_IMAGE HalImage;
|
||||
BOOT_BLOB BootIni;
|
||||
BOOT_BLOB DriversIni;
|
||||
BOOT_FILE_LIST BootDrivers;
|
||||
|
||||
// ACPI RSDP physical address.
|
||||
@@ -103,11 +105,11 @@ typedef struct BOOT_INFO {
|
||||
_Static_assert(sizeof(uintptr_t) == 8u, "BOOT_INFO requires x64 pointers");
|
||||
_Static_assert(sizeof(BOOT_TIME) == 20u, "BOOT_TIME ABI size changed");
|
||||
_Static_assert(sizeof(BOOT_BLOB) == 16u, "BOOT_BLOB ABI size changed");
|
||||
_Static_assert(sizeof(BOOT_FILE) == 80u, "BOOT_FILE ABI size changed");
|
||||
_Static_assert(sizeof(BOOT_FILE) == 144u, "BOOT_FILE ABI size changed");
|
||||
_Static_assert(sizeof(BOOT_FILE_LIST) == 16u, "BOOT_FILE_LIST ABI size changed");
|
||||
_Static_assert(sizeof(BOOT_IMAGE) == 32u, "BOOT_IMAGE ABI size changed");
|
||||
_Static_assert(offsetof(BOOT_INFO, MemoryMap) == 16u, "BOOT_INFO MemoryMap offset changed");
|
||||
_Static_assert(offsetof(BOOT_INFO, KernelImage) == 6200u, "BOOT_INFO KernelImage offset changed");
|
||||
_Static_assert(offsetof(BOOT_INFO, BootDrivers) == 6280u, "BOOT_INFO BootDrivers offset changed");
|
||||
_Static_assert(offsetof(BOOT_INFO, BootTime) == 6320u, "BOOT_INFO BootTime offset changed");
|
||||
_Static_assert(sizeof(BOOT_INFO) == 6344u, "BOOT_INFO ABI size changed");
|
||||
_Static_assert(offsetof(BOOT_INFO, BootDrivers) == 6296u, "BOOT_INFO BootDrivers offset changed");
|
||||
_Static_assert(offsetof(BOOT_INFO, BootTime) == 6336u, "BOOT_INFO BootTime offset changed");
|
||||
_Static_assert(sizeof(BOOT_INFO) == 6360u, "BOOT_INFO ABI size changed");
|
||||
@@ -0,0 +1,10 @@
|
||||
// Canonical names for kernel-issued bootstrap capabilities and their attenuated service views.
|
||||
#pragma once
|
||||
|
||||
#define NTL_BOOT_CAP_SYSTEM_VOLUME "boot.system-volume"
|
||||
#define NTL_BOOT_CAP_NAMESPACE_ROOT "boot.namespace-root"
|
||||
#define NTL_BOOT_CAP_VOLUME_MANAGER "boot.volume-manager"
|
||||
#define NTL_BOOT_CAP_INPUT_MANAGER "boot.input-manager"
|
||||
#define NTL_BOOT_CAP_DISPLAY_POLICY "boot.display-policy"
|
||||
#define NTL_BOOT_CAP_DISPLAY_PRESENT "boot.display-present"
|
||||
#define NTL_BOOT_CAP_RESOURCE_REGISTRATION "boot.resource-registration"
|
||||
@@ -24,6 +24,7 @@
|
||||
#define NTL_CONSOLE_OP_READ_INPUT 8u
|
||||
#define NTL_CONSOLE_OP_PEEK_INPUT 9u
|
||||
#define NTL_CONSOLE_OP_FLUSH_INPUT 10u
|
||||
#define NTL_CONSOLE_OP_WRITE_AT 11u
|
||||
|
||||
typedef struct NTL_CONSOLE_INFO {
|
||||
uint32_t Size;
|
||||
@@ -54,6 +55,12 @@ typedef struct NTL_CONSOLE_REQUEST {
|
||||
int32_t X;
|
||||
int32_t Y;
|
||||
} Cursor;
|
||||
struct {
|
||||
int32_t X;
|
||||
int32_t Y;
|
||||
uint32_t Length;
|
||||
char Data[NTL_CONSOLE_WRITE_MAX - 8u];
|
||||
} WriteAt;
|
||||
struct {
|
||||
char Character; // One legacy ASCII display cell, never one UTF-8 code point.
|
||||
uint8_t Reserved[3];
|
||||
@@ -113,6 +120,9 @@ NTL_CONSOLE_API KSTATUS NtlConsoleConnect(SYS_HANDLE* sessionOut);
|
||||
NTL_CONSOLE_API KSTATUS NtlConsoleClose(SYS_HANDLE session);
|
||||
NTL_CONSOLE_API KSTATUS NtlConsoleWrite(SYS_HANDLE session, const void* data, size_t length, size_t* writtenOut);
|
||||
NTL_CONSOLE_API KSTATUS NtlConsoleQueryInfo(SYS_HANDLE session, NTL_CONSOLE_INFO* infoOut);
|
||||
// Writes one ASCII text span at fixed console coordinates without moving the logical cursor.
|
||||
NTL_CONSOLE_API KSTATUS NtlConsoleWriteAt(SYS_HANDLE session, int32_t x, int32_t y,
|
||||
const char* text, size_t length, size_t* writtenOut);
|
||||
NTL_CONSOLE_API KSTATUS NtlConsoleSetCursor(SYS_HANDLE session, int32_t x, int32_t y);
|
||||
// Legacy cell operations accept ASCII glyph codes only. Unicode text uses
|
||||
// NtlConsoleWrite's byte stream until the RSSRV text renderer is upgraded.
|
||||
|
||||
@@ -72,5 +72,6 @@ struct DISPLAY_DEVICE {
|
||||
};
|
||||
|
||||
DISPLAY_API KSTATUS KeDisplayRegisterDevice(DISPLAY_DEVICE* device);
|
||||
DISPLAY_API KSTATUS KeDisplayUnregisterDevice(DISPLAY_DEVICE* device);
|
||||
DISPLAY_API KSTATUS KeDisplaySetPrimary(DISPLAY_DEVICE* device);
|
||||
DISPLAY_API void KeDisplayNotifyFrame(DISPLAY_DEVICE* device);
|
||||
@@ -128,7 +128,6 @@ struct DEVICE_OBJECT {
|
||||
|
||||
__declspec(dllexport) void IoInitializeDriverObject(DRIVER_OBJECT* driver,
|
||||
SYS_STRING_VIEW name);
|
||||
__declspec(dllexport) KSTATUS IoRegisterDriverObject(DRIVER_OBJECT* driver);
|
||||
|
||||
__declspec(dllexport) KSTATUS IoCreateDevice(DRIVER_OBJECT* driver,
|
||||
size_t extensionSize,
|
||||
|
||||
@@ -77,30 +77,11 @@ typedef struct IO_AUDIO_ENDPOINT_REGISTRATION {
|
||||
uint32_t HardwareGainStepMilliDb;
|
||||
} IO_AUDIO_ENDPOINT_REGISTRATION;
|
||||
|
||||
// Driver-visible services for registering and advancing kernel audio endpoints.
|
||||
typedef struct NTL_DDK_AUDIO {
|
||||
// Registers one endpoint and returns a provider-owned reference valid until unregister returns.
|
||||
KSTATUS (*RegisterEndpoint)(const IO_AUDIO_ENDPOINT_REGISTRATION* registration,
|
||||
IO_AUDIO_ENDPOINT** outEndpoint);
|
||||
|
||||
// Stops the endpoint, removes it from enumeration, and consumes the provider-owned reference.
|
||||
void (*UnregisterEndpoint)(IO_AUDIO_ENDPOINT* endpoint, IO_AUDIO_REMOVAL_MODE mode);
|
||||
|
||||
// Reports one completed hardware period from ISR/DPC completion processing.
|
||||
// presentedFrames, hardwarePositionFrames, and interruptCount must be monotonic for the
|
||||
// endpoint lifetime. timestampTicks uses the DDK monotonic microsecond source.
|
||||
void (*NotifyPeriod)(IO_AUDIO_ENDPOINT* endpoint,
|
||||
uint32_t completedPeriodIndex,
|
||||
uint64_t presentedFrames,
|
||||
uint64_t hardwarePositionFrames,
|
||||
uint64_t interruptCount,
|
||||
uint64_t timestampTicks);
|
||||
|
||||
// Updates jack-derived endpoint availability without changing endpoint identity.
|
||||
void (*SetEndpointAvailable)(IO_AUDIO_ENDPOINT* endpoint, bool available);
|
||||
|
||||
// Fails an endpoint and wakes every stream owner.
|
||||
void (*ReportEndpointFailure)(IO_AUDIO_ENDPOINT* endpoint,
|
||||
KSTATUS status,
|
||||
uint32_t flags);
|
||||
} NTL_DDK_AUDIO;
|
||||
__declspec(dllexport) KSTATUS IoAudioRegisterEndpoint(const IO_AUDIO_ENDPOINT_REGISTRATION* registration, IO_AUDIO_ENDPOINT** outEndpoint);
|
||||
__declspec(dllexport) void IoAudioUnregisterEndpoint(IO_AUDIO_ENDPOINT* endpoint, IO_AUDIO_REMOVAL_MODE mode);
|
||||
__declspec(dllexport) void IoAudioNotifyPeriod(IO_AUDIO_ENDPOINT* endpoint, uint32_t completedPeriodIndex, uint64_t presentedFrames, uint64_t hardwarePositionFrames, uint64_t interruptCount, uint64_t timestampTicks);
|
||||
__declspec(dllexport) void IoAudioSetEndpointAvailable(IO_AUDIO_ENDPOINT* endpoint, bool available);
|
||||
__declspec(dllexport) void IoAudioReportEndpointFailure(IO_AUDIO_ENDPOINT* endpoint, KSTATUS status, uint32_t flags);
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <ntlite/ddk/display.h>
|
||||
#include <ntlite/ddk/dpc.h>
|
||||
#include <ntlite/ddk/driver.h>
|
||||
#include <ntlite/ddk/device.h>
|
||||
#include <ntlite/ddk/event.h>
|
||||
#include <ntlite/ddk/file.h>
|
||||
#include <ntlite/ddk/interrupt.h>
|
||||
@@ -26,23 +27,3 @@
|
||||
#include <ntlite/ddk/thread.h>
|
||||
#include <ntlite/ddk/time.h>
|
||||
#include <ntlite/ddk/timer.h>
|
||||
|
||||
#define NTL_DDK_VERSION 1u
|
||||
|
||||
// Root DDK table passed to kernel drivers by the driver loader.
|
||||
typedef struct NTL_DDK {
|
||||
uint32_t Version;
|
||||
const NTL_DDK_DEBUG* Debug;
|
||||
const NTL_DDK_AUDIO* Audio;
|
||||
const NTL_DDK_DMA* Dma;
|
||||
const NTL_DDK_INTERRUPT* Interrupt;
|
||||
const NTL_DDK_MEMORY* Memory;
|
||||
const NTL_DDK_PLATFORM* Platform;
|
||||
const NTL_DDK_SCHEDULER* Scheduler;
|
||||
const NTL_DDK_TIMER* Timer;
|
||||
} NTL_DDK;
|
||||
|
||||
// Driver initialization record filled by DriverEntry in the DDK-table model.
|
||||
typedef struct NTL_DRIVER_INIT {
|
||||
const char* Name;
|
||||
} NTL_DRIVER_INIT;
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
// Kernel boot debug flags. Keep these contiguous and document every bit in boot.ini.
|
||||
#define KE_DEBUG_CONSOLE 0x00000001u // Mirror debug output to the firmware/debug console.
|
||||
#define KE_DEBUG_SELFTEST 0x00000002u // Run kernel and SMP boot self-tests.
|
||||
#define KE_DEBUG_SELFTEST 0x00000002u // Run optional kernel diagnostics and bootstrap checks.
|
||||
#define KE_DEBUG_FILE_LOG 0x00000004u // Retain debug output for kernel.log persistence.
|
||||
#define KE_DEBUG_POOL_CANARY 0x00000008u // Validate pool block canaries.
|
||||
#define KE_DEBUG_POOL_POISON 0x00000010u // Poison released pool blocks.
|
||||
@@ -21,11 +21,7 @@
|
||||
KE_DEBUG_POOL_QUARANTINE | KE_DEBUG_POOL_CALLERS | \
|
||||
KE_DEBUG_DISPLAY_ASSERTS | KE_DEBUG_LIST_CHECKS)
|
||||
|
||||
// Driver-visible debug output service table passed through NTL_DDK.
|
||||
typedef struct NTL_DDK_DEBUG {
|
||||
void (*Print)(const char* fmt, ...);
|
||||
uint32_t (*GetFlags)(void);
|
||||
} NTL_DDK_DEBUG;
|
||||
|
||||
|
||||
// Current fatal categories used by kernel-mode driver diagnostics.
|
||||
typedef enum KE_PANIC_CODE {
|
||||
@@ -44,3 +40,5 @@ __declspec(dllexport) void DbgPrint(const char* fmt, ...);
|
||||
|
||||
// Stops the system with a formatted diagnostic message.
|
||||
__declspec(dllexport) _Noreturn void KePanic(int32_t code, const char* fmt, ...);
|
||||
|
||||
__declspec(dllexport) uint32_t KeGetDebugFlags(void);
|
||||
@@ -0,0 +1,63 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <ntlite/status.h>
|
||||
|
||||
typedef struct NTL_DEVICE NTL_DEVICE;
|
||||
typedef struct NTL_DEVICE_RESOURCE NTL_DEVICE_RESOURCE;
|
||||
|
||||
typedef enum NTL_BUS_TYPE {
|
||||
NtlBusPci = 1,
|
||||
NtlBusUsb = 2,
|
||||
} NTL_BUS_TYPE;
|
||||
|
||||
typedef struct NTL_DEVICE_MATCH {
|
||||
NTL_BUS_TYPE Bus;
|
||||
const char* Id;
|
||||
} NTL_DEVICE_MATCH;
|
||||
|
||||
typedef KSTATUS (*NTL_DEVICE_START)(NTL_DEVICE* device);
|
||||
typedef void (*NTL_DEVICE_STOP)(NTL_DEVICE* device);
|
||||
typedef void (*NTL_DEVICE_CANCEL_START)(NTL_DEVICE* device);
|
||||
|
||||
typedef struct NTL_DEVICE_DESC {
|
||||
uint32_t Size;
|
||||
uint32_t Flags;
|
||||
const char* Name;
|
||||
const NTL_DEVICE_MATCH* Matches;
|
||||
uint32_t MatchCount;
|
||||
uint32_t ContextSize;
|
||||
NTL_DEVICE_START Start;
|
||||
NTL_DEVICE_STOP Stop;
|
||||
NTL_DEVICE_CANCEL_START CancelStart;
|
||||
} NTL_DEVICE_DESC;
|
||||
|
||||
#define NTL_DEVICE_DESCRIPTOR(nameValue, matchesValue, contextType, startFn, stopFn) \
|
||||
{ sizeof(NTL_DEVICE_DESC), 0u, (nameValue), (matchesValue), \
|
||||
(uint32_t)(sizeof(matchesValue) / sizeof((matchesValue)[0])), \
|
||||
(uint32_t)sizeof(contextType), (startFn), (stopFn), 0 }
|
||||
|
||||
typedef void (*NTL_DEVICE_RESOURCE_RELEASE)(void* context);
|
||||
|
||||
#ifdef NTL_KERNEL_BUILD
|
||||
#define NTL_DDK_DEVICE_API __declspec(dllexport)
|
||||
#else
|
||||
#define NTL_DDK_DEVICE_API __declspec(dllimport)
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
NTL_DDK_DEVICE_API void* NtlDeviceContext(NTL_DEVICE* device);
|
||||
NTL_DDK_DEVICE_API const char* NtlDeviceId(NTL_DEVICE* device, uint32_t index);
|
||||
NTL_DDK_DEVICE_API NTL_BUS_TYPE NtlDeviceBus(NTL_DEVICE* device);
|
||||
NTL_DDK_DEVICE_API KSTATUS NtlDeviceCompleteStart(NTL_DEVICE* device, KSTATUS result);
|
||||
NTL_DDK_DEVICE_API KSTATUS NtlDeviceOwnResource(NTL_DEVICE* device,
|
||||
void* context,
|
||||
NTL_DEVICE_RESOURCE_RELEASE release,
|
||||
NTL_DEVICE_RESOURCE** out);
|
||||
NTL_DDK_DEVICE_API KSTATUS NtlDeviceReleaseResource(NTL_DEVICE_RESOURCE* resource);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -64,83 +64,21 @@ typedef struct IO_DMA_STATS {
|
||||
uint64_t FailedMappings;
|
||||
} IO_DMA_STATS;
|
||||
|
||||
// Driver-visible DMA services. Device addresses are intentionally distinct
|
||||
// from physical addresses even while the first implementation maps them one to
|
||||
// one on x86-64.
|
||||
typedef struct NTL_DDK_DMA {
|
||||
// Returns the one adapter and requirement set owned by a device object.
|
||||
KSTATUS (*GetAdapter)(struct DEVICE_OBJECT* device,
|
||||
const IO_DMA_REQUIREMENTS* requirements,
|
||||
IO_DMA_ADAPTER** outAdapter);
|
||||
|
||||
// Releases one caller-owned adapter reference.
|
||||
void (*PutAdapter)(IO_DMA_ADAPTER* adapter);
|
||||
|
||||
// Allocates coherent memory with stable CPU and base device addresses. The
|
||||
// caller must quiesce every device reference before releasing the buffer.
|
||||
KSTATUS (*AllocateCommonBuffer)(IO_DMA_ADAPTER* adapter,
|
||||
size_t size,
|
||||
size_t alignment,
|
||||
IO_DMA_BUFFER** outBuffer);
|
||||
|
||||
// Releases a coherent common buffer.
|
||||
void (*FreeCommonBuffer)(IO_DMA_BUFFER* buffer);
|
||||
|
||||
// Returns the stable CPU address of a common buffer.
|
||||
void* (*GetCommonBufferAddress)(const IO_DMA_BUFFER* buffer);
|
||||
|
||||
// Returns the stable device address of a common buffer.
|
||||
uint64_t (*GetCommonBufferDeviceAddress)(const IO_DMA_BUFFER* buffer);
|
||||
|
||||
// Returns the caller-requested size of a common buffer.
|
||||
size_t (*GetCommonBufferSize)(const IO_DMA_BUFFER* buffer);
|
||||
|
||||
// Stabilizes a kernel range for one or more streaming mappings. Pinning
|
||||
// preserves the pages, but does not transfer ownership to a device.
|
||||
KSTATUS (*PinKernelMemory)(void* address,
|
||||
size_t size,
|
||||
IO_DMA_DIRECTION direction,
|
||||
MM_PINNED_MEMORY** outMemory);
|
||||
|
||||
// Stabilizes a process-owned user range for one or more mappings. The
|
||||
// descriptor retains the process and range until the final unpin.
|
||||
KSTATUS (*PinUserMemory)(struct PROCESS* process,
|
||||
void* address,
|
||||
size_t size,
|
||||
IO_DMA_DIRECTION direction,
|
||||
MM_PINNED_MEMORY** outMemory);
|
||||
|
||||
// Releases one pinned-memory reference after all mappings leave.
|
||||
void (*UnpinMemory)(MM_PINNED_MEMORY* memory);
|
||||
|
||||
// Maps a pinned subrange and creates a constrained segment list. A new
|
||||
// mapping begins CPU-owned and requires external operation serialization.
|
||||
KSTATUS (*MapPinnedMemory)(IO_DMA_ADAPTER* adapter,
|
||||
MM_PINNED_MEMORY* memory,
|
||||
size_t offset,
|
||||
size_t length,
|
||||
IO_DMA_DIRECTION direction,
|
||||
uint32_t flags,
|
||||
IO_DMA_MAPPING** outMapping);
|
||||
|
||||
// Releases a mapping and performs a final receive-side bounce copy only
|
||||
// when ownership was transferred to the device and not returned to the CPU.
|
||||
void (*UnmapMemory)(IO_DMA_MAPPING* mapping);
|
||||
|
||||
// Transfers mapping ownership from the CPU to the device. Streaming DMA
|
||||
// must not begin before this operation succeeds.
|
||||
KSTATUS (*SynchronizeForDevice)(IO_DMA_MAPPING* mapping);
|
||||
|
||||
// Transfers mapping ownership from the device to the CPU. CPU readers must
|
||||
// not consume receive data before this operation succeeds.
|
||||
KSTATUS (*SynchronizeForCpu)(IO_DMA_MAPPING* mapping);
|
||||
|
||||
// Returns the immutable segment count owned by a mapping.
|
||||
uint32_t (*GetSegmentCount)(const IO_DMA_MAPPING* mapping);
|
||||
|
||||
// Returns the immutable segment array owned by a mapping.
|
||||
const IO_DMA_SEGMENT* (*GetSegments)(const IO_DMA_MAPPING* mapping);
|
||||
|
||||
// Copies current DMA counters into caller-owned storage.
|
||||
void (*QueryStats)(IO_DMA_STATS* outStats);
|
||||
} NTL_DDK_DMA;
|
||||
__declspec(dllexport) KSTATUS IoDmaGetAdapter(struct DEVICE_OBJECT* device, const IO_DMA_REQUIREMENTS* requirements, IO_DMA_ADAPTER** outAdapter);
|
||||
__declspec(dllexport) void IoDmaPutAdapter(IO_DMA_ADAPTER* adapter);
|
||||
__declspec(dllexport) KSTATUS IoDmaAllocateCommonBuffer(IO_DMA_ADAPTER* adapter, size_t size, size_t alignment, IO_DMA_BUFFER** outBuffer);
|
||||
__declspec(dllexport) void IoDmaFreeCommonBuffer(IO_DMA_BUFFER* buffer);
|
||||
__declspec(dllexport) void* IoDmaGetCommonBufferAddress(const IO_DMA_BUFFER* buffer);
|
||||
__declspec(dllexport) uint64_t IoDmaGetCommonBufferDeviceAddress(const IO_DMA_BUFFER* buffer);
|
||||
__declspec(dllexport) size_t IoDmaGetCommonBufferSize(const IO_DMA_BUFFER* buffer);
|
||||
__declspec(dllexport) KSTATUS IoDmaPinKernelMemory(void* address, size_t size, IO_DMA_DIRECTION direction, MM_PINNED_MEMORY** outMemory);
|
||||
__declspec(dllexport) KSTATUS IoDmaPinUserMemory(struct PROCESS* process, void* address, size_t size, IO_DMA_DIRECTION direction, MM_PINNED_MEMORY** outMemory);
|
||||
__declspec(dllexport) void IoDmaUnpinMemory(MM_PINNED_MEMORY* memory);
|
||||
__declspec(dllexport) KSTATUS IoDmaMapPinnedMemory(IO_DMA_ADAPTER* adapter, MM_PINNED_MEMORY* memory, size_t offset, size_t length, IO_DMA_DIRECTION direction, uint32_t flags, IO_DMA_MAPPING** outMapping);
|
||||
__declspec(dllexport) void IoDmaUnmapMemory(IO_DMA_MAPPING* mapping);
|
||||
__declspec(dllexport) KSTATUS IoDmaSynchronizeForDevice(IO_DMA_MAPPING* mapping);
|
||||
__declspec(dllexport) KSTATUS IoDmaSynchronizeForCpu(IO_DMA_MAPPING* mapping);
|
||||
__declspec(dllexport) uint32_t IoDmaGetSegmentCount(const IO_DMA_MAPPING* mapping);
|
||||
__declspec(dllexport) const IO_DMA_SEGMENT* IoDmaGetSegments(const IO_DMA_MAPPING* mapping);
|
||||
__declspec(dllexport) void IoDmaQueryStats(IO_DMA_STATS* outStats);
|
||||
@@ -1,12 +1,2 @@
|
||||
// Public nt-lite DDK driver entry declarations.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ntlite/status.h>
|
||||
|
||||
struct NTL_DDK;
|
||||
struct NTL_DRIVER_INIT;
|
||||
|
||||
// Kernel driver entrypoint used by the future DDK-table driver model.
|
||||
typedef KSTATUS (*NTL_DRIVER_ENTRY_FN)(const struct NTL_DDK* ddk,
|
||||
struct NTL_DRIVER_INIT* init);
|
||||
#include <ntlite/ddk/module.h>
|
||||
@@ -11,14 +11,12 @@ typedef struct NTL_DDK_MSI_MESSAGE {
|
||||
uint32_t Reserved;
|
||||
} NTL_DDK_MSI_MESSAGE;
|
||||
|
||||
// Driver-visible interrupt services.
|
||||
typedef struct NTL_DDK_INTERRUPT {
|
||||
KSTATUS (*AllocateMsiVector)(uint8_t* outIrq);
|
||||
KSTATUS (*ComposeMsiMessage)(uint8_t irq, NTL_DDK_MSI_MESSAGE* messageOut);
|
||||
} NTL_DDK_INTERRUPT;
|
||||
|
||||
|
||||
// Allocates one kernel IRQ slot for MSI delivery.
|
||||
__declspec(dllexport) KSTATUS KeIrqAllocateMsiVector(uint8_t* outIrq);
|
||||
|
||||
// Registers a legacy interrupt handler for an IRQ slot.
|
||||
__declspec(dllexport) void KeIrqRegister(uint8_t irq, void (*handler)(void));
|
||||
|
||||
__declspec(dllexport) KSTATUS KeIrqComposeMsiMessage(uint8_t irq, NTL_DDK_MSI_MESSAGE* messageOut);
|
||||
@@ -69,16 +69,6 @@ typedef enum NTL_DDK_MAP_MODE {
|
||||
((uint32_t)(uint8_t)(a) | ((uint32_t)(uint8_t)(b) << 8) | \
|
||||
((uint32_t)(uint8_t)(c) << 16) | ((uint32_t)(uint8_t)(d) << 24))
|
||||
|
||||
// Driver-visible memory services. These are intentionally narrower than the
|
||||
// kernel memory-manager surface.
|
||||
typedef struct NTL_DDK_MEMORY {
|
||||
void* (*PhysToVirt)(uint64_t physicalAddress);
|
||||
KSTATUS (*MapPage)(uintptr_t virtualAddress,
|
||||
uint64_t physicalAddress,
|
||||
uint64_t pageFlags,
|
||||
NTL_DDK_MAP_MODE mode);
|
||||
} NTL_DDK_MEMORY;
|
||||
|
||||
// Allocates from kernel pool for current kernel-mode drivers.
|
||||
__declspec(dllexport) void* MmAllocatePool(size_t size);
|
||||
|
||||
@@ -90,3 +80,6 @@ __declspec(dllexport) void MmFreePool(void* ptr);
|
||||
|
||||
// Reallocates kernel pool memory.
|
||||
__declspec(dllexport) void* MmReallocatePool(void* ptr, size_t newSize);
|
||||
|
||||
__declspec(dllexport) void* MmDdkPhysToVirt(uint64_t physicalAddress);
|
||||
__declspec(dllexport) KSTATUS MmDdkMapPage(uintptr_t virtualAddress, uint64_t physicalAddress, uint64_t flags, NTL_DDK_MAP_MODE mode);
|
||||
@@ -0,0 +1,90 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <ntlite/status.h>
|
||||
#include <ntlite/ddk/device.h>
|
||||
|
||||
#define NTL_DDK_MODULE_EXPORT_NAME "NtlDriverModule"
|
||||
#define NTL_DDK_MODULE_SIGNATURE 0x324D444Eu // "NDM2" in little-endian memory.
|
||||
#define NTL_DDK_MODULE_ABI_VERSION 5u
|
||||
|
||||
typedef struct NTL_MODULE NTL_MODULE;
|
||||
typedef KSTATUS (*NTL_MODULE_INITIALIZE)(NTL_MODULE* module);
|
||||
typedef KSTATUS (*NTL_MODULE_PREPARE_SHUTDOWN)(NTL_MODULE* module);
|
||||
typedef void (*NTL_MODULE_COMMIT_SHUTDOWN)(NTL_MODULE* module);
|
||||
|
||||
struct DRIVER_OBJECT;
|
||||
|
||||
typedef struct NTL_MODULE_EXPORT {
|
||||
uint32_t Signature;
|
||||
uint32_t Version;
|
||||
uint32_t Size;
|
||||
uint32_t Flags;
|
||||
NTL_MODULE_INITIALIZE Initialize;
|
||||
const NTL_DEVICE_DESC* Devices;
|
||||
uint32_t DeviceCount;
|
||||
uint32_t Reserved;
|
||||
struct DRIVER_OBJECT* const* IoDrivers;
|
||||
uint32_t IoDriverCount;
|
||||
uint32_t Reserved2;
|
||||
NTL_MODULE_PREPARE_SHUTDOWN PrepareShutdown;
|
||||
NTL_MODULE_COMMIT_SHUTDOWN CommitShutdown;
|
||||
} NTL_MODULE_EXPORT;
|
||||
|
||||
#ifdef __cplusplus
|
||||
static_assert(sizeof(NTL_MODULE_EXPORT) == 72u, "DDK module export ABI changed");
|
||||
#define NTL_DDK_C_EXPORT extern "C"
|
||||
#else
|
||||
_Static_assert(sizeof(NTL_MODULE_EXPORT) == 72u, "DDK module export ABI changed");
|
||||
#define NTL_DDK_C_EXPORT
|
||||
#endif
|
||||
|
||||
#define NTL_DECLARE_MODULE(initialize_fn) \
|
||||
NTL_DDK_C_EXPORT __declspec(dllexport) const NTL_MODULE_EXPORT NtlDriverModule = { \
|
||||
NTL_DDK_MODULE_SIGNATURE, NTL_DDK_MODULE_ABI_VERSION, \
|
||||
(uint32_t)sizeof(NTL_MODULE_EXPORT), 0u, (initialize_fn), 0, 0u, 0u, 0, 0u, 0u, 0, 0 \
|
||||
}
|
||||
|
||||
#define NTL_DECLARE_DEVICE_MODULE(deviceArray) \
|
||||
NTL_DDK_C_EXPORT __declspec(dllexport) const NTL_MODULE_EXPORT NtlDriverModule = { \
|
||||
NTL_DDK_MODULE_SIGNATURE, NTL_DDK_MODULE_ABI_VERSION, \
|
||||
(uint32_t)sizeof(NTL_MODULE_EXPORT), 0u, 0, (deviceArray), \
|
||||
(uint32_t)(sizeof(deviceArray) / sizeof((deviceArray)[0])), 0u, 0, 0u, 0u, 0, 0 \
|
||||
}
|
||||
|
||||
#define NTL_DECLARE_IO_MODULE(initialize_fn, driverArray) \
|
||||
NTL_DDK_C_EXPORT __declspec(dllexport) const NTL_MODULE_EXPORT NtlDriverModule = { \
|
||||
NTL_DDK_MODULE_SIGNATURE, NTL_DDK_MODULE_ABI_VERSION, \
|
||||
(uint32_t)sizeof(NTL_MODULE_EXPORT), 0u, (initialize_fn), 0, 0u, 0u, \
|
||||
(driverArray), (uint32_t)(sizeof(driverArray) / sizeof((driverArray)[0])), 0u, 0, 0 \
|
||||
}
|
||||
|
||||
#define NTL_DECLARE_MODULE_WITH_SHUTDOWN(initialize_fn, prepareFn, commitFn) \
|
||||
NTL_DDK_C_EXPORT __declspec(dllexport) const NTL_MODULE_EXPORT NtlDriverModule = { \
|
||||
NTL_DDK_MODULE_SIGNATURE, NTL_DDK_MODULE_ABI_VERSION, \
|
||||
(uint32_t)sizeof(NTL_MODULE_EXPORT), 0u, (initialize_fn), 0, 0u, 0u, \
|
||||
0, 0u, 0u, (prepareFn), (commitFn) \
|
||||
}
|
||||
|
||||
#define NTL_DECLARE_IO_MODULE_WITH_SHUTDOWN(initialize_fn, driverArray, prepareFn, commitFn) \
|
||||
NTL_DDK_C_EXPORT __declspec(dllexport) const NTL_MODULE_EXPORT NtlDriverModule = { \
|
||||
NTL_DDK_MODULE_SIGNATURE, NTL_DDK_MODULE_ABI_VERSION, \
|
||||
(uint32_t)sizeof(NTL_MODULE_EXPORT), 0u, (initialize_fn), 0, 0u, 0u, \
|
||||
(driverArray), (uint32_t)(sizeof(driverArray) / sizeof((driverArray)[0])), 0u, \
|
||||
(prepareFn), (commitFn) \
|
||||
}
|
||||
|
||||
// Query the configured module identity; the returned string is borrowed.
|
||||
#ifdef NTL_KERNEL_BUILD
|
||||
#define NTL_DDK_MODULE_API __declspec(dllexport)
|
||||
#else
|
||||
#define NTL_DDK_MODULE_API __declspec(dllimport)
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
NTL_DDK_MODULE_API const char* NtlModuleGetName(const NTL_MODULE* module);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -36,6 +36,7 @@ typedef enum OBJECT_TYPE {
|
||||
ObjectTypeInputManager = 27,
|
||||
ObjectTypeDisplayManager = 28,
|
||||
ObjectTypePipeEndpoint = 29,
|
||||
ObjectTypeServiceRegistration = 30,
|
||||
} OBJECT_TYPE;
|
||||
|
||||
typedef enum OBJECT_LIFETIME_STATE {
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
// Pure driver-library helpers for walking PCI capability lists.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <ntlite/status.h>
|
||||
|
||||
typedef struct NTL_PCI_CONFIG_READER {
|
||||
void* Context;
|
||||
uint32_t (*Read32)(void* context, uint16_t offset);
|
||||
} NTL_PCI_CONFIG_READER;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Finds an ordinary PCI capability using an explicitly supplied config reader.
|
||||
// Returns NOT_FOUND for a valid exhausted list and DEVICE_ERROR for malformed lists.
|
||||
KSTATUS NtlPciFindCapability(const NTL_PCI_CONFIG_READER* reader,
|
||||
uint8_t firstOffset,
|
||||
uint8_t capabilityId,
|
||||
uint8_t* outOffset);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -4,18 +4,5 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// Driver-visible platform services. PCI bus policy stays in pci.sys; these are
|
||||
// only the raw platform hooks needed by the PCI bus driver itself.
|
||||
typedef struct NTL_DDK_PLATFORM {
|
||||
uint32_t (*PciRead32)(uint16_t segment,
|
||||
uint8_t bus,
|
||||
uint8_t device,
|
||||
uint8_t function,
|
||||
uint16_t offset);
|
||||
void (*PciWrite32)(uint16_t segment,
|
||||
uint8_t bus,
|
||||
uint8_t device,
|
||||
uint8_t function,
|
||||
uint16_t offset,
|
||||
uint32_t value);
|
||||
} NTL_DDK_PLATFORM;
|
||||
__declspec(dllexport) uint32_t KePciReadConfig32(uint16_t segment, uint8_t bus, uint8_t device, uint8_t function, uint16_t offset);
|
||||
__declspec(dllexport) void KePciWriteConfig32(uint16_t segment, uint8_t bus, uint8_t device, uint8_t function, uint16_t offset, uint32_t value);
|
||||
@@ -6,8 +6,7 @@
|
||||
#include <ntlite/status.h>
|
||||
#include <ntlite/ddk/thread.h>
|
||||
|
||||
// Driver-visible scheduler services.
|
||||
typedef struct NTL_DDK_SCHEDULER {
|
||||
KSTATUS (*DelayThread)(uint64_t milliseconds);
|
||||
void (*Yield)(void);
|
||||
} NTL_DDK_SCHEDULER;
|
||||
|
||||
|
||||
__declspec(dllexport) KSTATUS KeDelayThread(uint64_t milliseconds);
|
||||
__declspec(dllexport) void KeYield(void);
|
||||
Loaded 100 of 160 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user