Why is the first BIOS instruction located at 0xFFFFFFF0 ("top" of RAM)?

20,160

Solution 1

0xFFFFFFF0 is where an x86-compatible CPU starts executing instructions when it's powered on. That's a hardwired, unchangeable (without extra hardware) aspect of the CPU and different types of CPUs behave differently.

Why the first BIOS instruction is located at the "top" of a 4 GB RAM?

It's located at the "top" of 4 GB address space - and on power-on the BIOS or UEFI ROM is set to respond to reads of those addresses.

My theory on why this is:

Just about everything in programming works better with contiguous addresses. The CPU designer does not know what a system builder will want to do with the CPU, thus it's a bad idea for the CPU to require addresses smack in the middle of the space be required for various purposes. It's better to keep that "out of the way" at the top or bottom of the address space. Of course, keep in mind this decision was made when the 8086 was new, which did not have an MMU.

In the 8086, interrupt vectors existed at memory location 0 and above. Interrupt vectors need to be at known addresses and were desired to be in RAM for flexibility - yet it was not possible for the CPU designer to know how much RAM was going to be in a system. So starting from 0 and working up made sense for those (because no system in 1978 when the 8086 was invented would have 4 Gbytes of RAM - so expecting RAM to be at 0xFFFFFFF0 was not a good idea), and then ROM would have to be at the upper boundary.

Of course, starting with at least the 80286, interrupt vectors could be moved to a different starting location other than 0, but modern 64-bit x86 CPUs still boot up in 8086 mode, so everything still works the old way for compatibility (as ridiculous as it sounds in 2015 to still need your x86 CPU to be able to run DOS).

So since interrupt vectors start from 0 and work upward, ROM would have to start from the top and work downward.

What would happen if my computer has only 1 GB of RAM?

A 32-bit CPU has 4,294,967,296 addresses, numbered 0 (0x00000000) to 4294967295 (0xFFFFFFFF). ROM can live in some addresses and RAM can live in others. With the CPU's MMU this can even be switched on the fly. RAM does not have to live at all addresses.

With only 1 GB of RAM, some addresses will not have anything responding when they are read or written to. This can cause invalid data to be read when such addresses are accessed or a system lockup.

What about systems with more than 4 GB of RAM (e.g: 8 GB, 16 GB, etc.)?

Keeping it somewhat simple: 64-bit CPUs have more addresses (which is one of the things that makes them 64-bit - e.g. 0x0000000000000000 through 0xFFFFFFFFFFFFFFFF) for example, so the extra RAM "fits". Assuming the CPU is in long mode. Until then the RAM is there, just not addressable.

Why is stack initialized with some value (in this case, a value located at 0xFFFFFFF0)?

I can't immediately find anything on what x86 assigns the stack pointer at power-on, but it would eventually have to be reassigned by an initialization routine anyway once that routine finds out how much RAM is in the system. (@Eric Towers in the comments below reports that it is set to zero on power up.)

Solution 2

It isn't located at the top of RAM; it is located in ROM whose address is at the top of the memory address space, along with any memory on expansion cards, like Ethernet controllers. It is there so that it won't conflict with RAM, at least until you have 4 GB installed. Systems that have 4 GB or more of RAM can do two things to resolve the conflict. Cheap motherboards simply ignore the parts of RAM that conflict with where the ROM is located. Decent ones remap that RAM to appear to have an address above the 4 GB mark.

I'm not sure what you are asking about the stack. It certainly isn't initialized to be in ROM. When the CPU resets, it is initially in "real mode", where it acts just like the original 8086 and uses 16-bit segmented addressing, allowing it only to access 1 MB of memory. The BIOS code is located at top of that 1 MB. The BIOS picks somewhere in RAM to set up the stack and loads and executes the first sector of the first bootable drive. It is up to the OS to switch into 32 or 64-bit mode once it takes over and set up its own stacks (one per task/thread).

Solution 3

First, this has nothing to do with RAM, really. We're talking about address space here - even if you only have 16 MiB of memory, you still have the full 32 bits of address space on a 32-bit CPU.

This already answers your first question, really - at the time this was designed, real world PCs had nowhere near the full 4 GiB of memory; they were more in the range of 1-16 MiB of memory. The address space was, for all intents and purposes, free.

Now, why 0xFFFFFFF0 exactly? The CPU doesn't know how much of the BIOS there is. Some BIOSes may only take a few kilobytes, while others may take full megabytes of memory - and I'm not even getting into the various optional RAMs. The CPU must be hardwired to some address to start on - there's noöne to configure the CPU. But this is only a mapping of the address space - the address is mapped directly into the BIOS ROM chip (yes, this means you don't get access to the full 4 GiB of RAM at this point if you do have that many - but that isn't anything special, many devices require their own range in address space). On a 32-bit CPU, this address gives you full 16 bytes to do the very basic initialization - which is enough to setup your segments and, if needed, address mode (remember, x86 boots in 16-bit real mode - the address space isn't flat) and do a jump to the real boot "procedure". At this point, you don't use RAM at all - it's all just mapped ROM. In fact, RAM isn't even ready to be used at this point - that's one of the jobs of the BIOS POST! Now, you might be thinking - how does a 16-bit real mode access the address 0xFFFFFFF0? Sure, there's segments, so you have 20-bit address space, but that still isn't good enough. Well, there's a trick to it - the 12 high bits of the address are set until you execute your first long jump, giving you access to the high address space (while rejecting access to anything lower than 0xFFF00000 - until you execute a long jump).

All this are the things that are mostly hidden from programmers (not to mention users) on modern operating systems. You usually don't have any access to anything so low level - some things are already beyond salvage (you can't switch CPU modes willy-nilly), some are exclusively handled by the OS kernel.

So a nicer view comes from old-school coding on MS DOS. Another typical example of device memory being directly mapped to address space is direct access to video memory. For example, if you wanted to write text to the display fast, you wrote directly to address B800:0000 (plus offset - in 80x25 text mode, this meant (y * 80 + x) * 2 if my memory serves me right - two bytes per character, line by line). If you wanted to draw pixel-by-pixel, you used a graphics mode and the start address of A000:0000 (typically, 320x200 at 8 bits per pixel). Doing anything high-performance usually meant diving into device manuals, to figure out how to access them directly.

This survives to this day - it's just hidden. On Windows, you can see the memory addresses mapped to devices in the Device manager - just open properties of something like your network card, go to the Resources tab - all the Memory Range items are mappings from device memory to your main address space. And on 32-bit, you'll see that most of those devices are mapped above the 2 GiB (later 3 GiB) mark - again, to minimize conflicts with user-useable memory, though this is not really an issue with virtual memory (applications don't get anywhere near the real, hardware address space - they have their own virtualized chunk of memory, which might be mapped to RAM, ROM, devices or the page file, for example).

As for the stack, well, it should help to understand that by default, stack grows from the top. So if you do a push, the new stack pointer will be at 0xFFFFFEC - in other words, you're not trying to write to the BIOS init address :) Which of course means that the BIOS init routines can use the stack safely, before remapping it somewhere more useful. In old-school programming, before paging became the de facto default, the stack usually started on the end of RAM, and "stack overflow" happened when you started overwriting your application memory. Memory protection changed a lot of this, but in general, it maintains backwards compatibility as much as possible - note how even the most modern x86-64 CPU can still boot MS DOS 5 - or how Windows can still run many DOS applications that have no idea about paging.

Solution 4

In addition to the other points mentioned, it may be helpful to understand what an address is. While newer architectures complicate things, historically a machine would on each memory cycle output the desired address on 20 to 32 wires (depending upon the architecture, with some special tricks to note whether it needed to a pair or foursome of bytes simultaneously); various parts of the memory system would examine the state of those wires and activate themselves when they saw certain combinations of high and low values.

If a machine with 32 address wires only needed to use 1MB of RAM and 64KB of ROM [quite plausible for some embedded controllers] it might activate the RAM for all addresses where the top address wire was low and the ROM for all addresses where it was high. The bottom 20 address wires would then be tied to the RAM to select one of 1,048,576 bytes and the bottom 16 would be wired to the ROM as well, to select one of 65,536 bytes. The remaining 11 address wires would simply be not be connected to anything.

On such a machine, accesses to addresses 0x00100000-0x001FFFFF would be equivalent to accesses to RAM addresses 0x00000000-0x000FFFFF. Likewise with addresses 0x000200000-0x0002FFFFF, or 0x7FF00000-0x7FFFFFFFF. Addresses above 0x80000000 would all read ROM, with a 64K pattern repeating throughout the space.

Even though the processor has a 4,294,967,296-byte address space, there's no need to have hardware recognize that many distinct addresses. Putting the reset vector near the top of the address space is a design that will work well regardless of how much or how little RAM and ROM the system has and avoids the need to fully decode the address space.

Solution 5

upon RESET an 8088/8086 compatible cpu executes the instructions at 0FFFF0, which is 16 bytes below the 1 megabyte limit. normally the ROM at this location (in PC implementations) would be the BIOS, so at the end of the BIOS ROM, there is a jump to the start of the BIOS rom.

shown here: start vector and 'date' signature behind it, IBM 5150 PC 8KB eprom dump bios date: 10/19/1981

00001FEE  FF                db 0xff
00001FEF  FF                db 0xff
00001FF0  EA5BE000F0        jmp word 0xf000:0xe05b
00001FF5  3130              xor [bx+si],si
00001FF7  2F                das
00001FF8  3139              xor [bx+di],di

note that the addressing are of an 8KB $2000 rom, which places the start address (the absolute far JMP, to whichever other location, in this case within the 8KB rom itself, although not the lowest possible address within that rom) at $FFFF:$0 segmented or $FFFF0 linear.

as for compatibility: if some 'future' or current processor 'expects' it to have a whole lot more F's in front of the address, that doesn't matter. for compatibility of newer cpus in older systems the additional address lines remain unconnected and therefore the data on the databus is exactly the same. as long as the least significant bits remain FFFF0.

(in a system with just 1mb ram and the rom positioned in the end of that ram, and nothing else, it'll happily 'think' it's talking to the higher address yet get the exact same data, because those implementations have never heard of address lines higher than A19)

take note that the world is not just 'pcs'... the ibm pc was an 'accident', these processors were never specifically designed for 'pcs' and go into a whole lot of more things than just pcs (such as satellites, weapons sytems, etc). 32 and 64bit protected mode are usually -not- desired. (virtual 8086 mode is a lot more interesting as a reason to pick a newer (386+) version for example). therefore there is a lot more to 'backwards compatibility' than just 'will it run dos'.

Share:
20,160

Related videos on Youtube

Fernando Paladini
Author by

Fernando Paladini

I really need talk something about me here?

Updated on September 18, 2022

Comments

  • Fernando Paladini
    Fernando Paladini over 1 year

    I know that BIOS loads its first instruction from 0xFFFFFFF0, but why this specific address? I've a bunch of questions and hope you can help me with some of them, at least.

    My questions:

    • Why is the first BIOS instruction located at the "top" of a 4 GB RAM?
    • What would happen if my computer only has 1 GB of RAM?
    • What about systems with more than 4 GB of RAM (for example, 8 GB, 16 GB, etc.)?
    • Why is the stack initialized with some value (in this case, a value located at 0xFFFFFFF0)?

    I've read about that this afternoon, and I still don't get it.

    • Lightness Races in Orbit
      Lightness Races in Orbit over 8 years
      One question per question please.
    • imallett
      imallett over 8 years
      I like how the accepted answer doesn't even mention segmented memory or addressing modes at all, and the only place the A20 line is even touched is in the comments.
    • Nick T
      Nick T over 8 years
      Atmel AVRs start execution from address 0, while Freescale HCS08's start from 0xFFFE, iirc. Each processor family has its own characteristics.
    • MonkeyZeus
      MonkeyZeus over 8 years
      @imallett I like how you decide to complain about it here rather than asking the poster to update their answer with further information. I also like how you think that this knowledge that OP would be able to spot even though the purpose of asking a question is to get knowledge about stuff that one might now fully understand.
    • imallett
      imallett over 8 years
      @MonkeyZeus to date, 9 other commenters have already done that, and it still hasn't changed. My comment, while sarcastic, was not vacuous; it is a warning to future netizens, as well as to the OP.
    • marshal craft
      marshal craft over 6 years
      @Lightness Races in Orbit, one lane per car
    • Lightness Races in Orbit
      Lightness Races in Orbit over 6 years
      @marshalcraft: One car per car*. Stack Overflow is the lane. Thanks for playing.
  • Fernando Paladini
    Fernando Paladini over 8 years
    Now I understand that address space = RAM (at the bottom) + ROM (at the top) and everything makes a lot of sense. I don't know if I'm missing something, but I don't get the concept for when we got less or more than 4GB of RAM. If we got 1GB of RAM, ROM will be set-up after the 1 billion addresses because ROM comes right above (at the top) the RAM, right? If so and supposing that our ROM has 1mb, the correct address of the first instruction will be somewhat like 1 billion + (1mb - word_size), not 0xFFFFFFF0 (~4,096,xxx or (4GB - word_size)). Am I wrong? Am I being clear about my doubt?
  • LawrenceC
    LawrenceC over 8 years
    It's best to think of the address space as a big space in which things can be assigned by hardware. When the CPU reads/writes memory, it actually performs a communication over a bus, and hardware can make sure things like RAM or ROM is responding at specific address ranges. So such hardware would have to make sure a ROM responds at 0xFFFFFFF0 when the CPU is reset. There is no inherent obligation that ROM appears right after RAM. It can appear wherever the hardware tells it too, depending on the capabilities of such hardware.
  • LawrenceC
    LawrenceC over 8 years
    It's possible to have "holes" or unassigned spaces that aren't used by ROM, RAM, or anything - typically accessing those will cause a system lockup.
  • Fernando Paladini
    Fernando Paladini over 8 years
    Wow, now I get it. Thank you again! Do you know any place that I can have a complete (and understandable) reading about BIOS, bootstrap and OS setup?
  • Fernando Paladini
    Fernando Paladini over 8 years
    Thank you so much for the answer, but @LawrenceC give more details on his answer and helped me on how the whole thing works. Anyway, thank you! I give you an upvote :3
  • Jamie Hanrahan
    Jamie Hanrahan over 8 years
    This answer has a few issues. The "32 bit" nature of older processors limits virtual or linear address space, but not RAM. A 32-bit processor with PAE enabled can handle up to 64 GB of RAM. An x64 processor can handle up to 52-bit RAM addresses, meaning 4 petabytes of RAM. This is true even if it's in legacy mode, i.e. running a 32-bit operating system.
  • kasperd
    kasperd over 8 years
    This answer assumes that the CPU can use 32 address bits while in 16 bit mode. But in 16 bit mode it can only use 20 address bits. The address 0xFFFFFFF0 is not reachable until after the CPU has been switched to 32 bit mode. Last time I looked closely at BIOS code, the entry point was at 0xFFFF0.
  • user
    user over 8 years
    @kasperd Because of the way the 8086 segmented memory model works, FFFFh:FFF0h has the physical address FFFF0h, right near the top of the 8086's 1 MiB address space. If I recall correctly, converting from segment:offset to physical in the 8086 segmented memory model can be done by left-shifting the segment by four bits, then or-ing with the offset. Because FFFFh << 4 = FFFF0h and FFFF0h | FFF0h = FFFF0h, this works out nicely. (Am I establishing my age, here? I wonder if there's a way to reuse the parts of my brain that insist on remembering this kind of stuff...)
  • Ruslan
    Ruslan over 8 years
    @MichaelKjörling your calculation is wrong. Shifted segment and offset are not ORed, they are added. Thus logical FFFF:FFF0 is physical (1)0FFE0 (where leading 1 is present if A20 is enabled).
  • Ruslan
    Ruslan over 8 years
    @kasperd in 16-bit mode the CPU can easily use 32-bit addresses using address size override prefix 0x67. It could even start in unreal mode with CS base set to 0xfffffff0 (not sure whether it's actually done).
  • Wayne Jhukie
    Wayne Jhukie over 8 years
    @FernandoPaladini for a "complete" reading try drdobbs.com/parallel/booting-an-intel-architecture-system-pa‌​r/… , which has the little detail that at boot the CPU drives the top 12 address bits high (expanding the 20-bit real mode addresses into 32-bit hardware addresses).
  • kasperd
    kasperd over 8 years
    @Ruslan The existence of that opcode is not particular relevant to the question. The question is about what address the CPU loads the first instruction from. No CPU instruction can change what happens before the first CPU instruction is executed. And the choice of entry point predates that opcode.
  • kasperd
    kasperd over 8 years
    @pjc50 Assuming the information in that link is accurate, it could form the basis for a very good answer to the question. I don't see any answer mentioning a change of entry point or reasoning behind it.
  • psusi
    psusi over 8 years
    @JamieHanrahan, no, if it is running in 32 bit mode, then just like a cpu that doesn't even support 64 bit, it can only address 64 GB using PAE.
  • psusi
    psusi over 8 years
    @Ruslan, just to finish your thought: the actual reset vector then is FFFF:0000.
  • Luaan
    Luaan over 8 years
    @kasperd There's a hack in place - the memory manager has the high 12 bits set to 1 until the first long jump takes place. So yes, logically, you're working with 0xFFFF0, but in reality, it maps to 0xFFFFFFF0. I expect this was done for compatibility with the 8086 - both it and more modern CPUs appear to use 0xFFFF0, but the 32-bit CPUs actually access 0xFFFFFFF0 (mapped to BIOS ROM).
  • Luaan
    Luaan over 8 years
    @FernandoPaladini At this point in the boot sequence, nobody knows how much RAM is available - that's one of the things the BIOS has to setup. Until that happens, you need to have one exact address to map the BIOS ROM to get it to execute.
  • Jamie Hanrahan
    Jamie Hanrahan over 8 years
    @psusi: Sorry but that is incorrect. See support.amd.com/TechDocs/24593.pdf , AMD64 Architecture ...Volume 2: System Programming, section 5.2, "Legacy-Mode Page Translation". "...physical addresses as large as 52 bits." and figures 5-9 though 5-12. Or intel.com/content/dam/www/public/us/en/documents/manuals/…, Intel® 64 and IA-32 ... Volume 3, section 4.4, "PAE Paging": " PAE paging translates 32-bit linear addresses to 52-bit physical addresses.", and figures. 4-5 and 4-7.
  • psusi
    psusi over 8 years
    @JamieHanrahan, well I'll be a monkey's uncle... it certainly does appear to be possible now ( I assumed those bits that were "reserved" under 32 bit pae were reserved for flags rather than more address bits ), but I do still wonder if Windows can actually use this capability... it is known for some rather stupid things like limiting physical addresses to 0xFFFFFFFF even when the chipset remaps the 384 MB that is typically reserved for devices to above the 4 GB mark. Do you know if Windows will actually do this? I wouldn't be surprised if Linux did, but Windows is another story.
  • Eric Towers
    Eric Towers over 8 years
    After RESET/INIT, the (R)SP = (00000000)00000000h.
  • Luaan
    Luaan over 8 years
    @psusi Windows Server has supported PAE/AWE for quite a while - and so has other software, like MS SQL Server. I've worked with 32 GiB Windows 2000 servers, for example (I think that was the limit at the time, in fact). You did need the Enterprise/Datacenter edition, though. This is more or less obsolete nowadays, of course, with widespread 64-bit CPUs.
  • Jamie Hanrahan
    Jamie Hanrahan over 8 years
    32-bit Windows client versions have run in PAE mode by default for quite a while (since Vista, I think) because they need it to implement NX. But they still don't support RAM at addresses above 0xFFFFFFFF because some video drivers couldn't cope with it. 32-bit Server editions (which ended with Server 2008) still top out at 64 GB even though the hardware does support more if you're on an x64 CPU. msdn.microsoft.com/en-us/library/aa366778.aspx
  • Basic
    Basic over 8 years
    Excellent answer, just to expand and say that modern processors are starting to drop hacks like A20 line masking, so support for older edge-cases is dying.
  • Basic
    Basic over 8 years
    Good point - You won't find any 64-bit hardware that will support anything close to the addressable 64-bit memory space (or even 1x10^-12 of it).
  • SamB
    SamB over 8 years
    @JamieHanrahan: I thought it was since XP SP2 or so, at least with capable CPUs
  • psusi
    psusi over 8 years
    @Luaan, PAE, yes... I'm talking specifically about the ability to use it on a 64 bit cpu to access more than 64 gb of ram. But yea, I guess it is really just a pointless curiosity since if you have a 64 bit cpu you should just be running in 64 bit mode.
  • The Vee
    The Vee over 5 years
    To the last paragraph: BIOS can't use the stack "freely": it can't write to the ROM (to which 0xFFFFFFEC would be mapped). This means not only no push but for example no call either. These must wait until RAM is ready.