1.1 The CPU’s First Instruction

Borrowing an image from a friend online, it walks through the flow from x86 system boot all the way to the Linux kernel. From the diagram we can see that after power-on, the CPU fetches and executes its first instruction from address CS:IP = FFFF:0000.

Overall process from power-on to Start_kernel under the X86 architecture

This instruction belongs to the BIOS, but it is not the BIOS’s first instruction — in fact it is the last one. It is typically a jump instruction that transfers execution to an appropriate location inside the BIOS to continue.

You need to know that at this moment the CPU is in real mode (real mode, protected mode, system management mode). In real mode, the CPU manages memory using segmentation: the physical address accessed by the CPU is determined by the segment selector register CS and the instruction pointer register IP. Both registers are 16-bit registers. A 16-bit address can only address 2^16 = 64KB of memory, but in the 8086 the address bus had already been widened to 20 bits, so it could address up to 1MB. To form a 20-bit address, the CPU left-shifts the 16-bit register CS by 4 bits and adds the IP register; the resulting sum is a 20-bit value. With CS held fixed, the CPU can only reach addresses between CS and CS + 64KB; to go beyond that range you have to modify CS. This addressing mode is like a person walking a dog on a leash — as long as the person does not move, the dog cannot stray further than the length of the leash.

In the environment we are discussing, CS = 0xFFFF and IP = 0x0000, giving the physical address 0xFFFF0.

1.2 The First Instruction on a Modern CPU

In modern CPUs, the BIOS is stored in an SPI-flash chip. I found a simple architectural diagram online. The diagram is somewhat out of date, but it still serves to explain the point.

In today’s computers, the Northbridge has essentially disappeared, or you could say its function has been merged into the CPU die. So what you actually see is only the Southbridge. From the figure you can tell that the Northbridge’s main job was to provide a PCIe interface for plugging in a discrete graphics card, or to integrate a GPU inside the Northbridge itself. The Northbridge’s job was very narrow — spinning off a separate chip for such a single-purpose function was not cost-effective, and it only added unnecessary headaches for PCB layout. Merging the chip directly into the CPU die is clearly the sensible choice.

After the Northbridge is gone, the CPU talks to the Southbridge over the DMI bus. It is worth giving a brief overview of the bus topology inside an Intel CPU. Intel’s system bus is called QPI; it consists of 20 lanes, and each lane is a differential pair. Intel’s external bus is PCIe. PCIe can be thought of as a subset of QPI — it uses a similar transfer protocol and, at the physical layer, supports 4, 8, 16, 32 lanes and so on. The DMI bus is used almost exclusively between the CPU and the Southbridge; it is made up of 4 lanes and implements only the lower layers of the protocol, so it is transparent to the upper layers. If the CPU and Southbridge were connected through PCIe, devices on the Southbridge would hang directly off that PCIe bus; with the DMI bus, devices on the Southbridge still hang under the CPU’s CPU_BUS_NO0. The Southbridge carries an LPC bus controller and an SPI bus controller, and the SPI bus is where the flash chip holding the BIOS is attached. In some systems, the SPI bus controller itself hangs off the LPC bus.

Everything that traverses these buses are load/store memory-access instructions issued by the CPU (for x86, think of a mov-style assembly instruction) or in/out I/O instructions. The bus controller at each tier can be thought of as a bridge: it accepts a memory-access request from the upstream bus, converts it into one or more transactions on the downstream bus (such as SPI), gathers the data, and returns it to the upstream bus.

Setting aside I/O instructions for the moment and focusing only on memory access, the meaning of a bus address differs across buses. Take a typical 8MB SPI flash as an example. On the SPI bus, its address range is 0 to 8MB. From the CPU’s perspective, that 8MB of data is mapped to the top 8MB of the 4GB address space. The data at physical address 0xFFFF0 may physically sit at 0xFFFFFFF0 (i.e. 4GB − 16 bytes) on the SPI flash. When a read request targeting that address is routed to the SPI bus, the SPI controller performs a translation to address 0x7FFFF0 (8MB − 16 bytes) and issues it to the SPI flash. It then retrieves the data and returns a memory-read response to the CPU. Because QPI is an asynchronous bus, the SPI bus can take its time without holding anything up.

Saying that the CPU’s first instruction sits at 0xFFFF0 applies only to the 8086. For CPUs from the 80386 onward, that address becomes 0xFFFFFFF0.

We probably need to dig a bit deeper into the CPU’s segment registers. The CPU has a handful of segment registers: CS, DS, ES, FS — code segment, data segment, and so on. The address of the first instruction the CPU executes is jointly specified by CS and IP. At power-on, some internal hardware logic in the CPU runs a self-check to confirm that its state is healthy; if so, it writes the eax register to 0, otherwise it writes an error code.

The CPU then flushes caches, the TLB, and similar caching structures, sets the initial value of CS to 0xF000 and IP to 0xFFF0, yielding the physical address 0xF000 << 4 | 0xFFF0 = 0xFFFF0.

It looks straightforward at this point — but we are not quite done.

In modern CPUs, to speed up the computation of instruction addresses, every segment register is paired with two extra registers: Base and Limit. Base holds the segment base, and Limit holds the maximum offset. You cannot read or write Base and Limit directly with instructions; their values are set automatically by the CPU whenever a segment register is written. Usually Base equals the segment register shifted left by four bits — but CPU initialization is an exception. Here CS = 0xF000, yet its Base is 0xFFFF0000; with EIP = 0xFFF0, the corresponding instruction address is 0xFFFF0000 + 0xFFF0 = 0xFFFFFFF0. 0xFFFFFFF0 is therefore where the CPU will execute its very first instruction. This leads to a rather curious fact: the instruction address space 0x00000xFFFF (64KB) seen by a 16-bit program is translated by the CPU to the physical address range 0xFFFF00000xFFFFFFFF. In other words, from CPU initialization until the segment registers are rewritten (via a far-jump instruction), the instruction address range 0x00000xFFFF is mapped through the segment registers to the physical address range 0xFFFF00000xFFFFFFFF.

References:

http://www.cppblog.com/djxzh/archive/2015/07/12/uefi_resetvector.html

Last modified: 2026年7月13日

Author

Comments

Write a Reply or Comment

Your email address will not be published.