What does physical address 0 in x86 Linux contain

memoryx86

I am not sure if this question should go here or in reverseengineering.stackexchange.com

Quoting from wikipedia:

In the 8086 processor, the interrupt table is called IVT (interrupt
vector table). The IVT always resides at the same location in memory,
ranging from 0x0000 to 0x03ff, and consists of 256 four-byte real mode
far pointers (256 × 4 = 1024 bytes of memory).

This is what I find in qemu monitor:

(qemu) xp/128xw 0
0000000000000000: 0xf000ff53 0xf000ff53 0xf000e2c3 0xf000ff53
0000000000000010: 0xf000ff53 0xf000ff53 0xf000ff53 0xf000ff53
0000000000000020: 0xf000fea5 0xf000e987 0xf000d62c 0xf000d62c
0000000000000030: 0xf000d62c 0xf000d62c 0xf000ef57 0xf000d62c
0000000000000040: 0xc0005526 0xf000f84d 0xf000f841 0xf000e3fe
0000000000000050: 0xf000e739 0xf000f859 0xf000e82e 0xf000efd2
0000000000000060: 0xf000d648 0xf000e6f2 0xf000fe6e 0xf000ff53
0000000000000070: 0xf000ff53 0xf000ff53 0xf0006aa4 0xc0008930
0000000000000080: 0xf000ff53 0xf000ff53 0xf000ff53 0xf000ff53
0000000000000090: 0xf000ff53 0xf000ff53 0xf000ff53 0xf000ff53
00000000000000a0: 0xf000ff53 0xf000ff53 0xf000ff53 0xf000ff53
00000000000000b0: 0xf000ff53 0xf000ff53 0xf000ff53 0xf000ff53
00000000000000c0: 0xf000ff53 0xf000ff53 0xf000ff53 0xf000ff53
00000000000000d0: 0xf000ff53 0xf000ff53 0xf000ff53 0xf000ff53
00000000000000e0: 0xf000ff53 0xf000ff53 0xf000ff53 0xf000ff53
00000000000000f0: 0xf000ff53 0xf000ff53 0xf000ff53 0xf000ff53
0000000000000100: 0xf000ec59 0xf000ff53 0xf000ff53 0xc0006730
0000000000000110: 0xf000ff53 0xf000ff53 0xf000ff53 0xf000ff53
0000000000000120: 0xf000ff53 0xf000ff53 0xf000ff53 0xf000ff53
0000000000000130: 0xf000ff53 0xf000ff53 0xf000ff53 0xf000ff53
0000000000000140: 0xf000ff53 0xf000ff53 0xf000ff53 0xf000ff53
0000000000000150: 0xf000ff53 0xf000ff53 0xf000ff53 0xf000ff53
0000000000000160: 0xf000ff53 0xf000ff53 0xf000ff53 0xf000ff53
0000000000000170: 0xf000ff53 0xf000ff53 0xf000ff53 0xf000ff53
0000000000000180: 0x00000000 0x00000000 0x00000000 0x00000000
0000000000000190: 0x00000000 0x00000000 0x00000000 0xf000ff53
00000000000001a0: 0xf000ff53 0xf000ff53 0xf000ff53 0xf000ff53
00000000000001b0: 0xf000ff53 0xf000ff53 0xf000ff53 0xf000ff53
00000000000001c0: 0xf000d611 0xf000ec4e 0xf000ec4e 0xf000ec4e
00000000000001d0: 0xf000d61a 0xf000d623 0xf000d608 0xf000ec4e
00000000000001e0: 0xf000ff53 0x00000000 0xf000ff53 0xf000ff53
00000000000001f0: 0xf000ff53 0xf000ff53 0xf000ff53 0xf000ff53

I am not sure what to make of those values. It does not look like an interrupt descriptor table (dereferencing those values gives all nulls). So what am I actually looking at here?

Best Answer

Whatever your firmware left it containing.

On an ideal modern system, the processor never enters real mode at all, as I explained in this SU Q&A titled: What mode do modern 64-bit Intel chip PCs run the boot sector in?, the first KiB of physical memory is as irrelevant as Johan Myréen made it out to be in another answer here. But many modern firmwares (still) have compatibility support, meaning that

  • they can drop back (yes, back, given that they went directly from unreal mode to protected mode) from protected mode to real mode in order to run system softwares that are written for real mode, such as old style PC/AT boot programs in MBRs and VBRs; and
  • they provide the old real mode firmware APIs and set up all of the data structures for those APIs, that the aforementioned system softwares rely upon.

One of those data structures is the real mode IVT. The old real mode firmware APIs are based upon int instructions, and the real mode IVT is populated by the firmware as part of its initialization with pointers to the various firmware handling routines for those instructions.

Protected mode system softwares do not need the old real mode firmware APIs, and never run the processor in real mode, so the real mode IVT in the first 1KiB of physical memory is unused. (v8086 protected mode does not address physical address 00000000 and upwards, remember. It addresses logical addresses 00000000 and upwards, which are translated by page tables.) In modern EFI systems, the firmware hands over a memory map of physical memory to the operating system bootstrap, telling it which parts are reserved to the firmware for its own protected mode API purposes, and which parts the operating system is free to just go ahead and use for its pool of physical memory. In theory, the first page of physical memory can be in the latter category.

In practice, firstly, firmwares often mark the first page of physical memory as "boot services code", meaning that an operating system can claim it and just go ahead and use it as part of its physical memory pool, but only after the boot-time services of the EFI firmware have been shut down by the operating system and the firmware reduced to providing its run-time services only. An example of this can be seen in the Linux kernel log (with the add_efi_memmap option) shown by Finnbarr P. Murphy:

[ 0.000000] efi: mem00: type=3, attr=0xf, range=[0x0000000000000000-0x0000000000001000) (0MB)
which xe decodes with another program in a more human-readable form as:

[#00] Type: EfiBootServicesCode Attr: 0xF
      Phys: 0000000000000000-0000000000001000
      Virt: 0000000000000000-0000000000001000

In practice, secondly, Linux explicitly ignores this range of physical memory even if the firmware says that it can go ahead and use it. You'll find that on both EFI and non-EFI firmwares alike, once Linux has the physical memory map it patches it (in a function named trim_bios_range), resulting in kernel log messages such as:

[    0.000000] e820: update [mem 0x00000000-0x00000fff] usable ==> reserved

This is not so much to cope with modern EFI firmwares, where the real mode IVT is not part of the firmware API, as it is to cope with old PC98 firmwares, where it is part of the firmware API but the firmwares report it (via that self-same API) as physical memory available to be blithely overwritten by the operating system.

So whilst in theory that range of physical memory could contain arbitrary code or data, depending from the momentary needs of the kernel memory allocators and demand-paged virtual memory; in practice Linux just leaves it untouched as the firmware originally set it up.

And on your system the firmware had populated it with real mode IVT entries. Real mode IVT entries are just 16:16 far pointers, of course, and if you look at your memory using a 2-byte hexdump you can actually see this pretty clearly. Some examples:

  • Most of your IVT entries point to F000:FF53, an address in the real mode firmware ROM area. It is probably a dummy routine that does nothing more than an iret.
  • IVT entry 1E points to F000:6AA4, a table in that same ROM area.
  • IVT entry 1F points to C000:8930, a table in the real mode video ROM firmware area.
  • IVT entry 43 points to C000:6730, another table in the real mode video ROM firmware area.

Further reading

Related Question