/** @file
SnpDxe.c - Simple Network Protocol DXE Driver Implementation
AMI SNP DXE driver implementing EFI_SIMPLE_NETWORK_PROTOCOL over
an UNDI3.1 network interface.
Source structure matches AMI NetworkPkg files:
Snp.c, Start.c, Stop.c, Initialize.c, Reset.c,
Shutdown.c, Receive_filters.c, Station_address.c,
Statistics.c, Get_status.c, Transmit.c, Receive.c,
Mcast_ip_to_mac.c, Nvdata.c, ComponentName.c
plus library wrappers and ACPI I/O helpers compiled into the same module.
Decompiled from: SnpDxe.efi
Build path: e:\\hs\\Build\\HR6N0XMLK\\DEBUG_VS2015\\X64\\Build\\SnpDxe\\DEBUG\\SnpDxe.pdb
Copyright (c) AMI Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "SnpDxe.h"
//
// Global variable definitions
//
EFI_HANDLE gImageHandle = NULL;
EFI_SYSTEM_TABLE *gSystemTable = NULL;
EFI_BOOT_SERVICES *gBootServices = NULL;
EFI_RUNTIME_SERVICES *gRuntimeServices = NULL;
SNP_DRIVER *gSnpDriver = NULL;
VOID *gHobList = NULL;
//
// ============================================================================
// Internal Memory Operations (replacement for BaseMemoryLib)
// ============================================================================
/**
Fills a buffer with zeros using 8-byte aligned stores.
@param Buffer Pointer to the buffer to zero.
@param Length Size of the buffer in bytes.
@return Pointer to Buffer.
**/
VOID *
EFIAPI
InternalZeroMem (
VOID *Buffer,
UINTN Length
)
{
// Zero out 8 bytes at a time for alignment
SetMem (Buffer, Length, 0);
return Buffer;
}
/**
Copies memory from source to destination with overlap handling.
@param Destination Pointer to the destination buffer.
@param Source Pointer to the source buffer.
@param Length Number of bytes to copy.
@return Pointer to Destination.
**/
VOID *
EFIAPI
InternalCopyMem (
VOID *Destination,
CONST VOID *Source,
UINTN Length
)
{
return CopyMem (Destination, Source, Length);
}
/**
Fills a buffer with a specified value.
@param Buffer Pointer to the buffer to fill.
@param Count Number of bytes to fill.
@param Value Value to fill.
@return Pointer to Buffer.
**/
VOID *
EFIAPI
InternalSetMem (
VOID *Buffer,
UINTN Count,
UINT8 Value
)
{
return SetMem (Buffer, Count, Value);
}
/**
Compares two memory buffers.
@param Buffer1 Pointer to the first buffer.
@param Buffer2 Pointer to the second buffer.
@param Length Number of bytes to compare.
@return 0 if equal, non-zero if different.
**/
INTN
EFIAPI
InternalCompareMem (
CONST VOID *Buffer1,
CONST VOID *Buffer2,
UINTN Length
)
{
return CompareMem (Buffer1, Buffer2, Length);
}
//
// ============================================================================
// Library Wrappers (self-contained, with debug assertions)
// ============================================================================
/**
Wrapper for ZeroMem with assertion.
**/
VOID *
EFIAPI
SnpZeroMem (
VOID *Buffer,
UINTN Length
)
{
ASSERT (Buffer != NULL);
return InternalZeroMem (Buffer, Length);
}
/**
Wrapper for CopyMem with assertion.
**/
VOID *
EFIAPI
SnpCopyMem (
VOID *Destination,
CONST VOID *Source,
UINTN Length
)
{
ASSERT (Destination != NULL || Length == 0);
ASSERT (Source != NULL || Length == 0);
return InternalCopyMem (Destination, Source, Length);
}
/**
Wrapper for SetMem with assertion.
**/
VOID *
EFIAPI
SnpSetMem (
VOID *Buffer,
UINTN Count,
UINT8 Value
)
{
ASSERT (Buffer != NULL || Count == 0);
return InternalSetMem (Buffer, Count, Value);
}
/**
Wrapper for CompareMem with assertion.
**/
INTN
EFIAPI
SnpCompareMem (
CONST VOID *Buffer1,
CONST VOID *Buffer2,
UINTN Length
)
{
ASSERT (Buffer1 != NULL || Length == 0);
ASSERT (Buffer2 != NULL || Length == 0);
return InternalCompareMem (Buffer1, Buffer2, Length);
}
/**
Allocates a pool buffer.
@param AllocationSize Size of the allocation in bytes.
@return Pointer to the allocated buffer, or NULL on failure.
**/
VOID *
EFIAPI
SnpAllocatePool (
IN UINTN AllocationSize
)
{
VOID *Buffer;
Buffer = NULL;
gBootServices->AllocatePool (EfiBootServicesData, AllocationSize, &Buffer);
return Buffer;
}
/**
Allocates a pool buffer and copies data into it.
@param AllocationSize Size of the allocation in bytes.
@param Buffer Source data to copy.
@return Pointer to the allocated buffer, or NULL on failure.
**/
VOID *
EFIAPI
SnpAllocateCopyPool (
IN UINTN AllocationSize,
IN VOID *Buffer
)
{
VOID *NewBuffer;
NewBuffer = SnpAllocatePool (AllocationSize);
if (NewBuffer != NULL && Buffer != NULL && AllocationSize > 0) {
SnpCopyMem (NewBuffer, Buffer, AllocationSize);
}
return NewBuffer;
}
/**
Frees a pool buffer.
@param Buffer Pointer to the buffer to free.
**/
VOID
EFIAPI
SnpFreePool (
IN VOID *Buffer
)
{
ASSERT (Buffer != NULL);
gBootServices->FreePool (Buffer);
}
//
// ============================================================================
// String Operations
// ============================================================================
/**
Returns the length of a Unicode string.
@param String Pointer to the null-terminated Unicode string.
@return Number of Unicode characters, not including the null terminator.
**/
UINTN
EFIAPI
SnpStrLen (
IN CONST CHAR16 *String
)
{
UINTN Length;
ASSERT (String != NULL);
Length = 0;
if (String != NULL) {
while (String[Length] != 0) {
Length++;
}
}
return Length;
}
/**
Returns the length of an ASCII string.
@param String Pointer to the null-terminated ASCII string.
@return Number of ASCII characters, not including the null terminator.
**/
UINTN
EFIAPI
SnpAsciiStrLen (
IN CONST CHAR8 *String
)
{
UINTN Length;
ASSERT (String != NULL);
Length = 0;
if (String != NULL) {
while (String[Length] != 0) {
Length++;
}
}
return Length;
}
/**
Safe Unicode string length (max length bounded).
@param String Pointer to the null-terminated Unicode string.
@param MaxSize Maximum number of characters to check.
@return Number of Unicode characters, not including null terminator.
**/
UINTN
EFIAPI
SnpStrnLenS (
IN CONST CHAR16 *String,
IN UINTN MaxSize
)
{
UINTN Length;
ASSERT (String != NULL);
Length = 0;
if (String != NULL) {
while (String[Length] != 0 && Length < MaxSize) {
Length++;
}
}
return Length;
}
/**
Safe ASCII string length (max length bounded).
@param String Pointer to the null-terminated ASCII string.
@param MaxSize Maximum number of characters to check.
@return Number of ASCII characters, not including null terminator.
**/
UINTN
EFIAPI
SnpAsciiStrnLenS (
IN CONST CHAR8 *String,
IN UINTN MaxSize
)
{
UINTN Length;
ASSERT (String != NULL);
Length = 0;
if (String != NULL) {
while (String[Length] != 0 && Length < MaxSize) {
Length++;
}
}
return Length;
}
/**
Performs a case-sensitive comparison of two ASCII strings.
@param FirstString Pointer to the first ASCII string.
@param SecondString Pointer to the second ASCII string.
@retval 0 FirstString equals SecondString.
@retval <0 FirstString is less than SecondString.
@retval >0 FirstString is greater than SecondString.
**/
INTN
EFIAPI
SnpAsciiStrCmp (
IN CONST CHAR8 *FirstString,
IN CONST CHAR8 *SecondString
)
{
ASSERT (FirstString != NULL);
ASSERT (SecondString != NULL);
while (*FirstString != '\0' && *FirstString == *SecondString) {
FirstString++;
SecondString++;
}
return (INTN)((UINT8)*FirstString - (UINT8)*SecondString);
}
/**
Copies an ASCII string to a Unicode string.
@param Destination Pointer to the destination Unicode buffer.
@param Source Pointer to the source ASCII string.
@return Pointer to Destination.
**/
CHAR16 *
EFIAPI
SnpStrCpyAscii (
OUT CHAR16 *Destination,
IN CONST CHAR8 *Source
)
{
CHAR16 *DestPtr;
ASSERT (Destination != NULL);
ASSERT (Source != NULL);
DestPtr = Destination;
if (Destination != NULL && Source != NULL) {
while (*Source != '\0') {
*DestPtr++ = (CHAR16)*Source++;
}
*DestPtr = L'\0';
}
return Destination;
}
/**
Converts an unsigned integer to an ASCII string.
@param Value The unsigned integer value.
@param Buffer Pointer to the output buffer.
@return Length of the resulting string.
**/
UINTN
EFIAPI
SnpUtoA (
IN UINTN Value,
OUT CHAR8 *Buffer
)
{
CHAR8 Temp[20];
UINTN Index;
UINTN Length;
ASSERT (Buffer != NULL);
if (Buffer == NULL) {
return 0;
}
Index = 0;
if (Value == 0) {
Temp[Index++] = '0';
} else {
while (Value > 0) {
Temp[Index++] = (CHAR8)('0' + (Value % 10));
Value /= 10;
}
}
Length = Index;
while (Index > 0) {
*Buffer++ = Temp[--Index];
}
*Buffer = '\0';
return Length;
}
//
// ============================================================================
// Linked List Operations
// ============================================================================
/**
Checks if a LIST_ENTRY is valid.
@param ListEntry Pointer to the list entry to check.
@retval TRUE The list entry is valid.
@retval FALSE The list entry is not valid.
**/
BOOLEAN
EFIAPI
SnpIsListValid (
IN LIST_ENTRY *ListEntry
)
{
ASSERT (ListEntry != NULL);
if (ListEntry == NULL) {
return FALSE;
}
return (BOOLEAN)(ListEntry->Flink != NULL && ListEntry->Blink != NULL);
}
/**
Inserts a new entry at the tail of a doubly-linked list.
@param ListHead Pointer to the list head.
@param Entry Pointer to the entry to insert.
**/
VOID
EFIAPI
SnpInsertTailList (
IN LIST_ENTRY *ListHead,
IN LIST_ENTRY *Entry
)
{
ASSERT (ListHead != NULL);
ASSERT (Entry != NULL);
Entry->Flink = ListHead;
Entry->Blink = ListHead->Blink;
ListHead->Blink->Flink = Entry;
ListHead->Blink = Entry;
}
/**
Returns the first entry in a list.
@param List Pointer to the list head.
@return Pointer to the first list entry, or NULL if the list is empty.
**/
LIST_ENTRY *
EFIAPI
SnpGetFirstNode (
IN LIST_ENTRY *List
)
{
ASSERT (List != NULL);
if (List == NULL || List->Flink == List) {
return NULL;
}
return List->Flink;
}
/**
Returns the next entry in a list.
@param List Pointer to the list head.
@param Node Pointer to the current node.
@return Pointer to the next list entry.
**/
LIST_ENTRY *
EFIAPI
SnpGetNextNode (
IN LIST_ENTRY *List,
IN LIST_ENTRY *Node
)
{
ASSERT (List != NULL);
ASSERT (Node != NULL);
if (Node == NULL || Node->Flink == List) {
return NULL;
}
return Node->Flink;
}
/**
Checks if a list entry is the null entry (the list head).
@param List Pointer to the list head.
@param Node Pointer to the node to check.
@retval TRUE Node is the list head.
@retval FALSE Node is not the list head.
**/
BOOLEAN
EFIAPI
SnpIsNull (
IN LIST_ENTRY *List,
IN LIST_ENTRY *Node
)
{
ASSERT (List != NULL);
ASSERT (Node != NULL);
return (BOOLEAN)(Node == List);
}
/**
Checks if a list is empty.
@param ListHead Pointer to the list head.
@retval TRUE The list is empty.
@retval FALSE The list is not empty.
**/
BOOLEAN
EFIAPI
SnpIsListEmpty (
IN LIST_ENTRY *ListHead
)
{
ASSERT (ListHead != NULL);
if (ListHead == NULL) {
return TRUE;
}
return (BOOLEAN)(ListHead->Flink == ListHead);
}
/**
Removes an entry from a doubly-linked list.
@param Entry Pointer to the entry to remove.
**/
VOID
EFIAPI
SnpRemoveEntryList (
IN LIST_ENTRY *Entry
)
{
ASSERT (Entry != NULL);
Entry->Flink->Blink = Entry->Blink;
Entry->Blink->Flink = Entry->Flink;
}
//
// ============================================================================
// Debug and Assert Functions
// ============================================================================
/**
Prints a debug message.
@param ErrorLevel Debug error level.
@param Format Print format string.
@param ... Variable arguments.
**/
VOID
EFIAPI
SnpDebugPrint (
IN UINTN ErrorLevel,
IN CONST CHAR8 *Format,
...
)
{
VA_LIST Args;
VA_START (Args, Format);
if (gSystemTable != NULL && gSystemTable->ConOut != NULL) {
gSystemTable->ConOut->OutputString (gSystemTable->ConOut, (CHAR16 *)Format);
}
VA_END (Args);
}
/**
Asserts a condition; prints file/line/description and enters an infinite loop.
@param FileName Pointer to the source file name string.
@param LineNumber Line number of the assertion.
@param Description Pointer to the assertion description string.
**/
VOID
EFIAPI
SnpDebugAssert (
IN CONST CHAR8 *FileName,
IN UINTN LineNumber,
IN CONST CHAR8 *Description
)
{
//
// Print assertion information
//
SnpDebugPrint (
0x80000000,
"\nASSERT [SnpDxe] %a(%d): %a\n",
FileName,
LineNumber,
Description
);
//
// Deadlock to indicate fatal error
//
while (TRUE) {
CpuDeadLoop ();
}
}
//
// ============================================================================
// HOB and System Table Operations
// ============================================================================
/**
Gets the HOB list from the system table GUID.
@return Pointer to the HOB list, or NULL if not found.
**/
VOID *
EFIAPI
SnpGetHobList (
VOID
)
{
EFI_HOB_HANDOFF_INFO_TABLE *HobList;
if (gHobList != NULL) {
return gHobList;
}
//
// Find HOB list via system table configuration table
//
HobList = NULL;
if (gSystemTable != NULL) {
UINTN Index;
for (Index = 0; Index < gSystemTable->NumberOfTableEntries; Index++) {
if (CompareGuid (
&gSystemTable->ConfigurationTable[Index].VendorGuid,
&gEfiHobListGuid
)) {
HobList = (EFI_HOB_HANDOFF_INFO_TABLE *)gSystemTable->ConfigurationTable[Index].VendorTable;
break;
}
}
}
gHobList = HobList;
ASSERT (gHobList != NULL);
return gHobList;
}
/**
Gets a HOB from the system table.
@return Pointer to the HOB from the system table.
**/
VOID *
EFIAPI
SnpGetHobFromSystemTable (
VOID
)
{
return SnpGetHobList ();
}
/**
Gets the image protection cookie from the system table.
@param ImageHandle The image handle.
@return Pointer to the image protection cookie.
**/
EFI_GUID *
EFIAPI
SnpGetImageProtectionCookie (
IN EFI_HANDLE ImageHandle
)
{
EFI_GUID *Cookie;
Cookie = NULL;
if (gSystemTable != NULL) {
UINTN Index;
for (Index = 0; Index < gSystemTable->NumberOfTableEntries; Index++) {
if (CompareGuid (
&gSystemTable->ConfigurationTable[Index].VendorGuid,
&gEfiImageSecurityDatabaseGuid
)) {
Cookie = (EFI_GUID *)gSystemTable->ConfigurationTable[Index].VendorTable;
break;
}
}
}
ASSERT (Cookie != NULL);
return Cookie;
}
/**
Compares two GUIDs for a partial match.
@param Guid1 Pointer to the first GUID.
@param Guid2 Pointer to the second GUID.
@retval TRUE The GUIDs match.
@retval FALSE The GUIDs do not match.
**/
BOOLEAN
EFIAPI
SnpCompareGuidPartial (
IN CONST EFI_GUID *Guid1,
IN CONST EFI_GUID *Guid2
)
{
if (Guid1 == NULL || Guid2 == NULL) {
return FALSE;
}
return CompareGuid (Guid1, Guid2);
}
/**
Checks if a protocol GUID matches.
@param ProtocolGuid Pointer to the protocol GUID to check.
@return TRUE if the protocol GUID matches the expected value.
**/
BOOLEAN
EFIAPI
SnpIsProtocolGuidMatch (
IN EFI_GUID *ProtocolGuid
)
{
return CompareGuid (ProtocolGuid, &gEfiSimpleNetworkProtocolGuid);
}
/**
Creates a legacy boot event.
@return EFI_STATUS.
**/
EFI_STATUS
EFIAPI
SnpCreateLegacyBootEvent (
VOID
)
{
EFI_EVENT Event;
return gBootServices->CreateEvent (
EVT_SIGNAL_LEGACY_BOOT,
TPL_CALLBACK,
NULL,
NULL,
&Event
);
}
/**
Reads an unaligned 64-bit value.
@param Address Pointer to the potentially unaligned address.
@return The 64-bit value.
**/
UINT64
EFIAPI
SnpReadUnaligned64 (
IN CONST VOID *Address
)
{
UINT64 Value;
CopyMem (&Value, Address, sizeof (Value));
return Value;
}
//
// ============================================================================
// Component Name Support
// ============================================================================
/**
Parses a language string for Component Name protocol.
@param Language Language string to parse.
@param SupportedLanguages Supported languages list.
@return EFI_STATUS.
**/
EFI_STATUS
EFIAPI
SnpComponentNameParseLanguage (
IN CHAR8 *Language,
OUT CHAR8 **SupportedLanguages
)
{
//
// Simple direct language comparison
//
if (Language == NULL || SupportedLanguages == NULL) {
return EFI_INVALID_PARAMETER;
}
if (SnpAsciiStrCmp (Language, "en") == 0 ||
SnpAsciiStrCmp (Language, "eng") == 0) {
*SupportedLanguages = "eng";
return EFI_SUCCESS;
}
return EFI_UNSUPPORTED;
}
/**
Adds a language to the supported languages list.
@param Language Language to add.
**/
VOID
EFIAPI
SnpComponentNameAddLanguage (
IN CHAR8 *Language
)
{
//
// Stub - languages are predefined
//
}
//
// ============================================================================
// Print Library
// ============================================================================
/**
Simple formatted print (internal implementation).
@param Format Format string.
@param ... Variable arguments.
**/
VOID
EFIAPI
SnpPrintLib (
IN CONST CHAR8 *Format,
...
)
{
VA_LIST Args;
VA_START (Args, Format);
SnpPrintLibInternal (Format, Args);
VA_END (Args);
}
/**
Internal print library implementation (simplified).
@param Format Format string.
@param VaList Variable argument list.
**/
VOID
EFIAPI
SnpPrintLibInternal (
IN CONST CHAR8 *Format,
IN VA_LIST VaList
)
{
//
// Simplified print implementation that supports basic format specifiers.
// The full implementation would handle %s, %d, %x, %r, %a, etc.
//
if (gSystemTable != NULL && gSystemTable->ConOut != NULL) {
CHAR16 Buffer[256];
CHAR16 *BufPtr;
BufPtr = Buffer;
while (*Format != '\0' && (UINTN)(BufPtr - Buffer) < sizeof(Buffer) / sizeof(CHAR16) - 1) {
if (*Format == '%') {
Format++;
switch (*Format) {
case 's':
{
CHAR16 *Str;
Str = VA_ARG (VaList, CHAR16 *);
if (Str != NULL) {
while (*Str != '\0' && (UINTN)(BufPtr - Buffer) < sizeof(Buffer) / sizeof(CHAR16) - 1) {
*BufPtr++ = *Str++;
}
}
}
break;
case 'a':
{
CHAR8 *Str;
Str = VA_ARG (VaList, CHAR8 *);
if (Str != NULL) {
while (*Str != '\0' && (UINTN)(BufPtr - Buffer) < sizeof(Buffer) / sizeof(CHAR16) - 1) {
*BufPtr++ = (CHAR16)*Str++;
}
}
}
break;
case 'd':
case 'x':
{
UINTN Value;
Value = VA_ARG (VaList, UINTN);
BufPtr += SnpUtoA (Value, Buffer);
}
break;
case 'r':
{
EFI_STATUS Status;
Status = VA_ARG (VaList, EFI_STATUS);
//
// Print status code as hex
//
BufPtr += SnpUtoA ((UINTN)Status, Buffer);
}
break;
default:
break;
}
} else {
*BufPtr++ = (CHAR16)*Format;
}
Format++;
}
*BufPtr = L'\0';
gSystemTable->ConOut->OutputString (gSystemTable->ConOut, Buffer);
}
}
/**
Print function without format processing (direct string output).
@param String String to output.
@param ...
**/
VOID
EFIAPI
SnpPrintLibNoFormat (
IN CONST CHAR8 *String,
...
)
{
if (gSystemTable != NULL && gSystemTable->ConOut != NULL && String != NULL) {
CHAR16 UnicodeBuffer[256];
UINTN Index;
for (Index = 0; String[Index] != '\0' && Index < 255; Index++) {
UnicodeBuffer[Index] = (CHAR16)String[Index];
}
UnicodeBuffer[Index] = L'\0';
gSystemTable->ConOut->OutputString (gSystemTable->ConOut, UnicodeBuffer);
}
}
/**
Simple SPrint implementation.
@param Buffer Output buffer.
@param BufferSize Size of output buffer.
@param Format Format string.
@param ... Variable arguments.
**/
UINTN
EFIAPI
SnpSPrint (
OUT CHAR16 *Buffer,
IN UINTN BufferSize,
IN CONST CHAR16 *Format,
...
)
{
//
// Simplified SPrint - just copy format string for now
//
if (Buffer != NULL && Format != NULL && BufferSize > 0) {
UINTN Index;
for (Index = 0; Format[Index] != L'\0' && Index < BufferSize - 1; Index++) {
Buffer[Index] = Format[Index];
}
Buffer[Index] = L'\0';
return Index;
}
return 0;
}
//
// ============================================================================
// Callback Management
// ============================================================================
/**
Registers a callback event for network interface notifications.
@param This Pointer to the EFI_SIMPLE_NETWORK_PROTOCOL instance.
@param Event Event to register.
@param Tpl TPL for the callback.
@retval EFI_SUCCESS Callback registered.
@retval EFI_INVALID_PARAMETER Invalid parameter.
@retval EFI_ALREADY_STARTED Callback already exists.
@retval EFI_OUT_OF_RESOURCES Out of memory.
**/
EFI_STATUS
EFIAPI
SnpRegisterCallback (
IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
IN EFI_EVENT Event,
IN EFI_TPL Tpl
)
{
SNP_DRIVER *Snp;
CALLBACK_ENTRY *Entry;
if (This == NULL || Event == NULL) {
return EFI_INVALID_PARAMETER;
}
Snp = SNP_DRIVER_FROM_PROTOCOL (This);
//
// Check if callback already exists
//
Entry = (CALLBACK_ENTRY *)Snp->CallbackList.ForwardLink;
while (Entry != &Snp->CallbackList) {
if (Entry->Event == Event) {
return EFI_ALREADY_STARTED;
}
Entry = (CALLBACK_ENTRY *)Entry->ForwardLink;
}
//
// Allocate and add new callback entry
//
Entry = (CALLBACK_ENTRY *)SnpAllocatePool (sizeof (CALLBACK_ENTRY));
if (Entry == NULL) {
return EFI_OUT_OF_RESOURCES;
}
Entry->Event = Event;
Entry->Tpl = Tpl;
InsertTailList (&Snp->CallbackList, &Entry->Link);
return EFI_SUCCESS;
}
/**
Unregisters a callback event.
@param This Pointer to the EFI_SIMPLE_NETWORK_PROTOCOL instance.
@param Event Event to unregister.
@param Tpl TPL of the callback.
@retval EFI_SUCCESS Callback unregistered.
@retval EFI_INVALID_PARAMETER Invalid parameter.
@retval EFI_NOT_STARTED Callback not found.
**/
EFI_STATUS
EFIAPI
SnpUnregisterCallback (
IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
IN EFI_EVENT Event,
IN EFI_TPL Tpl
)
{
SNP_DRIVER *Snp;
CALLBACK_ENTRY *Entry;
if (This == NULL || Event == NULL) {
return EFI_INVALID_PARAMETER;
}
Snp = SNP_DRIVER_FROM_PROTOCOL (This);
//
// Find and remove callback entry
//
Entry = (CALLBACK_ENTRY *)Snp->CallbackList.ForwardLink;
while (Entry != &Snp->CallbackList) {
if (Entry->Event == Event) {
RemoveEntryList (&Entry->Link);
SnpFreePool (Entry);
return EFI_SUCCESS;
}
Entry = (CALLBACK_ENTRY *)Entry->ForwardLink;
}
return EFI_NOT_STARTED;
}
/**
Fires callbacks for a destroyed child handle.
@param Handle Handle being destroyed.
**/
VOID
EFIAPI
SnpDestroyChild (
IN EFI_HANDLE Handle
)
{
SNP_DRIVER *Snp;
ASSERT (Handle != NULL);
Snp = SNP_DRIVER_FROM_PROTOCOL (
(EFI_SIMPLE_NETWORK_PROTOCOL *)Handle
);
//
// Fire all registered callbacks
//
CALLBACK_ENTRY *Entry;
Entry = (CALLBACK_ENTRY *)Snp->CallbackList.ForwardLink;
while (Entry != &Snp->CallbackList) {
gBootServices->SignalEvent (Entry->Event);
Entry = (CALLBACK_ENTRY *)Entry->ForwardLink;
}
}
//
// ============================================================================
// ACPI I/O Callbacks
// ============================================================================
/**
ACPI I/O check callback - checks if an event is signaled.
@param Context SNP_DRIVER pointer.
@return EFI_STATUS.
**/
UINT64
EFIAPI
SnpAcpiCheck (
IN UINT64 Context
)
{
SNP_DRIVER *Snp;
Snp = (SNP_DRIVER *)Context;
return gBootServices->CheckEvent (Snp->TimerEvent);
}
/**
ACPI I/O lock acquire/release callback.
@param Lock Lock value.
@param Acquire TRUE to acquire, FALSE to release.
@return EFI_STATUS.
**/
UINT64
EFIAPI
SnpAcpiIoReadWrite (
IN UINT64 Lock,
IN BOOLEAN Acquire
)
{
//
// Lock acquire/release using EFI TPL
//
if (Acquire) {
gBS->RaiseTPL (TPL_NOTIFY);
} else {
gBS->RestoreTPL (TPL_APPLICATION);
}
return 0;
}
/**
ACPI I/O port read/write callback with size dispatch.
@param Context SNP_DRIVER pointer.
@param Write TRUE for write, FALSE for read.
@param AddressSize Size of the I/O access (1, 2, 4, or 8 bytes).
@return The read value or written value.
**/
UINT64
EFIAPI
SnpAcpiReadWrite (
IN UINT64 Context,
IN BOOLEAN Write,
IN UINT64 AddressSize
)
{
SNP_DRIVER *Snp;
UINTN IoAddress;
UINTN AccessSize;
Snp = (SNP_DRIVER *)Context;
IoAddress = Snp->IoPortAddress;
AccessSize = 0;
//
// Determine the access size
//
switch ((UINT8)AddressSize) {
case 2:
AccessSize = 1; // 16-bit
break;
case 4:
AccessSize = 2; // 32-bit
break;
case 8:
AccessSize = 3; // 64-bit
break;
case 1:
default:
AccessSize = 0; // 8-bit
break;
}
//
// Perform I/O read or write via the ACPI IO protocol
//
if (Snp->AcpiIo != NULL) {
if (Write) {
UINT8 WriteByte;
WriteByte = (UINT8)Snp->IoDataValue;
return Snp->AcpiIo->Io.Write (
Snp->AcpiIo,
(EFI_ACPI_IO_PROTOCOL_WIDTH)AccessSize,
IoAddress,
1,
&WriteByte
);
} else {
UINT8 ReadByte;
ReadByte = 0;
Snp->AcpiIo->Io.Read (
Snp->AcpiIo,
(EFI_ACPI_IO_PROTOCOL_WIDTH)AccessSize,
IoAddress,
1,
&ReadByte
);
return ReadByte;
}
}
return 0;
}
/**
Maps a virtual address to a physical address for UNDI DMA.
@param Snp SNP_DRIVER pointer.
@param HostAddress Virtual address to map.
@param NumPages Number of pages.
@param Direction DMA direction (0=read, 1=write, 2=readwrite).
@param DeviceAddress Returned physical address.
@return EFI_STATUS.
**/
UINT64
EFIAPI
SnpAcpiMap (
IN SNP_DRIVER *Snp,
IN UINT64 HostAddress,
IN UINT32 NumPages,
IN UINT32 Direction,
OUT UINT64 *DeviceAddress
)
{
UINTN Index;
UINTN DirectionMap;
if (HostAddress == 0) {
*DeviceAddress = 0;
return 0;
}
//
// Map direction
//
switch (Direction) {
case 1:
DirectionMap = 1; // Write
break;
case 2:
DirectionMap = 2; // ReadWrite
break;
case 0:
default:
DirectionMap = 2; // Default to ReadWrite
break;
}
//
// Find a free entry in the map list
//
for (Index = 0; Index < SNP_MAP_LIST_MAX; Index++) {
if (Snp->MapList[Index].VirtualAddr == 0) {
break;
}
}
if (Index >= SNP_MAP_LIST_MAX) {
SnpDebugPrint (64, "SNP maplist is FULL\n");
*DeviceAddress = 0;
return 0;
}
//
// Store the mapping
//
Snp->MapList[Index].VirtualAddr = HostAddress;
//
// Use ACPI IO protocol to map for DMA
//
if (Snp->AcpiIo != NULL) {
EFI_STATUS Status;
UINTN Pages;
EFI_PHYSICAL_ADDRESS PhysicalAddress;
Pages = NumPages;
PhysicalAddress = 0;
Status = Snp->AcpiIo->Map (
Snp->AcpiIo,
(EFI_ACPI_IO_PROTOCOL_DMA)DirectionMap,
(VOID *)(UINTN)HostAddress,
&Pages,
&PhysicalAddress
);
if (EFI_ERROR (Status)) {
Snp->MapList[Index].VirtualAddr = 0;
*DeviceAddress = 0;
return Status;
}
Snp->MapList[Index].PhysicalAddr = PhysicalAddress;
*DeviceAddress = PhysicalAddress;
return EFI_SUCCESS;
}
*DeviceAddress = HostAddress;
return EFI_SUCCESS;
}
/**
Unmaps a previously mapped address.
@param Snp SNP_DRIVER pointer.
@param HostAddress Virtual address to unmap.
@return EFI_STATUS.
**/
UINT64
EFIAPI
SnpAcpiUnmap (
IN SNP_DRIVER *Snp,
IN UINT64 HostAddress
)
{
UINTN Index;
//
// Find the entry and unmap
//
for (Index = 0; Index < SNP_MAP_LIST_MAX; Index++) {
if (Snp->MapList[Index].VirtualAddr == HostAddress) {
if (Snp->AcpiIo != NULL) {
Snp->AcpiIo->Unmap (
Snp->AcpiIo,
(VOID *)(UINTN)Snp->MapList[Index].PhysicalAddr
);
}
Snp->MapList[Index].VirtualAddr = 0;
Snp->MapList[Index].PhysicalAddr = 0;
return EFI_SUCCESS;
}
}
return EFI_NOT_FOUND;
}
/**
Flushes data between buffers for cache coherency.
@param Snp SNP_DRIVER pointer.
@param HostAddress Address to flush.
@param Length Length of data to flush.
@return EFI_STATUS.
**/
UINT64
EFIAPI
SnpAcpiFlush (
IN SNP_DRIVER *Snp,
IN UINT64 HostAddress,
IN UINTN Length
)
{
//
// Copy data between buffers for coherency
//
if (HostAddress != 0 && Length > 0) {
VOID *Buffer;
Buffer = (VOID *)(UINTN)HostAddress;
//
// Flush the data cache
//
if (Snp->AcpiIo != NULL) {
Snp->AcpiIo->Flush (Snp->AcpiIo);
}
}
return EFI_SUCCESS;
}
//
// ============================================================================
// UNDI Command Helper
// ============================================================================
/**
Stub function that should never be called. Indicates Hw.UNDI is not available.
@param This Pointer to the EFI_SIMPLE_NETWORK_PROTOCOL instance.
@retval EFI_UNSUPPORTED Always returns unsupported.
**/
EFI_STATUS
EFIAPI
SnpIssueHwUndiCommand (
IN EFI_SIMPLE_NETWORK_PROTOCOL *This
)
{
SnpDebugPrint (0x80000000, "\nIssueHwUndiCommand() - This should not be called!");
return EFI_UNSUPPORTED;
}
//
// ============================================================================
// Timer Callback
// ============================================================================
/**
Timer notification function that periodically polls GET_STATUS to check
for received frames. Signals the event if data is available.
@param Event The timer event.
@param Context SNP_DRIVER pointer.
**/
VOID
EFIAPI
SnpTimerNotifyFunction (
IN EFI_EVENT Event,
IN VOID *Context
)
{
SNP_DRIVER *Snp;
UINT32 InterruptStatus;
UINT32 DbBuffer[2];
if (Event == NULL || Context == NULL) {
return;
}
Snp = (SNP_DRIVER *)Context;
if (Snp->UndiState != SNP_STATE_INITIALIZED) {
return;
}
//
// Set up CDB for GET_STATUS
//
Snp->Cdb.OpCode = SNP_CDB_OPCODE_GET_STATUS;
Snp->Cdb.OpFlags = 0;
Snp->Cdb.CpbSize = 0;
Snp->Cdb.DbSize = 8;
Snp->Cdb.CpbAddr = 0;
Snp->Cdb.DbAddr = (UINT64)(UINTN)Snp->DbAddr;
Snp->Cdb.StatFlags = Snp->HwAddressSize;
Snp->Cdb.OpStatus = 0;
Snp->Cdb.StatCount = 0;
//
// Clear the DB buffer
//
SnpZeroMem (Snp->DbAddr, 8);
//
// Execute the UNDI GET_STATUS command
//
((void (*)(VOID *))Snp->UndiEntry)(&Snp->Cdb);
//
// Check interrupt status
//
if (Snp->Cdb.OpStatus == 0) {
//
// Copy the status from DB buffer
//
SnpCopyMem (&InterruptStatus, Snp->DbAddr, sizeof (InterruptStatus));
if (InterruptStatus != 0) {
//
// Signal the event to indicate data is available
//
gBootServices->SignalEvent (Event);
}
}
}
//
// ============================================================================
// File: Snp.c - Driver Entry Point and Driver Binding
// ============================================================================
/**
Module entry point. Saves global pointers and installs the driver binding protocol.
@param ImageHandle The firmware allocated handle for the EFI image.
@param SystemTable A pointer to the EFI System Table.
@retval EFI_SUCCESS Entry point executed successfully.
@retval EFI_OUT_OF_RESOURCES Out of resources.
**/
EFI_STATUS
EFIAPI
_ModuleEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
//
// Save global pointers
//
gImageHandle = ImageHandle;
gSystemTable = SystemTable;
gBootServices = SystemTable->BootServices;
gRuntimeServices = SystemTable->RuntimeServices;
//
// Get image protection cookie
//
SnpGetImageProtectionCookie (ImageHandle);
//
// Install the driver binding protocol and component name protocol
//
Status = gBootServices->InstallMultipleProtocolInterfaces (
&ImageHandle,
&gEfiDriverBindingProtocolGuid,
&gSnpDriverBinding,
&gEfiComponentName2ProtocolGuid,
&gComponentName2,
NULL
);
if (EFI_ERROR (Status)) {
SnpDebugPrint (0x80000000, "\nASSERT_EFI_ERROR (Status = %r)\n", Status);
ASSERT_EFI_ERROR (Status);
}
return Status;
}
//
// Global driver binding protocol instance
//
EFI_DRIVER_BINDING_PROTOCOL gSnpDriverBinding = {
SnpSupported,
SnpStart,
SnpDriverBindingStop,
0x10, // Version
NULL, // ImageHandle (filled at runtime)
NULL // ControllerHandle (filled at runtime)
};
//
// Global component name 2 protocol instance
//
EFI_COMPONENT_NAME2_PROTOCOL gComponentName2 = {
SnpComponentNameGetDriverName,
SnpComponentNameGetControllerName,
"eng"
};
/**
Entry point for the SNP DXE driver.
Reads the "NetworkStackVar" UEFI variable to determine if the network stack
should be enabled. Configures CMOS registers for network control and
conditionally installs the driver binding protocol.
@param ImageHandle The firmware allocated handle for the EFI image.
@param SystemTable A pointer to the EFI System Table.
@retval EFI_SUCCESS Driver initialized successfully.
@retval EFI_NOT_FOUND Network stack variable not found/disabled.
@retval EFI_INVALID_PARAMETER Invalid parameter.
**/
EFI_STATUS
EFIAPI
SnpDxeEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
UINTN DataSize;
UINT8 VarData[8];
UINT8 CmosValue;
DataSize = 10;
//
// Read "NetworkStackVar" from UEFI Runtime Services
//
Status = gRuntimeServices->GetVariable (
L"NetworkStackVar",
&gEfiGlobalVariableGuid,
0,
&DataSize,
&VarData
);
if (!EFI_ERROR (Status)) {
//
// Write CMOS registers for network control
//
// CMOS port 0x5F, 0x5E, 0x5D - network stack control
//
__outbyte (0x72, 0x5F);
__outbyte (0x73, VarData[0]);
__outbyte (0x72, 0x5E);
__outbyte (0x73, VarData[0]);
__outbyte (0x72, 0x5D);
__outbyte (0x73, VarData[1] << 7);
if (VarData[0] != 0) {
//
// Network stack is enabled - install driver binding
//
Status = gBootServices->InstallMultipleProtocolInterfaces (
&ImageHandle,
&gEfiDriverBindingProtocolGuid,
&gSnpDriverBinding,
&gEfiComponentName2ProtocolGuid,
&gComponentName2,
NULL
);
if (EFI_ERROR (Status)) {
SnpDebugPrint (0x80000000, "\nASSERT_EFI_ERROR (Status = %r)\n", Status);
ASSERT_EFI_ERROR (Status);
}
return Status;
}
//
// Network stack is disabled
//
return EFI_NOT_FOUND;
}
return Status;
}
/**
Notifies all SNP handles of a network interface identifier change.
@param Event The event being signaled.
@param Context Pointer to event context.
**/
VOID
EFIAPI
SnpNotifyNetworkInterfaceIdentifier (
IN EFI_EVENT Event,
IN VOID *Context
)
{
EFI_STATUS Status;
UINTN HandleCount;
EFI_HANDLE *HandleBuffer;
UINTN Index;
//
// Find all handles that have the SNP protocol installed
//
HandleBuffer = NULL;
HandleCount = 0;
Status = gBootServices->LocateHandleBuffer (
ByProtocol,
&gEfiSimpleNetworkProtocolGuid,
NULL,
&HandleCount,
&HandleBuffer
);
if (!EFI_ERROR (Status) && HandleBuffer != NULL) {
for (Index = 0; Index < HandleCount; Index++) {
//
// Reinstall the protocol to notify drivers
//
gBootServices->ReinstallProtocolInterface (
HandleBuffer[Index],
&gEfiSimpleNetworkProtocolGuid,
NULL,
NULL
);
}
gBootServices->FreePool (HandleBuffer);
}
}
/**
Notifies all SNP handles and triggers a PXE shutdown.
@param ImageHandle Optional image handle to filter.
**/
VOID
EFIAPI
SnpTriggerPxeShutdown (
IN EFI_HANDLE ImageHandle OPTIONAL
)
{
//
// First notify that interface identifier changed
//
SnpNotifyNetworkInterfaceIdentifier (NULL, NULL);
//
// Then trigger legacy boot shutdown
//
SnpCreateLegacyBootEvent ();
}
/**
Creates a child handle with the SNP protocol.
@param This Pointer to the EFI_DRIVER_BINDING_PROTOCOL.
@param ControllerHandle Handle of the controller.
@param RemainingDevicePath Remaining device path.
@retval EFI_SUCCESS Child handle created.
@retval others Error creating child.
**/
EFI_STATUS
EFIAPI
SnpCreateChild (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE ControllerHandle,
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
)
{
//
// For the SNP driver, child creation is handled within SnpStart
// This function is a stub for completeness
//
return EFI_UNSUPPORTED;
}
/**
Checks if the driver supports a given controller.
Validates that the controller has UNDI3.1 support by:
1. Opening the ACPI IO protocol on the controller
2. Checking the !PXE structure for valid signature, revision >= 3.1,
paragraph alignment, valid S/W entry point, and BusCnt > 0
@param This Pointer to the EFI_DRIVER_BINDING_PROTOCOL.
@param ControllerHandle Handle of the controller to test.
@param RemainingDevicePath Remaining device path.
@retval EFI_SUCCESS The controller is supported.
@retval EFI_UNSUPPORTED The controller is not supported.
@retval EFI_ALREADY_STARTED The controller is already started.
**/
EFI_STATUS
EFIAPI
SnpSupported (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE ControllerHandle,
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
)
{
EFI_STATUS Status;
EFI_ACPI_IO *AcpiIo;
EFI_PXE_BASE_CODE_PROTOCOL *Pxe;
PXE_DB *PxeDb;
//
// Try to open the ACPI IO protocol
//
Status = gBootServices->OpenProtocol (
ControllerHandle,
&gEfiAcpiIoProtocolGuid,
(VOID **)&AcpiIo,
This->DriverBindingHandle,
ControllerHandle,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Get the network interface identifier
//
PxeDb = NULL;
Status = gBootServices->OpenProtocol (
ControllerHandle,
&gEfiNetworkInterfaceIdentifierProtocolGuid_31,
(VOID **)&PxeDb,
This->DriverBindingHandle,
ControllerHandle,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (EFI_ERROR (Status)) {
if (Status != EFI_ALREADY_STARTED) {
SnpDebugPrint (64, "Support(): UNDI3.1 found on handle %p\n", ControllerHandle);
}
gBootServices->CloseProtocol (
ControllerHandle,
&gEfiAcpiIoProtocolGuid,
This->DriverBindingHandle,
ControllerHandle
);
return Status;
}
//
// Validate the !PXE structure
//
Pxe = (EFI_PXE_BASE_CODE_PROTOCOL *)PxeDb;
if (PxeDb->Identifier[0] == 1) {
//
// Check !PXE structure at IdPtr
//
if (((UINTN)PxeDb->IdPtr & 0xF) != 0) {
SnpDebugPrint (0x4000, "\n!PXE structure is not paragraph aligned.\n");
goto NotSupported;
}
if (*(UINT32 *)PxeDb->IdPtr != PXE_SIGNATURE) {
SnpDebugPrint (0x4000, "\n!PXE signature is not valid.\n");
goto NotSupported;
}
if (*(UINT8 *)(PxeDb->IdPtr + 6) < 2) {
SnpDebugPrint (0x4000, "\n!PXE.Rev is not supported.\n");
goto NotSupported;
}
if (*(UINT8 *)(PxeDb->IdPtr + 8) < 3) {
SnpDebugPrint (0x4000, "\n!PXE.MajorVer is not supported.\n");
goto NotSupported;
}
if (*(UINT8 *)(PxeDb->IdPtr + 8) == 3 && *(UINT8 *)(PxeDb->IdPtr + 9) != 0) {
SnpDebugPrint (0x4000, "\n!PXE.MinorVer is not supported.\n");
goto NotSupported;
}
if (*(INT32 *)(PxeDb->IdPtr + 12) < 0) {
goto NotSupported;
}
if (*(UINT64 *)(PxeDb->IdPtr + 16) < (UINT64)*(UINT8 *)(PxeDb->IdPtr + 4)) {
SnpDebugPrint (0x4000, "\n!PXE S/W entry point is not valid.\n");
goto NotSupported;
}
if (*(UINT8 *)(PxeDb->IdPtr + 27) == 0) {
SnpDebugPrint (0x4000, "\n!PXE.BusCnt is zero.\n");
goto NotSupported;
}
}
SnpDebugPrint (64, "Support(): supported on %p\n", ControllerHandle);
//
// Close the protocols we opened for validation
//
gBootServices->CloseProtocol (
ControllerHandle,
&gEfiAcpiIoProtocolGuid,
This->DriverBindingHandle,
ControllerHandle
);
return EFI_SUCCESS;
NotSupported:
gBootServices->CloseProtocol (
ControllerHandle,
&gEfiNetworkInterfaceIdentifierProtocolGuid_31,
This->DriverBindingHandle,
ControllerHandle
);
return EFI_UNSUPPORTED;
}
/**
Starts the SNP driver on a controller.
Allocates the SNP_DRIVER structure, sets up the SNP protocol function table,
and installs the protocol on the controller.
@param This Pointer to the EFI_DRIVER_BINDING_PROTOCOL.
@param ControllerHandle Handle of the controller to start.
@param RemainingDevicePath Remaining device path.
@retval EFI_SUCCESS Driver started successfully.
@retval EFI_OUT_OF_RESOURCES Out of resources.
@retval others Error starting the driver.
**/
EFI_STATUS
EFIAPI
SnpStart (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE ControllerHandle,
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
)
{
EFI_STATUS Status;
SNP_DRIVER *Snp;
EFI_ACPI_IO *AcpiIo;
EFI_PXE_BASE_CODE_PROTOCOL *Pxe;
PXE_DB *PxeDb;
EFI_EVENT TimerEvent;
//
// Open ACPI IO protocol
//
Status = gBootServices->OpenProtocol (
ControllerHandle,
&gEfiAcpiIoProtocolGuid,
(VOID **)&AcpiIo,
This->DriverBindingHandle,
ControllerHandle,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Allocate SNP_DRIVER structure
//
Status = gBootServices->AllocatePool (
EfiBootServicesData,
sizeof (SNP_DRIVER),
(VOID **)&Snp
);
if (EFI_ERROR (Status)) {
gBootServices->CloseProtocol (
ControllerHandle,
&gEfiAcpiIoProtocolGuid,
This->DriverBindingHandle,
ControllerHandle
);
return Status;
}
//
// Zero the structure
//
SnpZeroMem (Snp, sizeof (SNP_DRIVER));
Snp->Signature = SNP_DRIVER_SIGNATURE;
//
// Save ACPI IO pointer
//
Snp->AcpiIo = AcpiIo;
//
// Set up the SNP protocol function table
//
Snp->Snp.Revision = EFI_SIMPLE_NETWORK_PROTOCOL_REVISION;
Snp->Snp.Start = SnpStart;
Snp->Snp.Stop = SnpStop;
Snp->Snp.Initialize = SnpInitialize;
Snp->Snp.Reset = SnpReset;
Snp->Snp.Shutdown = SnpShutdownEntry;
Snp->Snp.ReceiveFilters = SnpReceiveFilters;
Snp->Snp.StationAddress = SnpStationAddress;
Snp->Snp.Statistics = SnpStatistics;
Snp->Snp.MCastIpToMac = SnpMcastIpToMac;
Snp->Snp.Nvdata = SnpNvdata;
Snp->Snp.GetStatus = SnpGetStatus;
Snp->Snp.Transmit = SnpTransmit;
Snp->Snp.Receive = SnpReceive;
//
// Set up Mode fields
//
Snp->Snp.Mode->State = EFI_NETWORK_STOPPED;
Snp->Snp.Mode->HwAddressSize = Snp->HwAddressSize;
Snp->Snp.Mode->MediaHeaderSize = 14; // Typical Ethernet header size
Snp->Snp.Mode->MaxPacketSize = 1514; // Typical Ethernet MTU
Snp->Snp.Mode->ReceiveFilterMask = EFI_SIMPLE_NETWORK_RECEIVE_UNICAST |
EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST |
EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST |
EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS |
EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST;
//
// Set Upcall function to issue HW UNDI commands
//
Snp->IssueHwUndiCommand = SnpIssueHwUndiCommand;
//
// Register for network interface identifier changes
//
Status = gBootServices->CreateEvent (
EVT_NOTIFY_SIGNAL,
TPL_CALLBACK,
SnpNotifyNetworkInterfaceIdentifier,
NULL,
&TimerEvent
);
if (EFI_ERROR (Status)) {
gBootServices->FreePool (Snp);
gBootServices->CloseProtocol (
ControllerHandle,
&gEfiAcpiIoProtocolGuid,
This->DriverBindingHandle,
ControllerHandle
);
return Status;
}
//
// Install SNP protocol on the controller
//
Status = gBootServices->InstallProtocolInterface (
&ControllerHandle,
&gEfiSimpleNetworkProtocolGuid,
EFI_NATIVE_INTERFACE,
&Snp->Snp
);
if (EFI_ERROR (Status)) {
gBootServices->CloseEvent (TimerEvent);
gBootServices->FreePool (Snp);
gBootServices->CloseProtocol (
ControllerHandle,
&gEfiAcpiIoProtocolGuid,
This->DriverBindingHandle,
ControllerHandle
);
return Status;
}
return EFI_SUCCESS;
}
/**
Stops the SNP driver on a controller.
Validates the driver signature, destroys callbacks, closes protocols,
closes timer events, calls shutdown and stop, and frees memory.
@param This Pointer to the EFI_DRIVER_BINDING_PROTOCOL.
@param ControllerHandle Handle of the controller to stop.
@param NumberOfChildren Number of child handles.
@param ChildHandleBuffer Buffer containing child handles.
@retval EFI_SUCCESS Driver stopped successfully.
@retval others Error stopping the driver.
**/
EFI_STATUS
EFIAPI
SnpDriverBindingStop (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE ControllerHandle,
IN UINTN NumberOfChildren,
IN EFI_HANDLE *ChildHandleBuffer
)
{
EFI_STATUS Status;
SNP_DRIVER *Snp;
EFI_SIMPLE_NETWORK_PROTOCOL *SnpProtocol;
//
// Open SNP protocol to get the driver instance
//
Status = gBootServices->OpenProtocol (
ControllerHandle,
&gEfiSimpleNetworkProtocolGuid,
(VOID **)&SnpProtocol,
This->DriverBindingHandle,
ControllerHandle,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (EFI_ERROR (Status)) {
return EFI_NOT_FOUND;
}
Snp = SNP_DRIVER_FROM_PROTOCOL (SnpProtocol);
//
// Destroy child callbacks
//
SnpDestroyChild (ControllerHandle);
//
// Uninstall SNP protocol
//
Status = gBootServices->UninstallProtocolInterface (
ControllerHandle,
&gEfiSimpleNetworkProtocolGuid,
&Snp->Snp
);
if (!EFI_ERROR (Status)) {
//
// Close timer events
//
if (Snp->TimerEvent != NULL) {
gBootServices->CloseEvent (Snp->TimerEvent);
}
if (Snp->TimerEvent2 != NULL) {
gBootServices->CloseEvent (Snp->TimerEvent2);
}
//
// Close protocols
//
gBootServices->CloseProtocol (
ControllerHandle,
&gEfiNetworkInterfaceIdentifierProtocolGuid_31,
This->DriverBindingHandle,
ControllerHandle
);
gBootServices->CloseProtocol (
ControllerHandle,
&gEfiAcpiIoProtocolGuid,
This->DriverBindingHandle,
ControllerHandle
);
//
// Shutdown and stop the UNDI
//
SnpShutdownInternal (Snp);
SnpStopEntry (Snp);
//
// Free ACPI IO buffer
//
if (Snp->AcpiIo != NULL) {
Snp->AcpiIo->FreeBuffer (Snp->AcpiIo, 1, (VOID *)Snp->CpbAddr);
Snp->AcpiIo->FreeBuffer (Snp->AcpiIo, 1, Snp);
}
//
// Free the driver instance
//
gBootServices->FreePool (Snp);
}
return Status;
}
//
// ============================================================================
// File: ComponentName.c - Component Name Protocol
// ============================================================================
/**
Retrieves the driver name.
@param This Pointer to the EFI_COMPONENT_NAME2_PROTOCOL.
@param Language Language code.
@param DriverName Returned driver name string.
@retval EFI_SUCCESS Name returned successfully.
**/
EFI_STATUS
EFIAPI
SnpComponentNameGetDriverName (
IN EFI_COMPONENT_NAME2_PROTOCOL *This,
IN CHAR8 *Language,
OUT CHAR16 **DriverName
)
{
EFI_STATUS Status;
Status = SnpComponentNameParseLanguage (Language, NULL);
if (!EFI_ERROR (Status)) {
*DriverName = L"SNP (MAC=xx-xx-xx-xx-xx-xx)";
}
return Status;
}
/**
Builds a controller name string in the format "SNP (MAC=xx-xx-xx-xx-xx-xx)".
@param Snp Pointer to the SNP_DRIVER instance.
@return EFI_STATUS.
**/
EFI_STATUS
EFIAPI
SnpGetControllerName (
IN SNP_DRIVER *Snp
)
{
CHAR16 NameBuffer[80];
UINTN Offset;
UINTN Index;
if (Snp == NULL) {
return EFI_INVALID_PARAMETER;
}
//
// Build "SNP (MAC=" prefix
//
Offset = SnpUtoA ((UINTN)NameBuffer, 0xA0, L"SNP (MAC=");
//
// Append MAC address bytes in hex format
//
for (Index = 0; Index < Snp->HwAddressSize; Index++) {
Offset += SnpUtoA (
(INTN)&NameBuffer[2 * Offset],
160 - 2 * Offset,
L"%02X-",
Snp->PermAddress[Index]
);
}
ASSERT (Offset > 0);
//
// Replace trailing '-' with ')'
//
NameBuffer[2 * Offset - 2] = L')';
NameBuffer[2 * Offset - 1] = L'\0';
//
// Store the name for retrieval (simplified)
//
return EFI_SUCCESS;
}
/**
Retrieves the controller name.
@param This Pointer to the EFI_COMPONENT_NAME2_PROTOCOL.
@param ControllerHandle Handle of the controller.
@param ChildHandle Child handle.
@param Language Language code.
@param ControllerName Returned controller name.
@retval EFI_SUCCESS Name returned successfully.
**/
EFI_STATUS
EFIAPI
SnpComponentNameGetControllerName (
IN EFI_COMPONENT_NAME2_PROTOCOL *This,
IN EFI_HANDLE ControllerHandle,
IN EFI_HANDLE ChildHandle,
IN CHAR8 *Language,
OUT CHAR16 **ControllerName
)
{
EFI_STATUS Status;
EFI_SIMPLE_NETWORK_PROTOCOL *SnpProtocol;
//
// Open the SNP protocol on the controller
//
Status = gBootServices->OpenProtocol (
ControllerHandle,
&gEfiSimpleNetworkProtocolGuid,
(VOID **)&SnpProtocol,
NULL,
ControllerHandle,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (!EFI_ERROR (Status)) {
Status = SnpGetControllerName (SnpProtocol);
if (!EFI_ERROR (Status)) {
Status = SnpComponentNameParseLanguage (Language, NULL);
}
}
return Status;
}
//
// ============================================================================
// File: Start.c - SNP Start Protocol
// ============================================================================
/**
Sets the CDB to opcode START and executes the UNDI command.
If PxeInit was called, sets up the ACPI I/O function table with 6 callbacks.
@param Snp Pointer to the SNP_DRIVER instance.
@return EFI_STATUS from the UNDI command.
**/
UINT64
EFIAPI
SnpStartEntry (
IN SNP_DRIVER *Snp
)
{
SNP_CDB *Cdb;
SNP_ACPI_IO *AcpiIoTable;
Cdb = &Snp->Cdb;
AcpiIoTable = Snp->AcpiIo;
//
// Set up the CDB
//
Cdb->OpCode = SNP_CDB_OPCODE_START;
Cdb->OpFlags = 0;
Cdb->CpbSize = 0;
Cdb->DbSize = 0;
Cdb->CpbAddr = 0;
Cdb->DbAddr = 0;
Cdb->OpStatus = 0;
Cdb->StatFlags = Snp->HwAddressSize;
//
// If PxeInit was previously called, set up the ACPI I/O function table
//
if (Snp->PxeInitCalled) {
Cdb->CpbAddr = (UINT64)(UINTN)AcpiIoTable;
Cdb->CpbSize = sizeof (SNP_ACPI_IO);
//
// Register the ACPI I/O callbacks
//
AcpiIoTable->Check = SnpAcpiCheck;
AcpiIoTable->ReadWrite = (UINT64 (*)(UINT64, UINT64, UINT64, UINT64))SnpAcpiIoReadWrite;
AcpiIoTable->ReadModifyWrite = NULL;
AcpiIoTable->IoRead = (UINT64 (*)(UINT64, UINT64))SnpAcpiReadWrite;
AcpiIoTable->IoWrite = (UINT64 (*)(UINT64, UINT64))SnpAcpiReadWrite;
AcpiIoTable->MemRead = (UINT64 (*)(UINT64, UINT64))SnpAcpiMap;
AcpiIoTable->MemWrite = (UINT64 (*)(UINT64, UINT64))SnpAcpiUnmap;
AcpiIoTable->Context = (UINT64 (*)(UINT64))(UINTN)Snp;
}
SnpDebugPrint (0x4000, "\nsnp->undi.start() ");
//
// Execute the UNDI START command
//
((void (*)(VOID *))Snp->UndiEntry)(Cdb);
if (Cdb->OpStatus != 0) {
SnpDebugPrint (0x80000000, "\nsnp->undi.start() %xh:%xh\n", Cdb->OpStatus, Cdb->StatFlags);
return EFI_DEVICE_ERROR;
}
//
// Update state to STARTED
//
Snp->UndiState = SNP_STATE_STARTED;
return EFI_SUCCESS;
}
/**
State-validating entry point for SnpStart. Validates the driver is in STOPPED state.
@param This Pointer to the EFI_SIMPLE_NETWORK_PROTOCOL.
@retval EFI_SUCCESS Start completed.
@retval EFI_NOT_STARTED Driver already started.
@retval EFI_DEVICE_ERROR Device error.
**/
EFI_STATUS
EFIAPI
SnpStartEntryState (
IN EFI_SIMPLE_NETWORK_PROTOCOL *This
)
{
SNP_DRIVER *Snp;
UINTN Index;
if (This == NULL) {
return EFI_INVALID_PARAMETER;
}
Snp = SNP_DRIVER_FROM_PROTOCOL (This);
if (Snp->UndiState != SNP_STATE_STOPPED) {
return EFI_NOT_STARTED;
}
//
// Call the start entry
//
EFI_STATUS Status;
Status = SnpStartEntry (Snp);
if (!EFI_ERROR (Status)) {
//
// Clear the map list
//
for (Index = 0; Index < SNP_MAP_LIST_MAX; Index++) {
Snp->MapList[Index].VirtualAddr = 0;
Snp->MapList[Index].PhysicalAddr = 0;
}
}
return Status;
}
//
// ============================================================================
// File: Stop.c - SNP Stop Protocol
// ============================================================================
/**
Sets the CDB to opcode STOP and executes the UNDI command.
@param Snp Pointer to the SNP_DRIVER instance.
@return EFI_STATUS from the UNDI command.
**/
UINT64
EFIAPI
SnpStopEntry (
IN SNP_DRIVER *Snp
)
{
SNP_CDB *Cdb;
Cdb = &Snp->Cdb;
//
// Set up the CDB for STOP
//
Cdb->OpCode = SNP_CDB_OPCODE_STOP;
Cdb->OpFlags = 0;
Cdb->CpbSize = 0;
Cdb->DbSize = 0;
Cdb->CpbAddr = 0;
Cdb->DbAddr = 0;
Cdb->StatCount = 0;
Cdb->StatData = 0;
Cdb->OpStatus = 0;
Cdb->StatFlags = Snp->HwAddressSize;
SnpDebugPrint (0x4000, "\nsnp->undi.stop() ");
//
// Execute the UNDI STOP command
//
((void (*)(VOID *))Snp->UndiEntry)(Cdb);
if (Cdb->OpStatus != 0) {
SnpDebugPrint (2, "\nsnp->undi.stop() %xh:%xh\n", Cdb->StatFlags, Cdb->OpStatus);
return EFI_DEVICE_ERROR;
}
//
// Update state to STOPPED
//
Snp->UndiState = SNP_STATE_STOPPED;
return EFI_SUCCESS;
}
/**
SNP Stop protocol entry point.
@param This Pointer to the EFI_SIMPLE_NETWORK_PROTOCOL.
@retval EFI_SUCCESS Stop completed.
@retval EFI_NOT_STARTED Driver not in STARTED state.
**/
EFI_STATUS
EFIAPI
SnpStop (
IN EFI_SIMPLE_NETWORK_PROTOCOL *This
)
{
SNP_DRIVER *Snp;
if (This == NULL) {
return EFI_INVALID_PARAMETER;
}
Snp = SNP_DRIVER_FROM_PROTOCOL (This);
if (Snp->UndiState != SNP_STATE_STARTED) {
return EFI_NOT_STARTED;
}
return SnpStopEntry (Snp);
}
//
// ============================================================================
// File: Initialize.c - SNP Initialize Protocol
// ============================================================================
/**
CDB opcode 5 (INITIALIZE). Allocates shared memory via ACPI and fills the CPB.
@param Snp Pointer to the SNP_DRIVER instance.
@param RxFilter Receive filter to set (0 = no filter, 1 = basic filter).
@return EFI_STATUS.
**/
UINT64
EFIAPI
SnpPxeInit (
IN SNP_DRIVER *Snp,
IN UINT16 RxFilter
)
{
SNP_CDB *Cdb;
SNP_CPB_INITIALIZE *Cpb;
EFI_STATUS Status;
UINT64 MemSize;
Cdb = &Snp->Cdb;
MemSize = Snp->MaxPacketSize;
//
// Allocate shared memory buffer if needed
//
if (MemSize != 0) {
UINTN NumPages;
NumPages = ((UINTN)(MemSize - 1) >> 12) + 1;
Status = Snp->AcpiIo->AllocateBuffer (
Snp->AcpiIo,
EfiBootServicesData,
NumPages,
(VOID **)&Cpb,
0
);
if (EFI_ERROR (Status)) {
SnpDebugPrint (0x80000000, "\nSnp->PxeInit() AllocateBuffer %xh (%r)\n", Status, Status);
return Status;
}
ASSERT (Cpb != NULL);
Snp->CpbAddr = Cpb;
}
//
// Fill CPB
//
Cpb = (SNP_CPB_INITIALIZE *)Snp->CpbAddr;
Cpb->MemAddr = (UINT64)(UINTN)Snp->CpbAddr;
Cpb->MemLength = (UINT32)MemSize;
Cpb->MemReserved = 0;
Cpb->Reserved = 0;
Cpb->RxFilter = 0;
//
// Set up the CDB
//
Cdb->OpCode = SNP_CDB_OPCODE_INITIALIZE;
Cdb->CpbAddr = (UINT64)(UINTN)Snp->CpbAddr;
Cdb->DbAddr = (UINT64)(UINTN)Snp->DbAddr;
Cdb->StatFlags = Snp->HwAddressSize;
Cdb->OpFlags = RxFilter;
Cdb->CpbSize = 36; // sizeof(SNP_CPB_INITIALIZE) = 0x24
Cdb->OpStatus = 0;
Cdb->StatCount = 0;
Cdb->StatData = 0;
SnpDebugPrint (0x4000, "\nSnp->undi.initialize() ");
//
// Execute the UNDI INITIALIZE command
//
((void (*)(VOID *))Snp->UndiEntry)(Cdb);
if ((Cdb->StatFlags & 0xC000) != 0xC000 || Cdb->OpStatus != 0) {
SnpDebugPrint (2, "\nSnp->undi.initialize() %xh:%xh\n", Cdb->StatFlags, Cdb->OpStatus);
//
// Free the allocated buffer on failure
//
if (Snp->CpbAddr != NULL) {
Snp->AcpiIo->FreeBuffer (
Snp->AcpiIo,
((UINTN)(MemSize - 1) >> 12) + 1,
Snp->CpbAddr
);
}
Snp->CpbAddr = NULL;
return EFI_DEVICE_ERROR;
}
//
// Update MediaPresent based on link status
//
if (RxFilter == 0) {
Snp->MediaPresent = (BOOLEAN)((Cdb->StatFlags & 1) == 0);
}
//
// Update state to INITIALIZED
//
Snp->UndiState = SNP_STATE_INITIALIZED;
return EFI_SUCCESS;
}
/**
SNP Initialize protocol entry point.
State must be STARTED. Creates a timer event, clears filters/MAC,
sets memory size, and calls PxeInit to initialize the UNDI.
@param This Pointer to the EFI_SIMPLE_NETWORK_PROTOCOL.
@param ExtraRxBufferSize Extra receive buffer size.
@param ExtraTxBufferSize Extra transmit buffer size.
@retval EFI_SUCCESS Initialize completed.
@retval EFI_NOT_STARTED Driver not started.
@retval EFI_DEVICE_ERROR Device error.
**/
EFI_STATUS
EFIAPI
SnpInitialize (
IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
IN UINTN ExtraRxBufferSize,
IN UINTN ExtraTxBufferSize
)
{
SNP_DRIVER *Snp;
EFI_STATUS Status;
if (This == NULL) {
return EFI_INVALID_PARAMETER;
}
Snp = SNP_DRIVER_FROM_PROTOCOL (This);
if (Snp == NULL) {
return EFI_INVALID_PARAMETER;
}
//
// Check state
//
if (Snp->UndiState == SNP_STATE_STOPPED) {
return EFI_NOT_STARTED;
}
if (Snp->UndiState == SNP_STATE_STARTED) {
//
// Create timer event for polling received frames
//
Status = gBootServices->CreateEvent (
EVT_TIMER | EVT_NOTIFY_SIGNAL,
TPL_CALLBACK,
SnpTimerNotifyFunction,
Snp,
&Snp->TimerEvent
);
if (EFI_ERROR (Status)) {
Snp->TimerEvent = NULL;
return EFI_OUT_OF_RESOURCES;
}
//
// Clear receive filter setting and multicast filter count
//
Snp->ReceiveFilterSetting = 0;
Snp->MCastFilterCnt = 0;
//
// Clear multicast filter buffer
//
SnpZeroMem (Snp->MCastFilter, sizeof (Snp->MCastFilter));
//
// Copy permanent MAC address to current address
//
SnpCopyMem (Snp->CurrentAddress, Snp->PermAddress, Snp->HwAddressSize);
//
// Set total memory size
//
Snp->MaxPacketSize = ExtraRxBufferSize + ExtraTxBufferSize + Snp->MaxPacketSize;
//
// Initialize with no filter first
//
if (Snp->MediaPresentSupported) {
Snp->MediaPresent = FALSE;
}
Status = SnpPxeInit (Snp, 1); // RxFilter = 1 (basic filter)
if (EFI_ERROR (Status)) {
gBootServices->CloseEvent (Snp->TimerEvent);
Snp->TimerEvent = NULL;
return Status;
}
//
// Start the timer for periodic polling
//
Status = gBootServices->SetTimer (
Snp->TimerEvent,
TimerPeriodic,
500000 // 500ms interval
);
if (!EFI_ERROR (Status)) {
//
// If media present supported, get initial status
//
if (Snp->MediaPresentSupported) {
SnpGetStatusInternal (Snp, NULL, FALSE);
}
}
} else {
return EFI_DEVICE_ERROR;
}
return Status;
}
//
// ============================================================================
// File: Reset.c - SNP Reset Protocol
// ============================================================================
/**
CDB opcode 6 (RESET). Resets the network adapter.
@param Snp Pointer to the SNP_DRIVER instance.
@return EFI_STATUS.
**/
UINT64
EFIAPI
SnpResetInternal (
IN SNP_DRIVER *Snp
)
{
SNP_CDB *Cdb;
Cdb = &Snp->Cdb;
//
// Set up the CDB for RESET
//
Cdb->OpCode = SNP_CDB_OPCODE_RESET;
Cdb->OpFlags = 0;
Cdb->CpbSize = 0;
Cdb->DbSize = 0;
Cdb->CpbAddr = 0;
Cdb->DbAddr = 0;
Cdb->StatCount = 0;
Cdb->StatData = 0;
Cdb->StatFlags = Snp->HwAddressSize;
SnpDebugPrint (0x4000, "\nsnp->undi.reset() ");
//
// Execute the UNDI RESET command
//
((void (*)(VOID *))Snp->UndiEntry)(Cdb);
if (Cdb->OpStatus != 0) {
SnpDebugPrint (0x80000000, "\nsnp->undi.reset() %xh:%xh\n", Cdb->StatFlags, Cdb->OpStatus);
return EFI_DEVICE_ERROR;
}
return EFI_SUCCESS;
}
/**
SNP Reset protocol entry point.
@param This Pointer to the EFI_SIMPLE_NETWORK_PROTOCOL.
@param ExtendedVerification Whether extended verification is requested.
@retval EFI_SUCCESS Reset completed.
@retval EFI_NOT_STARTED Driver not initialized.
**/
EFI_STATUS
EFIAPI
SnpReset (
IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
IN BOOLEAN ExtendedVerification
)
{
SNP_DRIVER *Snp;
if (This == NULL) {
return EFI_INVALID_PARAMETER;
}
Snp = SNP_DRIVER_FROM_PROTOCOL (This);
if (Snp->UndiState != SNP_STATE_INITIALIZED) {
return EFI_NOT_STARTED;
}
return SnpResetInternal (Snp);
}
//
// ============================================================================
// File: Shutdown.c - SNP Shutdown Protocol
// ============================================================================
/**
CDB opcode 7 (SHUTDOWN). Shuts down the UNDI and frees the ACPI memory buffer.
@param Snp Pointer to the SNP_DRIVER instance.
**/
VOID
EFIAPI
SnpShutdownInternal (
IN SNP_DRIVER *Snp
)
{
SNP_CDB *Cdb;
Cdb = &Snp->Cdb;
//
// Set up the CDB for SHUTDOWN
//
Cdb->OpCode = SNP_CDB_OPCODE_SHUTDOWN;
Cdb->OpFlags = 0;
Cdb->CpbSize = 0;
Cdb->DbSize = 0;
Cdb->CpbAddr = 0;
Cdb->DbAddr = 0;
Cdb->StatCount = 0;
Cdb->StatData = 0;
Cdb->StatFlags = Snp->HwAddressSize;
SnpDebugPrint (0x4000, "\nsnp->undi.shutdown() ");
//
// Execute the UNDI SHUTDOWN command
//
((void (*)(VOID *))Snp->UndiEntry)(Cdb);
if (Cdb->OpStatus != 0) {
SnpDebugPrint (0x80000000, "\nsnp->undi.shutdown() %xh:%xh\n", Cdb->StatFlags, Cdb->OpStatus);
return;
}
//
// Free the ACPI memory buffer
//
if (Snp->CpbAddr != NULL && Snp->AcpiIo != NULL) {
Snp->AcpiIo->FreeBuffer (
Snp->AcpiIo,
((UINTN)(Snp->MaxPacketSize - 1) >> 12) + 1,
Snp->CpbAddr
);
Snp->CpbAddr = NULL;
}
}
/**
SNP Shutdown protocol entry point.
@param This Pointer to the EFI_SIMPLE_NETWORK_PROTOCOL.
@retval EFI_SUCCESS Shutdown completed.
@retval EFI_NOT_STARTED Driver not initialized.
**/
EFI_STATUS
EFIAPI
SnpShutdownEntry (
IN EFI_SIMPLE_NETWORK_PROTOCOL *This
)
{
SNP_DRIVER *Snp;
if (This == NULL) {
return EFI_INVALID_PARAMETER;
}
Snp = SNP_DRIVER_FROM_PROTOCOL (This);
if (Snp->UndiState != SNP_STATE_INITIALIZED) {
return EFI_NOT_STARTED;
}
//
// Shutdown the UNDI
//
SnpShutdownInternal (Snp);
//
// Clear multicast filter count and receive filter setting
//
Snp->MCastFilterCnt = 0;
Snp->ReceiveFilterSetting = 0;
//
// Set state to STARTED
//
Snp->UndiState = SNP_STATE_STARTED;
//
// Clear multicast addresses
//
SnpZeroMem (Snp->MCastFilter, sizeof (Snp->MCastFilter));
//
// Copy permanent address back to current address
//
SnpCopyMem (Snp->CurrentAddress, Snp->PermAddress, Snp->HwAddressSize);
//
// Close timer event
//
if (Snp->TimerEvent != NULL) {
gBootServices->CloseEvent (Snp->TimerEvent);
Snp->TimerEvent = NULL;
}
return EFI_SUCCESS;
}
//
// ============================================================================
// File: Receive_filters.c - SNP ReceiveFilters Protocol
// ============================================================================
/**
CDB opcode 9 - enables receive filters.
@param Snp Pointer to the SNP_DRIVER instance.
@param Enable Bitmask of filters to enable.
@param MCastFilterCnt Number of multicast filter entries.
@param MCastFilter Pointer to multicast filter data.
@return EFI_STATUS.
**/
UINT64
EFIAPI
SnpReceiveFiltersEnable (
IN SNP_DRIVER *Snp,
IN UINT32 Enable,
IN UINTN MCastFilterCnt,
IN VOID *MCastFilter
)
{
SNP_CDB *Cdb;
Cdb = &Snp->Cdb;
//
// Set up the CDB for RECEIVE_FILTERS with enable flags
//
Cdb->OpCode = SNP_CDB_OPCODE_RECEIVE_FILTERS;
Cdb->OpFlags = (UINT16)Enable;
Cdb->CpbSize = 0;
Cdb->DbSize = 0;
Cdb->CpbAddr = (UINT64)(UINTN)MCastFilter;
Cdb->DbAddr = 0;
Cdb->OpStatus = 0;
Cdb->StatCount = 0;
Cdb->StatData = 0;
Cdb->StatFlags = Snp->HwAddressSize;
//
// Map filter bits to UNDI flags:
// BIT0 = UNICAST
// BIT1 = MULTICAST
// BIT2 = BROADCAST
// BIT3 = PROMISCUOUS
// BIT4 = PROMISCUOUS_MULTICAST
//
SnpDebugPrint (0x4000, "\nsnp->undi.receive_filters() ");
//
// Execute the UNDI RECEIVE_FILTERS command
//
((void (*)(VOID *))Snp->UndiEntry)(Cdb);
if (Cdb->OpStatus != 0) {
SnpDebugPrint (0x80000000, "\nsnp->undi.receive_filters() %xh:%xh\n", Cdb->StatFlags, Cdb->OpStatus);
return EFI_DEVICE_ERROR;
}
//
* Update software state
//
Snp->ReceiveFilterSetting |= Enable;
if (MCastFilterCnt > 0 && MCastFilter != NULL) {
Snp->MCastFilterCnt = (UINT32)MCastFilterCnt;
SnpCopyMem (Snp->MCastFilter, MCastFilter, MCastFilterCnt * sizeof (EFI_MAC_ADDRESS));
}
return EFI_SUCCESS;
}
/**
CDB opcode 9 - disables receive filters.
@param Snp Pointer to the SNP_DRIVER instance.
@param Disable Bitmask of filters to disable.
@param ResetMCastFilter Whether to reset the multicast filter list.
@return EFI_STATUS.
**/
UINT64
EFIAPI
SnpReceiveFiltersDisable (
IN SNP_DRIVER *Snp,
IN UINT32 Disable,
IN BOOLEAN ResetMCastFilter
)
{
SNP_CDB *Cdb;
Cdb = &Snp->Cdb;
//
// Set up the CDB for RECEIVE_FILTERS with disable flags
//
Cdb->OpCode = SNP_CDB_OPCODE_RECEIVE_FILTERS;
Cdb->OpFlags = (UINT16)Disable;
Cdb->CpbSize = 0;
Cdb->DbSize = 0;
Cdb->CpbAddr = 0;
Cdb->DbAddr = 0;
Cdb->OpStatus = 0;
Cdb->StatCount = 0;
Cdb->StatData = 0;
Cdb->StatFlags = Snp->HwAddressSize;
SnpDebugPrint (0x4000, "\nsnp->undi.receive_filters() ");
//
// Execute the UNDI RECEIVE_FILTERS command
//
((void (*)(VOID *))Snp->UndiEntry)(Cdb);
if (Cdb->OpStatus != 0) {
SnpDebugPrint (0x80000000, "\nsnp->undi.receive_filters() %xh:%xh\n", Cdb->StatFlags, Cdb->OpStatus);
return EFI_DEVICE_ERROR;
}
//
// Update software state
//
Snp->ReceiveFilterSetting &= ~Disable;
if (ResetMCastFilter) {
Snp->MCastFilterCnt = 0;
SnpZeroMem (Snp->MCastFilter, sizeof (Snp->MCastFilter));
}
return EFI_SUCCESS;
}
/**
Gets the current receive filter settings from the hardware and copies
multicast filter data back.
@param Snp Pointer to the SNP_DRIVER instance.
@return EFI_STATUS.
**/
UINT64
EFIAPI
SnpReceiveFiltersSet (
IN SNP_DRIVER *Snp
)
{
//
// Read back current filter settings from HW
//
SNP_CDB *Cdb;
Cdb = &Snp->Cdb;
//
// Issue a RECEIVE_FILTERS command to read settings
//
Cdb->OpCode = SNP_CDB_OPCODE_RECEIVE_FILTERS;
Cdb->OpFlags = 0;
Cdb->CpbSize = 0;
Cdb->DbSize = 0;
Cdb->CpbAddr = 0;
Cdb->DbAddr = 0;
Cdb->OpStatus = 0;
Cdb->StatCount = 0;
Cdb->StatData = 0;
Cdb->StatFlags = Snp->HwAddressSize;
//
// Execute the UNDI command
//
((void (*)(VOID *))Snp->UndiEntry)(Cdb);
if (Cdb->OpStatus != 0) {
SnpDebugPrint (0x80000000, "\nsnp->undi.receive_filters() %xh:%xh\n", Cdb->StatFlags, Cdb->OpStatus);
return EFI_DEVICE_ERROR;
}
//
// Copy the multicast filter data back from the hardware
//
if (Cdb->StatCount > 0) {
Snp->MCastFilterCnt = Cdb->StatCount;
SnpCopyMem (Snp->MCastFilter, (VOID *)(UINTN)Cdb->DbAddr, Cdb->StatCount * sizeof (EFI_MAC_ADDRESS));
}
return EFI_SUCCESS;
}
/**
SNP ReceiveFilters protocol entry point.
@param This Pointer to the EFI_SIMPLE_NETWORK_PROTOCOL.
@param Enable Filters to enable.
@param Disable Filters to disable.
@param ResetMCastFilter Whether to reset the multicast filter list.
@param MCastFilterCnt Number of multicast filter entries.
@param MCastFilter Pointer to multicast filter data.
@retval EFI_SUCCESS Operation completed.
@retval EFI_NOT_STARTED Driver not started or not initialized.
@retval EFI_INVALID_PARAMETER Invalid parameter.
**/
EFI_STATUS
EFIAPI
SnpReceiveFilters (
IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
IN UINT32 Enable,
IN UINT32 Disable,
IN BOOLEAN ResetMCastFilter,
IN UINTN MCastFilterCnt,
IN EFI_MAC_ADDRESS *MCastFilter
)
{
SNP_DRIVER *Snp;
EFI_STATUS Status;
if (This == NULL) {
return EFI_INVALID_PARAMETER;
}
Snp = SNP_DRIVER_FROM_PROTOCOL (This);
//
// Check state
//
if (Snp->UndiState == SNP_STATE_STOPPED) {
return EFI_NOT_STARTED;
}
if (Snp->UndiState == SNP_STATE_INITIALIZED) {
//
// Validate filter flags against supported mask
//
if ((~Snp->ReceiveFilterMask & Enable) != 0 || (~Snp->ReceiveFilterMask & Disable) != 0) {
return EFI_INVALID_PARAMETER;
}
if (ResetMCastFilter) {
Disable |= Snp->ReceiveFilterSetting & EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST;
MCastFilterCnt = 0;
} else if (MCastFilterCnt > Snp->MCastFilterMax) {
return EFI_INVALID_PARAMETER;
}
//
* Handle empty operation
//
if (Enable == 0 && Disable == 0 && !ResetMCastFilter && MCastFilterCnt == 0) {
return EFI_SUCCESS;
}
//
// Require multicast filter data when enabling multicast
//
if ((Enable & EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST) != 0 && MCastFilterCnt == 0) {
return EFI_INVALID_PARAMETER;
}
//
// Enable filters
//
if (Enable != 0 || MCastFilterCnt > 0) {
Status = SnpReceiveFiltersEnable (Snp, Enable, MCastFilterCnt, MCastFilter);
if (EFI_ERROR (Status)) {
return Status;
}
}
//
// Disable filters
//
if (Disable != 0 || ResetMCastFilter) {
Status = SnpReceiveFiltersDisable (Snp, Disable, ResetMCastFilter);
if (EFI_ERROR (Status)) {
return Status;
}
}
//
// Read back and update filter settings
//
return SnpReceiveFiltersSet (Snp);
}
return EFI_DEVICE_ERROR;
}
//
// ============================================================================
// File: Station_address.c - SNP StationAddress Protocol
// ============================================================================
/**
CDB opcode 10 - reads permanent, current, and multicast MAC addresses.
@param Snp Pointer to the SNP_DRIVER instance.
@return EFI_STATUS.
**/
UINT64
EFIAPI
SnpStationAddressGet (
IN SNP_DRIVER *Snp
)
{
SNP_CDB *Cdb;
Cdb = &Snp->Cdb;
//
// Set up CDB to read station address
//
Cdb->OpCode = SNP_CDB_OPCODE_STATION_ADDR;
Cdb->OpFlags = 0;
Cdb->CpbSize = 0;
Cdb->DbSize = 0;
Cdb->CpbAddr = (UINT64)(UINTN)Snp->PermAddress;
Cdb->DbAddr = (UINT64)(UINTN)Snp->CurrentAddress;
Cdb->OpStatus = 0;
Cdb->StatCount = 0;
Cdb->StatData = 0;
Cdb->StatFlags = Snp->HwAddressSize;
SnpDebugPrint (0x4000, "\nsnp->undi.station_addr() ");
//
// Execute the UNDI STATION_ADDR command
//
((void (*)(VOID *))Snp->UndiEntry)(Cdb);
if (Cdb->OpStatus != 0) {
SnpDebugPrint (0x80000000, "\nsnp->undi.station_addr() %xh:%xh\n", Cdb->StatFlags, Cdb->OpStatus);
return EFI_DEVICE_ERROR;
}
return EFI_SUCCESS;
}
/**
CDB opcode 10 - sets a new MAC address, then reads it back.
@param Snp Pointer to the SNP_DRIVER instance.
@return EFI_STATUS.
**/
UINT64
EFIAPI
SnpStationAddressSet (
IN SNP_DRIVER *Snp
)
{
SNP_CDB *Cdb;
Cdb = &Snp->Cdb;
//
// Set up CDB to write station address
//
Cdb->OpCode = SNP_CDB_OPCODE_STATION_ADDR;
Cdb->OpFlags = 1; // Write operation
Cdb->CpbSize = (UINT16)Snp->HwAddressSize;
Cdb->DbSize = 0;
Cdb->CpbAddr = (UINT64)(UINTN)Snp->CurrentAddress;
Cdb->DbAddr = 0;
Cdb->OpStatus = 0;
Cdb->StatCount = 0;
Cdb->StatData = 0;
Cdb->StatFlags = Snp->HwAddressSize;
SnpDebugPrint (0x4000, "\nsnp->undi.station_addr() ");
//
// Execute the UNDI STATION_ADDR command
//
((void (*)(VOID *))Snp->UndiEntry)(Cdb);
if (Cdb->OpStatus != 0) {
SnpDebugPrint (0x80000000, "\nsnp->undi.station_addr() %xh:%xh\n", Cdb->StatFlags, Cdb->OpStatus);
return EFI_DEVICE_ERROR;
}
//
// Now read back the address to verify
//
Cdb->OpFlags = 0; // Read operation
Cdb->CpbAddr = (UINT64)(UINTN)Snp->PermAddress;
Cdb->DbAddr = (UINT64)(UINTN)Snp->CurrentAddress;
((void (*)(VOID *))Snp->UndiEntry)(Cdb);
if (Cdb->OpStatus != 0) {
return EFI_DEVICE_ERROR;
}
return EFI_SUCCESS;
}
/**
SNP StationAddress protocol entry point.
@param This Pointer to the EFI_SIMPLE_NETWORK_PROTOCOL.
@param Reset Whether to reset to permanent address.
@param New Pointer to new MAC address.
@retval EFI_SUCCESS Operation completed.
@retval EFI_NOT_STARTED Driver not started.
**/
EFI_STATUS
EFIAPI
SnpStationAddress (
IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
IN BOOLEAN Reset,
IN EFI_MAC_ADDRESS *New
)
{
SNP_DRIVER *Snp;
if (This == NULL) {
return EFI_INVALID_PARAMETER;
}
Snp = SNP_DRIVER_FROM_PROTOCOL (This);
if (Snp->UndiState == SNP_STATE_STOPPED) {
return EFI_NOT_STARTED;
}
if (Reset) {
//
// Restore permanent address to current address
//
SnpCopyMem (Snp->CurrentAddress, Snp->PermAddress, Snp->HwAddressSize);
} else if (New != NULL) {
//
* Set new current address
//
SnpCopyMem (Snp->CurrentAddress, New, Snp->HwAddressSize);
}
return SnpStationAddressSet (Snp);
}
//
// ============================================================================
// File: Statistics.c - SNP Statistics Protocol
// ============================================================================
/**
CDB opcode 11 - reads statistics from the UNDI.
@param Snp Pointer to the SNP_DRIVER instance.
@param Reset Whether to reset the statistics.
@param StatisticsSize On input, size of the statistics buffer.
On output, size of the returned statistics.
@param StatisticsTable Pointer to the statistics table.
@return EFI_STATUS.
**/
UINT64
EFIAPI
SnpStatisticsInternal (
IN SNP_DRIVER *Snp,
IN BOOLEAN Reset,
IN OUT UINTN *StatisticsSize,
OUT EFI_NETWORK_STATISTICS *StatisticsTable
)
{
SNP_CDB *Cdb;
UINTN Count;
UINT64 *StatData;
if (Snp == NULL) {
return EFI_INVALID_PARAMETER;
}
Cdb = &Snp->Cdb;
//
// Check state
//
if (Snp->UndiState == SNP_STATE_STOPPED) {
return EFI_NOT_STARTED;
}
if (Snp->UndiState != SNP_STATE_INITIALIZED) {
return EFI_DEVICE_ERROR;
}
if (!Reset && StatisticsSize == NULL) {
return (StatisticsTable != NULL) ? EFI_INVALID_PARAMETER : EFI_SUCCESS;
}
StatData = (UINT64 *)Snp->DbAddr;
//
// Set up the CDB
//
Cdb->CpbSize = 0x7E; // sizeof(SNP_CPB_STATISTICS) with reserved fields
Cdb->OpCode = SNP_CDB_OPCODE_STATISTICS;
Cdb->StatFlags = Snp->HwAddressSize;
Cdb->CpbAddr = 0;
Cdb->OpStatus = 0;
Cdb->StatData = 0;
Cdb->StatCount = 0;
if (Reset) {
//
// Reset statistics
//
Cdb->OpFlags = 1;
Cdb->DbSize = 0;
Cdb->DbAddr = 0;
} else {
//
// Read statistics
//
Cdb->OpFlags = 0;
Cdb->DbSize = 520; // sizeof(SNP_DB_STATISTICS)
Cdb->DbAddr = (UINT64)(UINTN)StatData;
}
SnpDebugPrint (0x4000, "\nsnp->undi.statistics() ");
//
// Execute the UNDI STATISTICS command
//
((void (*)(VOID *))Snp->UndiEntry)(Cdb);
if (Cdb->OpStatus != 0) {
if (Cdb->OpStatus == 12) {
SnpDebugPrint (0x80000000, "\nsnp->undi.statistics() %xh:%xh\n", Cdb->StatFlags, Cdb->OpStatus);
return EFI_NOT_FOUND;
}
SnpDebugPrint (0x80000000, "\nsnp->undi.statistics() %xh:%xh\n", Cdb->StatFlags, Cdb->OpStatus);
return EFI_DEVICE_ERROR;
}
if (!Reset) {
//
* Return statistics size
//
if (StatisticsTable != NULL) {
if (*StatisticsSize != 0) {
SnpZeroMem (StatisticsTable, *StatisticsSize);
}
//
// Translate UNDI statistics to EFI_NETWORK_STATISTICS format
//
Count = 0;
UINT64 *Dest = (UINT64 *)StatisticsTable;
for (UINTN Index = 0; Index < 0x208; Index += 8) {
if (Count > *StatisticsSize / 8) {
break;
}
if ((StatData[0] & (1ULL << (Index / 8))) != 0) {
Dest[Count] = StatData[Index / 8];
Count++;
} else {
Dest[Count] = 0xFF;
Count++;
}
}
//
// Update remaining supported entries
//
for (UINTN Index = Count + 1; Index < 0x40; Index++) {
if ((StatData[0] & (1ULL << Index)) != 0) {
Count = Index;
}
}
*StatisticsSize = 8 * (Count + 1);
} else {
*StatisticsSize = 0x208; // 520 bytes = full statistics block
}
if (*StatisticsSize < 8 * (Count + 1)) {
return EFI_BUFFER_TOO_SMALL;
}
}
return EFI_SUCCESS;
}
//
// ============================================================================
// File: Get_status.c - SNP GetStatus Protocol
// ============================================================================
/**
CDB opcode 14 (GET_STATUS). Returns interrupt status flags,
recycles TX buffers, and updates MediaPresent.
@param Snp Pointer to the SNP_DRIVER instance.
@param InterruptStatus Optional pointer to receive interrupt status.
@param GetTxBuf Whether to check for TX buffer recycling.
@return EFI_STATUS.
**/
UINT64
EFIAPI
SnpGetStatusInternal (
IN SNP_DRIVER *Snp,
OUT UINT32 *InterruptStatus OPTIONAL,
IN BOOLEAN GetTxBuf
)
{
SNP_CDB *Cdb;
Cdb = &Snp->Cdb;
//
* Set up the CDB
//
Cdb->CpbSize = 0;
Cdb->OpCode = SNP_CDB_OPCODE_GET_STATUS;
Cdb->OpFlags = 0;
Cdb->StatData = 0;
Cdb->StatCount = 0;
Cdb->StatFlags = Snp->HwAddressSize;
if (InterruptStatus != NULL) {
Cdb->DbSize = sizeof (UINT32);
Cdb->DbAddr = (UINT64)(UINTN)InterruptStatus;
Cdb->CpbAddr = 0;
} else {
Cdb->DbSize = 0;
Cdb->DbAddr = 0;
Cdb->CpbAddr = 0;
}
SnpDebugPrint (0x4000, "\nsnp->undi.get_status() ");
//
// Execute the UNDI GET_STATUS command
//
((void (*)(VOID *))Snp->UndiEntry)(Cdb);
if (Cdb->OpStatus != 0) {
SnpDebugPrint (0x80000000, "\nsnp->undi.get_status() %xh:%xh\n", Cdb->StatFlags, Cdb->OpStatus);
return EFI_DEVICE_ERROR;
}
//
// Update MediaPresent based on link state
//
if ((Cdb->StatFlags & SNP_STATUS_LINK_UP_BIT) != 0) {
Snp->MediaPresent = TRUE;
} else {
Snp->MediaPresent = FALSE;
}
//
* Check for recycled TX buffers
//
if (GetTxBuf && Cdb->StatCount > 0) {
//
// Recycle TX buffers (max SNP_TX_BUFFER_MAX)
//
UINT32 Count;
Count = Cdb->StatCount;
if (Count > SNP_TX_BUFFER_MAX) {
Count = SNP_TX_BUFFER_MAX;
}
for (UINT32 Index = 0; Index < Count; Index++) {
if (Snp->TxBufCount < SNP_TX_BUFFER_MAX) {
Snp->TxBufRecycle[Snp->TxBufCount] = Cdb->StatData;
Snp->TxBufCount++;
}
}
}
return EFI_SUCCESS;
}
/**
SNP GetStatus protocol entry point.
@param This Pointer to the EFI_SIMPLE_NETWORK_PROTOCOL.
@param InterruptStatus Optional pointer to receive interrupt status.
@param TxBuf Optional pointer to receive TX buffer address.
@retval EFI_SUCCESS Status retrieved.
@retval EFI_NOT_STARTED Driver not started.
@retval EFI_DEVICE_ERROR Device error.
**/
EFI_STATUS
EFIAPI
SnpGetStatus (
IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
OUT UINT32 *InterruptStatus OPTIONAL,
OUT VOID **TxBuf OPTIONAL
)
{
SNP_DRIVER *Snp;
EFI_STATUS Status;
if (This == NULL) {
return EFI_INVALID_PARAMETER;
}
Snp = SNP_DRIVER_FROM_PROTOCOL (This);
if (Snp == NULL) {
return EFI_DEVICE_ERROR;
}
if (Snp->UndiState == SNP_STATE_STOPPED) {
return EFI_NOT_STARTED;
}
if (Snp->UndiState == SNP_STATE_INITIALIZED) {
//
// If TxBuf is requested and no recycled buffers are pending,
// do a hardware GET_STATUS to check for TX completions
//
BOOLEAN CheckTxBuf;
CheckTxBuf = (BOOLEAN)(Snp->TxBufCount == 0 && TxBuf != NULL);
Status = SnpGetStatusInternal (Snp, InterruptStatus, CheckTxBuf);
if (TxBuf != NULL) {
if (Snp->TxBufCount > 0) {
Snp->TxBufCount--;
*TxBuf = (VOID *)(UINTN)Snp->TxBufRecycle[Snp->TxBufCount];
} else {
*TxBuf = NULL;
}
}
} else {
//
* State is STARTED but not INITIALIZED
//
Status = EFI_DEVICE_ERROR;
}
return Status;
}
//
// ============================================================================
// File: Transmit.c - SNP Transmit Protocol
// ============================================================================
/**
CDB opcode 15 (FILL_HEADER). Builds the MAC header for a frame.
@param Snp Pointer to the SNP_DRIVER instance.
@param Buffer Pointer to the buffer.
@param HeaderSize Size of the header.
@param FullPacket Pointer to the full packet.
@param FullPacketSize Size of the full packet.
@param SrcAddr Source MAC address.
@param DestAddr Destination MAC address.
@param Protocol Protocol type.
@return EFI_STATUS.
**/
UINT64
EFIAPI
SnpFillHeader (
IN SNP_DRIVER *Snp,
IN VOID *Buffer,
IN UINTN HeaderSize,
IN EFI_MAC_ADDRESS *SrcAddr,
IN EFI_MAC_ADDRESS *DestAddr,
IN UINT16 *Protocol
)
{
SNP_CDB *Cdb;
SNP_DB_RECEIVE *Db;
UINT16 SwappedProtocol;
Db = (SNP_DB_RECEIVE *)Snp->CpbAddr;
//
// Set source and destination addresses in the data block
//
if (SrcAddr != NULL) {
SnpCopyMem ((VOID *)Db, SrcAddr, Snp->HwAddressSize);
} else {
SnpCopyMem ((VOID *)Db, Snp->PermAddress, Snp->HwAddressSize);
}
SnpCopyMem ((VOID *)((UINTN)Db + 32), DestAddr, Snp->HwAddressSize);
//
* Fill the DB fields
//
Db->PacketLen = (UINT32)HeaderSize;
Db->MediaHeaderLen = (UINT16)HeaderSize;
Db->FragCount = 2;
Db->ProtocolType = (UINT16)((Protocol[0] << 8) | ((UINT8 *)Protocol)[1]);
Db->DataOffset = (UINT32)HeaderSize;
Db->MediaHeaderFrag = (UINT32)HeaderSize;
Db->SrcAddr[0] = (UINTN)Buffer;
Db->DestAddr[0] = (UINTN)((UINT8 *)Buffer + HeaderSize);
Db->Reserved2[0] = 0;
Db->Reserved2[1] = 0;
Db->Status = 0;
//
* Fill the header using UNDI
//
Cdb = &Snp->Cdb;
Cdb->OpCode = SNP_CDB_OPCODE_FILL_HEADER;
Cdb->StatFlags = Snp->HwAddressSize;
Cdb->CpbSize = 0x014B8001; // Special fill header flags
Cdb->OpFlags = 0;
Cdb->DbSize = 0;
Cdb->DbAddr = 0;
Cdb->CpbAddr = (UINT64)(UINTN)Db;
Cdb->OpStatus = 0;
Cdb->StatData = 0;
Cdb->StatCount = 0;
SnpDebugPrint (0x4000, "\nSnp->undi.fill_header() ");
//
// Execute the UNDI FILL_HEADER command
//
((void (*)(VOID *))Snp->UndiEntry)(Cdb);
if (Cdb->OpStatus != 0) {
if (Cdb->OpStatus == 14) {
SnpDebugPrint (0x80000000, "\nSnp->undi.fill_header() %xh:%xh\n", Cdb->StatFlags, 14);
return EFI_INVALID_PARAMETER;
}
SnpDebugPrint (0x80000000, "\nSnp->undi.fill_header() %xh:%xh\n", Cdb->StatFlags, Cdb->OpStatus);
return EFI_DEVICE_ERROR;
}
return EFI_SUCCESS;
}
/**
CDB opcode 16 (TRANSMIT). Transmits a frame.
@param Snp Pointer to the SNP_DRIVER instance.
@param Buffer Pointer to the buffer containing the frame.
@param BufferSize Size of the frame.
@return EFI_STATUS.
**/
UINT64
EFIAPI
SnpTransmitInternal (
IN SNP_DRIVER *Snp,
IN VOID *Buffer,
IN UINTN BufferSize
)
{
SNP_CDB *Cdb;
Cdb = &Snp->Cdb;
//
* Set up the CDB for TRANSMIT
//
Cdb->OpCode = SNP_CDB_OPCODE_TRANSMIT;
Cdb->OpFlags = 0;
Cdb->StatFlags = Snp->HwAddressSize;
Cdb->CpbAddr = (UINT64)(UINTN)Buffer;
Cdb->CpbSize = (UINT16)BufferSize;
Cdb->DbSize = 0;
Cdb->DbAddr = 0;
Cdb->OpStatus = 0;
Cdb->StatCount = 0;
Cdb->StatData = 0;
SnpDebugPrint (0x4000, "\nSnp->undi.transmit() ");
//
// Execute the UNDI TRANSMIT command
//
((void (*)(VOID *))Snp->UndiEntry)(Cdb);
if (Cdb->OpStatus != 0) {
SnpDebugPrint (0x80000000, "\nSnp->undi.transmit() %xh:%xh\n", Cdb->StatFlags, Cdb->OpStatus);
return EFI_DEVICE_ERROR;
}
return EFI_SUCCESS;
}
/**
SNP Transmit protocol entry point.
@param This Pointer to the EFI_SIMPLE_NETWORK_PROTOCOL.
@param HeaderSize Size of the MAC header.
@param BufferSize Size of the buffer.
@param Buffer Pointer to the buffer.
@param SrcAddr Source MAC address.
@param DestAddr Destination MAC address.
@param Protocol Protocol type.
@retval EFI_SUCCESS Transmit completed.
@retval EFI_NOT_STARTED Driver not started.
@retval EFI_INVALID_PARAMETER Invalid parameter.
@retval EFI_BAD_BUFFER_SIZE Buffer size invalid.
**/
EFI_STATUS
EFIAPI
SnpTransmit (
IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
IN UINTN HeaderSize,
IN UINTN BufferSize,
IN VOID *Buffer,
IN EFI_MAC_ADDRESS *SrcAddr,
IN EFI_MAC_ADDRESS *DestAddr,
IN UINT16 *Protocol
)
{
SNP_DRIVER *Snp;
EFI_STATUS Status;
if (This == NULL) {
return EFI_INVALID_PARAMETER;
}
Snp = SNP_DRIVER_FROM_PROTOCOL (This);
if (Snp == NULL) {
return EFI_DEVICE_ERROR;
}
if (Snp->UndiState == SNP_STATE_STOPPED) {
return EFI_NOT_STARTED;
}
if (Snp->UndiState == SNP_STATE_INITIALIZED) {
if (Buffer == NULL) {
return EFI_INVALID_PARAMETER;
}
if (BufferSize < Snp->MaxPacketSize) {
return EFI_BAD_BUFFER_SIZE;
}
//
// If HeaderSize > 0, fill the header first
//
if (HeaderSize > 0) {
if (HeaderSize != Snp->Snp.Mode->MediaHeaderSize ||
SrcAddr == NULL || Protocol == NULL) {
return EFI_INVALID_PARAMETER;
}
Status = SnpFillHeader (
Snp,
Buffer,
HeaderSize,
SrcAddr,
DestAddr,
Protocol
);
if (EFI_ERROR (Status)) {
return Status;
}
}
//
* Transmit the frame
//
return SnpTransmitInternal (Snp, Buffer, BufferSize);
}
return EFI_DEVICE_ERROR;
}
//
// ============================================================================
// File: Receive.c - SNP Receive Protocol
// ============================================================================
/**
CDB opcode 17 (RECEIVE). Copies received data from the DB buffer.
@param Snp Pointer to the SNP_DRIVER instance.
@param Buffer Pointer to the receive buffer.
@param BufferSize On input, size of the buffer. On output, size of received data.
@param HeaderSize Optional pointer to receive header size.
@param SrcAddr Optional pointer to receive source address.
@param DestAddr Optional pointer to receive destination address.
@param Protocol Optional pointer to receive protocol type.
@return EFI_STATUS.
**/
UINT64
EFIAPI
SnpReceiveInternal (
IN SNP_DRIVER *Snp,
OUT VOID *Buffer,
OUT UINTN *BufferSize,
OUT UINTN *HeaderSize,
OUT UINT32 *SrcAddr,
OUT UINT32 *DestAddr,
OUT UINT16 *Protocol
)
{
SNP_CDB *Cdb;
SNP_DB_RECEIVE *Db;
UINTN ReceivedSize;
ReceivedSize = *BufferSize;
Cdb = &Snp->Cdb;
Db = (SNP_DB_RECEIVE *)Snp->DbAddr;
//
* Set up CPB and CDB
//
Cpb = (VOID *)(UINTN)Snp->CpbAddr;
*(UINT64 *)Cpb = (UINT64)(UINTN)Buffer;
*(UINT32 *)((UINTN)Cpb + 8) = (UINT32)ReceivedSize;
*(UINT32 *)((UINTN)Cpb + 12) = 0;
Cdb->OpCode = SNP_CDB_OPCODE_RECEIVE;
Cdb->CpbSize = 0x50; // sizeof(CPB)
Cdb->StatFlags = Snp->HwAddressSize;
Cdb->CpbAddr = (UINT64)(UINTN)Cpb;
Cdb->DbAddr = (UINT64)(UINTN)Db;
Cdb->OpStatus = 0;
Cdb->StatData = 0;
Cdb->DbSize = 0x100000;
Cdb->StatCount = 0;
SnpDebugPrint (0x4000, "\nsnp->undi.receive () ");
//
// Execute the UNDI RECEIVE command
//
((void (*)(VOID *))Snp->UndiEntry)(Cdb);
if (Cdb->OpStatus != 0) {
if (Cdb->OpStatus == 19) {
SnpDebugPrint (0x4000, "\nsnp->undi.receive () %xh:%xh\n", Cdb->StatFlags, 19);
return EFI_NO_MEDIA;
}
SnpDebugPrint (0x80000000, "\nsnp->undi.receive() %xh:%xh\n", Cdb->StatFlags, Cdb->OpStatus);
return EFI_DEVICE_ERROR;
}
//
* Copy received data info from the DB
//
*BufferSize = Db->DataOffset;
if (HeaderSize != NULL) {
*HeaderSize = Db->MediaHeaderLen;
}
if (SrcAddr != NULL) {
SnpCopyMem (SrcAddr, Db->SrcAddr, Snp->HwAddressSize);
}
if (DestAddr != NULL) {
SnpCopyMem (DestAddr, Db->DestAddr, Snp->HwAddressSize);
}
if (Protocol != NULL) {
*Protocol = (UINT16)(Db->ProtocolType >> 8) |
(UINT16)(Db->ProtocolType << 8);
}
//
// Update MediaPresent flag
//
if (Snp->MediaPresentSupported && !Snp->MediaPresent) {
Snp->MediaPresent = TRUE;
}
//
* Check if buffer was too small
//
if (ReceivedSize < *BufferSize) {
return EFI_BUFFER_TOO_SMALL;
}
return EFI_SUCCESS;
}
/**
SNP Receive protocol entry point.
@param This Pointer to the EFI_SIMPLE_NETWORK_PROTOCOL.
@param HeaderSize Optional pointer to receive header size.
@param BufferSize On input, size of the buffer. On output, size of received data.
@param Buffer Pointer to the receive buffer.
@param SrcAddr Optional pointer to receive source address.
@param DestAddr Optional pointer to receive destination address.
@param Protocol Optional pointer to receive protocol type.
@retval EFI_SUCCESS Receive completed.
@retval EFI_NOT_STARTED Driver not initialized.
@retval EFI_INVALID_PARAMETER Invalid parameter.
@retval EFI_BUFFER_TOO_SMALL Buffer too small.
**/
EFI_STATUS
EFIAPI
SnpReceive (
IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
OUT UINTN *HeaderSize,
OUT UINTN *BufferSize,
OUT VOID *Buffer,
OUT EFI_MAC_ADDRESS *SrcAddr,
OUT EFI_MAC_ADDRESS *DestAddr,
OUT UINT16 *Protocol
)
{
SNP_DRIVER *Snp;
if (This == NULL) {
return EFI_INVALID_PARAMETER;
}
Snp = SNP_DRIVER_FROM_PROTOCOL (This);
if (Snp->UndiState != SNP_STATE_INITIALIZED) {
return EFI_NOT_STARTED;
}
if (!(Snp->ReceiveFilterSetting & EFI_SIMPLE_NETWORK_RECEIVE_UNICAST)) {
return EFI_NOT_STARTED;
}
return SnpReceiveInternal (
Snp,
Buffer,
BufferSize,
HeaderSize,
(UINT32 *)SrcAddr,
(UINT32 *)DestAddr,
Protocol
);
}
//
// ============================================================================
// File: Mcast_ip_to_mac.c - SNP McastIpToMac Protocol
// ============================================================================
/**
CDB opcode 12 (MCAST_IP_TO_MAC). Converts an IP address to a MAC address.
@param Snp Pointer to the SNP_DRIVER instance.
@param Ip Pointer to the IP address.
@param Mac Pointer to receive the MAC address.
@param IPv6 Whether the IP address is IPv6.
@return EFI_STATUS.
**/
UINT64
EFIAPI
SnpMcastIpToMacInternal (
IN SNP_DRIVER *Snp,
IN EFI_IP_ADDRESS *Ip,
OUT EFI_MAC_ADDRESS *Mac,
IN BOOLEAN IPv6
)
{
SNP_CDB *Cdb;
Cdb = &Snp->Cdb;
//
* Set up the CDB for MCAST_IP_TO_MAC
//
Cdb->OpCode = SNP_CDB_OPCODE_MCAST_IP_TO_MAC;
Cdb->OpFlags = IPv6 ? 1 : 0;
Cdb->StatFlags = Snp->HwAddressSize;
Cdb->CpbAddr = (UINT64)(UINTN)Ip;
Cdb->CpbSize = (UINT16)(IPv6 ? 16 : 4);
Cdb->DbAddr = (UINT64)(UINTN)Mac;
Cdb->DbSize = (UINT16)Snp->HwAddressSize;
Cdb->OpStatus = 0;
Cdb->StatCount = 0;
Cdb->StatData = 0;
SnpDebugPrint (0x4000, "\nsnp->undi.mcast_ip_to_mac() ");
//
// Execute the UNDI MCAST_IP_TO_MAC command
//
((void (*)(VOID *))Snp->UndiEntry)(Cdb);
if (Cdb->OpStatus != 0) {
if (Cdb->OpStatus == 19) {
return EFI_NO_MEDIA;
}
SnpDebugPrint (0x80000000, "\nsnp->undi.mcast_ip_to_mac() %xh:%xh\n", Cdb->StatFlags, Cdb->OpStatus);
return EFI_DEVICE_ERROR;
}
return EFI_SUCCESS;
}
/**
SNP McastIpToMac protocol entry point.
@param This Pointer to the EFI_SIMPLE_NETWORK_PROTOCOL.
@param IPv6 Whether the IP address is IPv6.
@param Ip Pointer to the IP address.
@param Mac Pointer to receive the MAC address.
@retval EFI_SUCCESS Conversion completed.
**/
EFI_STATUS
EFIAPI
SnpMcastIpToMac (
IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
IN BOOLEAN IPv6,
IN EFI_IP_ADDRESS *Ip,
OUT EFI_MAC_ADDRESS *Mac
)
{
SNP_DRIVER *Snp;
if (This == NULL || Ip == NULL || Mac == NULL) {
return EFI_INVALID_PARAMETER;
}
Snp = SNP_DRIVER_FROM_PROTOCOL (This);
//
* Check state
//
if (Snp->UndiState == SNP_STATE_STOPPED) {
return EFI_NOT_STARTED;
}
if (Snp->UndiState != SNP_STATE_INITIALIZED) {
return EFI_DEVICE_ERROR;
}
return SnpMcastIpToMacInternal (Snp, Ip, Mac, IPv6);
}
//
// ============================================================================
// File: Nvdata.c - SNP Nvdata Protocol
// ============================================================================
/**
CDB opcode 13 (NVDATA). Reads NVRAM data from the UNDI.
@param Snp Pointer to the SNP_DRIVER instance.
@param Offset Offset into NVRAM data.
@param BufferSize Size of the buffer.
@param Buffer Pointer to the buffer.
@return EFI_STATUS.
**/
UINT64
EFIAPI
SnpNvdataInternal (
IN SNP_DRIVER *Snp,
IN UINTN Offset,
IN UINTN BufferSize,
OUT VOID *Buffer
)
{
SNP_CDB *Cdb;
//
* Validate parameters
//
if (Snp == NULL || Buffer == NULL || BufferSize == 0) {
return EFI_INVALID_PARAMETER;
}
if (BufferSize > SNP_NVDATA_SIZE) {
BufferSize = SNP_NVDATA_SIZE;
}
Cdb = &Snp->Cdb;
//
* Set up the CDB for NVDATA
//
Cdb->OpCode = SNP_CDB_OPCODE_NVDATA;
Cdb->OpFlags = 0;
Cdb->CpbAddr = (UINT64)(UINTN)&Offset;
Cdb->CpbSize = sizeof (UINTN);
Cdb->DbAddr = (UINT64)(UINTN)Buffer;
Cdb->DbSize = (UINT16)BufferSize;
Cdb->OpStatus = 0;
Cdb->StatCount = 0;
Cdb->StatData = 0;
Cdb->StatFlags = Snp->HwAddressSize;
SnpDebugPrint (0x4000, "\nsnp->undi.nvdata() ");
//
// Execute the UNDI NVDATA command
//
((void (*)(VOID *))Snp->UndiEntry)(Cdb);
if (Cdb->OpStatus != 0) {
if (Cdb->OpStatus == 12) {
return EFI_NOT_FOUND;
}
SnpDebugPrint (0x80000000, "\nsnp->undi.nvdata() %xh:%xh\n", Cdb->StatFlags, Cdb->OpStatus);
return EFI_DEVICE_ERROR;
}
return EFI_SUCCESS;
}
/**
SNP Nvdata protocol entry point.
@param This Pointer to the EFI_SIMPLE_NETWORK_PROTOCOL.
@param ReadWrite TRUE for read, FALSE for write.
@param Offset Offset into NVRAM.
@param BufferSize Size of the buffer.
@param Buffer Pointer to the buffer.
@retval EFI_SUCCESS NVRAM operation completed.
@retval EFI_INVALID_PARAMETER Invalid parameter.
@retval EFI_UNSUPPORTED Write not supported.
**/
EFI_STATUS
EFIAPI
SnpNvdata (
IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
IN BOOLEAN ReadWrite,
IN UINTN Offset,
IN UINTN BufferSize,
IN OUT VOID *Buffer
)
{
SNP_DRIVER *Snp;
if (This == NULL) {
return EFI_INVALID_PARAMETER;
}
Snp = SNP_DRIVER_FROM_PROTOCOL (This);
//
* Check state
//
if (Snp->UndiState == SNP_STATE_STOPPED) {
return EFI_NOT_STARTED;
}
if (Snp->UndiState != SNP_STATE_INITIALIZED) {
return EFI_DEVICE_ERROR;
}
//
* Validate parameters
//
if (Buffer == NULL || BufferSize == 0) {
return EFI_INVALID_PARAMETER;
}
if (!ReadWrite) {
return EFI_UNSUPPORTED;
}
//
* Validate offset and size
//
if (Offset + BufferSize > SNP_NVDATA_SIZE) {
return EFI_INVALID_PARAMETER;
}
return SnpNvdataInternal (Snp, Offset, BufferSize, Buffer);
}