Files

143 lines
5.2 KiB
C

// HAL output router. This file fans typed text streams out to registered physical and runtime endpoints.
#include <ntlite/atomic.h>
#include <stdint.h>
#include <string.h>
#include <intrin.h>
#include <kernel/hal/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 int32_t Published;
} HAL_OUTPUT_SLOT;
static HAL_OUTPUT_SLOT gOutputSlots[HAL_OUTPUT_ENDPOINT_CAPACITY];
static volatile int32_t gOutputRegistrationLock;
static volatile int64_t gOutputRecordCount;
static volatile int64_t gOutputByteCount;
static volatile int64_t gOutputEndpointWriteCount;
static volatile int64_t gOutputEmergencyRecordCount;
static volatile int32_t gOutputEndpointCount;
// Acquires the endpoint registration lock.
static void HalOutputAcquireRegistrationLock(void) {
while (NtlAtomicCompareExchange32(&gOutputRegistrationLock, 1, 0) != 0)
_mm_pause();
}
// Releases the endpoint registration lock.
static void HalOutputReleaseRegistrationLock(void) {
NtlAtomicExchange32(&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 NTLSTATUS_INVALID_PARAMETER;
KSTATUS status = NTLSTATUS_NO_MEMORY;
HalOutputAcquireRegistrationLock();
for (uint32_t i = 0; i < HAL_OUTPUT_ENDPOINT_CAPACITY; i++) {
HAL_OUTPUT_SLOT* slot = &gOutputSlots[i];
if (NtlAtomicLoad32(&slot->Published) != 0)
continue;
slot->Endpoint = *endpoint;
_ReadWriteBarrier();
NtlAtomicExchange32(&slot->Published, 1);
NtlAtomicIncrement32(&gOutputEndpointCount);
*endpointIdOut = i;
status = NTLSTATUS_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 NTLSTATUS_INVALID_PARAMETER;
HAL_OUTPUT_SLOT* slot = &gOutputSlots[endpointId];
if (NtlAtomicLoad32(&slot->Published) == 0)
return NTLSTATUS_NOT_FOUND;
NtlAtomicExchangeU32(&slot->Endpoint.StreamMask, streamMask);
return NTLSTATUS_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;
NtlAtomicIncrement64(&gOutputRecordCount);
NtlAtomicExchangeAdd64(&gOutputByteCount, (int64_t)record->Length);
if (record->Flags & HAL_OUTPUT_WRITE_EMERGENCY)
NtlAtomicIncrement64(&gOutputEmergencyRecordCount);
for (uint32_t i = 0; i < HAL_OUTPUT_ENDPOINT_CAPACITY; i++) {
HAL_OUTPUT_SLOT* slot = &gOutputSlots[i];
if (NtlAtomicLoad32(&slot->Published) == 0)
continue;
uint32_t streams = NtlAtomicLoadU32(&slot->Endpoint.StreamMask);
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);
NtlAtomicIncrement64(&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)NtlAtomicLoad64(&gOutputRecordCount);
statsOut->ByteCount = (uint64_t)NtlAtomicLoad64(&gOutputByteCount);
statsOut->EndpointWriteCount = (uint64_t)NtlAtomicLoad64(&gOutputEndpointWriteCount);
statsOut->EmergencyRecordCount = (uint64_t)NtlAtomicLoad64(&gOutputEmergencyRecordCount);
statsOut->EndpointCount = (uint32_t)NtlAtomicLoad32(&gOutputEndpointCount);
}
// 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;
}