System Management BIOS: Difference between revisions

From OSDev.wiki
Jump to navigation Jump to search
[unchecked revision][unchecked revision]
Content added Content deleted
No edit summary
m (Bot: Replace deprecated source tag with syntaxhighlight)
 
(14 intermediate revisions by 10 users not shown)
Line 1: Line 1:
System Management [[BIOS]] (SMBIOS) is a standard developed by [http://dmtf.org DMTF].
SMBIOS (System Management [[BIOS]]) is a standard developed by [https://www.dmtf.org DMTF] whose purpose is to deliver information about the hardware, by relying on the system firmware. Once booted, SMBIOS will let the OS access a table that contains a series of entries with various hardware information, separated in different structures of variable length.
The purpose of this standard is to allow the operating system to retrieve information about the PC.


It was first designed for Intel, and released in 1995. Nowadays it supports most desktop architectures, and its last specification was published the 21 of July, 2023.
On booting the SMBIOS will put a table somewhere in memory. By parsing this table it is possible to access information about the computer and its capabilities.


== Locating the SMBIOS Entry Point Table ==
== Entry Point Structure ==


SMBIOS provides a searchable (or querryable) structure called Entry Point Structure (or EPS) that contains a pointer to the SMBIOS Structure Table and some additional information, like its length, version, or number of structures.
The SMBIOS Entry Point Table is located somewhere between the addresses 0xF0000 and 0xFFFFF. To find the specific location of the start of the table it is necessary to search that region of memory for the string "_SM_".


=== Format ===
One example of how this can be done is demonstrated in the below code.
char *mem = (char *) 0xF0000;
while ((unsigned int) mem < 0x100000) {
if (mem[0] == '_' && mem[1] == 'S' && mem[2] == 'M' && mem[3] == '_') {
break;
}
mem++;
}


{| align="center" cellspacing="2"
Now mem contains the address of the Entry Point Table.
| width="50%" valign="top" |
Some old systems may not have the SMBIOS. So...
if ((unsigned int) mem == 0x100000) {
panic("No SMBIOS found!");
}


{| {{wikitable}}
== Parsing the Entry Point Table ==
|+ 32-bit Entry Point Structure Format
! Offset
! Name
! Size
|-
| 0x00 || Anchor String || 4 BYTEs
|-
| 0x04 || Entry Point Checksum || BYTE
|-
| 0x05 || Entry Point Lenght || BYTE
|-
| 0x06 || SMBIOS Major Version || BYTE
|-
| 0x07 || SMBIOS Minor Version || BYTE
|-
| 0x08 || SMBIOS Structure Maximum Size || WORD
|-
| 0x0A || Entry Point Revision || BYTE
|-
| 0x0B || Formatted Area || 5 BYTEs
|-
| 0x10 || Intermediate Anchor String || 5 BYTEs
|-
| 0x15 || Intermediate Checksum || BYTE
|-
| 0x16 || Structure Table Length || WORD
|-
| 0x18 || '''Structure Table Address''' || DWORD
|-
| 0x1C || Number of Structures || WORD
|-
| 0x1E || BCD Revision || BYTE
|}

| width="10%" valign="top" |

| width="50%" valign="top" |

{| {{wikitable}}
|+ 64-bit Entry Point Structure Format
! Offset
! Name
! Size
|-
| 0x00 || Anchor String || 5 BYTEs
|-
| 0x05 || Entry Point Checksum || BYTE
|-
| 0x06 || Entry Point Lenght || BYTE
|-
| 0x07 || SMBIOS Major Version || BYTE
|-
| 0x08 || SMBIOS Minor Version || BYTE
|-
| 0x09 || SMBIOS Docrev || BYTE
|-
| 0x0A || Entry Point Revision || BYTE
|-
| 0x0B || Reserved || BYTE
|-
| 0x0C || Structure Table Maximum Size || DWORD
|-
| 0x10 || '''Structure Table Adress''' || QWORD
|}

|}

=== Locating the entry point structure ===

==== Non-UEFI systems ====

Under systems without UEFI, the Entry Point Structure is located somewhere in physical memory from address 0x000F0000 to 0x00FFFFF, with a 16 byte alignment.

* In 32-bit architectures, the SMBIOS EPS first contains (with offset 0) a string with the value "_SM_". This is what will help us search the location of the EPS.

* In 64-bit architectures, as in 32-bit, it first contains a string, but with the value "_SM3_" (The 3 stands for SMBIOS 3, which is the version in 64-bit machines).

This example C code searches the entry point in a 64-bit architecture. All it does is iterate for every 16-byte aligned memory addresses (from 0x000F0000 to 0x000FFFFF) and comparing 4 bytes from that address with the string "_SM3_". To acomplish that, it is needed to verify the checksum value (add all bytes and see if the lowest 8 bits of the result are zero):

<syntaxhighlight lang="c">

/* Start address */
char *eps = 0x000F0000;

int length, i;
uint8_t checksum = 0;

while (eps <= (char*) 0x000FFFFF) {

/* Check Anchor String (64-bit version) */
if (!memcmp(eps, "_SM3_", 5)) {

length = eps[5];
checksum = 0;

/* Add all bytes */
for (i = 0; i < length; i++)
checksum += eps[i];

if (checksum == 0)
/* Done! */
break;
}

/* Next 16-byte-aligned address */
eps += 16;
}

</syntaxhighlight>

Now, ''eps'' contains the address of the Entry Point Structure. Some systems may not have the SMBIOS, so an error check may be a good idea:

<syntaxhighlight lang="c">
if ((unsigned int) eps == 0x00100000) {
/* Error, SMBIOS could not be located */
return -1;
}
</syntaxhighlight>

==== UEFI systems ====

On UEFI systems, the search-for-a-string method is not used to obtain the EPS. Instead, it is located by looking in the EFI Configuration Table for the SMBIOS Version 3 GUID (SMBIOS3_TABLE_GUID), which will contain a pointer to the structure.

This C code shows how that could get implemented using [[GNU-EFI]]:
<syntaxhighlight lang="c">

/* Will contain the address of the Entry Point Structure */
void *SMBIOS_Pointer = NULL;
UINTN status = LibGetSystemConfigurationTable(&SMBIOS3TableGuid, (void **)(&SMBIOS_Pointer));

/* Check all posible errors (maybe is not needed?) */
if (status != EFI_SUCCESS || SMBIOS_Pointer == NULL ||
CompareMem(SMBIOS_Pointer, "_SM3_", 5) ) {

/* Error, SMBIOS could not be located */
return -1;

}

</syntaxhighlight>

== Structure Table ==

Once the EPS is located in memory, information about the SMBIOS table can already be obtained. The most important one is the '''Structure Table Adress''', which is the address of the table that contains all the SMBIOS structures. The structures are located directly adjacent to each other in memory, with a new structure beginning as soon as another one ends. Each structure is composed of a header, a structure specific table, and a string table of variable length.

The first SMBIOS header is located at the '''Structure Table Adress'''. The value of ''type'' indicates what element the structure contains information about. ''length'' indicates the size of '''header + data table'''. The strings are not included in the length.


<syntaxhighlight lang="c">
The entry point table has the following structure
struct SMBIOSEntryPoint {
struct SMBIOSHeader {
uint8_t type;
char EntryPointString[4]; //This is _SM_
uint8_t length;
uchar Checksum; //This value summed with all the values of the table, should be 0 (overflow)
uint16_t handle;
uchar Length; //Length of the Entry Point Table. Since version 2.1 of SMBIOS, this is 0x1F
};
uchar MajorVersion; //Major Version of SMBIOS
</syntaxhighlight>
uchar MinorVersion; //Minor Version of SMBIOS
ushort MaxStructureSize; //Maximum size of a SMBIOS Structure (we will se later)
uchar EntryPointRevision; //...
char FormattedArea[5]; //...
char EntryPointString2[5]; //This is _DMI_
uchar Checksum2; //Checksum for values from EntryPointString2 to the end of table
ushort TableLength; //Length of the Table containing all the structures
uint TableAddress; //Address of the Table
ushort NumberOfStructures; //Number of structures in the table
uchar BCDRevision; //Unused
};


Immediately after the end of the header, is the data table. At the end of the data table (header address + length), the strings section starts. Each string is NULL terminated and is limited to 64 characters. Strings are referenced within tables by using an index into the string table (index 0 means that the string is effectively a NULL pointer and should be ignored). The first string begins immediately after the data, and the second string begins immediately after that, and so on. The string section itself is terminated by two consecutive zero bytes.
TableAddress contains the address of the table that contains all the structures with information about the PC.
All of the structures are located from [TableAddress] to [TableAddress + TableLength].
The structures are located directly adjacent to each other in memory, with a new structure beginning as soon as another one ends.
Each structure is composed of a header, a structure specific table, and a string table.


So, the end (and therefore the length) of the SMBIOS structure can be calculated by finding the two [[Null Character]]s in the string section. Your code might look like:
The format of the header is as follows.
<syntaxhighlight lang="c">
struct SMBIOSHeader {
size_t smbios_struct_len(struct SMBIOSHeader *hd)
uchar Type;
{
uchar Length;
size_t i;
ushort Handle;
const char *strtab = (char *)hd + hd->len;
};
// Scan until we find a double zero byte
for (i = 1; strtab[i - 1] != '\0' || strtab[i] != '\0'; i++)
;
return hd->len + i + 1;
}
</syntaxhighlight>


The final table is denoted by a ''type'' field of value 127.
Located at TableAddress is a SMBIOS header.
The value of Type indicates what element the structure contains information about. (see Header Types section)
Length indicates the size of header + data table. The strings are not included in the length.


As an example, the BIOS Struct (Type 0) might look like this:
Immediately after the end of the header is the data. At the end of the data table (Address + Length), the strings section starts. Each string is NULL terminated and is limited to 64 characters.


<syntaxhighlight lang="asm">
eg: the BIOS Struct (Type 0) is like this:
db 0 ; Indicates BIOS Structure Type |
db 0 ; Indicates BIOS Structure Type |
db 13h ; Length of information in bytes | HEADER
db 13h ; Length of information in bytes | HEADER
Line 79: Line 205:


db 0 ; End of structure
db 0 ; End of structure
</syntaxhighlight>


== Header Types ==
At the end of the BIOS table, another table will start.
The last structure has type 127.

= Header Types =
{| class="wikitable"
{| class="wikitable"
|-
|-
Line 117: Line 241:
|-
|-
| 19
| 19
| Memory Mapped Device Mapped Address's
| Memory Array Mapped Address
|-
| 20
| Memory Device Mapped Address (optional as of SMBIOS 2.5)
|-
|-
| 32
| 32
Line 126: Line 253:




== External links ==
==See Also==
* [http://www.dmtf.org/standards/smbios/ SMBIOS Specification]


===Articles===
= Threads =
* [[ACPI]]
* [[DMI]]

===Forum Threads===
* http://www.osdev.org/phpBB2/viewtopic.php?t=16687 Info (saved here through lack of info)
* http://www.osdev.org/phpBB2/viewtopic.php?t=16687 Info (saved here through lack of info)

===External Links===
* [http://www.dmtf.org/standards/smbios/ SMBIOS Specification]


[[Category:X86]]
[[Category:X86]]
[[Category:Standards]]
[[Category:Hardware Detection]]

Latest revision as of 05:43, 9 June 2024

SMBIOS (System Management BIOS) is a standard developed by DMTF whose purpose is to deliver information about the hardware, by relying on the system firmware. Once booted, SMBIOS will let the OS access a table that contains a series of entries with various hardware information, separated in different structures of variable length.

It was first designed for Intel, and released in 1995. Nowadays it supports most desktop architectures, and its last specification was published the 21 of July, 2023.

Entry Point Structure

SMBIOS provides a searchable (or querryable) structure called Entry Point Structure (or EPS) that contains a pointer to the SMBIOS Structure Table and some additional information, like its length, version, or number of structures.

Format

32-bit Entry Point Structure Format
Offset Name Size
0x00 Anchor String 4 BYTEs
0x04 Entry Point Checksum BYTE
0x05 Entry Point Lenght BYTE
0x06 SMBIOS Major Version BYTE
0x07 SMBIOS Minor Version BYTE
0x08 SMBIOS Structure Maximum Size WORD
0x0A Entry Point Revision BYTE
0x0B Formatted Area 5 BYTEs
0x10 Intermediate Anchor String 5 BYTEs
0x15 Intermediate Checksum BYTE
0x16 Structure Table Length WORD
0x18 Structure Table Address DWORD
0x1C Number of Structures WORD
0x1E BCD Revision BYTE
64-bit Entry Point Structure Format
Offset Name Size
0x00 Anchor String 5 BYTEs
0x05 Entry Point Checksum BYTE
0x06 Entry Point Lenght BYTE
0x07 SMBIOS Major Version BYTE
0x08 SMBIOS Minor Version BYTE
0x09 SMBIOS Docrev BYTE
0x0A Entry Point Revision BYTE
0x0B Reserved BYTE
0x0C Structure Table Maximum Size DWORD
0x10 Structure Table Adress QWORD

Locating the entry point structure

Non-UEFI systems

Under systems without UEFI, the Entry Point Structure is located somewhere in physical memory from address 0x000F0000 to 0x00FFFFF, with a 16 byte alignment.

  • In 32-bit architectures, the SMBIOS EPS first contains (with offset 0) a string with the value "_SM_". This is what will help us search the location of the EPS.
  • In 64-bit architectures, as in 32-bit, it first contains a string, but with the value "_SM3_" (The 3 stands for SMBIOS 3, which is the version in 64-bit machines).

This example C code searches the entry point in a 64-bit architecture. All it does is iterate for every 16-byte aligned memory addresses (from 0x000F0000 to 0x000FFFFF) and comparing 4 bytes from that address with the string "_SM3_". To acomplish that, it is needed to verify the checksum value (add all bytes and see if the lowest 8 bits of the result are zero):

    /* Start address */
    char *eps = 0x000F0000;

    int length, i;
    uint8_t checksum = 0;

    while (eps <= (char*) 0x000FFFFF) {

        /* Check Anchor String (64-bit version) */
        if (!memcmp(eps, "_SM3_", 5)) {

            length = eps[5];
            checksum = 0;

            /* Add all bytes */
            for (i = 0; i < length; i++)
                checksum += eps[i];

            if (checksum == 0)
                /* Done! */
                break;
        }

        /* Next 16-byte-aligned address */
        eps += 16;
    }

Now, eps contains the address of the Entry Point Structure. Some systems may not have the SMBIOS, so an error check may be a good idea:

    if ((unsigned int) eps == 0x00100000) {
        /* Error, SMBIOS could not be located */
        return -1;
    }

UEFI systems

On UEFI systems, the search-for-a-string method is not used to obtain the EPS. Instead, it is located by looking in the EFI Configuration Table for the SMBIOS Version 3 GUID (SMBIOS3_TABLE_GUID), which will contain a pointer to the structure.

This C code shows how that could get implemented using GNU-EFI:

    /* Will contain the address of the Entry Point Structure */
    void *SMBIOS_Pointer = NULL;
    UINTN status = LibGetSystemConfigurationTable(&SMBIOS3TableGuid, (void **)(&SMBIOS_Pointer));

    /* Check all posible errors (maybe is not needed?) */
    if (status != EFI_SUCCESS || SMBIOS_Pointer == NULL ||
        CompareMem(SMBIOS_Pointer, "_SM3_", 5) ) {

        /* Error, SMBIOS could not be located */
        return -1;

    }

Structure Table

Once the EPS is located in memory, information about the SMBIOS table can already be obtained. The most important one is the Structure Table Adress, which is the address of the table that contains all the SMBIOS structures. The structures are located directly adjacent to each other in memory, with a new structure beginning as soon as another one ends. Each structure is composed of a header, a structure specific table, and a string table of variable length.

The first SMBIOS header is located at the Structure Table Adress. The value of type indicates what element the structure contains information about. length indicates the size of header + data table. The strings are not included in the length.

struct SMBIOSHeader {
    uint8_t type;
    uint8_t length;
    uint16_t handle;
};

Immediately after the end of the header, is the data table. At the end of the data table (header address + length), the strings section starts. Each string is NULL terminated and is limited to 64 characters. Strings are referenced within tables by using an index into the string table (index 0 means that the string is effectively a NULL pointer and should be ignored). The first string begins immediately after the data, and the second string begins immediately after that, and so on. The string section itself is terminated by two consecutive zero bytes.

So, the end (and therefore the length) of the SMBIOS structure can be calculated by finding the two Null Characters in the string section. Your code might look like:

size_t smbios_struct_len(struct SMBIOSHeader *hd)
{
    size_t i;
    const char *strtab = (char *)hd + hd->len;
    // Scan until we find a double zero byte
    for (i = 1; strtab[i - 1] != '\0' || strtab[i] != '\0'; i++)
        ;
    return hd->len + i + 1;
}

The final table is denoted by a type field of value 127.

As an example, the BIOS Struct (Type 0) might look like this:

 db 0 ; Indicates BIOS Structure Type            |
 db 13h ; Length of information in bytes         | HEADER
 dw ? ; Reserved for handle                      |

 db 01h ; String 1 is the Vendor Name            |
 db 02h ; String 2 is the BIOS version           |
 dw 0E800h ; BIOS Starting Address               |
 db 03h ; String 3 is the BIOS Build Date        | DATA
 db 1 ; Size of BIOS ROM is 128K (64K * (1 + 1)) |
 dq BIOS_Char ; BIOS Characteristics             |
 db 0 ; BIOS Characteristics Extension Byte 1    |

 db System BIOS Vendor Name,0 ;                |
 db 4.04,0 ;                                   | STRINGS
 db 00/00/0000,0 ;                             |

 db 0 ; End of structure

Header Types

Code Description
0 BIOS Information
1 System Information
2 Mainboard Information
3 Enclosure/Chasis Information
4 Processor Information
7 Cache Information
9 System Slots Information
16 Physical Memory Array
17 Memory Device Information
19 Memory Array Mapped Address
20 Memory Device Mapped Address (optional as of SMBIOS 2.5)
32 System Boot Information

More details can be found in the Specification (see External Links)


See Also

Articles

Forum Threads

External Links