diff options
| author | Linn Crosetto <linn@hp.com> | 2013-08-13 17:46:41 -0400 |
|---|---|---|
| committer | H. Peter Anvin <hpa@linux.intel.com> | 2013-08-14 02:29:19 -0400 |
| commit | 30e46b574a1db7d14404e52dca8e1aa5f5155fd2 (patch) | |
| tree | 830c013b0e1bc37b77590df2998e664c89974571 | |
| parent | 2449f343e4adc778de1c3d45b5aa14fe788663f5 (diff) | |
x86: avoid remapping data in parse_setup_data()
Type SETUP_PCI, added by setup_efi_pci(), may advertise a ROM size
larger than early_memremap() is able to handle, which is currently
limited to 256kB. If this occurs it leads to a NULL dereference in
parse_setup_data().
To avoid this, remap the setup_data header and allow parsing functions
for individual types to handle their own data remapping.
Signed-off-by: Linn Crosetto <linn@hp.com>
Link: http://lkml.kernel.org/r/1376430401-67445-1-git-send-email-linn@hp.com
Acked-by: Yinghai Lu <yinghai@kernel.org>
Reviewed-by: Pekka Enberg <penberg@kernel.org>
Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
| -rw-r--r-- | arch/x86/include/asm/e820.h | 2 | ||||
| -rw-r--r-- | arch/x86/kernel/e820.c | 5 | ||||
| -rw-r--r-- | arch/x86/kernel/setup.c | 19 |
3 files changed, 13 insertions, 13 deletions
diff --git a/arch/x86/include/asm/e820.h b/arch/x86/include/asm/e820.h index cccd07fa5e3a..779c2efe2e97 100644 --- a/arch/x86/include/asm/e820.h +++ b/arch/x86/include/asm/e820.h | |||
| @@ -29,7 +29,7 @@ extern void e820_setup_gap(void); | |||
| 29 | extern int e820_search_gap(unsigned long *gapstart, unsigned long *gapsize, | 29 | extern int e820_search_gap(unsigned long *gapstart, unsigned long *gapsize, |
| 30 | unsigned long start_addr, unsigned long long end_addr); | 30 | unsigned long start_addr, unsigned long long end_addr); |
| 31 | struct setup_data; | 31 | struct setup_data; |
| 32 | extern void parse_e820_ext(struct setup_data *data); | 32 | extern void parse_e820_ext(u64 phys_addr, u32 data_len); |
| 33 | 33 | ||
| 34 | #if defined(CONFIG_X86_64) || \ | 34 | #if defined(CONFIG_X86_64) || \ |
| 35 | (defined(CONFIG_X86_32) && defined(CONFIG_HIBERNATION)) | 35 | (defined(CONFIG_X86_32) && defined(CONFIG_HIBERNATION)) |
diff --git a/arch/x86/kernel/e820.c b/arch/x86/kernel/e820.c index d32abeabbda5..174da5fc5a7b 100644 --- a/arch/x86/kernel/e820.c +++ b/arch/x86/kernel/e820.c | |||
| @@ -658,15 +658,18 @@ __init void e820_setup_gap(void) | |||
| 658 | * boot_params.e820_map, others are passed via SETUP_E820_EXT node of | 658 | * boot_params.e820_map, others are passed via SETUP_E820_EXT node of |
| 659 | * linked list of struct setup_data, which is parsed here. | 659 | * linked list of struct setup_data, which is parsed here. |
| 660 | */ | 660 | */ |
| 661 | void __init parse_e820_ext(struct setup_data *sdata) | 661 | void __init parse_e820_ext(u64 phys_addr, u32 data_len) |
| 662 | { | 662 | { |
| 663 | int entries; | 663 | int entries; |
| 664 | struct e820entry *extmap; | 664 | struct e820entry *extmap; |
| 665 | struct setup_data *sdata; | ||
| 665 | 666 | ||
| 667 | sdata = early_memremap(phys_addr, data_len); | ||
| 666 | entries = sdata->len / sizeof(struct e820entry); | 668 | entries = sdata->len / sizeof(struct e820entry); |
| 667 | extmap = (struct e820entry *)(sdata->data); | 669 | extmap = (struct e820entry *)(sdata->data); |
| 668 | __append_e820_map(extmap, entries); | 670 | __append_e820_map(extmap, entries); |
| 669 | sanitize_e820_map(e820.map, ARRAY_SIZE(e820.map), &e820.nr_map); | 671 | sanitize_e820_map(e820.map, ARRAY_SIZE(e820.map), &e820.nr_map); |
| 672 | early_iounmap(sdata, data_len); | ||
| 670 | printk(KERN_INFO "e820: extended physical RAM map:\n"); | 673 | printk(KERN_INFO "e820: extended physical RAM map:\n"); |
| 671 | e820_print_map("extended"); | 674 | e820_print_map("extended"); |
| 672 | } | 675 | } |
diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c index de337983f679..b6b45e4d6d3e 100644 --- a/arch/x86/kernel/setup.c +++ b/arch/x86/kernel/setup.c | |||
| @@ -426,25 +426,23 @@ static void __init reserve_initrd(void) | |||
| 426 | static void __init parse_setup_data(void) | 426 | static void __init parse_setup_data(void) |
| 427 | { | 427 | { |
| 428 | struct setup_data *data; | 428 | struct setup_data *data; |
| 429 | u64 pa_data; | 429 | u64 pa_data, pa_next; |
| 430 | 430 | ||
| 431 | pa_data = boot_params.hdr.setup_data; | 431 | pa_data = boot_params.hdr.setup_data; |
| 432 | while (pa_data) { | 432 | while (pa_data) { |
| 433 | u32 data_len, map_len; | 433 | u32 data_len, map_len, data_type; |
| 434 | 434 | ||
| 435 | map_len = max(PAGE_SIZE - (pa_data & ~PAGE_MASK), | 435 | map_len = max(PAGE_SIZE - (pa_data & ~PAGE_MASK), |
| 436 | (u64)sizeof(struct setup_data)); | 436 | (u64)sizeof(struct setup_data)); |
| 437 | data = early_memremap(pa_data, map_len); | 437 | data = early_memremap(pa_data, map_len); |
| 438 | data_len = data->len + sizeof(struct setup_data); | 438 | data_len = data->len + sizeof(struct setup_data); |
| 439 | if (data_len > map_len) { | 439 | data_type = data->type; |
| 440 | early_iounmap(data, map_len); | 440 | pa_next = data->next; |
| 441 | data = early_memremap(pa_data, data_len); | 441 | early_iounmap(data, map_len); |
| 442 | map_len = data_len; | ||
| 443 | } | ||
| 444 | 442 | ||
| 445 | switch (data->type) { | 443 | switch (data_type) { |
| 446 | case SETUP_E820_EXT: | 444 | case SETUP_E820_EXT: |
| 447 | parse_e820_ext(data); | 445 | parse_e820_ext(pa_data, data_len); |
| 448 | break; | 446 | break; |
| 449 | case SETUP_DTB: | 447 | case SETUP_DTB: |
| 450 | add_dtb(pa_data); | 448 | add_dtb(pa_data); |
| @@ -452,8 +450,7 @@ static void __init parse_setup_data(void) | |||
| 452 | default: | 450 | default: |
| 453 | break; | 451 | break; |
| 454 | } | 452 | } |
| 455 | pa_data = data->next; | 453 | pa_data = pa_next; |
| 456 | early_iounmap(data, map_len); | ||
| 457 | } | 454 | } |
| 458 | } | 455 | } |
| 459 | 456 | ||
