Files
nt-lite/code/kernel/hal/hal_output.c
T

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;
}