mirror of
				https://github.com/acidanthera/audk.git
				synced 2025-10-30 02:33:44 +01:00 
			
		
		
		
	git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@4062 6f19259b-4bc3-4df7-8a09-765794883524
		
			
				
	
	
		
			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;
 | |
| 
 | |
| CHAR8 *
 | |
| 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;
 | |
| }
 |