SNB graphics devices have a bug that prevent them from accessing certain memory ranges, namely anything below 1M and in the pages listed in the table. So reserve those at boot if set detect a SNB gfx device on the CPU to avoid GPU hangs. Stephane Marchesin had a similar patch to the page allocator awhile back, but rather than reserving pages up front, it leaked them at allocation time. Signed-off-by: Jesse Barnes <jbarnes at virtuousgeek.org> --- arch/x86/kernel/setup.c | 72 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c index 468e98d..bb9fabe 100644 --- a/arch/x86/kernel/setup.c +++ b/arch/x86/kernel/setup.c @@ -614,6 +614,75 @@ static __init void reserve_ibft_region(void) static unsigned reserve_low = CONFIG_X86_RESERVE_LOW << 10; +static bool __init snb_gfx_workaround_needed(void) +{ + int i; + u16 vendor, devid; + u16 snb_ids[] = { + 0x0102, + 0x0112, + 0x0122, + 0x0106, + 0x0116, + 0x0126, + 0x010a, + }; + + /* Assume no if something weird is going on with PCI */ + if (!early_pci_allowed()) + return false; + + vendor = read_pci_config_16(0, 2, 0, PCI_VENDOR_ID); + if (vendor != 0x8086) + return false; + + devid = read_pci_config_16(0, 2, 0, PCI_DEVICE_ID); + for (i = 0; i < ARRAY_SIZE(snb_ids); i++) + if (devid == snb_ids[i]) + return true; + + return false; +} + +static void __init trim_snb_ranges(void) +{ + /* + * Sandy Bridge graphics has trouble with certain ranges, exclude + * them from allocation. + */ + if (snb_gfx_workaround_needed()) { + unsigned long bad_ranges[] = { + 0x20050000, + 0x20110000, + 0x20130000, + 0x20138000, + 0x40004000, + }; + phys_addr_t mem; + int i; + + printk(KERN_DEBUG "reserving inaccessible SNB gfx pages\n"); + + for (;;) { + mem = memblock_find_in_range(0, 1<<20, PAGE_SIZE, + PAGE_SIZE); + if (!mem) + break; + memblock_reserve(mem, PAGE_SIZE); + printk(KERN_DEBUG "reserved 0x%08lx\n", mem); + } + + for (i = 0; i < ARRAY_SIZE(bad_ranges); i++) { + if (!memblock_reserve(bad_ranges[i], PAGE_SIZE)) + printk(KERN_DEBUG "reserved 0x%08lx\n", + bad_ranges[i]); + else + printk(KERN_DEBUG "failed to reserve 0x%08lx\n", + bad_ranges[i]); + } + } +} + static void __init trim_bios_range(void) { /* @@ -634,6 +703,7 @@ static void __init trim_bios_range(void) * take them out. */ e820_remove_range(BIOS_BEGIN, BIOS_END - BIOS_BEGIN, E820_RAM, 1); + sanitize_e820_map(e820.map, ARRAY_SIZE(e820.map), &e820.nr_map); } @@ -912,6 +982,8 @@ void __init setup_arch(char **cmdline_p) setup_real_mode(); + trim_snb_ranges(); + init_gbpages(); /* max_pfn_mapped is updated here */ -- 1.7.9.5