diff options
author | Jesse Barnes <jbarnes@virtuousgeek.org> | 2012-11-14 15:43:31 -0500 |
---|---|---|
committer | H. Peter Anvin <hpa@linux.intel.com> | 2013-01-11 17:26:38 -0500 |
commit | a9acc5365dbda29f7be2884efb63771dc24bd815 (patch) | |
tree | 15d39f3b236bc5956bbe487749852c326f9d1bc6 /arch | |
parent | 886d751a2ea99a160f2d0a472231566d9cb0cf58 (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.c | 78 |
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 | ||
611 | static unsigned reserve_low = CONFIG_X86_RESERVE_LOW << 10; | 611 | static unsigned reserve_low = CONFIG_X86_RESERVE_LOW << 10; |
612 | 612 | ||
613 | static 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 | */ | ||
647 | static 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 | */ | ||
683 | static void __init trim_platform_memory_ranges(void) | ||
684 | { | ||
685 | trim_snb_memory(); | ||
686 | } | ||
687 | |||
613 | static void __init trim_bios_range(void) | 688 | static 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 */ |