aboutsummaryrefslogtreecommitdiffstats
path: root/Lessons/Lesson_27/README.md
blob: 5248b24f1a2a5d429bc6ebb1ddc683cd4de771b3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
Let's try to get information from one of the system tables that we've discovered.

You can find the most recent version of the "System Management BIOS (SMBIOS) Reference Specification" on the DMTF site https://www.dmtf.org/dsp/DSP0134
For example now 3.4.0 is the most recent version https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.4.0.pdf

From the previous lesson we now, that SMBIOS table is declared under `gEfiSmbiosTableGuid`.

Let's create an app, that would print the address of this table.
```
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiLib.h>

#include <Library/BaseMemoryLib.h>

EFI_STATUS
EFIAPI
UefiMain (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  for (UINTN i=0; i<SystemTable->NumberOfTableEntries; i++) {
    if (CompareGuid(&(SystemTable->ConfigurationTable[i].VendorGuid), &gEfiSmbiosTableGuid)) {
      Print(L"SMBIOS table is placed at %p\n", SystemTable->ConfigurationTable[i].VendorTable);
    }
  }
  return EFI_SUCCESS;
}
```

In this code we've used `CompareGuid` function from the https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Library/BaseMemoryLib.h:
```
/**
  Compares two GUIDs.
  This function compares Guid1 to Guid2.  If the GUIDs are identical then TRUE is returned.
  If there are any bit differences in the two GUIDs, then FALSE is returned.
  If Guid1 is NULL, then ASSERT().
  If Guid2 is NULL, then ASSERT().
  @param  Guid1       A pointer to a 128 bit GUID.
  @param  Guid2       A pointer to a 128 bit GUID.
  @retval TRUE        Guid1 and Guid2 are identical.
  @retval FALSE       Guid1 and Guid2 are not identical.
**/
BOOLEAN
EFIAPI
CompareGuid (
  IN CONST GUID  *Guid1,
  IN CONST GUID  *Guid2
  );
```

Don't forget to add:
```
[Guids]
  gEfiSmbiosTableGuid
```
to app *inf file.

Let's build our app and execute it under OVMF:
```
FS0:\> SmbiosInfo.efi
SMBIOS table is placed at 7941000
```

# Get SMBIOS tables with `dmem`

UEFI shell has a command `dmem` for memory dump:
```
FS0:\> dmem -? -b
Displays the contents of system or device memory.

DMEM [-b] [address] [size] [-MMIO]

  -b      - Displays one screen at a time.
  -MMIO   - Forces address cycles to the PCI bus.
  address - Specifies a starting address in hexadecimal format.
  size    - Specifies the number of bytes to display in hexadecimal format.

NOTES:
  1. This command displays the contents of system memory or device memory.
  2. Enter address and size in hexadecimal format.
  3. If address is not specified, the contents of the UEFI System Table
     are displayed. Otherwise, memory starting at the specified address is displayed.
  4. Size specifies the number of bytes to display. If size is not specified,
     512 bytes are displayed.
  5. If MMIO is not specified, main system memory is displayed. Otherwise,
     device memory is displayed through the use of the
     EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL.

EXAMPLES:
  * To display the UEFI system table pointer entries:
    fs0:\> dmem

  * To display memory contents from 1af3088 with size of 16 bytes:
    Shell> dmem 1af3088 16

  * To display memory mapped IO contents from 1af3088 with a size of 16 bytes:
    Shell> dmem 1af3088 16 -MMIO
```

Let's use it to print 0x30 bytes from the 0x7941000 address that we count as a SMBIOS table pointer.
```
FS0:\> dmem 7941000 30
Memory Address 0000000007941000 30 Bytes
  07941000: 5F 53 4D 5F 26 1F 02 08-53 00 00 00 00 00 00 00  *_SM_&...S.......*
  07941010: 5F 44 4D 49 5F 0A 91 01-00 00 94 07 09 00 28 AF  *_DMI_.........(.*
  07941020: AF AF AF AF AF AF AF AF-AF AF AF AF AF AF AF AF  *................*
```

If you look at SMBIOS specification for SMBIOS Entry Point structure you'll see, that `_SM_` and `_DMI_` are predefined values in this structure.

![SMBIOS_entry_structure](SMBIOS_entry_structure.png?raw=true "SMBIOS_entry_structure")

You can find definition for the structure itself in edk2 under https://github.com/tianocore/edk2/blob/master/MdePkg/Include/IndustryStandard/SmBios.h
```
typedef struct {
  UINT8   AnchorString[4];
  UINT8   EntryPointStructureChecksum;
  UINT8   EntryPointLength;
  UINT8   MajorVersion;
  UINT8   MinorVersion;
  UINT16  MaxStructureSize;
  UINT8   EntryPointRevision;
  UINT8   FormattedArea[5];
  UINT8   IntermediateAnchorString[5];
  UINT8   IntermediateChecksum;
  UINT16  TableLength;
  UINT32  TableAddress;
  UINT16  NumberOfSmbiosStructures;
  UINT8   SmbiosBcdRevision;
} SMBIOS_TABLE_ENTRY_POINT;
```

If you calculate offsets for different fields you can parse memory dump:

![SMBIOS_entry_structure_dump](SMBIOS_entry_structure_dump.png?raw=true "SMBIOS_entry_structure_dump")

System has 9 SMBIOS structures placed from 0x07940000 to (0x07940000+0x191).

Now when we know address for SMBIOS structures, we can dump them as well.
```
FS0:\> dmem 07940000 191
Memory Address 0000000007940000 191 Bytes
  07940000: 01 1B 00 01 01 02 03 00-00 00 00 00 00 00 00 00  *................*
  07940010: 00 00 00 00 00 00 00 00-06 00 00 51 45 4D 55 00  *...........QEMU.*
  07940020: 53 74 61 6E 64 61 72 64-20 50 43 20 28 69 34 34  *Standard PC (i44*
  07940030: 30 46 58 20 2B 20 50 49-49 58 2C 20 31 39 39 36  *0FX + PIIX, 1996*
  07940040: 29 00 70 63 2D 69 34 34-30 66 78 2D 66 6F 63 61  *).pc-i440fx-foca*
  07940050: 6C 00 00 03 16 00 03 01-01 02 00 00 03 03 03 02  *l...............*
  07940060: 00 00 00 00 00 00 00 00-00 51 45 4D 55 00 70 63  *.........QEMU.pc*
  07940070: 2D 69 34 34 30 66 78 2D-66 6F 63 61 6C 00 00 04  *-i440fx-focal...*
  07940080: 2A 00 04 01 03 01 02 63-06 00 00 FD FB 8B 07 03  **......c........*
  07940090: 00 00 00 D0 07 D0 07 41-01 FF FF FF FF FF FF 00  *.......A........*
  079400A0: 00 00 01 01 01 02 00 01-00 43 50 55 20 30 00 51  *.........CPU 0.Q*
  079400B0: 45 4D 55 00 70 63 2D 69-34 34 30 66 78 2D 66 6F  *EMU.pc-i440fx-fo*
  079400C0: 63 61 6C 00 00 10 17 00-10 01 03 06 00 00 02 00  *cal.............*
  079400D0: FE FF 01 00 00 00 00 00-00 00 00 00 00 00 11 28  *...............(*
  079400E0: 00 11 00 10 FE FF FF FF-FF FF 80 00 09 00 01 00  *................*
  079400F0: 07 02 00 00 00 02 00 00-00 00 00 00 00 00 00 00  *................*
  07940100: 00 00 00 00 00 00 44 49-4D 4D 20 30 00 51 45 4D  *......DIMM 0.QEM*
  07940110: 55 00 00 13 1F 00 13 00-00 00 00 FF FF 01 00 00  *U...............*
  07940120: 10 01 00 00 00 00 00 00-00 00 00 00 00 00 00 00  *................*
  07940130: 00 00 00 00 20 0B 00 20-00 00 00 00 00 00 00 00  *.... .. ........*
  07940140: 00 00 1A 00 00 01 02 00-E8 03 00 08 00 00 00 00  *................*
  07940150: 00 00 00 00 1C 00 00 FF-FF 00 00 45 46 49 20 44  *...........EFI D*
  07940160: 65 76 65 6C 6F 70 6D 65-6E 74 20 4B 69 74 20 49  *evelopment Kit I*
  07940170: 49 20 2F 20 4F 56 4D 46-00 30 2E 30 2E 30 00 30  *I / OVMF.0.0.0.0*
  07940180: 32 2F 30 36 2F 32 30 31-35 00 00 7F 04 FF FE 00  *2/06/2015.......*
  07940190: 00                                               *.*
```

# Use EFI_SMBIOS_PROTOCOL to parse SMBIOS data

We can use direct pointer arithmetics to parse SMBIOS tables, but that would be very tedious.

Luckily UEFI PI specification defines a `EFI_SMBIOS_PROTOCOL`, that we can use to get SMBIOS data.

```
EFI_SMBIOS_PROTOCOL

Summary:
Allows consumers to log SMBIOS data records, and enables the producer to create the SMBIOS tables for a platform.

Protocol Interface Structure:
typedef struct _EFI_SMBIOS_PROTOCOL {
 EFI_SMBIOS_ADD Add;
 EFI_SMBIOS_UPDATE_STRINGUpdateString;
 EFI_SMBIOS_REMOVE Remove;
 EFI_SMBIOS_GET_NEXT GetNext;
 UINT8 MajorVersion;
 UINT8 MinorVersion;
} EFI_SMBIOS_PROTOCOL;

Member Description:
Add		Add an SMBIOS record including the formatted area and the optional strings
		that follow the formatted area.
UpdateString 	Update a string in the SMBIOS record.
Remove		Remove an SMBIOS record.
GetNext		Discover all SMBIOS records.
MajorVersion	The major revision of the SMBIOS specification supported.
MinorVersion	The minor revision of the SMBIOS specification supported.

Description:
This protocol provides an interface to add, remove or discover SMBIOS records. The driver which
produces this protocol is responsible for creating the SMBIOS data tables and installing the pointer
to the tables in the EFI System Configuration Table.
```

Right now we are interested in SMBIOS table parsing, so we need to utilize `GetNext` function:
```
EFI_SMBIOS_PROTOCOL.GetNext()

Summary:
Allow the caller to discover all or some of the SMBIOS records.
Prototype
typedef
EFI_STATUS
(EFIAPI *EFI_SMBIOS_GET_NEXT) (
 IN CONST EFI_SMBIOS_PROTOCOL *This,
 IN OUT EFI_SMBIOS_HANDLE *SmbiosHandle,
 IN EFI_SMBIOS_TYPE *Type, OPTIONAL
 OUT EFI_SMBIOS_TABLE_HEADER **Record,
 OUT EFI_HANDLE *ProducerHandle OPTIONAL
 );

Parameters:
This		The EFI_SMBIOS_PROTOCOL instance.
SmbiosHandle	On entry, points to the previous handle of the SMBIOS record. On exit, points to the
		next SMBIOS record handle. If it is FFFEh on entry, then the first SMBIOS record
		handle will be returned. If it returns FFFEh on exit, then there are no more SMBIOS
		records.
Type		On entry, it points to the type of the next SMBIOS record to return. If NULL, it
		indicates that the next record of any type will be returned. Type is not modified by
		the this function.
Record		On exit, points to a pointer to the the SMBIOS Record consisting of the formatted area
		followed by the unformatted area. The unformatted area optionally contains text
		strings.
ProducerHandle	On exit, points to the ProducerHandle registered by Add(). If no
		ProducerHandle was passed into Add() NULL is returned. If a NULL pointer is
		passed in no data will be returned

Description
This function allows all of the SMBIOS records to be discovered. It's possible to find
only the SMBIOS records that match the optional Type argument.

Status Codes Returned:
EFI_SUCCESS 	.SMBIOS record information was successfully returned in Record.
		SmbiosHandle is the handle of the current SMBIOS record
EFI_NOT_FOUND 	The SMBIOS record with SmbiosHandle was the last available record.
```

First let's get this protocol in our app.
```
EFI_SMBIOS_PROTOCOL* SmbiosProtocol;
EFI_STATUS Status = gBS->LocateProtocol (
                &gEfiSmbiosProtocolGuid,
                NULL,
                (VOID**)&SmbiosProtocol
                );
if (EFI_ERROR (Status)) {
  return Status;
}
```
To use it we need to add include to our app https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Protocol/Smbios.h:
```
#include <Protocol/Smbios.h>
```
And add protocol guid to our *.inf file:
```
[Protocols]
  gEfiSmbiosProtocolGuid
```

Now let's try to get SMBIOS tables. We would be using `SMBIOS_HANDLE_PI_RESERVED` as `EFI_SMBIOS_HANDLE` in protocol calls. You can find explanation in https://github.com/tianocore/edk2/blob/master/MdePkg/Include/IndustryStandard/SmBios.h:
```
///
/// Reference SMBIOS 2.7, chapter 6.1.2.
/// The UEFI Platform Initialization Specification reserves handle number FFFEh for its
/// EFI_SMBIOS_PROTOCOL.Add() function to mean "assign an unused handle number automatically."
/// This number is not used for any other purpose by the SMBIOS specification.
///
#define SMBIOS_HANDLE_PI_RESERVED 0xFFFE
```

Also before we start writing code look at the definition of `EFI_SMBIOS_TABLE_HEADER` structure, that we would receive on `GetNext` calls.
https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Protocol/Smbios.h
```
typedef SMBIOS_STRUCTURE    EFI_SMBIOS_TABLE_HEADER;
```
https://github.com/tianocore/edk2/blob/master/MdePkg/Include/IndustryStandard/SmBios.h
```
///
/// The Smbios structure header.
///
typedef struct {
  SMBIOS_TYPE    Type;
  UINT8          Length;
  SMBIOS_HANDLE  Handle;
} SMBIOS_STRUCTURE;
```

Now we are ready to write some code. Write a code to print all types of Smbios tables that are present in the system:
```
EFI_SMBIOS_HANDLE SmbiosHandle = SMBIOS_HANDLE_PI_RESERVED;
EFI_SMBIOS_TABLE_HEADER* Record;
Status = SmbiosProtocol->GetNext(SmbiosProtocol,
                                 &SmbiosHandle,
                                 NULL,
                                 &Record,
                                 NULL);
while (!EFI_ERROR(Status)) {
  Print (L"SMBIOS Type %d \n", Record->Type);
  Status = SmbiosProtocol->GetNext(SmbiosProtocol,
                                   &SmbiosHandle,
                                   NULL,
                                   &Record,
                                   NULL);
}
```

If you build our app and test it under OVMF you would get:
```
SMBIOS table is placed at 7941000
SMBIOS Type 1
SMBIOS Type 3
SMBIOS Type 4
SMBIOS Type 16
SMBIOS Type 17
SMBIOS Type 19
SMBIOS Type 32
SMBIOS Type 0
```

Ok, now let's write code that can parse information in these tables.

Let's start with Type 0 table. edk2 has a structure description at https://github.com/tianocore/edk2/blob/master/MdePkg/Include/IndustryStandard/SmBios.h:
```
///
/// BIOS Information (Type 0).
///
typedef struct {
  SMBIOS_STRUCTURE          Hdr;
  SMBIOS_TABLE_STRING       Vendor;
  SMBIOS_TABLE_STRING       BiosVersion;
  UINT16                    BiosSegment;
  SMBIOS_TABLE_STRING       BiosReleaseDate;
  UINT8                     BiosSize;
  MISC_BIOS_CHARACTERISTICS BiosCharacteristics;
  UINT8                     BIOSCharacteristicsExtensionBytes[2];
  UINT8                     SystemBiosMajorRelease;
  UINT8                     SystemBiosMinorRelease;
  UINT8                     EmbeddedControllerFirmwareMajorRelease;
  UINT8                     EmbeddedControllerFirmwareMinorRelease;
  //
  // Add for smbios 3.1.0
  //
  EXTENDED_BIOS_ROM_SIZE    ExtendedBiosSize;
} SMBIOS_TABLE_TYPE0;
```

In this structure `SMBIOS_STRUCTURE Hdr` is a mandatory field that is the same as `EFI_SMBIOS_TABLE_HEADER` that we receive from our protocol function call.

Also `SMBIOS_TABLE_STRING` is just an UINT8 value that defines a string number in an ASCII string array that is placed directly after the structure.

```
///
/// Text strings associated with a given SMBIOS structure are returned in the dmiStrucBuffer, appended directly after
/// the formatted portion of the structure. This method of returning string information eliminates the need for
/// application software to deal with pointers embedded in the SMBIOS structure. Each string is terminated with a null
/// (00h) BYTE and the set of strings is terminated with an additional null (00h) BYTE. When the formatted portion of
/// a SMBIOS structure references a string, it does so by specifying a non-zero string number within the structure's
/// string-set. For example, if a string field contains 02h, it references the second string following the formatted portion
/// of the SMBIOS structure. If a string field references no string, a null (0) is placed in that string field. If the
/// formatted portion of the structure contains string-reference fields and all the string fields are set to 0 (no string
/// references), the formatted section of the structure is followed by two null (00h) BYTES.
///
typedef UINT8 SMBIOS_TABLE_STRING;
```

So let's write a simple function that returns actual ASCII string from a `EFI_SMBIOS_TABLE_HEADER*` and a string number:
```
CHAR8* GetRecordString(EFI_SMBIOS_TABLE_HEADER* Record, UINTN number)
{
  if (!number)
    return "";

  CHAR8* String = (CHAR8*)Record + Record->Length;
  UINTN i=1;
  while (i < number) {
    String = String + AsciiStrSize(String);
    i++;
  }
  return String;
}
```
Here we've used `AsciiStrSize` function that is defined in https://github.com/tianocore/edk2/blob/master/MdePkg/Library/BaseLib/String.c file.
```
/**
  Returns the size of a Null-terminated ASCII string in bytes, including the
  Null terminator.
  This function returns the size, in bytes, of the Null-terminated ASCII string
  specified by String.
  If String is NULL, then ASSERT().
  If PcdMaximumAsciiStringLength is not zero and String contains more than
  PcdMaximumAsciiStringLength ASCII characters, not including the Null-terminator,
  then ASSERT().
  @param  String  A pointer to a Null-terminated ASCII string.
  @return The size of String.
**/
UINTN
EFIAPI
AsciiStrSize (
  IN      CONST CHAR8               *String
  )
```

Now we have everything to write actual parsing code in our `while` loop:

```
while (!EFI_ERROR(Status)) {
  Print (L"SMBIOS Type %d \n", Record->Type);
  switch (Record->Type) {
    case EFI_SMBIOS_TYPE_BIOS_INFORMATION: {
      SMBIOS_TABLE_TYPE0* Type0Record = (SMBIOS_TABLE_TYPE0*) Record;
      Print(L"\tVendor=%a\n", GetRecordString(Record, Type0Record->Vendor));
      Print(L"\tBiosVersion=%a\n", GetRecordString(Record, Type0Record->BiosVersion));
      Print(L"\tBiosReleaseDate=%a\n", GetRecordString(Record, Type0Record->BiosReleaseDate));
      Print(L"\tBiosSegment=0x%x\n", Type0Record->BiosSegment);
      Print(L"\tSystemBiosMajorRelease=0x%x\n", Type0Record->SystemBiosMajorRelease);
      Print(L"\tSystemBiosMinorRelease=0x%x\n", Type0Record->SystemBiosMinorRelease);
      break;
    }
    default:
      Print(L"\tTODO: Parsing for this table is not ready yet\n");
      break;
  }
  Status = SmbiosProtocol->GetNext(SmbiosProtocol,
                                   &SmbiosHandle,
                                   NULL,
                                   &Record,
                                   NULL);
}
```
To print ASCII strings here we've used `%a` format code (https://github.com/tianocore/edk/blob/master/Foundation/Library/Pei/PeiLib/Print/Print.c).


If you build and execute our app under OVMF you would get:
```
FS0:\> SmbiosInfo.efi
SMBIOS table is placed at 7941000

SMBIOS Type 1
        TODO: Parsing for this table is not ready yet
SMBIOS Type 3
        TODO: Parsing for this table is not ready yet
SMBIOS Type 4
        TODO: Parsing for this table is not ready yet
SMBIOS Type 16
        TODO: Parsing for this table is not ready yet
SMBIOS Type 17
        TODO: Parsing for this table is not ready yet
SMBIOS Type 19
        TODO: Parsing for this table is not ready yet
SMBIOS Type 32
        TODO: Parsing for this table is not ready yet
SMBIOS Type 0
        Vendor=EFI Development Kit II / OVMF
        BiosVersion=0.0.0
        BiosReleaseDate=02/06/2015
        BiosSegment=0xE800
        SystemBiosMajorRelease=0x0
        SystemBiosMinorRelease=0x0
```

If you look closely at `dmem` dump from earlier, you'll see that these are the exact strings that were actually present in memory.

We can easily to add code to parse other SMBIOS tables.

For example here is some parsing code for table type 1 structure:
```
case EFI_SMBIOS_TYPE_SYSTEM_INFORMATION: {
  SMBIOS_TABLE_TYPE1* Type1Record = (SMBIOS_TABLE_TYPE1*) Record;
  Print(L"\tManufacturer=%a\n", GetRecordString(Record, Type1Record->Manufacturer));
  Print(L"\tProductName=%a\n", GetRecordString(Record, Type1Record->ProductName));
  Print(L"\tVersion=%a\n", GetRecordString(Record, Type1Record->Version));
  Print(L"\tSerialNumber=%a\n", GetRecordString(Record, Type1Record->SerialNumber));
  Print(L"\tUUID=%g\n", Type1Record->Uuid);
  Print(L"\tWakeUpType=%d\n", Type1Record->WakeUpType);
  Print(L"\tSKUNumber=%a\n", GetRecordString(Record, Type1Record->SKUNumber));
  Print(L"\tFamily=%a\n", GetRecordString(Record, Type1Record->Family));
  break;
}
```
Structure itself is:
```
typedef struct {
  SMBIOS_STRUCTURE        Hdr;
  SMBIOS_TABLE_STRING     Manufacturer;
  SMBIOS_TABLE_STRING     ProductName;
  SMBIOS_TABLE_STRING     Version;
  SMBIOS_TABLE_STRING     SerialNumber;
  GUID                    Uuid;
  UINT8                   WakeUpType;           ///< The enumeration value from MISC_SYSTEM_WAKEUP_TYPE.
  SMBIOS_TABLE_STRING     SKUNumber;
  SMBIOS_TABLE_STRING     Family;
} SMBIOS_TABLE_TYPE1;
```

Build and execute:
```
FS0:\> SmbiosInfo.efi
SMBIOS table is placed at 7941000

SMBIOS Type 1
        Manufacturer=QEMU
        ProductName=Standard PC (i440FX + PIIX, 1996)
        Version=pc-i440fx-focal
        SerialNumber=
        UUID=00000000-0000-0000-0000-000000000000
        WakeUpType=6
        SKUNumber=
        Family=
SMBIOS Type 3
        TODO: Parsing for this table is not ready yet
SMBIOS Type 4
        TODO: Parsing for this table is not ready yet
SMBIOS Type 16
        TODO: Parsing for this table is not ready yet
SMBIOS Type 17
        TODO: Parsing for this table is not ready yet
SMBIOS Type 19
        TODO: Parsing for this table is not ready yet
SMBIOS Type 32
        TODO: Parsing for this table is not ready yet
SMBIOS Type 0
        Vendor=EFI Development Kit II / OVMF
        BiosVersion=0.0.0
        BiosReleaseDate=02/06/2015
        BiosSegment=0xE800
        SystemBiosMajorRelease=0x0
        SystemBiosMinorRelease=0x0
```

As you remember the same information that is present in these SMBIOS tables is present in the main BIOS menu:

![BIOS_menu](BIOS_menu.png?raw=true "BIOS_menu")

And in case you wonder where OVMF defines all these information for its SMBIOS structures, checkout https://github.com/tianocore/edk2/blob/master/OvmfPkg/SmbiosPlatformDxe/SmbiosPlatformDxe.c and implementation of a `EFI_SMBIOS_PROTOCOL` is placed here https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Universal/SmbiosDxe/SmbiosDxe.c

# `smbiosview` command

We can use `EFI_SMBIOS_PROTOCOL` to parse SMBIOS table information, but actually if you just want to see SMBIOS information there is a better option.

UEFI shell has a `smbiosview` command that does exactly what we need.

You can checkout sources for this command here: https://github.com/tianocore/edk2/tree/master/ShellPkg/Library/UefiShellDebug1CommandsLib/SmbiosView

First checkout help for this command:
```
FS0:\> smbiosview -?
Displays SMBIOS information.

SMBIOSVIEW [-t SmbiosType]|[-h SmbiosHandle]|[-s]|[-a]

  -t            - Displays all structures of SmbiosType.
  -h            - Displays structure of SmbiosHandle.
  -s            - Displays a statistics table.
  -a            - Displays all information.
  SmbiosType    - Specifies a SMBIOS structure type.
  SmbiosHandle  - Specifies a SMBIOS structure unique 16-bit handle.

NOTES:
  1. The SmbiosType parameter supports the following types:
       0  - BIOS Information
       1  - System Information
       2  - Baseboard Information
       3  - System Enclosure
       4  - Processor Information
       5  - Memory Controller Information
       6  - Memory Module Information
       7  - Cache Information
       8  - Port Connector Information
       9  - System Slots
       10 - On Board Devices Information
       11 - OEM Strings
       12 - System Configuration Options
       13 - BIOS Language Information
       14 - Group Associations
       15 - System Event Log
       16 - Physical Memory Array
       17 - Memory Device
       18 - 32-bit Memory Error Information
       19 - Memory Array Mapped Address
       20 - Memory Device Mapped Address
       21 - Built-in Pointing Device
       22 - Portable Battery
       23 - System Reset
       24 - Hardware Security
       25 - System Power Controls
       26 - Voltage Probe
       27 - Cooling Device
       28 - Temperature Probe
       29 - Electrical Current Probe
       30 - Out-Of-Band Remote Access
       31 - Boot Integrity Services (BIS) Entry Point
       32 - System Boot Information
       33 - 64-Bit Memory Error Information
       34 - Management Device
       35 - Management Device Component
       36 - Management Device Threshold Data
       37 - Memory Channel
       38 - IPMI Device Information
       39 - System Power Supply
       40 - Additional Information
       41 - Onboard Devices Extended Information
       42 - Management Controller Host Interface
       43 - TPM Device
       44 - Processor Additional Information
  2. Enter the SmbiosHandle parameter in hexadecimal format.
     Do not use the '0x' prefix format for hexadecimal values.
  3. Internal commands:
       :q --------  quit smbiosview
       :0 --------  Change smbiosview display NONE info
       :1 --------  Change smbiosview display OUTLINE info
       :2 --------  Change smbiosview display NORMAL info
       :3 --------  Change smbiosview display DETAIL info
       /? --------  Show help
```

Try to dump one of the structures that we've tried to parse manually:
```
FS0:\> smbiosview -t 0
SMBIOS Entry Point Structure:
Anchor String:        _SM_
EPS Checksum:         0x26
Entry Point Len:      31
Version:              2.8
Number of Structures: 9
Max Struct size:      83
Table Address:        0x7940000
Table Length:         401
Entry Point revision: 0x0
SMBIOS BCD Revision:  0x28
Inter Anchor:         _DMI_
Inter Checksum:       0xA
Formatted Area:
  00000000: 00 00 00 00 00                                   *.....*

=========================================================
Query Structure, conditions are:
QueryType   = 0
QueryHandle = Random
ShowType    = SHOW_DETAIL


=========================================================
Type=0, Handle=0x0
Dump Structure as:
Index=7,Length=0x4A,Addr=0x7940141
00000000: 00 1A 00 00 01 02 00 E8-03 00 08 00 00 00 00 00  *................*
FS0:\> 0: 00 00 00 1C 00 00 FF FF-00 00 45 46 49 20 44 65  *..........EFI De*
00000020: 76 65 6C 6F 70 6D 65 6E-74 20 4B 69 74 20 49 49  *velopment Kit II*
00000030: 20 2F 20 4F 56 4D 46 00-30 2E 30 2E 30 00 30 32  * / OVMF.0.0.0.02*
00000040: 2F 30 36 2F 32 30 31 35-00 00                    */06/2015..*
Structure Type: BIOS Information
Format part Len : 26
Structure Handle: 0
Vendor: EFI Development Kit II / OVMF
BiosVersion: 0.0.0
BiosSegment: 0xE800
BiosReleaseDate: 02/06/2015
BiosSize:  64 KB
BIOS Characteristics:
BIOS Characteristics Not Supported
 Bits 32:47 are reserved for BIOS Vendor
 Bits 48:64 are reserved for System Vendor
BIOS Characteristics Extension Byte1:
BIOS Characteristics Extension Byte2:
Enable Targeted Content Distribution
UEFI Specification is supported
The SMBIOS table describes a virtual machine
 Bits 5:7 are reserved for future assignment
SystemBiosMajorRelease: 0
SystemBiosMinorRelease: 0
EmbeddedControllerFirmwareMajorRelease: 255
EmbeddedControllerFirmwareMinorRelease: 255
```
As you can see it is all the same info that we've received using `EFI_SMBIOS_PROTOCOL`.

You can use this command to see what is inside all of the SMBIOS structures using:
```
FS0:\> smbiosview -b
...
```

The output is too big to paste here, so check it out yourself!