How does an x86 CPU start executing from 0xFFFFFFF0 if it starts in 16-bit mode? [duplicate]
Solution 1:
I finally found the answer in the Coreboot documentation:
Whenever an x86 CPU wakes up after reset, it does it in Real Mode. This mode is limited to 1MiB address space and 64k offsets and the reset vector of the original 8086/88 was located at 0xFFFF0.
As there was no change even if we run current processors like P3, these newer CPUs also feels like they where start at 0xF000:0xFFF0 after a reset. But they do not. The base of the code segment register is 0xFFFF0000 after reset, so the CPU generates a physical address of 0xFFFFFFF0 to the chipset. And the chipset is responsible to forward this area to the boot ROM. Its confusing: The CPU "thinks" it runs code at 0xF000:0xFFF0 but instead it uses code at 0xFFFFFFF0. The developers must have been tanked up when they realised this design into silicon.
So it seems the Intel documentation talks about the physical address as used "on the wire", i.e. when accessing the real bus. And this is independent of the CPU mode (the bus doesn't know or care about a CPU mode, it's the CPUs duty to translate these things).
Solution 2:
To see your question in action you will need a hardware debugger. And the proper documentation is from Intel, to quote: http://download.intel.com/design/processor/manuals/253668.pdf, section 9.1.4:
The first instruction that is fetched and executed following a hardware reset is located at physical address FFFFFFF0H. This address is 16 bytes below the processor’s uppermost physical address. The EPROM containing the software initialization code must be located at this address.
This means BIOS ROM, FYI, not your normal RAM, ie, the content is hardwired. And remember, at this stage, RAM memory is not even setup, and VGA memory (which is different from RAM) is not even available and initialized.
The address FFFFFFF0H is beyond the 1-MByte addressable range of the processor while in real-address mode. The processor is initialized to this starting address as follows. The CS register has two parts: the visible segment selector part and the hidden base address part. In real-address mode, the base address is normally formed by shifting the 16-bit segment selector value 4 bits to the left to produce a 20-bit base address. However, during a hardware reset, the segment selector in the CS register is loaded with F000H and the base address is loaded with FFFF0000H. The starting address is thus formed by adding the base address to the value in the EIP register (that is, FFFF0000 + FFF0H = FFFFFFF0H). The first time the CS register is loaded with a new value after a hardware reset, the processor will follow the normal rule for address translation in real-address mode (that is, [CS base address = CS segment selector * 16]). To insure that the base address in the CS register remains unchanged until the EPROM based software initialization code is completed, the code must not contain a far jump or far call or allow an interrupt to occur (which would cause the CS selector value to be changed)
During this time, the BIOS essentially is initializing the hardware and the memory itself, while still executing in real mode. Then finally the VGA BIOS (which exists in your VGA card, addressable at 0xc700) is executed etc etc. But this is going beyond the current question. But the quoted remarks above essentially answered your question.
Solution 3:
If processor is in real-mode how can it access the memory > 1MB (0xFFFFFFF0H)
Almost nothing inside the CPU cares about "CPU mode". When executing normal instructions; what actually matters are things like default code size, segment base, segment limit, segment type, etc., and the CPU mode is irrelevant. It's only things like segment register loads and interrupt handlers where CPU mode matters (in fact, other than paging, I'd wouldn't be surprised if the only things that care about CPU mode are things implemented in micro-code).
Because CPU mode is mostly irrelevant for normal instructions (and because things like default code size, segment base, segment limit, segment type, etc. are the only things that actually matter); at power on or reset the CPU can set "abnormal" values (values that aren't normally possible for the CPU mode) into segment registers and the rest of the CPU won't care. Specifically; it can do "CS.base_address = 0xFFFF0000
" (which isn't possible for real mode where a CS segment register load would do "CS.base_address = 16_bit_CS.value << 4
").
The end result is that all memory accesses that involve CS (and pass segment limit checks) end up going to the (linear) address "0xFFFF0000 + offset
" even though the CPU is in "real mode" and even though this isn't normally possible for real mode.
Note that in real mode addresses are not limited to 1 MiB. For example, if you load 0xFFFF into a segment register then CPU will set that segment register's hidden info to "segment.base = 0x000FFFF0" and addresses using that segment will end up with (linear) addresses from 0x000FFFF0 to 0x0010FFEF. This is why (when 80286 was released) we needed the "A20 gate" for compatibility with ancient software (to force the 20th address bit to be zero without the CPU knowing).
Also note that while "CS.base_address = 0xFFFF0000
" isn't normal for real mode; software can switch to protected mode and load a "code size = 16-bit, segment limit
64 KiB, segment base = 0xFFFF000" descriptor into CS; and then switch back to real mode without reloading CS. The end result would be the same "abnormal CS base" that the CPU sets up at power on or reset.
Of course (regardless of how an abnormal value got into CS.base) any normal CS segment register load that's executed in real mode will cause "CS.base" to be set to a normal value; so firmware would have to be ensure that no CS segment register loads occur while it's executing in "real mode" at the abnormal address.
How this happens or what happens when RAM in < 4GB ( say 2GB)
The physical address space is used for RAM, and ROM, and memory mapped devices. The ROM (and not RAM) will be just below the "4 GiB" address. For example, if the ROM is 2 MiB, then it'd be in the physical address range from 0xFFE00000 to 0xFFFFFFFF. Note that at power on firmware can not use RAM (it has to figure out what type and size of memory modules are installed and configure the memory controller to suit, before it can expect RAM to work).
If the BIOS is mapped at 0x000FFFFFH why processor starts executing at 0xFFFFFFF0H
Originally (80286 and older CPUs) the BIOS actually was mapped at 0x000FFFFF. For (some) 80386 and later CPUs this is only emulated for compatibility reasons. Instead; the firmware copies a small piece of itself from ROM (in the area ending at physical address 0xFFFFFFFF) to RAM (in the area ending at physical address 0x000FFFFF); and then configures the memory controller so that writes made to this area of RAM are ignored (so that memory controller doesn't forward these writes to the RAM chips).
Note that for "pure UEFI" systems (not including "hybrid BIOS + UEFI" systems), there's no reason for firmware to set up the "legacy BIOS area" ending at physical address 0x000FFFFF; and RAM in this area may be usable RAM (configured as "allow writes" in memory controller, etc). In the same way, the other legacy areas (for VGA and device ROMs) aren't needed for "pure UEFI" either; and in theory (for a computer with 2 GiB of RAM or less) there's no reason (except for SMM stealing a little) you can't just have single contiguous area of normal RAM from 0x00000000 to 0x7FFFFFFF.