Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5394f80f authored by Jeremy Fitzhardinge's avatar Jeremy Fitzhardinge Committed by Ingo Molnar
Browse files

x86: check for and defend against BIOS memory corruption



Some BIOSes have been observed to corrupt memory in the low 64k.  This
change:
 - Reserves all memory which does not have to be in that area, to
   prevent it from being used as general memory by the kernel.  Things
   like the SMP trampoline are still in the memory, however.
 - Clears the reserved memory so we can observe changes to it.
 - Adds a function check_for_bios_corruption() which checks and reports on
   memory becoming unexpectedly non-zero.  Currently it's called in the
   x86 fault handler, and the powermanagement debug output.

Signed-off-by: default avatarJeremy Fitzhardinge <jeremy@goop.org>
Signed-off-by: default avatarIngo Molnar <mingo@elte.hu>
parent 7686ad56
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -360,6 +360,11 @@ and is between 256 and 4096 characters. It is defined in the file
			Format: <io>,<irq>,<mode>
			See header of drivers/net/hamradio/baycom_ser_hdx.c.

	bios_corruption_check=0/1 [X86]
			Some BIOSes seem to corrupt the first 64k of memory
			when doing things like suspend/resume.  Setting this
			option will scan the memory looking for corruption.

	boot_delay=	Milliseconds to delay each printk during boot.
			Values larger than 10 seconds (10000) are changed to
			no delay (0).
+3 −0
Original line number Diff line number Diff line
@@ -201,6 +201,9 @@ config X86_TRAMPOLINE
	depends on X86_SMP || (X86_VOYAGER && SMP) || (64BIT && ACPI_SLEEP)
	default y

config X86_CHECK_BIOS_CORRUPTION
        def_bool y

config KTIME_SCALAR
	def_bool X86_32
source "init/Kconfig"
+87 −0
Original line number Diff line number Diff line
@@ -578,6 +578,89 @@ static struct x86_quirks default_x86_quirks __initdata;

struct x86_quirks *x86_quirks __initdata = &default_x86_quirks;

/*
 * Some BIOSes seem to corrupt the low 64k of memory during events
 * like suspend/resume and unplugging an HDMI cable.  Reserve all
 * remaining free memory in that area and fill it with a distinct
 * pattern.
 */
#ifdef CONFIG_X86_CHECK_BIOS_CORRUPTION
#define MAX_SCAN_AREAS	8
static struct e820entry scan_areas[MAX_SCAN_AREAS];
static int num_scan_areas;

static void __init setup_bios_corruption_check(void)
{
	u64 addr = PAGE_SIZE;	/* assume first page is reserved anyway */

	while(addr < 0x10000 && num_scan_areas < MAX_SCAN_AREAS) {
		u64 size;
		addr = find_e820_area_size(addr, &size, PAGE_SIZE);

		if (addr == 0)
			break;

		if ((addr + size) > 0x10000)
			size = 0x10000 - addr;

		if (size == 0)
			break;

		e820_update_range(addr, size, E820_RAM, E820_RESERVED);
		scan_areas[num_scan_areas].addr = addr;
		scan_areas[num_scan_areas].size = size;
		num_scan_areas++;

		/* Assume we've already mapped this early memory */
		memset(__va(addr), 0, size);

		addr += size;
	}

	printk(KERN_INFO "scanning %d areas for BIOS corruption\n",
	       num_scan_areas);
	update_e820();
}

static int __read_mostly bios_corruption_check = 1;

void check_for_bios_corruption(void)
{
	int i;
	int corruption = 0;

	if (!bios_corruption_check)
		return;

	for(i = 0; i < num_scan_areas; i++) {
		unsigned long *addr = __va(scan_areas[i].addr);
		unsigned long size = scan_areas[i].size;

		for(; size; addr++, size -= sizeof(unsigned long)) {
			if (!*addr)
				continue;
			printk(KERN_ERR "Corrupted low memory at %p (%lx phys) = %08lx\n",
			       addr, __pa(addr), *addr);
			corruption = 1;
			*addr = 0;
		}
	}

	if (corruption)
		dump_stack();
}

static int set_bios_corruption_check(char *arg)
{
	char *end;

	bios_corruption_check = simple_strtol(arg, &end, 10);

	return (*end == 0) ? 0 : -EINVAL;
}
early_param("bios_corruption_check", set_bios_corruption_check);
#endif

/*
 * Determine if we were loaded by an EFI loader.  If so, then we have also been
 * passed the efi memmap, systab, etc., so we should use these data structures
@@ -750,6 +833,10 @@ void __init setup_arch(char **cmdline_p)
	high_memory = (void *)__va(max_pfn * PAGE_SIZE - 1) + 1;
#endif

#ifdef CONFIG_X86_CHECK_BIOS_CORRUPTION
	setup_bios_corruption_check();
#endif

	/* max_pfn_mapped is updated here */
	max_low_pfn_mapped = init_memory_mapping(0, max_low_pfn<<PAGE_SHIFT);
	max_pfn_mapped = max_low_pfn_mapped;
+2 −0
Original line number Diff line number Diff line
@@ -848,6 +848,8 @@ void __kprobes do_page_fault(struct pt_regs *regs, unsigned long error_code)
 * Oops. The kernel tried to access some bad page. We'll have to
 * terminate things with extreme prejudice.
 */
	check_for_bios_corruption();

#ifdef CONFIG_X86_32
	bust_spinlocks(1);
#else
+1 −0
Original line number Diff line number Diff line
@@ -254,6 +254,7 @@ static char *pm_verb(int event)

static void pm_dev_dbg(struct device *dev, pm_message_t state, char *info)
{
	check_for_bios_corruption();
	dev_dbg(dev, "%s%s%s\n", info, pm_verb(state.event),
		((state.event & PM_EVENT_SLEEP) && device_may_wakeup(dev)) ?
		", may wakeup" : "");
Loading