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