3 Copyright (c) 2005 - 2006, Intel Corporation
4 All rights reserved. This program and the accompanying materials
5 are licensed and made available under the terms and conditions of the BSD License
6 which accompanies this distribution. The full text of the license may be found at
7 http://opensource.org/licenses/bsd-license.php
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
18 Network debug facility. The debug information is wrapped in
19 SYSLOG packets, then sent over SNP. This debug facility can't
20 be used by SNP. Apply caution when used in MNP and non-network
21 module because SNP is most likely not "thread safe". We assume
22 that the SNP supports the EHTERNET.
30 #include <Protocol/SimpleNetwork.h>
32 #include <Library/BaseLib.h>
33 #include <Library/NetLib.h>
34 #include <Library/UefiBootServicesTableLib.h>
35 #include <Library/UefiRuntimeServicesTableLib.h>
36 #include <Library/MemoryAllocationLib.h>
37 #include <Library/BaseMemoryLib.h>
38 #include <Library/PrintLib.h>
42 // Any error level digitally larger than mNetDebugLevelMax
43 // will be silently discarded.
45 UINTN mNetDebugLevelMax
= NETDEBUG_LEVEL_ERROR
;
46 UINT32 mSyslogPacketSeq
= 0xDEADBEEF;
49 // You can change mSyslogDstMac mSyslogDstIp and mSyslogSrcIp
50 // here to direct the syslog packets to the syslog deamon. The
51 // default is broadcast to both the ethernet and IP.
53 UINT8 mSyslogDstMac
[NET_ETHER_ADDR_LEN
] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
54 UINT32 mSyslogDstIp
= 0xffffffff;
55 UINT32 mSyslogSrcIp
= 0;
75 Locate the handles that support SNP, then open one of them
76 to send the syslog packets. The caller isn't required to close
77 the SNP after use because the SNP is opened by HandleProtocol.
81 @return The point to SNP if one is properly openned. Otherwise NULL
84 EFI_SIMPLE_NETWORK_PROTOCOL
*
89 EFI_SIMPLE_NETWORK_PROTOCOL
*Snp
;
96 // Locate the handles which has SNP installed.
99 Status
= gBS
->LocateHandleBuffer (
101 &gEfiSimpleNetworkProtocolGuid
,
107 if (EFI_ERROR (Status
) || (HandleCount
== 0)) {
112 // Try to open one of the ethernet SNP protocol to send packet
116 for (Index
= 0; Index
< HandleCount
; Index
++) {
117 Status
= gBS
->HandleProtocol (
119 &gEfiSimpleNetworkProtocolGuid
,
123 if ((Status
== EFI_SUCCESS
) && (Snp
!= NULL
) &&
124 (Snp
->Mode
->IfType
== NET_IFTYPE_ETHERNET
) &&
125 (Snp
->Mode
->MaxPacketSize
>= NET_SYSLOG_PACKET_LEN
)) {
133 gBS
->FreePool (Handles
);
139 Transmit a syslog packet synchronously through SNP. The Packet
140 already has the ethernet header prepended. This function should
141 fill in the source MAC because it will try to locate a SNP each
142 time it is called to avoid the problem if SNP is unloaded.
143 This code snip is copied from MNP.
145 @param Packet The Syslog packet
146 @param Length The length of the packet
148 @retval EFI_DEVICE_ERROR Failed to locate a usable SNP protocol
149 @retval EFI_TIMEOUT Timeout happened to send the packet.
150 @retval EFI_SUCCESS Packet is sent.
159 EFI_SIMPLE_NETWORK_PROTOCOL
*Snp
;
162 EFI_EVENT TimeoutEvent
;
165 Snp
= SyslogLocateSnp ();
168 return EFI_DEVICE_ERROR
;
171 Ether
= (ETHER_HEAD
*) Packet
;
172 CopyMem (Ether
->SrcMac
, Snp
->Mode
->CurrentAddress
.Addr
, NET_ETHER_ADDR_LEN
);
175 // Start the timeout event.
177 Status
= gBS
->CreateEvent (
185 if (EFI_ERROR (Status
)) {
189 Status
= gBS
->SetTimer (TimeoutEvent
, TimerRelative
, NET_SYSLOG_TX_TIMEOUT
);
191 if (EFI_ERROR (Status
)) {
197 // Transmit the packet through SNP.
199 Status
= Snp
->Transmit (Snp
, 0, Length
, Packet
, NULL
, NULL
, NULL
);
201 if ((Status
!= EFI_SUCCESS
) && (Status
!= EFI_NOT_READY
)) {
202 Status
= EFI_DEVICE_ERROR
;
207 // If Status is EFI_SUCCESS, the packet is put in the transmit queue.
208 // if Status is EFI_NOT_READY, the transmit engine of the network
209 // interface is busy. Both need to sync SNP.
215 // Get the recycled transmit buffer status.
217 Snp
->GetStatus (Snp
, NULL
, (VOID
**) &TxBuf
);
219 if (!EFI_ERROR (gBS
->CheckEvent (TimeoutEvent
))) {
220 Status
= EFI_TIMEOUT
;
224 } while (TxBuf
== NULL
);
226 if ((Status
== EFI_SUCCESS
) || (Status
== EFI_TIMEOUT
)) {
231 // Status is EFI_NOT_READY. Restart the timer event and
232 // call Snp->Transmit again.
234 gBS
->SetTimer (TimeoutEvent
, TimerRelative
, NET_SYSLOG_TX_TIMEOUT
);
237 gBS
->SetTimer (TimeoutEvent
, TimerCancel
, 0);
240 gBS
->CloseEvent (TimeoutEvent
);
246 Compute checksum for a bulk of data. This code is copied from the
249 @param Bulk Pointer to the data.
250 @param Len Length of the data, in bytes.
252 @retval UINT16 The computed checksum.
266 Sum
+= *(UINT16
*) Bulk
;
272 // Add left-over byte, if any
275 Sum
+= *(UINT8
*) Bulk
;
279 // Fold 32-bit sum to 16 bits
282 Sum
= (Sum
& 0xffff) + (Sum
>> 16);
285 return (UINT16
) ~Sum
;
290 Build a syslog packet, including the Ethernet/Ip/Udp headers
293 @param Buf The buffer to put the packet data
294 @param BufLen The lenght of the Buf
295 @param Level Syslog servity level
296 @param Module The module that generates the log
297 @param File The file that contains the current log
298 @param Line The line of code in the File that contains the
300 @param Message The log message
302 @return The length of the syslog packet built.
318 EFI_UDP4_HEADER
*Udp4
;
324 // Fill in the Ethernet header. Leave alone the source MAC.
325 // SyslogSendPacket will fill in the address for us.
327 Ether
= (ETHER_HEAD
*) Buf
;
328 CopyMem (Ether
->DstMac
, mSyslogDstMac
, NET_ETHER_ADDR_LEN
);
329 ZeroMem (Ether
->SrcMac
, NET_ETHER_ADDR_LEN
);
331 Ether
->EtherType
= HTONS (0x0800); // IP protocol
333 Buf
+= sizeof (ETHER_HEAD
);
334 BufLen
-= sizeof (ETHER_HEAD
);
337 // Fill in the IP header
339 Ip4
= (IP4_HEAD
*) Buf
;
344 Ip4
->Id
= (UINT16
) mSyslogPacketSeq
;
347 Ip4
->Protocol
= 0x11;
349 Ip4
->Src
= mSyslogSrcIp
;
350 Ip4
->Dst
= mSyslogDstIp
;
352 Buf
+= sizeof (IP4_HEAD
);
353 BufLen
-= sizeof (IP4_HEAD
);
356 // Fill in the UDP header, Udp checksum is optional. Leave it zero.
358 Udp4
= (EFI_UDP4_HEADER
*) Buf
;
359 Udp4
->SrcPort
= HTONS (514);
360 Udp4
->DstPort
= HTONS (514);
364 Buf
+= sizeof (EFI_UDP4_HEADER
);
365 BufLen
-= sizeof (EFI_UDP4_HEADER
);
368 // Build the syslog message body with <PRI> Timestamp machine module Message
370 Pri
= ((NET_SYSLOG_FACILITY
& 31) << 3) | (Level
& 7);
371 gRT
->GetTime (&Time
, NULL
);
374 // Use %a to format the ASCII strings, %s to format UNICODE strings
377 Len
+= (UINT32
) AsciiSPrint (
380 "<%d> %a %d %d:%d:%d ",
382 MonthName
[Time
.Month
-1],
390 Len
+= (UINT32
) AsciiSPrint (
391 (CHAR8
*) (Buf
+ Len
),
393 "Tiano %a: %a (Line: %d File: %a)",
402 // OK, patch the IP length/checksum and UDP length fields.
404 Len
+= sizeof (EFI_UDP4_HEADER
);
405 Udp4
->Length
= HTONS ((UINT16
) Len
);
407 Len
+= sizeof (IP4_HEAD
);
408 Ip4
->TotalLen
= HTONS ((UINT16
) Len
);
409 Ip4
->Checksum
= SyslogChecksum ((UINT8
*) Ip4
, sizeof (IP4_HEAD
));
411 return Len
+ sizeof (ETHER_HEAD
);
416 Allocate a buffer, then format the message to it. This is a
417 help function for the NET_DEBUG_XXX macros. The PrintArg of
418 these macros treats the variable length print parameters as a
419 single parameter, and pass it to the NetDebugASPrint. For
420 example, NET_DEBUG_TRACE ("Tcp", ("State transit to %a\n", Name))
423 NETDEBUG_LEVEL_TRACE,
427 NetDebugASPrint ("State transit to %a\n", Name)
429 This is exactly what we want.
431 @param Format The ASCII format string.
432 @param ... The variable length parameter whose format is
433 determined by the Format string.
435 @return The buffer containing the formatted message, or NULL if failed to
436 @return allocate memory.
448 Buf
= AllocatePool (NET_DEBUG_MSG_LEN
);
454 VA_START (Marker
, Format
);
455 AsciiVSPrint ((CHAR8
*) Buf
, NET_DEBUG_MSG_LEN
, (CHAR8
*) Format
, Marker
);
463 Output a debug message to syslog. This function will locate a
464 instance of SNP then send the message through it. Because it
465 isn't open the SNP BY_DRIVER, apply caution when using it.
467 @param Level The servity level of the message.
468 @param Module The Moudle that generates the log.
469 @param File The file that contains the log.
470 @param Line The exact line that contains the log.
471 @param Message The user message to log.
473 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the packet
474 @retval EFI_SUCCESS The log is discard because that it is more verbose
475 than the mNetDebugLevelMax. Or, it has been sent
493 // Check whether the message should be sent out
495 if (Message
== NULL
) {
496 return EFI_OUT_OF_RESOURCES
;
499 if (Level
> mNetDebugLevelMax
) {
500 Status
= EFI_SUCCESS
;
505 // Allocate a maxium of 1024 bytes, the caller should ensure
506 // that the message plus the ethernet/ip/udp header is shorter
509 Packet
= AllocatePool (NET_SYSLOG_PACKET_LEN
);
511 if (Packet
== NULL
) {
512 Status
= EFI_OUT_OF_RESOURCES
;
517 // Build the message: Ethernet header + IP header + Udp Header + user data
519 Len
= SyslogBuildPacket (
521 NET_SYSLOG_PACKET_LEN
,
530 Status
= SyslogSendPacket (Packet
, Len
);
531 gBS
->FreePool (Packet
);
534 gBS
->FreePool (Message
);