Files
nt-lite/code/kernel/ke_system_service.c
T

114 lines
4.6 KiB
C

// Kernel well-known system-service registry. This file intentionally owns only numeric service discovery.
#include <kernel/ke_system_service.h>
#include <kernel/ke_port.h>
#include <kernel/ke_spinlock.h>
#include <kernel/ob_object.h>
#include <kernel/ob_object.h>
static SPINLOCK gSystemServiceLock = SPINLOCK_INIT;
static PORT* gSystemServices[SYS_SERVICE_MAX + 1u];
// Returns whether one numeric service identifier is valid for the current ABI.
static bool KiSystemServiceIdValid(SYS_SERVICE_ID serviceId) {
return serviceId > SYS_SERVICE_INVALID && serviceId <= SYS_SERVICE_MAX;
}
// 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.
KSTATUS KeSystemServiceRegister(SYS_SERVICE_ID serviceId, PORT* listener) {
if (!KiSystemServiceIdValid(serviceId) || !listener || !KePortIsListener(listener))
return NTLSTATUS_INVALID_PARAMETER;
uintptr_t flags = KeAcquireSpinlock(&gSystemServiceLock);
if (gSystemServices[serviceId]) {
KeReleaseSpinlock(&gSystemServiceLock, flags);
return NTLSTATUS_ALREADY_EXISTS;
}
gSystemServices[serviceId] = listener;
KeReleaseSpinlock(&gSystemServiceLock, flags);
return NTLSTATUS_SUCCESS;
}
// Removes one registration only when the caller still names the registered listener object.
KSTATUS KeSystemServiceUnregister(SYS_SERVICE_ID serviceId, PORT* listener) {
if (!KiSystemServiceIdValid(serviceId) || !listener || !KePortIsListener(listener))
return NTLSTATUS_INVALID_PARAMETER;
uintptr_t flags = KeAcquireSpinlock(&gSystemServiceLock);
if (!gSystemServices[serviceId]) {
KeReleaseSpinlock(&gSystemServiceLock, flags);
return NTLSTATUS_NOT_FOUND;
}
if (gSystemServices[serviceId] != listener) {
KeReleaseSpinlock(&gSystemServiceLock, flags);
return NTLSTATUS_ACCESS_DENIED;
}
gSystemServices[serviceId] = (PORT*)0;
KeReleaseSpinlock(&gSystemServiceLock, flags);
return NTLSTATUS_SUCCESS;
}
// References one currently registered listener for a connect operation.
KSTATUS KeSystemServiceReference(SYS_SERVICE_ID serviceId, PORT** listenerOut) {
if (!listenerOut)
return NTLSTATUS_INVALID_PARAMETER;
*listenerOut = (PORT*)0;
if (!KiSystemServiceIdValid(serviceId))
return NTLSTATUS_INVALID_PARAMETER;
uintptr_t flags = KeAcquireSpinlock(&gSystemServiceLock);
PORT* listener = gSystemServices[serviceId];
if (!listener || !ObTryReferenceObject((OBJECT*)listener)) {
KeReleaseSpinlock(&gSystemServiceLock, flags);
return NTLSTATUS_NOT_FOUND;
}
KeReleaseSpinlock(&gSystemServiceLock, flags);
*listenerOut = listener;
return NTLSTATUS_SUCCESS;
}
// Removes any weak registration whose listener is entering terminal object destruction.
void KeSystemServiceUnregisterListener(PORT* listener) {
if (!listener)
return;
uintptr_t flags = KeAcquireSpinlock(&gSystemServiceLock);
for (SYS_SERVICE_ID id = 1; id <= SYS_SERVICE_MAX; ++id) {
if (gSystemServices[id] == listener)
gSystemServices[id] = (PORT*)0;
}
KeReleaseSpinlock(&gSystemServiceLock, flags);
}