x86/boot: Reorganize and clean up the BIOS area reservation code
So the reserve_ebda_region() code has accumulated a number of problems over the years that make it really difficult to read and understand: - The calculation of 'lowmem' and 'ebda_addr' is an unnecessarily interleaved mess of first lowmem, then ebda_addr, then lowmem tweaks... - 'lowmem' here means 'super low mem' - i.e. 16-bit addressable memory. In other parts of the x86 code 'lowmem' means 32-bit addressable memory... This makes it super confusing to read. - It does not help at all that we have various memory range markers, half of which are 'start of range', half of which are 'end of range' - but this crucial property is not obvious in the naming at all ... gave me a headache trying to understand all this. - Also, the 'ebda_addr' name sucks: it highlights that it's an address (which is obvious, all values here are addresses!), while it does not highlight that it's the _start_ of the EBDA region ... - 'BIOS_LOWMEM_KILOBYTES' says a lot of things, except that this is the only value that is a pointer to a value, not a memory range address! - The function name itself is a misnomer: it says 'reserve_ebda_region()' while its main purpose is to reserve all the firmware ROM typically between 640K and 1MB, while the 'EBDA' part is only a small part of that ... - Likewise, the paravirt quirk flag name 'ebda_search' is misleading as well: this too should be about whether to reserve firmware areas in the paravirt case. - In fact thinking about this as 'end of RAM' is confusing: what this function *really* wants to reserve is firmware data and code areas! Once the thinking is inverted from a mixed 'ram' and 'reserved firmware area' notion to a pure 'reserved area' notion everything becomes a lot clearer. To improve all this rewrite the whole code (without changing the logic): - Firstly invert the naming from 'lowmem end' to 'BIOS reserved area start' and propagate this concept through all the variable names and constants. BIOS_RAM_SIZE_KB_PTR // was: BIOS_LOWMEM_KILOBYTES BIOS_START_MIN // was: INSANE_CUTOFF ebda_start // was: ebda_addr bios_start // was: lowmem BIOS_START_MAX // was: LOWMEM_CAP - Then clean up the name of the function itself by renaming it to reserve_bios_regions() and renaming the ::ebda_search paravirt flag to ::reserve_bios_regions. - Fix up all the comments (fix typos), harmonize and simplify their formulation and remove comments that become unnecessary due to the much better naming all around. Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
Родитель
4ff5308744
Коммит
edce21216a
|
@ -17,7 +17,7 @@ static inline unsigned int get_bios_ebda(void)
|
|||
return address; /* 0 means none */
|
||||
}
|
||||
|
||||
void reserve_ebda_region(void);
|
||||
void reserve_bios_regions(void);
|
||||
|
||||
#ifdef CONFIG_X86_CHECK_BIOS_CORRUPTION
|
||||
/*
|
||||
|
|
|
@ -168,14 +168,14 @@ struct x86_legacy_devices {
|
|||
* struct x86_legacy_features - legacy x86 features
|
||||
*
|
||||
* @rtc: this device has a CMOS real-time clock present
|
||||
* @ebda_search: it's safe to search for the EBDA signature in the hardware's
|
||||
* @reserve_bios_regions: it's safe to search for the EBDA signature in the hardware's
|
||||
* low RAM
|
||||
* @devices: legacy x86 devices, refer to struct x86_legacy_devices
|
||||
* documentation for further details.
|
||||
*/
|
||||
struct x86_legacy_features {
|
||||
int rtc;
|
||||
int ebda_search;
|
||||
int reserve_bios_regions;
|
||||
struct x86_legacy_devices devices;
|
||||
};
|
||||
|
||||
|
|
|
@ -6,66 +6,104 @@
|
|||
#include <asm/bios_ebda.h>
|
||||
|
||||
/*
|
||||
* This function reserves all conventional PC system BIOS related
|
||||
* firmware memory areas (some of which are data, some of which
|
||||
* are code), that must not be used by the kernel as available
|
||||
* RAM.
|
||||
*
|
||||
* The BIOS places the EBDA/XBDA at the top of conventional
|
||||
* memory, and usually decreases the reported amount of
|
||||
* conventional memory (int 0x12) too. This also contains a
|
||||
* workaround for Dell systems that neglect to reserve EBDA.
|
||||
* The same workaround also avoids a problem with the AMD768MPX
|
||||
* chipset: reserve a page before VGA to prevent PCI prefetch
|
||||
* into it (errata #56). Usually the page is reserved anyways,
|
||||
* unless you have no PS/2 mouse plugged in.
|
||||
* conventional memory (int 0x12) too.
|
||||
*
|
||||
* This functions is deliberately very conservative. Losing
|
||||
* memory in the bottom megabyte is rarely a problem, as long
|
||||
* as we have enough memory to install the trampoline. Using
|
||||
* memory that is in use by the BIOS or by some DMA device
|
||||
* the BIOS didn't shut down *is* a big problem.
|
||||
* This means that as a first approximation on most systems we can
|
||||
* guess the reserved BIOS area by looking at the low BIOS RAM size
|
||||
* value and assume that everything above that value (up to 1MB) is
|
||||
* reserved.
|
||||
*
|
||||
* But life in firmware country is not that simple:
|
||||
*
|
||||
* - This code also contains a quirk for Dell systems that neglect
|
||||
* to reserve the EBDA area in the 'RAM size' value ...
|
||||
*
|
||||
* - The same quirk also avoids a problem with the AMD768MPX
|
||||
* chipset: reserve a page before VGA to prevent PCI prefetch
|
||||
* into it (errata #56). (Usually the page is reserved anyways,
|
||||
* unless you have no PS/2 mouse plugged in.)
|
||||
*
|
||||
* - Plus paravirt systems don't have a reliable value in the
|
||||
* 'BIOS RAM size' pointer we can rely on, so we must quirk
|
||||
* them too.
|
||||
*
|
||||
* Due to those various problems this function is deliberately
|
||||
* very conservative and tries to err on the side of reserving
|
||||
* too much, to not risk reserving too little.
|
||||
*
|
||||
* Losing a small amount of memory in the bottom megabyte is
|
||||
* rarely a problem, as long as we have enough memory to install
|
||||
* the SMP bootup trampoline which *must* be in this area.
|
||||
*
|
||||
* Using memory that is in use by the BIOS or by some DMA device
|
||||
* the BIOS didn't shut down *is* a big problem to the kernel,
|
||||
* obviously.
|
||||
*/
|
||||
|
||||
#define BIOS_LOWMEM_KILOBYTES 0x413
|
||||
#define LOWMEM_CAP 0x9f000U /* Absolute maximum */
|
||||
#define INSANE_CUTOFF 0x20000U /* Less than this = insane */
|
||||
#define BIOS_RAM_SIZE_KB_PTR 0x413
|
||||
|
||||
void __init reserve_ebda_region(void)
|
||||
#define BIOS_START_MIN 0x20000U /* 128K, less than this is insane */
|
||||
#define BIOS_START_MAX 0x9f000U /* 640K, absolute maximum */
|
||||
|
||||
void __init reserve_bios_regions(void)
|
||||
{
|
||||
unsigned int lowmem, ebda_addr;
|
||||
unsigned int bios_start, ebda_start;
|
||||
|
||||
/*
|
||||
* To determine the position of the EBDA and the
|
||||
* end of conventional memory, we need to look at
|
||||
* the BIOS data area. In a paravirtual environment
|
||||
* that area is absent. We'll just have to assume
|
||||
* that the paravirt case can handle memory setup
|
||||
* correctly, without our help.
|
||||
* NOTE: In a paravirtual environment the BIOS reserved
|
||||
* area is absent. We'll just have to assume that the
|
||||
* paravirt case can handle memory setup correctly,
|
||||
* without our help.
|
||||
*/
|
||||
if (!x86_platform.legacy.ebda_search)
|
||||
if (!x86_platform.legacy.reserve_bios_regions)
|
||||
return;
|
||||
|
||||
/* end of low (conventional) memory */
|
||||
lowmem = *(unsigned short *)__va(BIOS_LOWMEM_KILOBYTES);
|
||||
lowmem <<= 10;
|
||||
|
||||
/* start of EBDA area */
|
||||
ebda_addr = get_bios_ebda();
|
||||
/* Get the start address of the EBDA page: */
|
||||
ebda_start = get_bios_ebda();
|
||||
|
||||
/*
|
||||
* Note: some old Dells seem to need 4k EBDA without
|
||||
* reporting so, so just consider the memory above 0x9f000
|
||||
* to be off limits (bugzilla 2990).
|
||||
* Quirk: some old Dells seem to have a 4k EBDA without
|
||||
* reporting so in their BIOS RAM size value, so just
|
||||
* consider the memory above 640K to be off limits
|
||||
* (bugzilla 2990).
|
||||
*
|
||||
* We detect this case by filtering for nonsensical EBDA
|
||||
* addresses below 128K, where we can assume that they
|
||||
* are bogus and bump it up to a fixed 640K value:
|
||||
*/
|
||||
if (ebda_start < BIOS_START_MIN)
|
||||
ebda_start = BIOS_START_MAX;
|
||||
|
||||
/* If the EBDA address is below 128K, assume it is bogus */
|
||||
if (ebda_addr < INSANE_CUTOFF)
|
||||
ebda_addr = LOWMEM_CAP;
|
||||
/*
|
||||
* BIOS RAM size is encoded in kilobytes, convert it
|
||||
* to bytes to get a first guess at where the BIOS
|
||||
* firmware area starts:
|
||||
*/
|
||||
bios_start = *(unsigned short *)__va(BIOS_RAM_SIZE_KB_PTR);
|
||||
bios_start <<= 10;
|
||||
|
||||
/* If lowmem is less than 128K, assume it is bogus */
|
||||
if (lowmem < INSANE_CUTOFF)
|
||||
lowmem = LOWMEM_CAP;
|
||||
/*
|
||||
* If bios_start is less than 128K, assume it is bogus
|
||||
* and bump it up to 640K:
|
||||
*/
|
||||
if (bios_start < BIOS_START_MIN)
|
||||
bios_start = BIOS_START_MAX;
|
||||
|
||||
/* Use the lower of the lowmem and EBDA markers as the cutoff */
|
||||
lowmem = min(lowmem, ebda_addr);
|
||||
lowmem = min(lowmem, LOWMEM_CAP); /* Absolute cap */
|
||||
/*
|
||||
* Use the lower of the bios_start and ebda_start
|
||||
* as the starting point, but don't allow it to
|
||||
* go beyond 640K:
|
||||
*/
|
||||
bios_start = min(bios_start, ebda_start);
|
||||
bios_start = min(bios_start, BIOS_START_MAX);
|
||||
|
||||
/* reserve all memory between lowmem and the 1MB mark */
|
||||
memblock_reserve(lowmem, 0x100000 - lowmem);
|
||||
/* Reserve all memory between bios_start and the 1MB mark: */
|
||||
memblock_reserve(bios_start, 0x100000 - bios_start);
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ static void __init i386_default_early_setup(void)
|
|||
x86_init.resources.reserve_resources = i386_reserve_resources;
|
||||
x86_init.mpparse.setup_ioapic_ids = setup_ioapic_ids_from_mpc;
|
||||
|
||||
reserve_ebda_region();
|
||||
reserve_bios_regions();
|
||||
}
|
||||
|
||||
asmlinkage __visible void __init i386_start_kernel(void)
|
||||
|
|
|
@ -183,7 +183,7 @@ void __init x86_64_start_reservations(char *real_mode_data)
|
|||
copy_bootdata(__va(real_mode_data));
|
||||
|
||||
x86_early_init_platform_quirks();
|
||||
reserve_ebda_region();
|
||||
reserve_bios_regions();
|
||||
|
||||
switch (boot_params.hdr.hardware_subarch) {
|
||||
case X86_SUBARCH_INTEL_MID:
|
||||
|
|
|
@ -7,12 +7,12 @@
|
|||
void __init x86_early_init_platform_quirks(void)
|
||||
{
|
||||
x86_platform.legacy.rtc = 1;
|
||||
x86_platform.legacy.ebda_search = 0;
|
||||
x86_platform.legacy.reserve_bios_regions = 0;
|
||||
x86_platform.legacy.devices.pnpbios = 1;
|
||||
|
||||
switch (boot_params.hdr.hardware_subarch) {
|
||||
case X86_SUBARCH_PC:
|
||||
x86_platform.legacy.ebda_search = 1;
|
||||
x86_platform.legacy.reserve_bios_regions = 1;
|
||||
break;
|
||||
case X86_SUBARCH_XEN:
|
||||
case X86_SUBARCH_LGUEST:
|
||||
|
|
Загрузка…
Ссылка в новой задаче