How does UEFI work?
Asked Answered
G

5

24

I was studying about bootloaders when exactly came upon the term UEFI. I can understand some things about UEFI. But still, In what mode(Real,Protected,Long) does a system with UEFI start? If normal boot loaders cant work with UEFI, Then what is the alternate of boot loader while dealing with UEFI? And do I need any other programming to create one, than assembly?

Gomer answered 26/8, 2015 at 9:52 Comment(0)
L
20

UEFI firmware runs in 64 bit long mode for 64 bit platforms and flat mode for 32 bit platforms; Unlike BIOS, UEFI features its own architecture, independent of the CPU, and its own device drivers. UEFI can mount partitions and read certain file systems.

When an x86 computer equipped with UEFI, the interface searches the system storage for a partition labeled with a specific globally unique identifier (GUID) that marks it as the EFI System Partition (ESP). BTW Windows doesn't mount this partition and you cannot see it in the OS. But there is a trick, you simply change the partition type (using HexWorkshop) in VBR to regular FAT32 code and it will be mounted into the OS.

This partition contains applications compiled for the EFI architecture. In general you don't have to deal with assembler to write a UEFI application/loader, it's just a regular C code. By the default it is located at "EFI/BOOT/BOOTX64.EFI". When a bootloader is selected, manually or automatically, UEFI reads it into the memory and yields control of the boot process to it.

Landahl answered 27/8, 2015 at 17:1 Comment(8)
I heard that "CPUs start in 16bit mode. UEFI runs in 32bit or 64bit. " So in that case what runs before the UEFI?Gensmer
@Gensmer bootloader , usually it’s quite primitive piece of software that might not even use 32 bit memory addressesLandahl
from what I understand, the UEFI starts in 16bit github.com/tianocore/edk2/blob/master/UefiCpuPkg/ResetVector/… and here is where it transitions from 16bit real mode to 32bit protected mode github.com/tianocore/edk2/blob/master/IntelFsp2Pkg/FspSecCore/…Gensmer
also when I asked "what runs before the UEFI? " you said "bootloader". But the bootloader is not before the UEFI !! Grub is a bootloader, it runs after the UEFI . (perhaps bootloader is an ambiguous term in some way)Gensmer
@Gensmer yes you're right, bootloader, in this regard, is a bit ambiguous. But grab is a very high-level bootloader, I was talking about the code inside flash nonvolatile memory chip soldered on the motherboard, code that actually does hardware initialization, like setting up MMU, virtual memory controllers, any external peripherals, and who knows what else...Landahl
@barlop: the first instruction that executes after power-up is fetched from a physical address hard-wired into the CPU (the reset vector). The CPU is in "unreal" mode at this point, with the CS segment base outside of the low 1MiB, but otherwise the same as 16-bit real mode. The system designer makes sure that address is mapped to a flash ROM. That code will eventually switch to 64-bit mode before reading anything from disk. But before that it has to configure the DRAM controllers and stuff like that! It will usually be using cache-as-RAM (no fill) mode for some of that time...Timelag
@Alex could you help me on this topic #66949666Dipole
On linux, you can simply find the ESP (EFI System Partition) with fdisk -l. On my box it is /dev/nvme0n1p1. And then mount it as usual sudo mount /dev/nvme0n1p1 ~/esp. And then you can check the content within ~/esp.Hedda
S
18

In the following, some sentences are a copy, amalgamation and wording improvement of the best sources I've seen, and then I improve on and correct their unknowns / errors by using my own hardware case study.

Boot Guard

Intel Boot Guard is a technology introduced by Intel in the 4th Intel Core generation (Haswell) to verify the boot process. This is accomplished by flashing the public key of the BIOS signature into the field programmable fuses (FPFs), a one-time programmable memory inside Intel ME (in the PCH), during the manufacturing process; in this way it has the public key of the BIOS and it can verify the correct signature during every subsequent boot. Once enabled by the manufacturer, Intel Boot Guard can't be disabled anymore.

Boot Guard has two separate modes, according to Intel. Typical PC OEMs configure it to “Verified Boot” mode. The PC manufacturer fuses their public key into the hardware itself. If the UEFI firmware isn’t signed by the OEM—i.e., created by the OEM—the computer will halt and refuse to boot. That’s why you can’t modify the UEFI firmware. There’s also a second option: “Measured Boot” mode, where the hardware uses Intel TXT to secure stores information about the boot process (in a trusted platform module (TPM)) or Intel Platform Trust Technology (PTT) with the aid of SMX. The operating system could then examine this information, and—if there was a problem—present an error to the user.

Secure Boot

When enabled and fully configured, Secure Boot helps a computer resist attacks and infection from malware. Secure Boot detects tampering with boot loaders, key operating system files, and unauthorized option ROMs by validating their digital signatures. Detections are blocked from running before they can attack or infect the system. UEFI Secure Boot assumes the OEM platform firmware is a Trusted Computing Base (TCB) (i.e. that it's been initialised with BootGuard technologies and trusts it implicitly).


Verified Boot Process

Pre-UEFI stage

When the OEM receives the PCH, the ME is still in "Manufacturing Mode" and runs a special part of the firmware that will copy the "OEM Public Key Hash" and "Boot Guard Profile Configuration" policy values from its section of the flash ROM into the field programable fuses (FPFs) so that they are permanent and unchangable. It then sets a fuse to indicate that it has exited from manufacturing mode so that this portion of the firmware will not run again. These values can be adjusted in the flash image with the Intel Flash Image Tool (FITC), but unless you have a way to force the ME into manufacturing mode then the values in the flash image are ignored.

The Bootguard profile is as follows:

enter image description here

Protect BIOS Environment Enabled: if set, this possibly means that the ACM will copy the IBB segments into the CPU cache so that it runs in a cache-as-RAM (CAR) mode and has all DMA disabled to prevent devices from being able to modify it.

As soon as power is available, the ME CPU boots up from its on-die boot ROM, checks some straps and fuses to determine its configuration, and typically then copies the flash partition table (FPT) from the ME region of the SPI flash (which is typically below the BIOS region) to its on-die SRAM. It locates the ME region by using the flash descriptor at the lowest address of the SPI flash.

enter image description here

The boot ROM locates the FTPR partition using the FPT and copies it from the SPI flash into the on-die SRAM. It then checks that the SHA-1 hash of the key stored in the partition manifest matches the one in its on-die ROM and validates the RSA signature on the rest of the partition manifest. The partition table contains hashes of each of the modules in the partition, allowing the modules to be validated after they are copied into the on-die SRAM for execution.

enter image description here

When the ME boots the x86 CPU, possibly in bup.met. The BIST and then the BSP MP Initialisation algorithm takes place. The legacy reset vector at CS:FFF0 (where CS is 0xF000 and the segment descriptor cache contains the base 0xFFFF0000) is no longer the first instruction the x86 CPU executes on reset. Instead, on-die microcode fetches the FIT pointer at 0xFFFFFFC0 (using 0xF000:FFC0 with the unreal mode segment descriptor hack – because this is the initial state of the registers), which points to the FIT table somewhere in the SPI flash BIOS region.

This image shows the 16 byte FIT entries with their modes 3 bytes from the end.

#pragma pack (1)
typedef struct {
  UINT64     Address;
  UINT8      Size[3];
  UINT8      Rsvd;
  UINT16     Version;
  UINT8      Type:7;
  UINT8      C_V:1;
  UINT8      Checksum;
} FIRMWARE_INTERFACE_TABLE_ENTRY;

On my system, there is a FIT pointer at FFFFFFC0 to FFD90100

The FIT table points to microcode updates, ACMs, the BootGuard Boot Policy Manifest (which contains an IBBS) and BootGuard Key manifest etc.

My FIT does not contain any 0x7 entries. Record Types 7 is used by legacy Intel® TXT FIT boot only and is not needed, if latter is not used.

The BootGuard Boot Policy (IBBM) contains:

Intel BootGuard Boot Policy Manifest found at base FD3C00h
Tag: ACBP Version: 10h HeaderVersion: 01h
PMBPMVersion: 10h PBSVN: 00h ACMSVN: 02h NEMDataStack: 0010h

Initial Boot Block Element found at base FD3C28h
Tag: IBBS       Version: 10h         Unknown: 0Fh
Flags: 00000000h    IbbMchBar: FED10000h VtdBar: 00000000h
PmrlBase: FED90000h PmrlLimit: 00000000h  EntryPoint: 00100000h

Post IBB Hash:
0000000000000000000000000000000000000000000000000000000000000000

IBB Digest:
B9CCC06B77AEACC51768981D07CBE9E43D34DB6795752C4B998312241B26F874

IBB Segments:
Flags: 0000h Address: FFE10000h Size: 001C3C00h
Flags: 0000h Address: FFFD4C00h Size: 00000080h
Flags: 0000h Address: FFFD5C80h Size: 0000A380h
Flags: 0000h Address: FFFE8000h Size: 00018000h

Boot Policy Signature Element found at base FD3CDDh
Tag: PMSG Version: 10h

Boot Policy RSA Public Key (Exponent: 10001h):
....
Boot Policy RSA Public Key Hash:
....
Boot Policy RSA Signature:
....  

The Key Manifest contains:

Intel BootGuard Key Manifest (KEYM) found at base FD4C80h
Tag: KEYM Version: 10h KmVersion: 10h KmSvn: 00h KmId: 0Fh

Key Manifest RSA Public Key Hash:
...
Boot Policy RSA Public Key Hash:
...
Key Manifest RSA Public Key (Exponent: 10001h):
...
Key Manifest RSA Signature:
... //it does actually contain a signature, I just removed these for space

Cache as RAM (CAR) (aka. AC-RAM, No fill mode and No evict mode) is then set up by the microcode. The FIT is then searched for microcode updates that match the CPU ID. The current microcode copies them linearly from flash into L3 cache and decrypts with an on-die symmetric AES key, then validates with (on-die?) RSA key. It's likely that these microcode updates also contain the key hashes for the ACM. The microcode update is then applied by writing to the UCODE MSR and the CPU reads it out I assume through normal memory accesses. The FIT absolutely has to contain a microcode patch and it patches the microcode SRAM to complement the already existing microcode ROM. [1]

Next, the microcode goes back to the FIT to find the Startup ACM (aka BIOS or Bootguard ACM) and does an odd copy of it into L3 (looks like multiple hyperthreads are copying 4KB chunks? -- according to that source). The ACM contains an RSA public key; the microcode compares it against either an on-die key or one stored in the microcode update and halts the CPU if it does not match. The microcode then checks the signature on the ACM and again halts if it does not match.

The Startup ACM runs entirely out of L3. The ACM receives the OEM public key hash (the Key Hash that verifies the Key Manifest) and Bootguard Profile from the ME via MSR.

The ACM reads the BootGuard Key Manifest from the SPI flash (pointed to by the FIT and identified by __KEYM__) into L3 and hashes the RSA public key stored in it. If it doesn't match the OEM public key hash or if the OEM public key signature on the Key Manifest or if the stored KmSvn isn't right, the ACM takes action based on the Bootguard Profile bits. If it does match, it locates the Bootguard Policy in the FIT (and identified by __ACBP__) and copies it into L3. The ACM then computes the hash of the RSA public key in the Policy and compares it to the SHA256 hash stored in the Key Manifest. If that fails to match, or if the RSA signature on the Policy doesn't match, then the ACM again takes action based on the Profile settings.

The ACM uses the now validated Bootguard Policy structure to read the Initial Boot Block (IBB) segments into L3, hashing them as they are copied. If this computed hash doesn't match the "IBB Digest" in the Policy, the ACM takes action based on the Profile settings. The 4th IBB segment contains the reset vector, so it prevents that from being modified. It's unclear whether it leaves the CPU in CAR mode or whether it just leaves them cached in the L3 but without eviction disabled i.e. without CAR mode left enabled. SEC however needs to (disable) and set up a new CAR mode.

enter image description here

Taking a dump of physical memory using RwEverything, or downloading CSME System Tools for your ME version and then performing fptw64 -d -me -bios dump.bin will dump the flash (and often you will only be able to dump the BIOS region and not the ME region). The dump is now analysable in UEFITool NE Alpha 58, and the FIT shows the EntryPoint of the ACM in the body. So if you right click, extract it and open it up in IDA as 32 bit, the entry point will be at 3BB1h + 18h = 3BC9h

3BC9   mov     ax, ds
3BCC   mov     ss, ax
3BCF   mov     es, ax
3BD2   mov     fs, ax
3BD5   mov     gs, ax
3BD8   mov     esp, ebp
3BDA   add     esp, 1000h
3BE0   mov     eax, ebp
3BE2   add     eax, 4C8h
3BE7   lidt    fword ptr [eax]
3BEA   push    ebp
3BEB   call    sub_392A
3BF0   mov     ebx, eax
3BF2   mov     edx, 0
3BF7   mov     eax, 3
3BFC   getsec

The startup ACM is always the final ACM and it GETSEC[EXITAC]s to the IBBS base + entry point address (0xFEE90000, which falls in the first IBB) in the bootguard policy manifest, which appears to be in the ME region and presumably contains code to switch the CPU back to unreal mode and jump to the legacy reset vector in the 4th IBB segment. GETSEC can only be executed in protected mode, so clearly the CPU is in protected mode in the startup ACM, so must be enabled by the microcode before entry. The legacy reset vector is at 0xFFFFFFF0, which on my system is a relative jump to FFFFFFF5 - 3BD = FFFFFC38, which is the SEC core entry point.

SEC

enter image description here

The SEC core is a Raw section from FFFFCA14 - FFFFCA17, a PE32 image from FFFFCA18 - FFFFFFBB, a raw section from FFFFFFBC - FFFFFFBF, and a raw section from FFFFFFC0-FFFFFFFF (which contains the reset vector).

The entry point of the PE32 Image in the SEC Core that is jumped to by the reset vector contains:

0x00:  DB E3                      fninit 
0x02:  0F 6E C0                   movd   mm0, eax   //move BIST value to mm0
0x05:  0F 31                      rdtsc  
0x07:  0F 6E EA                   movd   mm5, edx
0x0a:  0F 6E F0                   movd   mm6, eax  //save tsc
0x0d:  66 33 C0                   xor    eax, eax //clear eax

0x10:  8E C0                      mov    es, ax
0x12:  8C C8                      mov    ax, cs
0x14:  8E D8                      mov    ds, ax
0x16:  B8 00 F0                   mov    ax, 0xf000
0x19:  8E C0                      mov    es, ax
0x1b:  67 26 A0 F0 FF 00 00       mov    al, byte ptr es:[0xfff0]
0x22:  3C EA                      cmp    al, 0xea
0x24:  74 0E                      je     0x34   //if ea is at ffff0h then jump to the 0xf000e05b check 

0x26:  BA F9 0C                   mov    dx, 0xcf9
0x29:  EC                         in     al, dx    //read port 0xcf9
0x2a:  3C 04                      cmp    al, 4    
0x2c:  75 25                      jne    0x53      
0x2e:  BA F9 0C                   mov    dx, 0xcf9 //perform warm reset since if CPU only reset is issued not all MSRs are restored to their defaults
0x31:  B0 06                      mov    al, 6
0x33:  EE                         out    dx, al  

0x34:  67 66 26 A1 F1 FF 00 00    mov    eax, dword ptr es:[0xfff1]
0x3c:  66 3D 5B E0 00 F0          cmp    eax, 0xf000e05b
0x42:  75 0F                      jne    0x53      //if it isn't, move to notwarmstart

0x44:  B9 1B 00                   mov    cx, 0x1b //if it is equal, read bsp bit from apic_base msr
0x47:  0F 32                      rdmsr  
0x49:  F6 C4 01                   test   ah, 1
0x4c:  74 41                      je     0x8f   //if the and operation with 00000001b produces a zero result i.e. it's an AP then jump to cli, hlt

0x4e:  EA F0 FF 00 F0             ljmp   0xf000:0xfff0 //if it's the BSP, exit unreal mode by far jumping to 0xffff0 which reloads the segment descriptor cache with a 0 base

notwarmstart:
0x53:  B0 01                      mov    al, 1
0x55:  E6 80                      out    0x80, al  //send 1 as a debug POST code
0x57:  66 BE 68 FF FF FF          mov    esi, 0xffffff68
0x5d:  66 2E 0F 01 14             lgdt   cs:[si] //loads 32&16 GDT pointer (not 16&6, due to 66 prefix) at 16bit address fff68 in si into GDTR (base:ffffff28 limit:003f); will be accessing alias and not shadow ROM

//enter 16 bit protected mode//
0x62:  0F 20 C0                   mov    eax, cr0
0x65:  66 83 C8 03                or     eax, 3   //Set PE bit (bit #0) & MP bit (bit #1)
0x69:  0F 22 C0                   mov    cr0, eax  //Activate protected mode
0x6c:  0F 20 E0                   mov    eax, cr4 
0x6f:  66 0D 00 06 00 00          or     eax, 0x600 //Set OSFXSR bit (bit #9) & OSXMMEXCPT bit (bit #10)
0x75:  0F 22 E0                   mov    cr4, eax

//set up selectors for 32 bit protected mode entry
0x78:  B8 18 00                   mov    ax, 0x18 //segment descriptor at 0x18 in GDT is (raw): 00cf93000000ffff
0x7b:  8E D8                      mov    ds, ax
0x7d:  8E C0                      mov    es, ax
0x7f:  8E E0                      mov    fs, ax
0x81:  8E E8                      mov    gs, ax
0x83:  8E D0                      mov    ss, ax
0x85:  66 BE 6E FF FF FF          mov    esi, 0xffffff6e
0x8b:  66 2E FF 2C                ljmp   cs:[si]   //transition to flat 32 bit protected mode and jump to address at 0x0:0xffffff6e aka. 0xffffff6e which is fffffcd8. CS contains 0 remember (it's the base that is 0xffff) so it will load the first entry.  This address is also in the SEC Core PE32 Image
                                                  
0x8f:  FA                         cli    
0x90:  F4                         hlt    
.
.
.
Spike answered 3/5, 2020 at 1:5 Comment(2)
According to this source, FPFs reside in the PCH, not the CPU. Another source states: "Newer security and management engines shipped with select Intel platforms in and after 2013 support a feature called field programmable fuses". Since Intel ME resides in the PCH, this also seems to confirm that FPFs are located there.Bounded
@Bounded they're in the PCH (or the CPU if the PCH is rolled into the CPU like it is on some of the newer laptop architectures). I'll get round to this answer one day. I got sidetrackedSpike
T
8

Here's a good answer to this question:

Other modern 64-bit machines have new EFI firmwares. These don't load a bootstrap program from sector #0 of a disc at all. They bootstrap by the EFI Boot Manager loading and running an EFI boot loader application. Such programs are run in protected mode. This is the EFI bootstrap process.

EFI firmwares in general switch to protected mode within a few instructions of exiting processor reset. Switching to protected mode is done early on in the so-called "SEC Phase" of EFI firmware initialization. Technically, 32-bit and greater x86 processors don't even start in real mode proper, but in what is colloquially known as unreal mode. (The initial segment descriptor for the CS register does not describe the conventional real mode mapping and is what makes this "unreal".)

As such, it could be said that those EFI systems never enter real mode proper at all, when bootstrapping natively to an EFI bootloader (i.e. when they don't employ a compatibility support module), since they switch from unreal mode directly to protected mode and stay in protected mode from then on.

Tiphanie answered 26/8, 2015 at 18:23 Comment(3)
There is NO protected mode for UEFI! UEFI works in 64 bit long mode! Some platforms may use 32 bit flat mode. The terminology is wrong at first place that's why it confuses a lot of people. Back in the days everything that was not x86 mode was called protected mode. But that is not entirely correct.Landahl
@Alex I did link you where UEFI starts in 16bit mode and moves to 32bit mode. You might seem to call that a boot loader of some sort, but the fact is that where it starts in 16bit mode is part of UEFIGensmer
@barlop: I'd say that starting in 16-bit (un)real mode is part of x86 early booting. It isn't EFI / UEFI (or legacy BIOS) until the firmware is ready to load something from disk and provide a standardized config and boot API to the code that it loads. Before that the firmware has to get this specific system's memory controllers configured, and stuff like that, and that part is fully private and isn't governed by any standard. 16-bit isn't part of UEFI, so yes, a UEFI firmware has to have switched out of that mode before jumping to any code it loaded from disk, and provide that ABI.Timelag
M
2

When you asked "In what mode(Real,Protected,Long) does a system with UEFI start?", what do you mean? The processor starts executing in a mode that is similar to the mode of the 80386 of the past. But do you really care. Don't you really care about the mode of the processor when your OS loader code is given control? And you care about what services are provided to your OS loader code.

The environment is defined in the UEFI spec. Latest Versions of the UEFI Specifications

As for which language you use, assembly is good to start with. It's probably easier to use C or some other high level language after a bit.

Other background:

There are a lot of terms here that we are not always careful about using correctly.

The code that executes when the processor comes out of reset is system firmware that does a lot of initialization of various hardware in the system.

In the old days on a x86 PC system before the UEFI Forum existed, the system firmware was called BIOS. The BIOS in those days executed all of its initialization code, then loaded some code off of a floppy diskette or the hard drive and jumped into the code. The BIOS also provided some interfaces between the hardware and the operating system to help isolate the OS from hardware differences. Nothing was standardized though. The only standards were the OS and applications level software that used the BIOS interfaces.

If the OS and apps functioned correctly then the BIOS was considered correct. But you could only prove correctness by lack of failure. Thus new OSs or applications would work on one correct system yet fail on a different correct system.

Today we've tried to provide some actual standardization of those interfaces. They are defined by the UEFI Forum. Today I can prove that my system is correct according to meeting the UEFI specifications.

When people say things like the UEFI, they are usually referring to the actual system firmware installed on the system that is executed before the OS starts executing. But a lot of us still toss around these terms like we've spilled alphabet soup.

The boot loader is actually OS owned code that is loaded in memory by the system firmware and the system firmware gives control of the hardware to the boot loader. One could say that the end of the system firmware is the UEFI Boot Loader. Or you could just say BDS uses the system policy to find the OS. And you could still find someone that disagreed with the words.

Metamorphism answered 3/9, 2015 at 7:8 Comment(2)
the term UEFI boot loader is not a good term. A boot loader e.g. GRUB, is not part of the UEFI.. It runs after the UEFI. There are boot loaders written for UEFI superuser.com/questions/1112090/what-is-a-uefi-bootloaderGensmer
you ask if we care about the mode when giving control. Of course, if you want to create an OS, you need to load a gdt and such, you have to do different things based on the mode, or not?Influence
I
2

If you wish to know more about how UEFI works then the post UEFI boot: how does that actually work, then? by Adam Williamson is highly recommended.

He answers the questions you have and its a good read:

Now let’s look at how booting works on a UEFI system. Even if you don’t grasp the details of this post, grasp this: it is completely different. Completely and utterly different from how BIOS booting works. You cannot apply any of your understanding of BIOS booting to native UEFI booting. You cannot make a little tweak to a system designed for the world of BIOS booting and apply it to native UEFI booting. You need to understand that it is a completely different world.

The Wikipedia page Unified Extensible Firmware Interface is also a useful resource.

Inodorous answered 2/5, 2018 at 22:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.