aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorJesse Barnes <jbarnes@virtuousgeek.org>2012-11-14 15:43:31 -0500
committerH. Peter Anvin <hpa@linux.intel.com>2013-01-11 17:26:38 -0500
commita9acc5365dbda29f7be2884efb63771dc24bd815 (patch)
tree15d39f3b236bc5956bbe487749852c326f9d1bc6 /arch
parent886d751a2ea99a160f2d0a472231566d9cb0cf58 (diff)
x86/Sandy Bridge: reserve pages when integrated graphics is present
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. [ hpa: made a number of stylistic changes, marked arrays as static const, and made less verbose; use "memblock=debug" for full verbosity. ] Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org> Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
Diffstat (limited to 'arch')
-rw-r--r--arch/x86/kernel/setup.c78
1 files changed, 78 insertions, 0 deletions
diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c
index 23ddd558fbd5..9dcb32545032 100644
--- a/arch/x86/kernel/setup.c
+++ b/arch/x86/kernel/setup.c
@@ -610,6 +610,81 @@ static __init void reserve_ibft_region(void)
610 610
611static unsigned reserve_low = CONFIG_X86_RESERVE_LOW << 10; 611static unsigned reserve_low = CONFIG_X86_RESERVE_LOW << 10;
612 612
613static bool __init snb_gfx_workaround_needed(void)
614{
615 int i;
616 u16 vendor, devid;
617 static const u16 snb_ids[] = {
618 0x0102,
619 0x0112,
620 0x0122,
621 0x0106,
622 0x0116,
623 0x0126,
624 0x010a,
625 };
626
627 /* Assume no if something weird is going on with PCI */
628 if (!early_pci_allowed())
629 return false;
630
631 vendor = read_pci_config_16(0, 2, 0, PCI_VENDOR_ID);
632 if (vendor != 0x8086)
633 return false;
634
635 devid = read_pci_config_16(0, 2, 0, PCI_DEVICE_ID);
636 for (i = 0; i < ARRAY_SIZE(snb_ids); i++)
637 if (devid == snb_ids[i])
638 return true;
639
640 return false;
641}
642
643/*
644 * Sandy Bridge graphics has trouble with certain ranges, exclude
645 * them from allocation.
646 */
647static void __init trim_snb_memory(void)
648{
649 static const unsigned long bad_pages[] = {
650 0x20050000,
651 0x20110000,
652 0x20130000,
653 0x20138000,
654 0x40004000,
655 };
656 int i;
657
658 if (!snb_gfx_workaround_needed())
659 return;
660
661 printk(KERN_DEBUG "reserving inaccessible SNB gfx pages\n");
662
663 /*
664 * Reserve all memory below the 1 MB mark that has not
665 * already been reserved.
666 */
667 memblock_reserve(0, 1<<20);
668
669 for (i = 0; i < ARRAY_SIZE(bad_pages); i++) {
670 if (memblock_reserve(bad_pages[i], PAGE_SIZE))
671 printk(KERN_WARNING "failed to reserve 0x%08lx\n",
672 bad_pages[i]);
673 }
674}
675
676/*
677 * Here we put platform-specific memory range workarounds, i.e.
678 * memory known to be corrupt or otherwise in need to be reserved on
679 * specific platforms.
680 *
681 * If this gets used more widely it could use a real dispatch mechanism.
682 */
683static void __init trim_platform_memory_ranges(void)
684{
685 trim_snb_memory();
686}
687
613static void __init trim_bios_range(void) 688static void __init trim_bios_range(void)
614{ 689{
615 /* 690 /*
@@ -630,6 +705,7 @@ static void __init trim_bios_range(void)
630 * take them out. 705 * take them out.
631 */ 706 */
632 e820_remove_range(BIOS_BEGIN, BIOS_END - BIOS_BEGIN, E820_RAM, 1); 707 e820_remove_range(BIOS_BEGIN, BIOS_END - BIOS_BEGIN, E820_RAM, 1);
708
633 sanitize_e820_map(e820.map, ARRAY_SIZE(e820.map), &e820.nr_map); 709 sanitize_e820_map(e820.map, ARRAY_SIZE(e820.map), &e820.nr_map);
634} 710}
635 711
@@ -908,6 +984,8 @@ void __init setup_arch(char **cmdline_p)
908 984
909 setup_real_mode(); 985 setup_real_mode();
910 986
987 trim_platform_memory_ranges();
988
911 init_gbpages(); 989 init_gbpages();
912 990
913 /* max_pfn_mapped is updated here */ 991 /* max_pfn_mapped is updated here */