mirror of https://github.com/acidanthera/audk.git
537 lines
14 KiB
C
537 lines
14 KiB
C
/** @file
|
|
|
|
Copyright (c) 2005 - 2006, Intel Corporation
|
|
All rights reserved. This program and the accompanying materials
|
|
are licensed and made available under the terms and conditions of the BSD License
|
|
which accompanies this distribution. The full text of the license may be found at
|
|
http://opensource.org/licenses/bsd-license.php
|
|
|
|
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
|
|
|
|
Module Name:
|
|
|
|
NetDebug.c
|
|
|
|
Abstract:
|
|
|
|
Network debug facility. The debug information is wrapped in
|
|
SYSLOG packets, then sent over SNP. This debug facility can't
|
|
be used by SNP. Apply caution when used in MNP and non-network
|
|
module because SNP is most likely not "thread safe". We assume
|
|
that the SNP supports the EHTERNET.
|
|
|
|
|
|
**/
|
|
|
|
|
|
#include <PiDxe.h>
|
|
|
|
#include <Protocol/SimpleNetwork.h>
|
|
|
|
#include <Library/BaseLib.h>
|
|
#include <Library/NetLib.h>
|
|
#include <Library/UefiBootServicesTableLib.h>
|
|
#include <Library/UefiRuntimeServicesTableLib.h>
|
|
#include <Library/MemoryAllocationLib.h>
|
|
#include <Library/BaseMemoryLib.h>
|
|
#include <Library/PrintLib.h>
|
|
|
|
|
|
//
|
|
// Any error level digitally larger than mNetDebugLevelMax
|
|
// will be silently discarded.
|
|
//
|
|
UINTN mNetDebugLevelMax = NETDEBUG_LEVEL_ERROR;
|
|
UINT32 mSyslogPacketSeq = 0xDEADBEEF;
|
|
|
|
//
|
|
// You can change mSyslogDstMac mSyslogDstIp and mSyslogSrcIp
|
|
// here to direct the syslog packets to the syslog deamon. The
|
|
// default is broadcast to both the ethernet and IP.
|
|
//
|
|
UINT8 mSyslogDstMac [NET_ETHER_ADDR_LEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
|
|
UINT32 mSyslogDstIp = 0xffffffff;
|
|
UINT32 mSyslogSrcIp = 0;
|
|
|
|
UINT8 *
|
|
MonthName[] = {
|
|
"Jan",
|
|
"Feb",
|
|
"Mar",
|
|
"Apr",
|
|
"May",
|
|
"Jun",
|
|
"Jul",
|
|
"Aug",
|
|
"Sep",
|
|
"Oct",
|
|
"Nov",
|
|
"Dec"
|
|
};
|
|
|
|
|
|
/**
|
|
Locate the handles that support SNP, then open one of them
|
|
to send the syslog packets. The caller isn't required to close
|
|
the SNP after use because the SNP is opened by HandleProtocol.
|
|
|
|
None
|
|
|
|
@return The point to SNP if one is properly openned. Otherwise NULL
|
|
|
|
**/
|
|
EFI_SIMPLE_NETWORK_PROTOCOL *
|
|
SyslogLocateSnp (
|
|
VOID
|
|
)
|
|
{
|
|
EFI_SIMPLE_NETWORK_PROTOCOL *Snp;
|
|
EFI_STATUS Status;
|
|
EFI_HANDLE *Handles;
|
|
UINTN HandleCount;
|
|
UINTN Index;
|
|
|
|
//
|
|
// Locate the handles which has SNP installed.
|
|
//
|
|
Handles = NULL;
|
|
Status = gBS->LocateHandleBuffer (
|
|
ByProtocol,
|
|
&gEfiSimpleNetworkProtocolGuid,
|
|
NULL,
|
|
&HandleCount,
|
|
&Handles
|
|
);
|
|
|
|
if (EFI_ERROR (Status) || (HandleCount == 0)) {
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Try to open one of the ethernet SNP protocol to send packet
|
|
//
|
|
Snp = NULL;
|
|
|
|
for (Index = 0; Index < HandleCount; Index++) {
|
|
Status = gBS->HandleProtocol (
|
|
Handles[Index],
|
|
&gEfiSimpleNetworkProtocolGuid,
|
|
(VOID **) &Snp
|
|
);
|
|
|
|
if ((Status == EFI_SUCCESS) && (Snp != NULL) &&
|
|
(Snp->Mode->IfType == NET_IFTYPE_ETHERNET) &&
|
|
(Snp->Mode->MaxPacketSize >= NET_SYSLOG_PACKET_LEN)) {
|
|
|
|
break;
|
|
}
|
|
|
|
Snp = NULL;
|
|
}
|
|
|
|
gBS->FreePool (Handles);
|
|
return Snp;
|
|
}
|
|
|
|
|
|
/**
|
|
Transmit a syslog packet synchronously through SNP. The Packet
|
|
already has the ethernet header prepended. This function should
|
|
fill in the source MAC because it will try to locate a SNP each
|
|
time it is called to avoid the problem if SNP is unloaded.
|
|
This code snip is copied from MNP.
|
|
|
|
@param Packet The Syslog packet
|
|
@param Length The length of the packet
|
|
|
|
@retval EFI_DEVICE_ERROR Failed to locate a usable SNP protocol
|
|
@retval EFI_TIMEOUT Timeout happened to send the packet.
|
|
@retval EFI_SUCCESS Packet is sent.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
SyslogSendPacket (
|
|
IN UINT8 *Packet,
|
|
IN UINT32 Length
|
|
)
|
|
{
|
|
EFI_SIMPLE_NETWORK_PROTOCOL *Snp;
|
|
ETHER_HEAD *Ether;
|
|
EFI_STATUS Status;
|
|
EFI_EVENT TimeoutEvent;
|
|
UINT8 *TxBuf;
|
|
|
|
Snp = SyslogLocateSnp ();
|
|
|
|
if (Snp == NULL) {
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
Ether = (ETHER_HEAD *) Packet;
|
|
CopyMem (Ether->SrcMac, Snp->Mode->CurrentAddress.Addr, NET_ETHER_ADDR_LEN);
|
|
|
|
//
|
|
// Start the timeout event.
|
|
//
|
|
Status = gBS->CreateEvent (
|
|
EVT_TIMER,
|
|
TPL_NOTIFY,
|
|
NULL,
|
|
NULL,
|
|
&TimeoutEvent
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
Status = gBS->SetTimer (TimeoutEvent, TimerRelative, NET_SYSLOG_TX_TIMEOUT);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
goto ON_EXIT;
|
|
}
|
|
|
|
for (;;) {
|
|
//
|
|
// Transmit the packet through SNP.
|
|
//
|
|
Status = Snp->Transmit (Snp, 0, Length, Packet, NULL, NULL, NULL);
|
|
|
|
if ((Status != EFI_SUCCESS) && (Status != EFI_NOT_READY)) {
|
|
Status = EFI_DEVICE_ERROR;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If Status is EFI_SUCCESS, the packet is put in the transmit queue.
|
|
// if Status is EFI_NOT_READY, the transmit engine of the network
|
|
// interface is busy. Both need to sync SNP.
|
|
//
|
|
TxBuf = NULL;
|
|
|
|
do {
|
|
//
|
|
// Get the recycled transmit buffer status.
|
|
//
|
|
Snp->GetStatus (Snp, NULL, (VOID **) &TxBuf);
|
|
|
|
if (!EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) {
|
|
Status = EFI_TIMEOUT;
|
|
break;
|
|
}
|
|
|
|
} while (TxBuf == NULL);
|
|
|
|
if ((Status == EFI_SUCCESS) || (Status == EFI_TIMEOUT)) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Status is EFI_NOT_READY. Restart the timer event and
|
|
// call Snp->Transmit again.
|
|
//
|
|
gBS->SetTimer (TimeoutEvent, TimerRelative, NET_SYSLOG_TX_TIMEOUT);
|
|
}
|
|
|
|
gBS->SetTimer (TimeoutEvent, TimerCancel, 0);
|
|
|
|
ON_EXIT:
|
|
gBS->CloseEvent (TimeoutEvent);
|
|
return Status;
|
|
}
|
|
|
|
|
|
/**
|
|
Compute checksum for a bulk of data. This code is copied from the
|
|
Netbuffer library.
|
|
|
|
@param Bulk Pointer to the data.
|
|
@param Len Length of the data, in bytes.
|
|
|
|
@retval UINT16 The computed checksum.
|
|
|
|
**/
|
|
UINT16
|
|
SyslogChecksum (
|
|
IN UINT8 *Bulk,
|
|
IN UINT32 Len
|
|
)
|
|
{
|
|
register UINT32 Sum;
|
|
|
|
Sum = 0;
|
|
|
|
while (Len > 1) {
|
|
Sum += *(UINT16 *) Bulk;
|
|
Bulk += 2;
|
|
Len -= 2;
|
|
}
|
|
|
|
//
|
|
// Add left-over byte, if any
|
|
//
|
|
if (Len > 0) {
|
|
Sum += *(UINT8 *) Bulk;
|
|
}
|
|
|
|
//
|
|
// Fold 32-bit sum to 16 bits
|
|
//
|
|
while (Sum >> 16) {
|
|
Sum = (Sum & 0xffff) + (Sum >> 16);
|
|
}
|
|
|
|
return (UINT16) ~Sum;
|
|
}
|
|
|
|
|
|
/**
|
|
Build a syslog packet, including the Ethernet/Ip/Udp headers
|
|
and user's message.
|
|
|
|
@param Buf The buffer to put the packet data
|
|
@param BufLen The lenght of the Buf
|
|
@param Level Syslog servity level
|
|
@param Module The module that generates the log
|
|
@param File The file that contains the current log
|
|
@param Line The line of code in the File that contains the
|
|
current log
|
|
@param Message The log message
|
|
|
|
@return The length of the syslog packet built.
|
|
|
|
**/
|
|
UINT32
|
|
SyslogBuildPacket (
|
|
UINT8 *Buf,
|
|
UINT32 BufLen,
|
|
UINT32 Level,
|
|
UINT8 *Module,
|
|
UINT8 *File,
|
|
UINT32 Line,
|
|
UINT8 *Message
|
|
)
|
|
{
|
|
ETHER_HEAD *Ether;
|
|
IP4_HEAD *Ip4;
|
|
EFI_UDP4_HEADER *Udp4;
|
|
EFI_TIME Time;
|
|
UINT32 Pri;
|
|
UINT32 Len;
|
|
|
|
//
|
|
// Fill in the Ethernet header. Leave alone the source MAC.
|
|
// SyslogSendPacket will fill in the address for us.
|
|
//
|
|
Ether = (ETHER_HEAD *) Buf;
|
|
CopyMem (Ether->DstMac, mSyslogDstMac, NET_ETHER_ADDR_LEN);
|
|
ZeroMem (Ether->SrcMac, NET_ETHER_ADDR_LEN);
|
|
|
|
Ether->EtherType = HTONS (0x0800); // IP protocol
|
|
|
|
Buf += sizeof (ETHER_HEAD);
|
|
BufLen -= sizeof (ETHER_HEAD);
|
|
|
|
//
|
|
// Fill in the IP header
|
|
//
|
|
Ip4 = (IP4_HEAD *) Buf;
|
|
Ip4->HeadLen = 5;
|
|
Ip4->Ver = 4;
|
|
Ip4->Tos = 0;
|
|
Ip4->TotalLen = 0;
|
|
Ip4->Id = (UINT16) mSyslogPacketSeq;
|
|
Ip4->Fragment = 0;
|
|
Ip4->Ttl = 16;
|
|
Ip4->Protocol = 0x11;
|
|
Ip4->Checksum = 0;
|
|
Ip4->Src = mSyslogSrcIp;
|
|
Ip4->Dst = mSyslogDstIp;
|
|
|
|
Buf += sizeof (IP4_HEAD);
|
|
BufLen -= sizeof (IP4_HEAD);
|
|
|
|
//
|
|
// Fill in the UDP header, Udp checksum is optional. Leave it zero.
|
|
//
|
|
Udp4 = (EFI_UDP4_HEADER*) Buf;
|
|
Udp4->SrcPort = HTONS (514);
|
|
Udp4->DstPort = HTONS (514);
|
|
Udp4->Length = 0;
|
|
Udp4->Checksum = 0;
|
|
|
|
Buf += sizeof (EFI_UDP4_HEADER);
|
|
BufLen -= sizeof (EFI_UDP4_HEADER);
|
|
|
|
//
|
|
// Build the syslog message body with <PRI> Timestamp machine module Message
|
|
//
|
|
Pri = ((NET_SYSLOG_FACILITY & 31) << 3) | (Level & 7);
|
|
gRT->GetTime (&Time, NULL);
|
|
|
|
//
|
|
// Use %a to format the ASCII strings, %s to format UNICODE strings
|
|
//
|
|
Len = 0;
|
|
Len += (UINT32) AsciiSPrint (
|
|
(CHAR8 *) Buf,
|
|
BufLen,
|
|
"<%d> %a %d %d:%d:%d ",
|
|
Pri,
|
|
MonthName [Time.Month-1],
|
|
Time.Day,
|
|
Time.Hour,
|
|
Time.Minute,
|
|
Time.Second
|
|
);
|
|
Len--;
|
|
|
|
Len += (UINT32) AsciiSPrint (
|
|
(CHAR8 *) (Buf + Len),
|
|
BufLen - Len,
|
|
"Tiano %a: %a (Line: %d File: %a)",
|
|
Module,
|
|
Message,
|
|
Line,
|
|
File
|
|
);
|
|
Len--;
|
|
|
|
//
|
|
// OK, patch the IP length/checksum and UDP length fields.
|
|
//
|
|
Len += sizeof (EFI_UDP4_HEADER);
|
|
Udp4->Length = HTONS ((UINT16) Len);
|
|
|
|
Len += sizeof (IP4_HEAD);
|
|
Ip4->TotalLen = HTONS ((UINT16) Len);
|
|
Ip4->Checksum = SyslogChecksum ((UINT8 *) Ip4, sizeof (IP4_HEAD));
|
|
|
|
return Len + sizeof (ETHER_HEAD);
|
|
}
|
|
|
|
|
|
/**
|
|
Allocate a buffer, then format the message to it. This is a
|
|
help function for the NET_DEBUG_XXX macros. The PrintArg of
|
|
these macros treats the variable length print parameters as a
|
|
single parameter, and pass it to the NetDebugASPrint. For
|
|
example, NET_DEBUG_TRACE ("Tcp", ("State transit to %a\n", Name))
|
|
if extracted to:
|
|
NetDebugOutput (
|
|
NETDEBUG_LEVEL_TRACE,
|
|
"Tcp",
|
|
__FILE__,
|
|
__LINE__,
|
|
NetDebugASPrint ("State transit to %a\n", Name)
|
|
)
|
|
This is exactly what we want.
|
|
|
|
@param Format The ASCII format string.
|
|
@param ... The variable length parameter whose format is
|
|
determined by the Format string.
|
|
|
|
@return The buffer containing the formatted message, or NULL if failed to
|
|
@return allocate memory.
|
|
|
|
**/
|
|
UINT8 *
|
|
NetDebugASPrint (
|
|
UINT8 *Format,
|
|
...
|
|
)
|
|
{
|
|
VA_LIST Marker;
|
|
UINT8 *Buf;
|
|
|
|
Buf = AllocatePool (NET_DEBUG_MSG_LEN);
|
|
|
|
if (Buf == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
VA_START (Marker, Format);
|
|
AsciiVSPrint ((CHAR8 *) Buf, NET_DEBUG_MSG_LEN, (CHAR8 *) Format, Marker);
|
|
VA_END (Marker);
|
|
|
|
return Buf;
|
|
}
|
|
|
|
|
|
/**
|
|
Output a debug message to syslog. This function will locate a
|
|
instance of SNP then send the message through it. Because it
|
|
isn't open the SNP BY_DRIVER, apply caution when using it.
|
|
|
|
@param Level The servity level of the message.
|
|
@param Module The Moudle that generates the log.
|
|
@param File The file that contains the log.
|
|
@param Line The exact line that contains the log.
|
|
@param Message The user message to log.
|
|
|
|
@retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the packet
|
|
@retval EFI_SUCCESS The log is discard because that it is more verbose
|
|
than the mNetDebugLevelMax. Or, it has been sent
|
|
out.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
NetDebugOutput (
|
|
UINT32 Level,
|
|
UINT8 *Module,
|
|
UINT8 *File,
|
|
UINT32 Line,
|
|
UINT8 *Message
|
|
)
|
|
{
|
|
UINT8 *Packet;
|
|
UINT32 Len;
|
|
EFI_STATUS Status;
|
|
|
|
//
|
|
// Check whether the message should be sent out
|
|
//
|
|
if (Message == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
if (Level > mNetDebugLevelMax) {
|
|
Status = EFI_SUCCESS;
|
|
goto ON_EXIT;
|
|
}
|
|
|
|
//
|
|
// Allocate a maxium of 1024 bytes, the caller should ensure
|
|
// that the message plus the ethernet/ip/udp header is shorter
|
|
// than this
|
|
//
|
|
Packet = AllocatePool (NET_SYSLOG_PACKET_LEN);
|
|
|
|
if (Packet == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto ON_EXIT;
|
|
}
|
|
|
|
//
|
|
// Build the message: Ethernet header + IP header + Udp Header + user data
|
|
//
|
|
Len = SyslogBuildPacket (
|
|
Packet,
|
|
NET_SYSLOG_PACKET_LEN,
|
|
Level,
|
|
Module,
|
|
File,
|
|
Line,
|
|
Message
|
|
);
|
|
|
|
mSyslogPacketSeq++;
|
|
Status = SyslogSendPacket (Packet, Len);
|
|
gBS->FreePool (Packet);
|
|
|
|
ON_EXIT:
|
|
gBS->FreePool (Message);
|
|
return Status;
|
|
}
|