115 lines
5.5 KiB
Plaintext
115 lines
5.5 KiB
Plaintext
|
Memory access handling explanation
|
||
|
by Brendan Trotter:
|
||
|
|
||
|
For the local APIC/s, any read or write to a CPU's own local APIC is
|
||
|
handled internally and does not go to the bus. If the read/write misses
|
||
|
this area then the read/write does go to the bus (where other CPU's ignore
|
||
|
it).
|
||
|
|
||
|
This means if 2 CPUs have different local APIC addresses and one CPU tries
|
||
|
to write to the area used by the second CPU's local APIC, then it will go
|
||
|
to the bus and will not access the second CPU's local APIC.
|
||
|
This applies in all cases (e.g. hyper-threading and dual core work the same).
|
||
|
|
||
|
For I/O APICs, the device is on the bus and should override anything that
|
||
|
is "underneath" it. For example, if you relocate the I/O APIC to
|
||
|
0x00000000, then a read or write to this area will not reach the RAM
|
||
|
underneath. In a similar way, if someone maps a PCI device to 0xFEC00000
|
||
|
(or somewhere that overlaps the I/O APIC) then a write to this area will
|
||
|
not reach the PCI device.
|
||
|
|
||
|
This leads to something like the following for accesses originating from a
|
||
|
CPU:
|
||
|
|
||
|
if (address_is_within_this_CPUs_local_APIC_area)
|
||
|
do_local_APIC_access();
|
||
|
else if (address_is_within_an_I/O_APIC_area)
|
||
|
do_I/O_APIC_access();
|
||
|
else if (address_is_within_a_PCI_device_area)
|
||
|
do_PCI_access();
|
||
|
else if (address_is_within_RAM_area)
|
||
|
do_RAM_access();
|
||
|
else printf("Bogus address!\n");
|
||
|
|
||
|
For an accesses originating from a PCI device (e.g. PCI bus masters), there
|
||
|
is no access to any CPUs local APIC. It'd go like:
|
||
|
|
||
|
if (address_is_within_an_I/O_APIC_area)
|
||
|
do_I/O_APIC_access();
|
||
|
else if (address_is_within_a_PCI_device_area)
|
||
|
do_PCI_access();
|
||
|
else if (address_is_within_RAM_area)
|
||
|
do_RAM_access();
|
||
|
else printf("Bogus address from PCI device!\n");
|
||
|
|
||
|
In both cases it is complicated by the configuration of the PCI host
|
||
|
controller/s and any "PCI to PCI" bridges. Fortunately this can be ignored
|
||
|
by Bochs as it doesn't support PCI bridges (except for the host controller
|
||
|
itself which can handle all accesses). Bochs may need to worry about the
|
||
|
"PCI to LPC" bridge though. For example, even though a PCI device can
|
||
|
read/write to the I/O APIC, an ISA device behind the PCI to LPC bridge
|
||
|
can't. This means for an ISA bus master you'd have something like:
|
||
|
|
||
|
if (address_is_within_a_PCI_device_area)
|
||
|
do_PCI_access();
|
||
|
else if (address_is_within_RAM_area)
|
||
|
do_RAM_access();
|
||
|
else printf("Bogus address from PCI device!\n");
|
||
|
|
||
|
This complicates things for the ISA DMA controllers, which should not be
|
||
|
able to read/write to the I/O APIC - for e.g. if the I/O APIC base is set
|
||
|
to 0x00000000, then an ISA DMA transfer that writes to 0x00000000 should
|
||
|
write to RAM not the I/O APIC (a PCI bus master would write to the I/O APIC
|
||
|
in the same situation).
|
||
|
|
||
|
I'm not convinced modelling real hardware 100% correctly is necessary
|
||
|
though - it would only matter for very rare situations (e.g. when the OS
|
||
|
stuffs things up badly). A normal OS will not stuff things up like this
|
||
|
(i.e. a normal OS won't map the I/O APIC to an area that overlaps RAM or
|
||
|
anything else). For OS developers (who might stuff things up), it'd
|
||
|
probably be better to panic anyway - e.g. "BX_PANIC: I/O APIC base set to
|
||
|
an address that overlaps RAM or a memory mapped device".
|
||
|
|
||
|
In general, the CPU has an "address bus" which consists of data lines,
|
||
|
address lines and 2 others lines. One of these other lines is the "I/O
|
||
|
select" line - if you do "mov [0x000000AA],al" and then do "out 0xAA,al"
|
||
|
you'd get almost the same thing on the CPUs bus (the only difference would
|
||
|
be the state of the "I/O select" line). When the CPU does an access that is
|
||
|
intended for I/O port space it just asserts the "I/O select" line.
|
||
|
|
||
|
The second line is for SMM which works just like the I/O select line.
|
||
|
|
||
|
When the CPU accesses a memory location normally the "SMM select" line is
|
||
|
not asserted and normal memory is accessed. When the CPU is in SMM mode the
|
||
|
"SMM select" line is asserted for memory accesses. This means that the CPU
|
||
|
can use 3 completely seperate address spaces (one for normal memory, one
|
||
|
for I/O space and another for SMRAM). How the chipset treats these lines
|
||
|
depends on what the CPU is used for - for example, these lines could be
|
||
|
ignored so that all types of accesses are the same (which means I/O port
|
||
|
instructions would access memory locations from 0x00000000 to 0x0000FFFF
|
||
|
and there'd be no seperate SMRAM area). For "PC compatible" computers the
|
||
|
"I/O select" line does select a completely seperate address space, but the
|
||
|
"SMM select" line does not. Instead, the chipset uses it to disable access
|
||
|
to the video display memory (and enable access to the RAM underneath).
|
||
|
|
||
|
Fortunately, access to the SMRAM area is also controlled by the chipset,
|
||
|
such that the CPU can access SMRAM regardless of whether it asserts it's
|
||
|
"SMM select" line or not. As mentioned in my previous email, for the I440FX
|
||
|
chipset it's called the System Management RAM Control Register (or SMRCR),
|
||
|
and is in the PCI host controller's PCI configuration space at offset 0x72.
|
||
|
|
||
|
Returning to what I wrote earlier, this leads to something like the
|
||
|
following for accesses originating from a CPU:
|
||
|
|
||
|
if (address_is_within_this_CPUs_local_APIC_area)
|
||
|
do_local_APIC_access();
|
||
|
else if ((CPU_is_in_SMM_mode || chipset_SMRCR_enabled) && address_is_within_SMM_area)
|
||
|
do_SMM_access();
|
||
|
else if (address_is_within_an_I/O_APIC_area)
|
||
|
do_I/O_APIC_access();
|
||
|
else if (address_is_within_a_PCI_device_area)
|
||
|
do_PCI_access();
|
||
|
else if (address_is_within_RAM_area)
|
||
|
do_RAM_access();
|
||
|
else printf("Bogus address!\n");
|