143 lines
5.4 KiB
C
143 lines
5.4 KiB
C
// HAL output router. This file fans typed text streams out to registered physical and runtime endpoints.
|
|
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <intrin.h>
|
|
#include <ntlite/ddi/hal_api.h>
|
|
#include <kernel/hal/hal_output.h>
|
|
|
|
#define HAL_OUTPUT_ENDPOINT_CAPACITY 8u
|
|
|
|
typedef struct HAL_OUTPUT_SLOT {
|
|
HAL_OUTPUT_ENDPOINT Endpoint;
|
|
volatile long Published;
|
|
} HAL_OUTPUT_SLOT;
|
|
|
|
static HAL_OUTPUT_SLOT gOutputSlots[HAL_OUTPUT_ENDPOINT_CAPACITY];
|
|
static volatile long gOutputRegistrationLock;
|
|
static volatile long long gOutputRecordCount;
|
|
static volatile long long gOutputByteCount;
|
|
static volatile long long gOutputEndpointWriteCount;
|
|
static volatile long long gOutputEmergencyRecordCount;
|
|
static volatile long gOutputEndpointCount;
|
|
|
|
// Acquires the endpoint registration lock.
|
|
static void HalOutputAcquireRegistrationLock(void) {
|
|
while (_InterlockedCompareExchange(&gOutputRegistrationLock, 1, 0) != 0)
|
|
_mm_pause();
|
|
}
|
|
|
|
// Releases the endpoint registration lock.
|
|
static void HalOutputReleaseRegistrationLock(void) {
|
|
_InterlockedExchange(&gOutputRegistrationLock, 0);
|
|
}
|
|
|
|
// Registers one output endpoint in the fixed endpoint table.
|
|
static KSTATUS HalOutputRegisterEndpoint(const HAL_OUTPUT_ENDPOINT* endpoint, uint32_t* endpointIdOut) {
|
|
if (!endpoint || !endpointIdOut || !endpoint->Name || !endpoint->Write ||
|
|
endpoint->StreamMask == 0 || (endpoint->StreamMask & ~HAL_OUTPUT_STREAM_ALL) != 0)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
KSTATUS status = STATUS_NO_MEMORY;
|
|
HalOutputAcquireRegistrationLock();
|
|
for (uint32_t i = 0; i < HAL_OUTPUT_ENDPOINT_CAPACITY; i++) {
|
|
HAL_OUTPUT_SLOT* slot = &gOutputSlots[i];
|
|
if (_InterlockedCompareExchange(&slot->Published, 0, 0) != 0)
|
|
continue;
|
|
|
|
slot->Endpoint = *endpoint;
|
|
_ReadWriteBarrier();
|
|
_InterlockedExchange(&slot->Published, 1);
|
|
_InterlockedIncrement(&gOutputEndpointCount);
|
|
*endpointIdOut = i;
|
|
status = STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
HalOutputReleaseRegistrationLock();
|
|
return status;
|
|
}
|
|
|
|
// Replaces the stream subscription mask for a registered endpoint.
|
|
static KSTATUS HalOutputSetEndpointStreams(uint32_t endpointId, uint32_t streamMask) {
|
|
if (endpointId >= HAL_OUTPUT_ENDPOINT_CAPACITY ||
|
|
(streamMask & ~HAL_OUTPUT_STREAM_ALL) != 0)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
HAL_OUTPUT_SLOT* slot = &gOutputSlots[endpointId];
|
|
if (_InterlockedCompareExchange(&slot->Published, 0, 0) == 0)
|
|
return STATUS_NOT_FOUND;
|
|
|
|
_InterlockedExchange((volatile long*)&slot->Endpoint.StreamMask, (long)streamMask);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
// Fans one text record out to every subscribed endpoint.
|
|
static void HalOutputWrite(const HAL_OUTPUT_RECORD* record) {
|
|
if (!record || !record->Buffer || record->Length == 0 ||
|
|
(record->Stream & HAL_OUTPUT_STREAM_ALL) == 0 ||
|
|
(record->Stream & (record->Stream - 1u)) != 0)
|
|
return;
|
|
|
|
_InterlockedIncrement64(&gOutputRecordCount);
|
|
_InterlockedExchangeAdd64(&gOutputByteCount, (long long)record->Length);
|
|
if (record->Flags & HAL_OUTPUT_WRITE_EMERGENCY)
|
|
_InterlockedIncrement64(&gOutputEmergencyRecordCount);
|
|
|
|
for (uint32_t i = 0; i < HAL_OUTPUT_ENDPOINT_CAPACITY; i++) {
|
|
HAL_OUTPUT_SLOT* slot = &gOutputSlots[i];
|
|
if (_InterlockedCompareExchange(&slot->Published, 0, 0) == 0)
|
|
continue;
|
|
|
|
uint32_t streams = (uint32_t)_InterlockedCompareExchange(
|
|
(volatile long*)&slot->Endpoint.StreamMask, 0, 0);
|
|
if ((streams & record->Stream) == 0)
|
|
continue;
|
|
if ((record->Flags & HAL_OUTPUT_WRITE_EMERGENCY) &&
|
|
!(slot->Endpoint.Capabilities & HAL_OUTPUT_ENDPOINT_PANIC_SAFE))
|
|
continue;
|
|
|
|
HAL_OUTPUT_ENDPOINT_WRITE write = slot->Endpoint.Write;
|
|
void* context = slot->Endpoint.Context;
|
|
if (!write)
|
|
continue;
|
|
|
|
write(context, record);
|
|
_InterlockedIncrement64(&gOutputEndpointWriteCount);
|
|
}
|
|
}
|
|
|
|
// Returns a snapshot of output router counters.
|
|
static void HalOutputQueryStats(HAL_OUTPUT_STATS* statsOut) {
|
|
if (!statsOut)
|
|
return;
|
|
|
|
memset(statsOut, 0, sizeof(*statsOut));
|
|
statsOut->RecordCount = (uint64_t)_InterlockedCompareExchange64(&gOutputRecordCount, 0, 0);
|
|
statsOut->ByteCount = (uint64_t)_InterlockedCompareExchange64(&gOutputByteCount, 0, 0);
|
|
statsOut->EndpointWriteCount = (uint64_t)_InterlockedCompareExchange64(&gOutputEndpointWriteCount, 0, 0);
|
|
statsOut->EmergencyRecordCount = (uint64_t)_InterlockedCompareExchange64(&gOutputEmergencyRecordCount, 0, 0);
|
|
statsOut->EndpointCount = (uint32_t)_InterlockedCompareExchange(&gOutputEndpointCount, 0, 0);
|
|
}
|
|
|
|
// Initializes the fixed HAL output endpoint table.
|
|
void HalOutputInitialize(HAL_OUTPUT_ROUTER* output) {
|
|
if (!output)
|
|
return;
|
|
|
|
memset(gOutputSlots, 0, sizeof(gOutputSlots));
|
|
gOutputRegistrationLock = 0;
|
|
gOutputRecordCount = 0;
|
|
gOutputByteCount = 0;
|
|
gOutputEndpointWriteCount = 0;
|
|
gOutputEmergencyRecordCount = 0;
|
|
gOutputEndpointCount = 0;
|
|
|
|
memset(output, 0, sizeof(*output));
|
|
output->RegisterEndpoint = HalOutputRegisterEndpoint;
|
|
output->SetEndpointStreams = HalOutputSetEndpointStreams;
|
|
output->Write = HalOutputWrite;
|
|
output->QueryStats = HalOutputQueryStats;
|
|
output->SerialEndpointId = HAL_OUTPUT_ENDPOINT_INVALID;
|
|
output->BootFramebufferEndpointId = HAL_OUTPUT_ENDPOINT_INVALID;
|
|
}
|