Skip to content

Commit

Permalink
x86/Sandy Bridge: reserve pages when integrated graphics is present
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Jesse Barnes authored and H. Peter Anvin committed Jan 11, 2013
1 parent 886d751 commit a9acc53
Showing 1 changed file with 78 additions and 0 deletions.
78 changes: 78 additions & 0 deletions arch/x86/kernel/setup.c
Original file line number Diff line number Diff line change
Expand Up @@ -610,6 +610,81 @@ 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;
static const 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;
}

/*
* Sandy Bridge graphics has trouble with certain ranges, exclude
* them from allocation.
*/
static void __init trim_snb_memory(void)
{
static const unsigned long bad_pages[] = {
0x20050000,
0x20110000,
0x20130000,
0x20138000,
0x40004000,
};
int i;

if (!snb_gfx_workaround_needed())
return;

printk(KERN_DEBUG "reserving inaccessible SNB gfx pages\n");

/*
* Reserve all memory below the 1 MB mark that has not
* already been reserved.
*/
memblock_reserve(0, 1<<20);

for (i = 0; i < ARRAY_SIZE(bad_pages); i++) {
if (memblock_reserve(bad_pages[i], PAGE_SIZE))
printk(KERN_WARNING "failed to reserve 0x%08lx\n",
bad_pages[i]);
}
}

/*
* Here we put platform-specific memory range workarounds, i.e.
* memory known to be corrupt or otherwise in need to be reserved on
* specific platforms.
*
* If this gets used more widely it could use a real dispatch mechanism.
*/
static void __init trim_platform_memory_ranges(void)
{
trim_snb_memory();
}

static void __init trim_bios_range(void)
{
/*
Expand All @@ -630,6 +705,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);
}

Expand Down Expand Up @@ -908,6 +984,8 @@ void __init setup_arch(char **cmdline_p)

setup_real_mode();

trim_platform_memory_ranges();

init_gbpages();

/* max_pfn_mapped is updated here */
Expand Down

0 comments on commit a9acc53

Please sign in to comment.