Skip to contentSkip to conversation

x86, boot: clean up setup_data handling

PATCH In review Yinghai Lu 3 participants 12 messages Mar 8, 2015 – Mar 9, 2015

Subsystems (6)
Stable branch x86 PCI x86 MM +2 more subsystems
lore
Share & tools
lore

Yinghai Lu

author
#0 lore raw

v3 cover x86, boot: clean up setup_data handling

Now setup_data is reserved via memblock and e820 and different handlers have different ways, and it is confusing.
1. SETUP_E820_EXT: is consumed early and will not copy or access again. have memory wasted.
2. SETUP_EFI: is accessed via ioremap every time at early stage. have memory wasted.
3. SETUP_DTB: is copied locally.
have memory wasted.
4. SETUP_PCI: is accessed via ioremap for every pci devices, even run-time.
5. SETUP_KASLR: is accessed early, will not copy or access again. have memory wasted.

Also setup_data is exported to debugfs for debug purpose.

Here will convert to let every handler to decide how to handle it. and will not reserve the setup_data generally, so will not waste memory and also make memblock/e820 keep page aligned.
1. not touch E820 anymore.
2. copy SETUP_EFI to __initdata variable and access it without ioremap.
3. SETUP_DTB: reserver and copy to local and free.
4. SETUP_PCI: reverve localy and convert to list, to avoid keeping ioremap.
5. export SETUP_PCI via sysfs.

Also put them in:
git://git.kernel.org/pub/scm/linux/kernel/git/yinghai/linux-yinghai.git for-x86-4.0-rc2-setup_data

Should be materials for v4.1.

Thanks

Yinghai Lu

-v3: separated with kasl patches, and fix early_ioremap return checking.

Yinghai Lu (8):
x86: Kill E820_RESERVED_KERN
x86, efi: Copy SETUP_EFI data and access directly x86, of: Let add_dtb reserve setup_data locally x86, boot: Add add_pci handler for SETUP_PCI
x86: Kill not used setup_data handling code x86, boot, PCI: Convert SETUP_PCI data to list x86, boot, PCI: Copy SETUP_PCI rom to kernel space x86, boot, PCI: Export SETUP_PCI data via sysfs

 arch/x86/include/asm/efi.h       |   2 +-
 arch/x86/include/asm/pci.h       |   4 +
 arch/x86/include/asm/prom.h      |   9 +-
 arch/x86/include/uapi/asm/e820.h |   9 --
 arch/x86/kernel/devicetree.c     |  43 +++---
 arch/x86/kernel/e820.c           |   6 +-
 arch/x86/kernel/kdebugfs.c       | 142 ------------------
 arch/x86/kernel/setup.c          |  52 ++-----
 arch/x86/kernel/tboot.c          |   3 +-
 arch/x86/mm/init_64.c            |  11 +-
 arch/x86/pci/common.c            | 316 ++++++++++++++++++++++++++++++++++++---
 arch/x86/platform/efi/efi.c      |  13 +-
 arch/x86/platform/efi/efi_64.c   |  13 +-
 arch/x86/platform/efi/quirks.c   |  23 +--
 14 files changed, 371 insertions(+), 275 deletions(-)
Signature · 1 line
1.8.4.5
author
#1 lore raw

PATCH v3 1/8 x86: Kill E820_RESERVED_KERN

Now we are using memblock to do early resource reserver/allocation instead of using e820 map directly, and setup_data is reserved in memblock early already.
Also kexec generate setup_data and pass pointer to second kernel, so second kernel reserve setup_data by their own.
(Now kexec-tools create SETUP_EFI and SETUP_E820_EXT).

We can kill E820_RESERVED_KERN and not touch e820 map at all.

That will fix bug in mark_nonsave_region that can not handle that
case: E820_RAM and E820_RESERVED_KERN ranges are continuous and boundary is not page aligned.

Bugzilla: https://bugzilla.opensuse.org/show_bug.cgi?id=913885

---
 arch/x86/include/uapi/asm/e820.h |  9 ---------
 arch/x86/kernel/e820.c           |  6 ++----
 arch/x86/kernel/setup.c          | 26 --------------------------
 arch/x86/kernel/tboot.c          |  3 +--
 arch/x86/mm/init_64.c            | 11 ++++-------
 5 files changed, 7 insertions(+), 48 deletions(-)
arch/x86/include/uapi/asm/e820.h +0 -9
index d993e33..edc8a71 100644
@@ -33,15 +33,6 @@
 #define E820_NVS	4
 #define E820_UNUSABLE	5
 
-
-/*
- * reserved RAM used by kernel itself
- * if CONFIG_INTEL_TXT is enabled, memory of this type will be
- * included in the S3 integrity calculation and so should not include
- * any memory that BIOS might alter over the S3 transition
- */
-#define E820_RESERVED_KERN        128
-
 #ifndef __ASSEMBLY__
 #include <linux/types.h>
 struct e820entry {
arch/x86/kernel/e820.c +2 -4
index 46201de..2a6bed9 100644
@@ -134,7 +134,6 @@ static void __init e820_print_type(u32 type)
 {
 	switch (type) {
 	case E820_RAM:
-	case E820_RESERVED_KERN:
 		printk(KERN_CONT "usable");
 		break;
 	case E820_RESERVED:
@@ -688,7 +687,7 @@ void __init e820_mark_nosave_regions(unsigned long limit_pfn)
 			register_nosave_region(pfn, PFN_UP(ei->addr));
 
 		pfn = PFN_DOWN(ei->addr + ei->size);
-		if (ei->type != E820_RAM && ei->type != E820_RESERVED_KERN)
+		if (ei->type != E820_RAM)
 			register_nosave_region(PFN_UP(ei->addr), pfn);
 
 		if (pfn >= limit_pfn)
@@ -902,7 +901,6 @@ void __init finish_e820_parsing(void)
 static inline const char *e820_type_to_string(int e820_type)
 {
 	switch (e820_type) {
-	case E820_RESERVED_KERN:
 	case E820_RAM:	return "System RAM";
 	case E820_ACPI:	return "ACPI Tables";
 	case E820_NVS:	return "ACPI Non-volatile Storage";
@@ -1077,7 +1075,7 @@ void __init memblock_x86_fill(void)
 		if (end != (resource_size_t)end)
 			continue;
 
-		if (ei->type != E820_RAM && ei->type != E820_RESERVED_KERN)
+		if (ei->type != E820_RAM)
 			continue;
 
 		memblock_add(ei->addr, ei->size);
arch/x86/kernel/setup.c +0 -26
index 912f124..aed343b 100644
@@ -478,30 +478,6 @@ static void __init parse_setup_data(void)
 	}
 }
 
-static void __init e820_reserve_setup_data(void)
-{
-	struct setup_data *data;
-	u64 pa_data;
-	int found = 0;
-
-	pa_data = boot_params.hdr.setup_data;
-	while (pa_data) {
-		data = early_memremap(pa_data, sizeof(*data));
-		e820_update_range(pa_data, sizeof(*data)+data->len,
-			 E820_RAM, E820_RESERVED_KERN);
-		found = 1;
-		pa_data = data->next;
-		early_iounmap(data, sizeof(*data));
-	}
-	if (!found)
-		return;
-
-	sanitize_e820_map(e820.map, ARRAY_SIZE(e820.map), &e820.nr_map);
-	memcpy(&e820_saved, &e820, sizeof(struct e820map));
-	printk(KERN_INFO "extended physical RAM map:\n");
-	e820_print_map("reserve setup_data");
-}
-
 static void __init memblock_x86_reserve_range_setup_data(void)
 {
 	struct setup_data *data;
@@ -1037,8 +1013,6 @@ void __init setup_arch(char **cmdline_p)
 		early_dump_pci_devices();
 #endif
 
-	/* update the e820_saved too */
-	e820_reserve_setup_data();
 	finish_e820_parsing();
 
 	if (efi_enabled(EFI_BOOT))
arch/x86/kernel/tboot.c +1 -2
index 91a4496..3c2752a 100644
@@ -195,8 +195,7 @@ static int tboot_setup_sleep(void)
 	tboot->num_mac_regions = 0;
 
 	for (i = 0; i < e820.nr_map; i++) {
-		if ((e820.map[i].type != E820_RAM)
-		 && (e820.map[i].type != E820_RESERVED_KERN))
+		if (e820.map[i].type != E820_RAM)
 			continue;
 
 		add_mac_region(e820.map[i].addr, e820.map[i].size);
arch/x86/mm/init_64.c +4 -7
index c30efb6..63520ec 100644
@@ -354,8 +354,7 @@ phys_pte_init(pte_t *pte_page, unsigned long addr, unsigned long end,
 		next = (addr & PAGE_MASK) + PAGE_SIZE;
 		if (addr >= end) {
 			if (!after_bootmem &&
-			    !e820_any_mapped(addr & PAGE_MASK, next, E820_RAM) &&
-			    !e820_any_mapped(addr & PAGE_MASK, next, E820_RESERVED_KERN))
+			    !e820_any_mapped(addr & PAGE_MASK, next, E820_RAM))
 				set_pte(pte, __pte(0));
 			continue;
 		}
@@ -401,9 +400,8 @@ phys_pmd_init(pmd_t *pmd_page, unsigned long address, unsigned long end,
 
 		next = (address & PMD_MASK) + PMD_SIZE;
 		if (address >= end) {
-			if (!after_bootmem &&
-			    !e820_any_mapped(address & PMD_MASK, next, E820_RAM) &&
-			    !e820_any_mapped(address & PMD_MASK, next, E820_RESERVED_KERN))
+			if (!after_bootmem && !e820_any_mapped(
+					address & PMD_MASK, next, E820_RAM))
 				set_pmd(pmd, __pmd(0));
 			continue;
 		}
@@ -476,8 +474,7 @@ phys_pud_init(pud_t *pud_page, unsigned long addr, unsigned long end,
 		next = (addr & PUD_MASK) + PUD_SIZE;
 		if (addr >= end) {
 			if (!after_bootmem &&
-			    !e820_any_mapped(addr & PUD_MASK, next, E820_RAM) &&
-			    !e820_any_mapped(addr & PUD_MASK, next, E820_RESERVED_KERN))
+			    !e820_any_mapped(addr & PUD_MASK, next, E820_RAM))
 				set_pud(pud, __pud(0));
 			continue;
 		}
Signature · 1 line
1.8.4.5
#2 lore raw

Re: [PATCH v3 1/8] x86: Kill E820_RESERVED_KERN

Quoted 7 lines · Yinghai Lu

Now we are using memblock to do early resource reserver/allocation instead of using e820 map directly, and setup_data is reserved in memblock early already.
Also kexec generate setup_data and pass pointer to second kernel, so second kernel reserve setup_data by their own.
(Now kexec-tools create SETUP_EFI and SETUP_E820_EXT).

Quoted 7 lines

We can kill E820_RESERVED_KERN and not touch e820 map at all.

That will fix bug in mark_nonsave_region that can not handle that
case: E820_RAM and E820_RESERVED_KERN ranges are continuous and boundary is not page aligned.

Bugzilla: https://bugzilla.opensuse.org/show_bug.cgi?id=913885

Is this the bug referenced in the commit message that is fixed? If so, it's only a bug for resume, correct? I'm not sure if that's clear enough just from the commit message, I was looking at this patch for an e820 problem I'm currently facing on 3.3.

Quoted 5 lines

Hmm, although the bug is reported for a 3.12 kernel, I assume this is for stable 3.10+? If so, it should apply fine with the exception of removing e820_reserve_setup_data() from setup_arch() rather than memblock_x86_reserve_range_setup_data(). Or is it for 3.2 as well and needs to be completely rebased for that kernel?

author
#3 lore raw

Re: [PATCH v3 1/8] x86: Kill E820_RESERVED_KERN

Quoted 6 lines · David Rientjes

Hmm, although the bug is reported for a 3.12 kernel, I assume this is for stable 3.10+? If so, it should apply fine with the exception of removing e820_reserve_setup_data() from setup_arch() rather than memblock_x86_reserve_range_setup_data(). Or is it for 3.2 as well and needs to be completely rebased for that kernel?

For 3.10+, we will need to following patches, otherwise will have warning for SETUP_PCI with ioremap.

for 3.2 that does not SETUP_PCI, should be ok, but will need rebase.

Thanks

Yinghai

#4 lore raw

Re: [PATCH v3 1/8] x86: Kill E820_RESERVED_KERN

In reply to David Rientjes
Quoted 15 lines · David Rientjes
Quoted 13 lines · Yinghai Lu

Now we are using memblock to do early resource reserver/allocation instead of using e820 map directly, and setup_data is reserved in memblock early already.
Also kexec generate setup_data and pass pointer to second kernel, so second kernel reserve setup_data by their own.
(Now kexec-tools create SETUP_EFI and SETUP_E820_EXT).

We can kill E820_RESERVED_KERN and not touch e820 map at all.

That will fix bug in mark_nonsave_region that can not handle that
case: E820_RAM and E820_RESERVED_KERN ranges are continuous and boundary is not page aligned.

Quoted 6 lines
Quoted 1 line

Bugzilla: https://bugzilla.opensuse.org/show_bug.cgi?id=913885

Is this the bug referenced in the commit message that is fixed? If so, it's only a bug for resume, correct? I'm not sure if that's clear enough just from the commit message, I was looking at this patch for an e820 problem I'm currently facing on 3.3.

Yinghai's patches fixed the e820 not page aligned issue that's one of the issues on bug reporter's machine. I found another issue of the BIOS that sometimes it doesn't really keep the e820 table unchanging for hibernate resuming, this BIOS issue causes the total available page number checking fail. I will file another openSUSE bug to separate those 2 issues.

Quoted 7 lines
Quoted 6 lines
Quoted 5 lines

Hmm, although the bug is reported for a 3.12 kernel, I assume this is for stable 3.10+? If so, it should apply fine with the exception of removing e820_reserve_setup_data() from setup_arch() rather than memblock_x86_reserve_range_setup_data(). Or is it for 3.2 as well and needs to be completely rebased for that kernel?

Thanks a lot!
Joey Lee

author
#5 lore raw

PATCH v3 2/8 x86, efi: Copy SETUP_EFI data and access directly

In reply to Yinghai Lu

The copy will be in __initdata, and it is small.

We can use pointer to access the setup_data instead of using early_memmap everywhere.

---
 arch/x86/include/asm/efi.h     |  2 +-
 arch/x86/platform/efi/efi.c    | 13 ++-----------
 arch/x86/platform/efi/efi_64.c | 13 ++++++++++++-
 arch/x86/platform/efi/quirks.c | 23 ++++++-----------------
 4 files changed, 21 insertions(+), 30 deletions(-)
arch/x86/include/asm/efi.h +1 -1
index 25bce45..edbecd6 100644
@@ -114,7 +114,7 @@ struct efi_setup_data {
 	u64 reserved[8];
 };
 
-extern u64 efi_setup;
+extern struct efi_setup_data *efi_setup;
 
 #ifdef CONFIG_EFI
 
arch/x86/platform/efi/efi.c +2 -11
index dbc8627..1cd38e8 100644
@@ -68,7 +68,7 @@ static efi_config_table_type_t arch_tables[] __initdata = {
 	{NULL_GUID, NULL, NULL},
 };
 
-u64 efi_setup;		/* efi setup_data physical address */
+struct efi_setup_data *efi_setup __initdata; /* cached efi setup_data pointer */
 
 static int add_efi_memmap __initdata;
 static int __init setup_add_efi_memmap(char *arg)
@@ -225,20 +225,13 @@ static int __init efi_systab_init(void *phys)
 {
 	if (efi_enabled(EFI_64BIT)) {
 		efi_system_table_64_t *systab64;
-		struct efi_setup_data *data = NULL;
+		struct efi_setup_data *data = efi_setup;
 		u64 tmp = 0;
 
-		if (efi_setup) {
-			data = early_memremap(efi_setup, sizeof(*data));
-			if (!data)
-				return -ENOMEM;
-		}
 		systab64 = early_memremap((unsigned long)phys,
 					 sizeof(*systab64));
 		if (systab64 == NULL) {
 			pr_err("Couldn't map the system table!\n");
-			if (data)
-				early_memunmap(data, sizeof(*data));
 			return -ENOMEM;
 		}
 
@@ -271,8 +264,6 @@ static int __init efi_systab_init(void *phys)
 		tmp |= data ? data->tables : systab64->tables;
 
 		early_memunmap(systab64, sizeof(*systab64));
-		if (data)
-			early_memunmap(data, sizeof(*data));
 #ifdef CONFIG_X86_32
 		if (tmp >> 32) {
 			pr_err("EFI data located above 4GB, disabling EFI.\n");
arch/x86/platform/efi/efi_64.c +12 -1
index 17e80d8..a541c6c 100644
@@ -292,9 +292,20 @@ void __iomem *__init efi_ioremap(unsigned long phys_addr, unsigned long size,
 	return (void __iomem *)__va(phys_addr);
 }
 
+static struct efi_setup_data efi_setup_data __initdata;
+
 void __init parse_efi_setup(u64 phys_addr, u32 data_len)
 {
-	efi_setup = phys_addr + sizeof(struct setup_data);
+	struct efi_setup_data *data;
+
+	data = early_memremap(phys_addr + sizeof(struct setup_data),
+			      sizeof(*data));
+	if (!data)
+		return;
+
+	efi_setup_data = *data;
+	early_memunmap(data, sizeof(*data));
+	efi_setup = &efi_setup_data;
 }
 
 void __init efi_runtime_mkexec(void)
arch/x86/platform/efi/quirks.c +6 -17
index 1c7380d..45fec7d 100644
@@ -203,9 +203,8 @@ void __init efi_free_boot_services(void)
  */
 int __init efi_reuse_config(u64 tables, int nr_tables)
 {
-	int i, sz, ret = 0;
+	int i, sz;
 	void *p, *tablep;
-	struct efi_setup_data *data;
 
 	if (!efi_setup)
 		return 0;
@@ -213,22 +212,15 @@ int __init efi_reuse_config(u64 tables, int nr_tables)
 	if (!efi_enabled(EFI_64BIT))
 		return 0;
 
-	data = early_memremap(efi_setup, sizeof(*data));
-	if (!data) {
-		ret = -ENOMEM;
-		goto out;
-	}
-
-	if (!data->smbios)
-		goto out_memremap;
+	if (!efi_setup->smbios)
+		return 0;
 
 	sz = sizeof(efi_config_table_64_t);
 
 	p = tablep = early_memremap(tables, nr_tables * sz);
 	if (!p) {
 		pr_err("Could not map Configuration table!\n");
-		ret = -ENOMEM;
-		goto out_memremap;
+		return -ENOMEM;
 	}
 
 	for (i = 0; i < efi.systab->nr_tables; i++) {
@@ -237,15 +229,12 @@ int __init efi_reuse_config(u64 tables, int nr_tables)
 		guid = ((efi_config_table_64_t *)p)->guid;
 
 		if (!efi_guidcmp(guid, SMBIOS_TABLE_GUID))
-			((efi_config_table_64_t *)p)->table = data->smbios;
+			((efi_config_table_64_t *)p)->table = efi_setup->smbios;
 		p += sz;
 	}
 	early_memunmap(tablep, nr_tables * sz);
 
-out_memremap:
-	early_memunmap(data, sizeof(*data));
-out:
-	return ret;
+	return 0;
 }
 
 void __init efi_apply_memmap_quirks(void)
Signature · 1 line
1.8.4.5
author
#6 lore raw

PATCH v3 3/8 x86, of: Let add_dtb reserve setup_data locally

In reply to Yinghai Lu

We will not reserve setup_data in generic code. Every handler need to reserve and copy setup_data locally.

Current dtd handling already have code for copying, just add reserve code.

Also simplify code a bit by storing real dtb size.

Cc Rob Herring <[email protected]>
Cc David Vrabel <[email protected]>
---
 arch/x86/include/asm/prom.h  |  9 ++++++---
 arch/x86/kernel/devicetree.c | 43 +++++++++++++++++++++++++------------------
 2 files changed, 31 insertions(+), 21 deletions(-)
arch/x86/include/asm/prom.h +6 -3
index 1d081ac..fb716eddc 100644
@@ -24,17 +24,20 @@
 
 #ifdef CONFIG_OF
 extern int of_ioapic;
-extern u64 initial_dtb;
-extern void add_dtb(u64 data);
 void x86_of_pci_init(void);
 void x86_dtb_init(void);
 #else
-static inline void add_dtb(u64 data) { }
 static inline void x86_of_pci_init(void) { }
 static inline void x86_dtb_init(void) { }
 #define of_ioapic 0
 #endif
 
+#ifdef CONFIG_OF_FLATTREE
+extern void add_dtb(u64 data);
+#else
+static inline void add_dtb(u64 data) { }
+#endif
+
 extern char cmd_line[COMMAND_LINE_SIZE];
 
 #endif /* __ASSEMBLY__ */
arch/x86/kernel/devicetree.c +25 -18

80 lines not on this page · raw message

Signature · 1 line
1.8.4.5
author
#7 lore raw

PATCH v3 4/8 x86, boot: Add add_pci handler for SETUP_PCI

In reply to Yinghai Lu

Let it reserve setup_data, and keep it's own list.

Also clear the hdr.setup_data, as all handler now handle or reserve setup_data locally already.

---
 arch/x86/include/asm/pci.h |  2 ++
 arch/x86/kernel/setup.c    |  8 ++++++++
 arch/x86/pci/common.c      | 45 +++++++++++++++++++++++++++++++--------------
 3 files changed, 41 insertions(+), 14 deletions(-)
arch/x86/include/asm/pci.h +2 -0
index 4e370a5..7fbd5f3 100644
@@ -87,8 +87,10 @@ static inline void pci_dma_burst_advice(struct pci_dev *pdev,
 	*strat = PCI_DMA_BURST_INFINITY;
 	*strategy_parameter = ~0UL;
 }
+void add_pci(u64 pa_data);
 #else
 static inline void early_quirks(void) { }
+static inline void add_pci(u64 pa_data) { }
 #endif
 
 extern void pci_iommu_alloc(void);
arch/x86/kernel/setup.c +8 -0
index aed343b..94f95e0 100644
@@ -458,6 +458,8 @@ static void __init parse_setup_data(void)
 		pa_next = data->next;
 		early_iounmap(data, sizeof(*data));
 
+		printk(KERN_DEBUG "setup_data type: %d @ %#010llx\n",
+				data_type, pa_data);
 		switch (data_type) {
 		case SETUP_E820_EXT:
 			parse_e820_ext(pa_data, data_len);
@@ -465,6 +467,9 @@ static void __init parse_setup_data(void)
 		case SETUP_DTB:
 			add_dtb(pa_data);
 			break;
+		case SETUP_PCI:
+			add_pci(pa_data);
+			break;
 		case SETUP_EFI:
 			parse_efi_setup(pa_data, data_len);
 			break;
@@ -472,10 +477,13 @@ static void __init parse_setup_data(void)
 			parse_kaslr_setup(pa_data, data_len);
 			break;
 		default:
+			pr_warn("Unknown setup_data type: %d @ %#010llx ignored!\n",
+				data_type, pa_data);
 			break;
 		}
 		pa_data = pa_next;
 	}
+	boot_params.hdr.setup_data = 0; /* all done */
 }
 
 static void __init memblock_x86_reserve_range_setup_data(void)
arch/x86/pci/common.c +31 -14

68 lines not on this page · raw message

Signature · 1 line
1.8.4.5
author
#8 lore raw

PATCH v3 5/8 x86: Kill not used setup_data handling code

In reply to Yinghai Lu
---
 arch/x86/kernel/kdebugfs.c | 142 ---------------------------------------------
 arch/x86/kernel/setup.c    |  17 ------
 2 files changed, 159 deletions(-)
arch/x86/kernel/kdebugfs.c +0 -142
index dc1404b..c8ca86c 100644
@@ -21,142 +21,6 @@ struct dentry *arch_debugfs_dir;
 EXPORT_SYMBOL(arch_debugfs_dir);
 
 #ifdef CONFIG_DEBUG_BOOT_PARAMS
-struct setup_data_node {
-	u64 paddr;
-	u32 type;
-	u32 len;
-};
-
-static ssize_t setup_data_read(struct file *file, char __user *user_buf,
-			       size_t count, loff_t *ppos)
-{
-	struct setup_data_node *node = file->private_data;
-	unsigned long remain;
-	loff_t pos = *ppos;
-	struct page *pg;
-	void *p;
-	u64 pa;
-
-	if (pos < 0)
-		return -EINVAL;
-
-	if (pos >= node->len)
-		return 0;
-
-	if (count > node->len - pos)
-		count = node->len - pos;
-
-	pa = node->paddr + sizeof(struct setup_data) + pos;
-	pg = pfn_to_page((pa + count - 1) >> PAGE_SHIFT);
-	if (PageHighMem(pg)) {
-		p = ioremap_cache(pa, count);
-		if (!p)
-			return -ENXIO;
-	} else
-		p = __va(pa);
-
-	remain = copy_to_user(user_buf, p, count);
-
-	if (PageHighMem(pg))
-		iounmap(p);
-
-	if (remain)
-		return -EFAULT;
-
-	*ppos = pos + count;
-
-	return count;
-}
-
-static const struct file_operations fops_setup_data = {
-	.read		= setup_data_read,
-	.open		= simple_open,
-	.llseek		= default_llseek,
-};
-
-static int __init
-create_setup_data_node(struct dentry *parent, int no,
-		       struct setup_data_node *node)
-{
-	struct dentry *d, *type, *data;
-	char buf[16];
-
-	sprintf(buf, "%d", no);
-	d = debugfs_create_dir(buf, parent);
-	if (!d)
-		return -ENOMEM;
-
-	type = debugfs_create_x32("type", S_IRUGO, d, &node->type);
-	if (!type)
-		goto err_dir;
-
-	data = debugfs_create_file("data", S_IRUGO, d, node, &fops_setup_data);
-	if (!data)
-		goto err_type;
-
-	return 0;
-
-err_type:
-	debugfs_remove(type);
-err_dir:
-	debugfs_remove(d);
-	return -ENOMEM;
-}
-
-static int __init create_setup_data_nodes(struct dentry *parent)
-{
-	struct setup_data_node *node;
-	struct setup_data *data;
-	int error;
-	struct dentry *d;
-	struct page *pg;
-	u64 pa_data;
-	int no = 0;
-
-	d = debugfs_create_dir("setup_data", parent);
-	if (!d)
-		return -ENOMEM;
-
-	pa_data = boot_params.hdr.setup_data;
-
-	while (pa_data) {
-		node = kmalloc(sizeof(*node), GFP_KERNEL);
-		if (!node) {
-			error = -ENOMEM;
-			goto err_dir;
-		}
-
-		pg = pfn_to_page((pa_data+sizeof(*data)-1) >> PAGE_SHIFT);
-		if (PageHighMem(pg)) {
-			data = ioremap_cache(pa_data, sizeof(*data));
-			if (!data) {
-				kfree(node);
-				error = -ENXIO;
-				goto err_dir;
-			}
-		} else
-			data = __va(pa_data);
-
-		node->paddr = pa_data;
-		node->type = data->type;
-		node->len = data->len;
-		error = create_setup_data_node(d, no, node);
-		pa_data = data->next;
-
-		if (PageHighMem(pg))
-			iounmap(data);
-		if (error)
-			goto err_dir;
-		no++;
-	}
-
-	return 0;
-
-err_dir:
-	debugfs_remove(d);
-	return error;
-}
-
 static struct debugfs_blob_wrapper boot_params_blob = {
 	.data		= &boot_params,
 	.size		= sizeof(boot_params),
@@ -181,14 +45,8 @@ static int __init boot_params_kdebugfs_init(void)
 	if (!data)
 		goto err_version;
 
-	error = create_setup_data_nodes(dbp);
-	if (error)
-		goto err_data;
-
 	return 0;
 
-err_data:
-	debugfs_remove(data);
 err_version:
 	debugfs_remove(version);
 err_dir:
arch/x86/kernel/setup.c +0 -17
index 94f95e0..a7e688c 100644
@@ -486,20 +486,6 @@ static void __init parse_setup_data(void)
 	boot_params.hdr.setup_data = 0; /* all done */
 }
 
-static void __init memblock_x86_reserve_range_setup_data(void)
-{
-	struct setup_data *data;
-	u64 pa_data;
-
-	pa_data = boot_params.hdr.setup_data;
-	while (pa_data) {
-		data = early_memremap(pa_data, sizeof(*data));
-		memblock_reserve(pa_data, sizeof(*data) + data->len);
-		pa_data = data->next;
-		early_iounmap(data, sizeof(*data));
-	}
-}
-
 /*
  * --------- Crashkernel reservation ------------------------------
  */
@@ -1006,9 +992,6 @@ void __init setup_arch(char **cmdline_p)
 
 	x86_report_nx();
 
-	/* after early param, so could get panic from serial */
-	memblock_x86_reserve_range_setup_data();
-
 	if (acpi_mps_check()) {
 #ifdef CONFIG_X86_LOCAL_APIC
 		disable_apic = 1;
Signature · 1 line
1.8.4.5
author
#9 lore raw

PATCH v3 6/8 x86, boot, PCI: Convert SETUP_PCI data to list

In reply to Yinghai Lu

So we could avoid ioremap every time later.

---
 arch/x86/include/asm/pci.h |  2 ++
 arch/x86/kernel/setup.c    |  1 +
 arch/x86/pci/common.c      | 77 +++++++++++++++++++++++++++++++++++++---------
 3 files changed, 65 insertions(+), 15 deletions(-)
arch/x86/include/asm/pci.h +2 -0
index 7fbd5f3..99b261f 100644
@@ -88,9 +88,11 @@ static inline void pci_dma_burst_advice(struct pci_dev *pdev,
 	*strategy_parameter = ~0UL;
 }
 void add_pci(u64 pa_data);
+int fill_setup_pci_entries(void);
 #else
 static inline void early_quirks(void) { }
 static inline void add_pci(u64 pa_data) { }
+static inline int fill_setup_pci_entries(void) { }
 #endif
 
 extern void pci_iommu_alloc(void);
arch/x86/kernel/setup.c +1 -0
index a7e688c..39940a7 100644
@@ -1195,6 +1195,7 @@ void __init setup_arch(char **cmdline_p)
 	acpi_boot_init();
 	sfi_init();
 	x86_dtb_init();
+	fill_setup_pci_entries();
 
 	/*
 	 * get boot-time SMP configuration:
arch/x86/pci/common.c +62 -15

105 lines not on this page · raw message

Signature · 1 line
1.8.4.5
author
#10 lore raw

PATCH v3 7/8 x86, boot, PCI: Copy SETUP_PCI rom to kernel space

In reply to Yinghai Lu

As EFI stub code could put them high when on 32bit or with exactmap= on 64bit conf.

Check if the range is mapped, otherwise allocate new one and have the rom data copied. So we could access them directly.

---
 arch/x86/pci/common.c | 47 +++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 45 insertions(+), 2 deletions(-)
arch/x86/pci/common.c +45 -2
index 67dc2a9..15e1b3f 100644
@@ -697,6 +697,48 @@ struct firmware_setup_pci_entry {
 
 static LIST_HEAD(setup_pci_entries);
 
+static phys_addr_t check_copy(phys_addr_t start, unsigned long size)
+{
+	unsigned long start_pfn = PFN_DOWN(start);
+	unsigned long end_pfn = PFN_UP(start + size);
+	unsigned char *p, *q;
+	phys_addr_t pa_p, pa_q;
+	long sz = size;
+
+	if (pfn_range_is_mapped(start_pfn, end_pfn))
+		return start;
+
+	/* allocate and copy */
+	pa_p = memblock_alloc(size, PAGE_SIZE);
+	if (!pa_p)
+		return start;
+
+	p = phys_to_virt(pa_p);
+
+	pa_q = start;
+	while (sz > 0) {
+		long chunk_size = 64<<10;
+
+		if (chunk_size > sz)
+			chunk_size = sz;
+
+		q = early_memremap(pa_q, chunk_size);
+		if (!q) {
+			memblock_free(pa_p, size);
+			return start;
+		}
+		memcpy(p, q, chunk_size);
+		early_memunmap(q, chunk_size);
+		p += chunk_size;
+		pa_q += chunk_size;
+		sz -= chunk_size;
+	}
+
+	memblock_free(start, size);
+
+	return pa_p;
+}
+
 int __init fill_setup_pci_entries(void)
 {
 	struct setup_data *data;
@@ -726,8 +768,9 @@ int __init fill_setup_pci_entries(void)
 		entry->vendor = rom->vendor;
 		entry->devid = rom->devid;
 		entry->pcilen = rom->pcilen;
-		entry->romdata = pa_data +
-				 offsetof(struct pci_setup_rom, romdata);
+		entry->romdata = check_copy(pa_data +
+				      offsetof(struct pci_setup_rom, romdata),
+				      rom->pcilen);
 
 		list_add(&entry->list, &setup_pci_entries);
 
Signature · 1 line
1.8.4.5
author
#11 lore raw

PATCH v3 8/8 x86, boot, PCI: Export SETUP_PCI data via sysfs

In reply to Yinghai Lu

So we could let kexec-tools to rebuild SETUP_PCI and pass it to second kernel if needed.

Now kexec-tools already build SETUP_EFI and SETUP_E820EXT.

---
 arch/x86/pci/common.c | 175 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 175 insertions(+)
arch/x86/pci/common.c +175 -0
index 15e1b3f..502e707 100644
@@ -685,6 +685,8 @@ void __init add_pci(u64 pa_data)
 
 struct firmware_setup_pci_entry {
 	struct list_head list;
+	struct kobject kobj;
+	struct bin_attribute *rom_attr;
 	uint16_t vendor;
 	uint16_t devid;
 	uint64_t pcilen;
@@ -806,6 +808,179 @@ int pcibios_add_device(struct pci_dev *dev)
 	return 0;
 }
 
+#ifdef CONFIG_SYSFS
+static inline struct firmware_setup_pci_entry *
+to_setup_pci_entry(struct kobject *kobj)
+{
+	return container_of(kobj, struct firmware_setup_pci_entry, kobj);
+}
+
+static ssize_t vendor_show(struct firmware_setup_pci_entry *entry, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "0x%04llx\n",
+			(unsigned long long)entry->vendor);
+}
+
+static ssize_t devid_show(struct firmware_setup_pci_entry *entry, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "0x%04llx\n",
+			(unsigned long long)entry->devid);
+}
+
+static ssize_t pcilen_show(struct firmware_setup_pci_entry *entry, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "0x%llx\n",
+			(unsigned long long)entry->pcilen);
+}
+
+static ssize_t segment_show(struct firmware_setup_pci_entry *entry, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "0x%04llx\n",
+			(unsigned long long)entry->segment);
+}
+
+static ssize_t bus_show(struct firmware_setup_pci_entry *entry, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "0x%02llx\n",
+			(unsigned long long)entry->bus);
+}
+
+static ssize_t device_show(struct firmware_setup_pci_entry *entry, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "0x%02llx\n",
+			(unsigned long long)entry->device);
+}
+
+static ssize_t function_show(struct firmware_setup_pci_entry *entry, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "0x%1llx\n",
+			(unsigned long long)entry->function);
+}
+
+struct setup_pci_attribute {
+	struct attribute attr;
+	ssize_t (*show)(struct firmware_setup_pci_entry *entry, char *buf);
+};
+
+static inline struct setup_pci_attribute *to_setup_pci_attr(
+							struct attribute *attr)
+{
+	return container_of(attr, struct setup_pci_attribute, attr);
+}
+
+static ssize_t setup_pci_attr_show(struct kobject *kobj,
+				   struct attribute *attr, char *buf)
+{
+	struct firmware_setup_pci_entry *entry = to_setup_pci_entry(kobj);
+	struct setup_pci_attribute *setup_pci_attr = to_setup_pci_attr(attr);
+
+	return setup_pci_attr->show(entry, buf);
+}
+
+static struct setup_pci_attribute setup_pci_vendor_attr = __ATTR_RO(vendor);
+static struct setup_pci_attribute setup_pci_devid_attr = __ATTR_RO(devid);
+static struct setup_pci_attribute setup_pci_pcilen_attr = __ATTR_RO(pcilen);
+static struct setup_pci_attribute setup_pci_segment_attr = __ATTR_RO(segment);
+static struct setup_pci_attribute setup_pci_bus_attr = __ATTR_RO(bus);
+static struct setup_pci_attribute setup_pci_device_attr = __ATTR_RO(device);
+static struct setup_pci_attribute setup_pci_function_attr = __ATTR_RO(function);
+
+/*
+ * These are default attributes that are added for every memmap entry.
+ */
+static struct attribute *def_attrs[] = {
+	&setup_pci_vendor_attr.attr,
+	&setup_pci_devid_attr.attr,
+	&setup_pci_pcilen_attr.attr,
+	&setup_pci_segment_attr.attr,
+	&setup_pci_bus_attr.attr,
+	&setup_pci_device_attr.attr,
+	&setup_pci_function_attr.attr,
+	NULL
+};
+
+static const struct sysfs_ops setup_pci_attr_ops = {
+	.show = setup_pci_attr_show,
+};
+
+static struct kobj_type __refdata setup_pci_ktype = {
+	.sysfs_ops      = &setup_pci_attr_ops,
+	.default_attrs  = def_attrs,
+};
+
+static ssize_t setup_pci_rom_read(struct file *filp, struct kobject *kobj,
+				  struct bin_attribute *bin_attr, char *buf,
+				  loff_t off, size_t count)
+{
+	struct firmware_setup_pci_entry *entry = to_setup_pci_entry(kobj);
+
+	if (off >= entry->pcilen)
+		count = 0;
+	else {
+		unsigned char *rom = phys_to_virt(entry->romdata);
+
+		if (off + count > entry->pcilen)
+			count = entry->pcilen - off;
+
+		memcpy(buf, rom + off, count);
+	}
+
+	return count;
+}
+
+static int __init add_sysfs_fw_setup_pci_entry(
+					struct firmware_setup_pci_entry *entry)
+{
+	int retval = 0;
+	static int setup_pci_entries_nr;
+	static struct kset *setup_pci_kset;
+	struct bin_attribute *attr;
+
+	kobject_init(&entry->kobj, &setup_pci_ktype);
+
+	if (!setup_pci_kset) {
+		setup_pci_kset = kset_create_and_add("setup_pci", NULL,
+						     firmware_kobj);
+		if (!setup_pci_kset)
+			return -ENOMEM;
+	}
+
+	entry->kobj.kset = setup_pci_kset;
+	retval = kobject_add(&entry->kobj, NULL, "%d", setup_pci_entries_nr++);
+	if (retval) {
+		kobject_put(&entry->kobj);
+		return retval;
+	}
+
+	attr = kzalloc(sizeof(*attr), GFP_ATOMIC);
+	if (!attr)
+		return -ENOMEM;
+
+	sysfs_bin_attr_init(attr);
+	attr->size = entry->pcilen;
+	attr->attr.name = "rom";
+	attr->attr.mode = S_IRUSR;
+	attr->read = setup_pci_rom_read;
+	retval = sysfs_create_bin_file(&entry->kobj, attr);
+	if (retval)
+		kfree(attr);
+	entry->rom_attr = attr;
+
+	return retval;
+}
+
+static int __init firmware_setup_pci_init(void)
+{
+	struct firmware_setup_pci_entry *entry;
+
+	list_for_each_entry(entry, &setup_pci_entries, list)
+		add_sysfs_fw_setup_pci_entry(entry);
+
+	return 0;
+}
+late_initcall(firmware_setup_pci_init);
+#endif
+
 int pcibios_enable_device(struct pci_dev *dev, int mask)
 {
 	int err;
Signature · 1 line
1.8.4.5