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
@@ -1,12 +1,12 @@
|
||||
// Init service-configuration parser. Malformed or ambiguous service policy is rejected at boot.
|
||||
|
||||
#include "service_config.h"
|
||||
#include <ntlite/bootstrap_capabilities.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <ntlite/status.h>
|
||||
|
||||
#define INIT_SERVICE_KEY_TYPE 0x00000001u
|
||||
#define INIT_SERVICE_KEY_IMAGE 0x00000002u
|
||||
#define INIT_SERVICE_KEY_START 0x00000004u
|
||||
#define INIT_SERVICE_KEY_CRITICAL 0x00000008u
|
||||
@@ -14,7 +14,9 @@
|
||||
#define INIT_SERVICE_KEY_READY_TIMEOUT 0x00000020u
|
||||
#define INIT_SERVICE_KEY_STOP_TIMEOUT 0x00000040u
|
||||
#define INIT_SERVICE_KEY_REQUIRES 0x00000080u
|
||||
#define INIT_SERVICE_REQUIRED_KEYS 0x000000FFu
|
||||
#define INIT_SERVICE_KEY_GRANTS 0x00000100u
|
||||
#define INIT_SERVICE_KEY_EXPORTS 0x00000200u
|
||||
#define INIT_SERVICE_REQUIRED_KEYS 0x000003FEu
|
||||
|
||||
typedef struct INIT_PARSED_SERVICE {
|
||||
INIT_SERVICE_DESCRIPTOR Descriptor;
|
||||
@@ -155,17 +157,65 @@ static bool parse_requires(char* value, INIT_PARSED_SERVICE* service) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Parses explicit capability-source names without associating them with service roles.
|
||||
static bool parse_grants(char* value, INIT_PARSED_SERVICE* service) {
|
||||
service->Descriptor.GrantCount = 0;
|
||||
if (!*value) return true;
|
||||
char* cursor = value;
|
||||
for (;;) {
|
||||
char* comma = strchr(cursor, ',');
|
||||
if (comma) *comma = '\0';
|
||||
char* name = trim_left(cursor);
|
||||
trim_right(name);
|
||||
size_t length = strlen(name);
|
||||
if (length == 0 || length > INIT_SERVICE_GRANT_NAME_MAX ||
|
||||
service->Descriptor.GrantCount >= INIT_SERVICE_GRANT_MAX) return false;
|
||||
for (size_t i = 0; i < length; ++i)
|
||||
if (!is_service_id_char(name[i])) return false;
|
||||
if (!strchr(name, '.') || name[0] == '.' || name[length - 1u] == '.') return false;
|
||||
if (strcmp(name, "service.control") == 0 || strncmp(name, "publish.", 8u) == 0)
|
||||
return false;
|
||||
for (uint32_t i = 0; i < service->Descriptor.GrantCount; ++i)
|
||||
if (strcmp(service->Descriptor.Grants[i], name) == 0) return false;
|
||||
if (!copy_bounded(service->Descriptor.Grants[service->Descriptor.GrantCount],
|
||||
sizeof(service->Descriptor.Grants[0]), name)) return false;
|
||||
++service->Descriptor.GrantCount;
|
||||
if (!comma) break;
|
||||
cursor = comma + 1;
|
||||
if (!*cursor) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Reads the declared READY export names, which are an exact startup contract.
|
||||
static bool parse_exports(char* value, INIT_PARSED_SERVICE* service) {
|
||||
service->Descriptor.ExportCount = 0;
|
||||
if (!*value) return true;
|
||||
char* cursor = value;
|
||||
for (;;) {
|
||||
char* comma = strchr(cursor, ',');
|
||||
if (comma) *comma = '\0';
|
||||
char* name = trim_left(cursor);
|
||||
trim_right(name);
|
||||
if (!NtlServiceExportNameValid(name) ||
|
||||
service->Descriptor.ExportCount >= INIT_SERVICE_EXPORT_MAX) return false;
|
||||
for (uint32_t i = 0; i < service->Descriptor.ExportCount; ++i)
|
||||
if (strcmp(name, service->Descriptor.Exports[i]) == 0) return false;
|
||||
if (!copy_bounded(service->Descriptor.Exports[service->Descriptor.ExportCount],
|
||||
sizeof(service->Descriptor.Exports[0]), name)) return false;
|
||||
++service->Descriptor.ExportCount;
|
||||
if (!comma) break;
|
||||
cursor = comma + 1;
|
||||
if (!*cursor) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Applies one known key to the current service and rejects duplicates or invalid values.
|
||||
static bool parse_service_key(INIT_PARSED_SERVICE* service, char* key, char* value) {
|
||||
uint32_t bit = 0;
|
||||
|
||||
if (strcmp(key, "type") == 0) {
|
||||
bit = INIT_SERVICE_KEY_TYPE;
|
||||
if (strcmp(value, "resource") == 0) service->Descriptor.Type = InitServiceTypeResource;
|
||||
else if (strcmp(value, "dwm") == 0) service->Descriptor.Type = InitServiceTypeDwm;
|
||||
else if (strcmp(value, "generic") == 0) service->Descriptor.Type = InitServiceTypeGeneric;
|
||||
else return false;
|
||||
} else if (strcmp(key, "image") == 0) {
|
||||
if (strcmp(key, "image") == 0) {
|
||||
bit = INIT_SERVICE_KEY_IMAGE;
|
||||
if (!validate_image_path(value) ||
|
||||
!copy_bounded(service->Descriptor.ImagePath, sizeof(service->Descriptor.ImagePath), value))
|
||||
@@ -192,6 +242,12 @@ static bool parse_service_key(INIT_PARSED_SERVICE* service, char* key, char* val
|
||||
} else if (strcmp(key, "requires") == 0) {
|
||||
bit = INIT_SERVICE_KEY_REQUIRES;
|
||||
if (!parse_requires(value, service)) return false;
|
||||
} else if (strcmp(key, "grants") == 0) {
|
||||
bit = INIT_SERVICE_KEY_GRANTS;
|
||||
if (!parse_grants(value, service)) return false;
|
||||
} else if (strcmp(key, "exports") == 0) {
|
||||
bit = INIT_SERVICE_KEY_EXPORTS;
|
||||
if (!parse_exports(value, service)) return false;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
@@ -270,7 +326,8 @@ static KSTATUS parse_services_ini(char* buffer, uint32_t* serviceCountOut, uint3
|
||||
line = saved ? end + 1u : end;
|
||||
}
|
||||
|
||||
if (serviceCount == 0) return NTLSTATUS_INVALID_PARAMETER;
|
||||
if (serviceCount == 0)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
*serviceCountOut = serviceCount;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
@@ -279,14 +336,59 @@ static KSTATUS parse_services_ini(char* buffer, uint32_t* serviceCountOut, uint3
|
||||
static KSTATUS validate_services(uint32_t serviceCount,
|
||||
INIT_SERVICE_CONFIG* configOut,
|
||||
uint32_t* errorLineOut) {
|
||||
uint32_t resourceCount = 0;
|
||||
uint16_t resourceIndex = 0;
|
||||
uint32_t dwmCount = 0;
|
||||
uint16_t dwmIndex = UINT16_MAX;
|
||||
|
||||
memset(configOut, 0, sizeof(*configOut));
|
||||
configOut->ResourceServiceIndex = UINT16_MAX;
|
||||
configOut->DwmServiceIndex = UINT16_MAX;
|
||||
|
||||
static const char* const bootGrants[] = {
|
||||
NTL_BOOT_CAP_NAMESPACE_ROOT, NTL_BOOT_CAP_VOLUME_MANAGER, NTL_BOOT_CAP_INPUT_MANAGER,
|
||||
NTL_BOOT_CAP_DISPLAY_POLICY, NTL_BOOT_CAP_DISPLAY_PRESENT, NTL_BOOT_CAP_RESOURCE_REGISTRATION
|
||||
};
|
||||
for (uint32_t i = 0; i < serviceCount; ++i) {
|
||||
INIT_SERVICE_DESCRIPTOR* descriptor = &gParsedServices[i].Descriptor;
|
||||
for (uint32_t g = 0; g < descriptor->GrantCount; ++g) {
|
||||
const char* name = descriptor->Grants[g];
|
||||
bool known = false;
|
||||
for (uint32_t b = 0; b < sizeof(bootGrants) / sizeof(bootGrants[0]); ++b)
|
||||
if (strcmp(name, bootGrants[b]) == 0) known = true;
|
||||
if (known) continue;
|
||||
if (strncmp(name, "boot.", 5u) == 0) {
|
||||
if (errorLineOut) *errorLineOut = gParsedServices[i].SectionLine;
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
}
|
||||
// Service export sources require an explicit dependency; the longest
|
||||
// matching service ID owns a qualified export name.
|
||||
int32_t provider = -1;
|
||||
size_t prefixLength = 0;
|
||||
for (uint32_t j = 0; j < serviceCount; ++j) {
|
||||
const char* id = gParsedServices[j].Descriptor.Id;
|
||||
size_t length = strlen(id);
|
||||
if (length > prefixLength && strncmp(name, id, length) == 0 &&
|
||||
name[length] == '.' && name[length + 1u] != '\0') {
|
||||
provider = (int32_t)j;
|
||||
prefixLength = length;
|
||||
}
|
||||
}
|
||||
bool declaredDependency = false;
|
||||
if (provider >= 0 && (uint32_t)provider != i &&
|
||||
gParsedServices[provider].Descriptor.StartPolicy == InitServiceStartAuto) {
|
||||
for (uint32_t d = 0; d < gParsedServices[i].DependencyCount; ++d)
|
||||
if (strcmp(gParsedServices[i].DependencyIds[d],
|
||||
gParsedServices[provider].Descriptor.Id) == 0)
|
||||
declaredDependency = true;
|
||||
}
|
||||
if (declaredDependency) {
|
||||
const char* exportName = name + prefixLength + 1u;
|
||||
bool declaredExport = false;
|
||||
const INIT_SERVICE_DESCRIPTOR* source = &gParsedServices[provider].Descriptor;
|
||||
for (uint32_t e = 0; e < source->ExportCount; ++e)
|
||||
if (strcmp(source->Exports[e], exportName) == 0) declaredExport = true;
|
||||
if (!declaredExport) declaredDependency = false;
|
||||
}
|
||||
if (!declaredDependency) {
|
||||
if (errorLineOut) *errorLineOut = gParsedServices[i].SectionLine;
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < serviceCount; ++i) {
|
||||
INIT_PARSED_SERVICE* parsed = &gParsedServices[i];
|
||||
@@ -297,6 +399,13 @@ static KSTATUS validate_services(uint32_t serviceCount,
|
||||
|
||||
INIT_SERVICE_DESCRIPTOR* descriptor = &configOut->Services[i];
|
||||
*descriptor = parsed->Descriptor;
|
||||
for (uint32_t e = 0; e < descriptor->ExportCount; ++e) {
|
||||
if (strlen(descriptor->Id) + 1u + strlen(descriptor->Exports[e]) >
|
||||
INIT_SERVICE_GRANT_NAME_MAX) {
|
||||
if (errorLineOut) *errorLineOut = parsed->SectionLine;
|
||||
return NTLSTATUS_NAME_TOO_LONG;
|
||||
}
|
||||
}
|
||||
descriptor->DependencyCount = parsed->DependencyCount;
|
||||
for (uint32_t d = 0; d < parsed->DependencyCount; ++d) {
|
||||
int32_t dependency = find_parsed_service(gParsedServices, serviceCount, parsed->DependencyIds[d]);
|
||||
@@ -306,48 +415,6 @@ static KSTATUS validate_services(uint32_t serviceCount,
|
||||
}
|
||||
descriptor->Dependencies[d] = (uint16_t)dependency;
|
||||
}
|
||||
|
||||
if (descriptor->Type == InitServiceTypeResource) {
|
||||
++resourceCount;
|
||||
resourceIndex = (uint16_t)i;
|
||||
} else if (descriptor->Type == InitServiceTypeDwm) {
|
||||
++dwmCount;
|
||||
dwmIndex = (uint16_t)i;
|
||||
}
|
||||
}
|
||||
|
||||
if (resourceCount != 1u || dwmCount > 1u) return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
INIT_SERVICE_DESCRIPTOR* resource = &configOut->Services[resourceIndex];
|
||||
if (resource->StartPolicy != InitServiceStartAuto || !resource->Critical ||
|
||||
resource->RestartPolicy != InitServiceRestartNever || resource->DependencyCount != 0u) {
|
||||
if (errorLineOut) *errorLineOut = gParsedServices[resourceIndex].SectionLine;
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (dwmCount == 1u) {
|
||||
INIT_SERVICE_DESCRIPTOR* dwm = &configOut->Services[dwmIndex];
|
||||
bool requiresResource = false;
|
||||
for (uint32_t d = 0; d < dwm->DependencyCount; ++d)
|
||||
if (dwm->Dependencies[d] == resourceIndex) requiresResource = true;
|
||||
if (dwm->StartPolicy == InitServiceStartAuto &&
|
||||
(dwm->Critical || dwm->RestartPolicy != InitServiceRestartNever || !requiresResource)) {
|
||||
if (errorLineOut) *errorLineOut = gParsedServices[dwmIndex].SectionLine;
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
}
|
||||
}
|
||||
|
||||
// The generic adapter publishes READY listeners through RSSRV, so automatic generic services require it.
|
||||
for (uint32_t i = 0; i < serviceCount; ++i) {
|
||||
INIT_SERVICE_DESCRIPTOR* generic = &configOut->Services[i];
|
||||
if (generic->Type != InitServiceTypeGeneric || generic->StartPolicy != InitServiceStartAuto) continue;
|
||||
bool requiresResource = false;
|
||||
for (uint32_t d = 0; d < generic->DependencyCount; ++d)
|
||||
if (generic->Dependencies[d] == resourceIndex) requiresResource = true;
|
||||
if (generic->Critical || generic->RestartPolicy != InitServiceRestartNever || !requiresResource) {
|
||||
if (errorLineOut) *errorLineOut = gParsedServices[i].SectionLine;
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
}
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < serviceCount; ++i) {
|
||||
@@ -406,8 +473,8 @@ static KSTATUS validate_services(uint32_t serviceCount,
|
||||
}
|
||||
|
||||
configOut->ServiceCount = serviceCount;
|
||||
configOut->ResourceServiceIndex = resourceIndex;
|
||||
configOut->DwmServiceIndex = dwmIndex;
|
||||
if (configOut->StartupCount + 2u > SYS_WAIT_MANY_MAX_HANDLES)
|
||||
return NTLSTATUS_LIMIT_REACHED;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user