]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Library/DxeNetLib/NetDebug.c
e182b741f5c0d38ffc26353298f0d4d75f690e53
[mirror_edk2.git] / MdeModulePkg / Library / DxeNetLib / NetDebug.c
1 /** @file
2
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
8
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.
11
12 Module Name:
13
14 NetDebug.c
15
16 Abstract:
17
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.
23
24
25 **/
26
27
28 #include <PiDxe.h>
29
30 #include <Protocol/SimpleNetwork.h>
31
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>
39
40
41 //
42 // Any error level digitally larger than mNetDebugLevelMax
43 // will be silently discarded.
44 //
45 UINTN mNetDebugLevelMax = NETDEBUG_LEVEL_ERROR;
46 UINT32 mSyslogPacketSeq = 0xDEADBEEF;
47
48 //
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.
52 //
53 UINT8 mSyslogDstMac [NET_ETHER_ADDR_LEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
54 UINT32 mSyslogDstIp = 0xffffffff;
55 UINT32 mSyslogSrcIp = 0;
56
57 CHAR8 *
58 MonthName[] = {
59 "Jan",
60 "Feb",
61 "Mar",
62 "Apr",
63 "May",
64 "Jun",
65 "Jul",
66 "Aug",
67 "Sep",
68 "Oct",
69 "Nov",
70 "Dec"
71 };
72
73
74 /**
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.
78
79 None
80
81 @return The point to SNP if one is properly openned. Otherwise NULL
82
83 **/
84 EFI_SIMPLE_NETWORK_PROTOCOL *
85 SyslogLocateSnp (
86 VOID
87 )
88 {
89 EFI_SIMPLE_NETWORK_PROTOCOL *Snp;
90 EFI_STATUS Status;
91 EFI_HANDLE *Handles;
92 UINTN HandleCount;
93 UINTN Index;
94
95 //
96 // Locate the handles which has SNP installed.
97 //
98 Handles = NULL;
99 Status = gBS->LocateHandleBuffer (
100 ByProtocol,
101 &gEfiSimpleNetworkProtocolGuid,
102 NULL,
103 &HandleCount,
104 &Handles
105 );
106
107 if (EFI_ERROR (Status) || (HandleCount == 0)) {
108 return NULL;
109 }
110
111 //
112 // Try to open one of the ethernet SNP protocol to send packet
113 //
114 Snp = NULL;
115
116 for (Index = 0; Index < HandleCount; Index++) {
117 Status = gBS->HandleProtocol (
118 Handles[Index],
119 &gEfiSimpleNetworkProtocolGuid,
120 (VOID **) &Snp
121 );
122
123 if ((Status == EFI_SUCCESS) && (Snp != NULL) &&
124 (Snp->Mode->IfType == NET_IFTYPE_ETHERNET) &&
125 (Snp->Mode->MaxPacketSize >= NET_SYSLOG_PACKET_LEN)) {
126
127 break;
128 }
129
130 Snp = NULL;
131 }
132
133 gBS->FreePool (Handles);
134 return Snp;
135 }
136
137
138 /**
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.
144
145 @param Packet The Syslog packet
146 @param Length The length of the packet
147
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.
151
152 **/
153 EFI_STATUS
154 SyslogSendPacket (
155 IN UINT8 *Packet,
156 IN UINT32 Length
157 )
158 {
159 EFI_SIMPLE_NETWORK_PROTOCOL *Snp;
160 ETHER_HEAD *Ether;
161 EFI_STATUS Status;
162 EFI_EVENT TimeoutEvent;
163 UINT8 *TxBuf;
164
165 Snp = SyslogLocateSnp ();
166
167 if (Snp == NULL) {
168 return EFI_DEVICE_ERROR;
169 }
170
171 Ether = (ETHER_HEAD *) Packet;
172 CopyMem (Ether->SrcMac, Snp->Mode->CurrentAddress.Addr, NET_ETHER_ADDR_LEN);
173
174 //
175 // Start the timeout event.
176 //
177 Status = gBS->CreateEvent (
178 EVT_TIMER,
179 TPL_NOTIFY,
180 NULL,
181 NULL,
182 &TimeoutEvent
183 );
184
185 if (EFI_ERROR (Status)) {
186 return Status;
187 }
188
189 Status = gBS->SetTimer (TimeoutEvent, TimerRelative, NET_SYSLOG_TX_TIMEOUT);
190
191 if (EFI_ERROR (Status)) {
192 goto ON_EXIT;
193 }
194
195 for (;;) {
196 //
197 // Transmit the packet through SNP.
198 //
199 Status = Snp->Transmit (Snp, 0, Length, Packet, NULL, NULL, NULL);
200
201 if ((Status != EFI_SUCCESS) && (Status != EFI_NOT_READY)) {
202 Status = EFI_DEVICE_ERROR;
203 break;
204 }
205
206 //
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.
210 //
211 TxBuf = NULL;
212
213 do {
214 //
215 // Get the recycled transmit buffer status.
216 //
217 Snp->GetStatus (Snp, NULL, (VOID **) &TxBuf);
218
219 if (!EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) {
220 Status = EFI_TIMEOUT;
221 break;
222 }
223
224 } while (TxBuf == NULL);
225
226 if ((Status == EFI_SUCCESS) || (Status == EFI_TIMEOUT)) {
227 break;
228 }
229
230 //
231 // Status is EFI_NOT_READY. Restart the timer event and
232 // call Snp->Transmit again.
233 //
234 gBS->SetTimer (TimeoutEvent, TimerRelative, NET_SYSLOG_TX_TIMEOUT);
235 }
236
237 gBS->SetTimer (TimeoutEvent, TimerCancel, 0);
238
239 ON_EXIT:
240 gBS->CloseEvent (TimeoutEvent);
241 return Status;
242 }
243
244
245 /**
246 Compute checksum for a bulk of data. This code is copied from the
247 Netbuffer library.
248
249 @param Bulk Pointer to the data.
250 @param Len Length of the data, in bytes.
251
252 @retval UINT16 The computed checksum.
253
254 **/
255 UINT16
256 SyslogChecksum (
257 IN UINT8 *Bulk,
258 IN UINT32 Len
259 )
260 {
261 register UINT32 Sum;
262
263 Sum = 0;
264
265 while (Len > 1) {
266 Sum += *(UINT16 *) Bulk;
267 Bulk += 2;
268 Len -= 2;
269 }
270
271 //
272 // Add left-over byte, if any
273 //
274 if (Len > 0) {
275 Sum += *(UINT8 *) Bulk;
276 }
277
278 //
279 // Fold 32-bit sum to 16 bits
280 //
281 while (Sum >> 16) {
282 Sum = (Sum & 0xffff) + (Sum >> 16);
283 }
284
285 return (UINT16) ~Sum;
286 }
287
288
289 /**
290 Build a syslog packet, including the Ethernet/Ip/Udp headers
291 and user's message.
292
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
299 current log
300 @param Message The log message
301
302 @return The length of the syslog packet built.
303
304 **/
305 UINT32
306 SyslogBuildPacket (
307 UINT8 *Buf,
308 UINT32 BufLen,
309 UINT32 Level,
310 UINT8 *Module,
311 UINT8 *File,
312 UINT32 Line,
313 UINT8 *Message
314 )
315 {
316 ETHER_HEAD *Ether;
317 IP4_HEAD *Ip4;
318 EFI_UDP4_HEADER *Udp4;
319 EFI_TIME Time;
320 UINT32 Pri;
321 UINT32 Len;
322
323 //
324 // Fill in the Ethernet header. Leave alone the source MAC.
325 // SyslogSendPacket will fill in the address for us.
326 //
327 Ether = (ETHER_HEAD *) Buf;
328 CopyMem (Ether->DstMac, mSyslogDstMac, NET_ETHER_ADDR_LEN);
329 ZeroMem (Ether->SrcMac, NET_ETHER_ADDR_LEN);
330
331 Ether->EtherType = HTONS (0x0800); // IP protocol
332
333 Buf += sizeof (ETHER_HEAD);
334 BufLen -= sizeof (ETHER_HEAD);
335
336 //
337 // Fill in the IP header
338 //
339 Ip4 = (IP4_HEAD *) Buf;
340 Ip4->HeadLen = 5;
341 Ip4->Ver = 4;
342 Ip4->Tos = 0;
343 Ip4->TotalLen = 0;
344 Ip4->Id = (UINT16) mSyslogPacketSeq;
345 Ip4->Fragment = 0;
346 Ip4->Ttl = 16;
347 Ip4->Protocol = 0x11;
348 Ip4->Checksum = 0;
349 Ip4->Src = mSyslogSrcIp;
350 Ip4->Dst = mSyslogDstIp;
351
352 Buf += sizeof (IP4_HEAD);
353 BufLen -= sizeof (IP4_HEAD);
354
355 //
356 // Fill in the UDP header, Udp checksum is optional. Leave it zero.
357 //
358 Udp4 = (EFI_UDP4_HEADER*) Buf;
359 Udp4->SrcPort = HTONS (514);
360 Udp4->DstPort = HTONS (514);
361 Udp4->Length = 0;
362 Udp4->Checksum = 0;
363
364 Buf += sizeof (EFI_UDP4_HEADER);
365 BufLen -= sizeof (EFI_UDP4_HEADER);
366
367 //
368 // Build the syslog message body with <PRI> Timestamp machine module Message
369 //
370 Pri = ((NET_SYSLOG_FACILITY & 31) << 3) | (Level & 7);
371 gRT->GetTime (&Time, NULL);
372
373 //
374 // Use %a to format the ASCII strings, %s to format UNICODE strings
375 //
376 Len = 0;
377 Len += (UINT32) AsciiSPrint (
378 (CHAR8 *) Buf,
379 BufLen,
380 "<%d> %a %d %d:%d:%d ",
381 Pri,
382 MonthName [Time.Month-1],
383 Time.Day,
384 Time.Hour,
385 Time.Minute,
386 Time.Second
387 );
388 Len--;
389
390 Len += (UINT32) AsciiSPrint (
391 (CHAR8 *) (Buf + Len),
392 BufLen - Len,
393 "Tiano %a: %a (Line: %d File: %a)",
394 Module,
395 Message,
396 Line,
397 File
398 );
399 Len--;
400
401 //
402 // OK, patch the IP length/checksum and UDP length fields.
403 //
404 Len += sizeof (EFI_UDP4_HEADER);
405 Udp4->Length = HTONS ((UINT16) Len);
406
407 Len += sizeof (IP4_HEAD);
408 Ip4->TotalLen = HTONS ((UINT16) Len);
409 Ip4->Checksum = SyslogChecksum ((UINT8 *) Ip4, sizeof (IP4_HEAD));
410
411 return Len + sizeof (ETHER_HEAD);
412 }
413
414
415 /**
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))
421 if extracted to:
422 NetDebugOutput (
423 NETDEBUG_LEVEL_TRACE,
424 "Tcp",
425 __FILE__,
426 __LINE__,
427 NetDebugASPrint ("State transit to %a\n", Name)
428 )
429 This is exactly what we want.
430
431 @param Format The ASCII format string.
432 @param ... The variable length parameter whose format is
433 determined by the Format string.
434
435 @return The buffer containing the formatted message, or NULL if failed to
436 @return allocate memory.
437
438 **/
439 UINT8 *
440 NetDebugASPrint (
441 UINT8 *Format,
442 ...
443 )
444 {
445 VA_LIST Marker;
446 UINT8 *Buf;
447
448 Buf = AllocatePool (NET_DEBUG_MSG_LEN);
449
450 if (Buf == NULL) {
451 return NULL;
452 }
453
454 VA_START (Marker, Format);
455 AsciiVSPrint ((CHAR8 *) Buf, NET_DEBUG_MSG_LEN, (CHAR8 *) Format, Marker);
456 VA_END (Marker);
457
458 return Buf;
459 }
460
461
462 /**
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.
466
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.
472
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
476 out.
477
478 **/
479 EFI_STATUS
480 NetDebugOutput (
481 UINT32 Level,
482 UINT8 *Module,
483 UINT8 *File,
484 UINT32 Line,
485 UINT8 *Message
486 )
487 {
488 UINT8 *Packet;
489 UINT32 Len;
490 EFI_STATUS Status;
491
492 //
493 // Check whether the message should be sent out
494 //
495 if (Message == NULL) {
496 return EFI_OUT_OF_RESOURCES;
497 }
498
499 if (Level > mNetDebugLevelMax) {
500 Status = EFI_SUCCESS;
501 goto ON_EXIT;
502 }
503
504 //
505 // Allocate a maxium of 1024 bytes, the caller should ensure
506 // that the message plus the ethernet/ip/udp header is shorter
507 // than this
508 //
509 Packet = AllocatePool (NET_SYSLOG_PACKET_LEN);
510
511 if (Packet == NULL) {
512 Status = EFI_OUT_OF_RESOURCES;
513 goto ON_EXIT;
514 }
515
516 //
517 // Build the message: Ethernet header + IP header + Udp Header + user data
518 //
519 Len = SyslogBuildPacket (
520 Packet,
521 NET_SYSLOG_PACKET_LEN,
522 Level,
523 Module,
524 File,
525 Line,
526 Message
527 );
528
529 mSyslogPacketSeq++;
530 Status = SyslogSendPacket (Packet, Len);
531 gBS->FreePool (Packet);
532
533 ON_EXIT:
534 gBS->FreePool (Message);
535 return Status;
536 }