linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* Early cpio decoder and ACPI table override via initrd making use of it
@ 2012-08-30  9:29 Thomas Renninger
  2012-08-30  9:29 ` [PATCH 1/2] lib: Add early cpio decoder Thomas Renninger
  2012-08-30  9:29 ` [PATCH 2/2] ACPI: Override arbitrary ACPI tables via initrd for debugging Thomas Renninger
  0 siblings, 2 replies; 8+ messages in thread
From: Thomas Renninger @ 2012-08-30  9:29 UTC (permalink / raw)
  To: hpa
  Cc: linux-kernel, lenb, robert.moore, ming.m.lin, initramfs, bigeasy,
	vojcek, eric.piel, linux-acpi, yinghai

This is based on the early cpio decoder from hpa.
The first patch is exactly the patch (41750d31fc9599fd81763e685) from git repo:
kernel/git/hpa/linux-earlyinitramfs. Ony the Makefile was slightly
adjusted to latest Linus kernel (3.6.0-rc3).

Because of the special requirements hpa mentioned, I gave up the cleaner
callback approach. It may make sense to revive it if there are potential
more users and let the very early code call the cpio decoder directly and
let others use the callback interface I had made up.

Anyway, it would be great to see these two patches showing up in a
mainline branch and linux-next asap, so that they can slip in with the next
(3.7) merge window.

Working on ACPI features or debugging ACPI problems is a nightmare on Linux
currently. ACPI table overrding via initrd makes working on such problems
a lot easier.

   Thomas


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH 1/2] lib: Add early cpio decoder
  2012-08-30  9:29 Early cpio decoder and ACPI table override via initrd making use of it Thomas Renninger
@ 2012-08-30  9:29 ` Thomas Renninger
  2012-09-04 17:00   ` H. Peter Anvin
  2012-08-30  9:29 ` [PATCH 2/2] ACPI: Override arbitrary ACPI tables via initrd for debugging Thomas Renninger
  1 sibling, 1 reply; 8+ messages in thread
From: Thomas Renninger @ 2012-08-30  9:29 UTC (permalink / raw)
  To: hpa
  Cc: linux-kernel, lenb, robert.moore, ming.m.lin, initramfs, bigeasy,
	vojcek, eric.piel, linux-acpi, yinghai, H. Peter Anvin,
	Thomas Renninger

From: "H. Peter Anvin" <hpa@linux.intel.com>

Add a simple cpio decoder without library dependencies for the purpose
of extracting components from the initramfs blob for early kernel
uses.  Intended consumers so far are microcode and ACPI override.

Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
CC: Thomas Renninger <trenn@suse.de>
Link: http://lkml.kernel.org/r/201203261651.29640.trenn@suse.de
Signed-off-by: Thomas Renninger <trenn@suse.de>
---
 include/linux/earlycpio.h |   13 ++++
 lib/Makefile              |    2 +-
 lib/earlycpio.c           |  173 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 187 insertions(+), 1 deletions(-)
 create mode 100644 include/linux/earlycpio.h
 create mode 100644 lib/earlycpio.c

diff --git a/include/linux/earlycpio.h b/include/linux/earlycpio.h
new file mode 100644
index 0000000..06db026
--- /dev/null
+++ b/include/linux/earlycpio.h
@@ -0,0 +1,13 @@
+#ifndef _LINUX_EARLYCPIO_H
+#define _LINUX_EARLYCPIO_H
+
+#include <linux/types.h>
+
+struct cpio_data {
+	void *data;
+	size_t size;
+};
+
+struct cpio_data find_cpio_data(const char *name, const void *data, size_t len);
+
+#endif /* _LINUX_EARLYCPIO_H */
diff --git a/lib/Makefile b/lib/Makefile
index 42d283e..0924041 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -12,7 +12,7 @@ lib-y := ctype.o string.o vsprintf.o cmdline.o \
 	 idr.o int_sqrt.o extable.o prio_tree.o \
 	 sha1.o md5.o irq_regs.o reciprocal_div.o argv_split.o \
 	 proportions.o flex_proportions.o prio_heap.o ratelimit.o show_mem.o \
-	 is_single_threaded.o plist.o decompress.o
+	 is_single_threaded.o plist.o decompress.o earlycpio.o
 
 lib-$(CONFIG_MMU) += ioremap.o
 lib-$(CONFIG_SMP) += cpumask.o
diff --git a/lib/earlycpio.c b/lib/earlycpio.c
new file mode 100644
index 0000000..b16b80b
--- /dev/null
+++ b/lib/earlycpio.c
@@ -0,0 +1,173 @@
+/* ----------------------------------------------------------------------- *
+ *
+ *   Copyright 2012 Intel Corporation; author H. Peter Anvin
+ *
+ *   This file is part of the Linux kernel, and is made available
+ *   under the terms of the GNU General Public License version 2, as
+ *   published by the Free Software Foundation.
+ *
+ *   This program is distributed in the hope it will be useful, but
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *   General Public License for more details.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * earlycpio.c
+ *
+ * Find a specific cpio member; must precede any compressed content.
+ * This is used to locate data items in the initramfs used by the
+ * kernel itself during early boot (before the main initramfs is
+ * decompressed.)  It is the responsibility of the initramfs creator
+ * to ensure that these items are uncompressed at the head of the
+ * blob.  Depending on the boot loader or package tool that may be a
+ * separate file or part of the same file.
+ *
+ * For some architectures, e.g. i386, this file must compile to have
+ * no relocations and no library dependencies, so it can be called from
+ * a nonstandard environment.  Therefore some normal library functions
+ * are inlined in this file.
+ */
+
+#include <linux/earlycpio.h>
+#include <linux/kernel.h>
+
+enum cpio_fields {
+	C_MAGIC,
+	C_INO,
+	C_MODE,
+	C_UID,
+	C_GID,
+	C_NLINK,
+	C_MTIME,
+	C_FILESIZE,
+	C_MAJ,
+	C_MIN,
+	C_RMAJ,
+	C_RMIN,
+	C_NAMESIZE,
+	C_CHKSUM,
+	C_NFIELDS
+};
+
+#ifdef CONFIG_X86
+static inline size_t strlen(const char *name)
+{
+	size_t n = -1;
+
+	asm("repne; scasb"
+	    : "+D" (name), "+c" (n)
+	    : "a" (0));
+
+	return -2 - n;
+}
+
+static inline int memcmp(const void *p1, const void *p2, size_t n)
+{
+	unsigned char rv;
+
+	asm("repe; cmpsb; setne %0"
+	    : "=r" (rv), "+S" (p1), "+D" (p2), "+c" (n));
+
+	return rv;
+}
+#else
+static inline size_t strlen(const char *name)
+{
+	size_t n = 0;
+
+	while (*name++)
+		n++;
+
+	return n;
+}
+
+static inline int memcmp(const void *p1, const void *p2, size_t n)
+{
+	const unsigned char *u1 = p1;
+	const unsigned char *u2 = p2;
+	int d;
+
+	while (n--) {
+		d = *u2++ - *u1++;
+		if (d)
+			return d;
+	}
+	return 0;
+}
+#endif
+
+struct cpio_data __cpuinit find_cpio_data(const char *name,
+					  const void *data, size_t len)
+{
+	const size_t cpio_header_len = 8*C_NFIELDS - 2;
+	struct cpio_data cd = { NULL, 0 };
+	const char *p, *dptr, *nptr;
+	unsigned int ch[C_NFIELDS], *chp, v;
+	unsigned char c, x;
+	size_t mynamesize = strlen(name) + 1;
+	int i, j;
+
+	p = data;
+
+	while (len > cpio_header_len) {
+		if (!*p) {
+			/* All cpio headers need to be 4-byte aligned */
+			p += 4;
+			len -= 4;
+			continue;
+		}
+
+		j = 6;		/* The magic field is only 6 characters */
+		chp = ch;
+		for (i = C_NFIELDS; i; i--) {
+			v = 0;
+			while (j--) {
+				v <<= 4;
+				c = *p++;
+
+				x = c - '0';
+				if (x < 10) {
+					v += x;
+					continue;
+				}
+
+				x = (c | 0x20) - 'a';
+				if (x < 6) {
+					v += x + 10;
+					continue;
+				}
+
+				goto quit; /* Invalid hexadecimal */
+			}
+			*chp++ = v;
+			j = 8;	/* All other fields are 8 characters */
+		}
+
+		if ((ch[C_MAGIC] - 0x070701) > 1)
+			goto quit; /* Invalid magic */
+
+		len -= cpio_header_len;
+
+		dptr = PTR_ALIGN(p + ch[C_NAMESIZE], 4);
+		nptr = PTR_ALIGN(dptr + ch[C_FILESIZE], 4);
+
+		if (nptr > p + len || dptr < p || nptr < dptr)
+			goto quit; /* Buffer overrun */
+
+		if ((ch[C_MODE] & 0170000) == 0100000 &&
+		    ch[C_NAMESIZE] == mynamesize &&
+		    !memcmp(p, name, mynamesize)) {
+			cd.data = (void *)dptr;
+			cd.size = ch[C_FILESIZE];
+			return cd; /* Found it! */
+		}
+
+		len -= (nptr - p);
+		p = nptr;
+	}
+
+quit:
+	return cd;
+}
-- 
1.7.6.1


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 2/2] ACPI: Override arbitrary ACPI tables via initrd for debugging
  2012-08-30  9:29 Early cpio decoder and ACPI table override via initrd making use of it Thomas Renninger
  2012-08-30  9:29 ` [PATCH 1/2] lib: Add early cpio decoder Thomas Renninger
@ 2012-08-30  9:29 ` Thomas Renninger
  2012-08-30  9:34   ` Thomas Renninger
  1 sibling, 1 reply; 8+ messages in thread
From: Thomas Renninger @ 2012-08-30  9:29 UTC (permalink / raw)
  To: hpa
  Cc: linux-kernel, lenb, robert.moore, ming.m.lin, initramfs, bigeasy,
	vojcek, eric.piel, linux-acpi, yinghai, Thomas Renninger

Details can be found in:
Documentation/acpi/initrd_table_override.txt

Signed-off-by: Thomas Renninger <trenn@suse.de>
CC: eric.piel@tremplin-utc.net
CC: vojcek@tlen.pl
CC: Lin Ming <ming.m.lin@intel.com>
CC: lenb@kernel.org
CC: robert.moore@intel.com
CC: hpa@zytor.com
CC: yinghai@kernel.org
---
 Documentation/acpi/initrd_table_override.txt |  122 ++++++++++++++++
 arch/x86/kernel/setup.c                      |    4 +
 drivers/acpi/Kconfig                         |    9 ++
 drivers/acpi/osl.c                           |  200 ++++++++++++++++++++++++--
 include/linux/acpi.h                         |    4 +
 5 files changed, 328 insertions(+), 11 deletions(-)
 create mode 100644 Documentation/acpi/initrd_table_override.txt

diff --git a/Documentation/acpi/initrd_table_override.txt b/Documentation/acpi/initrd_table_override.txt
new file mode 100644
index 0000000..22222c0
--- /dev/null
+++ b/Documentation/acpi/initrd_table_override.txt
@@ -0,0 +1,122 @@
+Overriding ACPI tables via initrd
+=================================
+
+1) Introduction (What is this about)
+2) What is this for
+3) How does it work
+4) References (Where to retrieve userspace tools)
+
+1) What is this about
+---------------------
+
+If ACPI_INITRD_TABLE_OVERRIDE compile option is true, it is possible to
+override nearly any ACPI table provided by the BIOS with an instrumented,
+modified one.
+
+For a full list of ACPI tables that can be overridden, take a look at
+the char *table_sigs[MAX_ACPI_SIGNATURE]; definition in drivers/acpi/osl.c
+All ACPI tables iasl (Intel's ACPI compiler and disassembler) knows should
+be overridable, except:
+   - ACPI_SIG_RSDP (has a signature of 6 bytes)
+   - ACPI_SIG_FACS (does not have an ordinary ACPI table header)
+Both could get implemented as well.
+
+
+2) What is this for
+-------------------
+
+Please keep in mind that this is a debug option.
+ACPI tables should not get overridden for productive use.
+If BIOS ACPI tables are overridden the kernel will get tainted with the
+TAINT_OVERRIDDEN_ACPI_TABLE flag.
+Complain to your platform/BIOS vendor if you find a bug which is that sever
+that a workaround is not accepted in the Linus kernel.
+
+Still, it can and should be enabled in any kernel, because:
+  - There is no functional change with not instrumented initrds
+  - It provides a powerful feature to easily debug and test ACPI BIOS table
+    compatibility with the Linux kernel.
+
+Until now it was only possible to override the DSDT by compiling it into
+the kernel. This is a nightmare when trying to work on ACPI related bugs
+and a lot bugs got stuck because of that.
+Even for people with enough kernel knowledge, building a kernel to try out
+things is very time consuming. Also people may have to browse and modify the
+ACPI interpreter code to find a possible BIOS bug. With this feature, people
+can correct the ACPI tables and try out quickly whether this is the root cause
+that needs to get addressed in the kernel.
+
+This could even ease up testing for BIOS providers who could flush their BIOS
+to test, but overriding table via initrd is much easier and quicker.
+For example one could prepare different initrds overriding NUMA tables with
+different affinity settings. Set up a script, let the machine reboot and
+run tests over night and one can get a picture how these settings influence
+the Linux kernel and which values are best.
+
+People can instrument the dynamic ACPI (ASL) code (for example with debug
+statements showing up in syslog when the ACPI code is processed, etc.),
+to better understand BIOS to OS interfaces, to hunt down ACPI BIOS code related
+bugs quickly or to easier develop ACPI based drivers.
+
+Intstrumenting ACPI code in SSDTs is now much easier. Before, one had to copy
+all SSDTs into the DSDT to compile it into the kernel for testing
+(because only DSDT could get overridden). That's what the acpi_no_auto_ssdt
+boot param is for: the BIOS provided SSDTs are ignored and all have to get
+copied into the DSDT, complicated and time consuming.
+
+Much more use cases, depending on which ACPI parts you are working on...
+
+
+3) How does it work
+-------------------
+
+# Extract the machine's ACPI tables:
+cd /tmp
+acpidump >acpidump
+acpixtract -a acpidump
+# Disassemble, modify and recompile them:
+iasl -d *.dat
+# For example add this statement into a _PRT (PCI Routing Table) function
+# of the DSDT:
+Store("HELLO WORLD", debug)
+iasl -sa DSDT.dsl
+# Add the raw ACPI tables to an uncompressed cpio archive.
+# They must be put into /kernel/firmware/acpi/table[0-9].dat files inside the
+# cpio archive. Starting with table0.dat
+# The one uncompressed cpio archive and it must be the first.
+# Other, typically compressed cpio archives, must be
+# concatenated on top of the uncompressed one.
+mkdir -p kernel/firmware/acpi
+cp DSDT.aml kernel/firmware/acpi/table0.dat
+# Add other ACPI tables, the filename's number must increase in order
+# A maximum of ten tables (table9.dat) are allowed:
+iasl -sa FACP.dsl
+iasl -sa SSDT1.dsl
+cp FACP.aml kernel/firmware/acpi/table1.dat
+cp SSDT1.aml kernel/firmware/acpi/table2.dat
+# Create the uncompressed cpio archive and concatenate the orginal initrd
+# on top:
+find kernel | cpio -H newc --create > /boot/instrumented_initrd
+cat /boot/initrd >>/boot/instrumented_initrd
+# reboot with increased acpi debug level, e.g. boot params:
+acpi.debug_level=0x2 acpi.debug_layer=0xFFFFFFFF
+# and check your syslog:
+[    1.268089] ACPI: PCI Interrupt Routing Table [\_SB_.PCI0._PRT]
+[    1.272091] [ACPI Debug]  String [0x0B] "HELLO WORLD"
+
+iasl is able to disassemble and recompile quite a lot different,
+also static ACPI tables.
+
+4) Where to retrieve userspace tools
+------------------------------------
+
+iasl and acpixtract are part of Intel's ACPICA project:
+http://acpica.org/
+and should be packaged by distributions (for example in the acpica package
+on SUSE).
+
+acpidump can be found in Len Browns pmtools:
+ftp://kernel.org/pub/linux/kernel/people/lenb/acpi/utils/pmtools/acpidump
+This tool is also part of the acpica package on SUSE.
+Alternatively, used ACPI tables can be retrieved via sysfs in latest kernels:
+/sys/firmware/acpi/tables
diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c
index f4b9b80..6a91058 100644
--- a/arch/x86/kernel/setup.c
+++ b/arch/x86/kernel/setup.c
@@ -941,6 +941,10 @@ void __init setup_arch(char **cmdline_p)
 
 	reserve_initrd();
 
+#ifdef CONFIG_ACPI_INITRD_TABLE_OVERRIDE
+	acpi_initrd_override((void *)initrd_start, initrd_end - initrd_start);
+#endif
+
 	reserve_crashkernel();
 
 	vsmp_init();
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index 8099895..a508f77 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -261,6 +261,15 @@ config ACPI_CUSTOM_DSDT
 	bool
 	default ACPI_CUSTOM_DSDT_FILE != ""
 
+config ACPI_INITRD_TABLE_OVERRIDE
+	bool
+	default y
+	help
+	  This option provides functionality to override arbitrary ACPI tables
+	  via initrd. No functional change if no ACPI tables are passed via
+	  initrd, therefore it's safe to say Y.
+	  See Documentation/acpi/initrd_table_override.txt for details
+
 config ACPI_BLACKLIST_YEAR
 	int "Disable ACPI for systems before Jan 1st this year" if X86_32
 	default 0
diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c
index 9eaf708..c549c7d 100644
--- a/drivers/acpi/osl.c
+++ b/drivers/acpi/osl.c
@@ -534,6 +534,134 @@ acpi_os_predefined_override(const struct acpi_predefined_names *init_val,
 	return AE_OK;
 }
 
+#ifdef CONFIG_ACPI_INITRD_TABLE_OVERRIDE
+#include <linux/earlycpio.h>
+#include <linux/memblock.h>
+
+#include <asm/e820.h>
+
+static u64 acpi_tables_addr;
+static int all_tables_size;
+
+/* Copied from acpica/tbutils.c:acpi_tb_checksum() */
+u8 __init acpi_table_checksum(u8 *buffer, u32 length)
+{
+	u8 sum = 0;
+	u8 *end = buffer + length;
+
+	while (buffer < end)
+		sum = (u8) (sum + *(buffer++));
+	return sum;
+}
+
+/* All but ACPI_SIG_RSDP and ACPI_SIG_FACS: */
+static const char * const table_sigs[] = {
+	ACPI_SIG_BERT, ACPI_SIG_CPEP, ACPI_SIG_ECDT, ACPI_SIG_EINJ,
+	ACPI_SIG_ERST, ACPI_SIG_HEST, ACPI_SIG_MADT, ACPI_SIG_MSCT,
+	ACPI_SIG_SBST, ACPI_SIG_SLIT, ACPI_SIG_SRAT, ACPI_SIG_ASF,
+	ACPI_SIG_BOOT, ACPI_SIG_DBGP, ACPI_SIG_DMAR, ACPI_SIG_HPET,
+	ACPI_SIG_IBFT, ACPI_SIG_IVRS, ACPI_SIG_MCFG, ACPI_SIG_MCHI,
+	ACPI_SIG_SLIC, ACPI_SIG_SPCR, ACPI_SIG_SPMI, ACPI_SIG_TCPA,
+	ACPI_SIG_UEFI, ACPI_SIG_WAET, ACPI_SIG_WDAT, ACPI_SIG_WDDT,
+	ACPI_SIG_WDRT, ACPI_SIG_DSDT, ACPI_SIG_FADT, ACPI_SIG_PSDT,
+	ACPI_SIG_RSDT, ACPI_SIG_XSDT, ACPI_SIG_SSDT, NULL };
+
+/* Non-fatal errors: Affected tables/files are ignored */
+#define INVALID_TABLE(x, name) \
+	{ pr_err("ACPI OVERRIDE: " x " [%s]\n", name); continue; }
+
+#define ACPI_HEADER_SIZE sizeof(struct acpi_table_header)
+
+/* Must not increase 10 or needs code modifcation below */
+#define ACPI_OVERRIDE_TABLES 10
+
+void __init acpi_initrd_override(void *data, size_t size)
+{
+	int sig, no, table_nr = 0, offset = 0;
+	struct acpi_table_header *table;
+	char cpio_path[32] = "kernel/firmware/acpi/tableX.dat";
+	struct cpio_data file;
+	struct cpio_data early_initrd_files[ACPI_OVERRIDE_TABLES];
+	char *p;
+
+	if (data == NULL)
+		return;
+
+	for (no = 0; no < ACPI_OVERRIDE_TABLES; no++) {
+		cpio_path[26] = '0' + no;
+		printk(KERN_DEBUG "ACPI override: Trying to find %s\n",
+		       cpio_path);
+		file = find_cpio_data(cpio_path, data, size);
+		if (!file.data)
+			break;
+
+		if (file.size < sizeof(struct acpi_table_header))
+			INVALID_TABLE("Table smaller than ACPI header",
+				      cpio_path);
+
+		table = file.data;
+
+		for (sig = 0; table_sigs[sig]; sig++)
+			if (!memcmp(table->signature, table_sigs[sig], 4))
+				break;
+
+		if (!table_sigs[sig])
+			INVALID_TABLE("Unknown signature", cpio_path);
+		if (file.size != table->length)
+			INVALID_TABLE("File length does not match table length",
+				      cpio_path);
+		if (acpi_table_checksum(file.data, table->length))
+			INVALID_TABLE("Bad table checksum", cpio_path);
+
+		pr_info("%4.4s ACPI table found in initrd [%s][0x%x]\n",
+			table->signature, cpio_path, table->length);
+
+		all_tables_size += table->length;
+		early_initrd_files[table_nr].data = file.data;
+		early_initrd_files[table_nr].size = file.size;
+		table_nr++;
+	}
+	if (table_nr == 0)
+		return;
+
+	acpi_tables_addr =
+		memblock_find_in_range(0, max_low_pfn_mapped << PAGE_SHIFT,
+				       all_tables_size, PAGE_SIZE);
+	if (!acpi_tables_addr) {
+		WARN_ON(1);
+		return;
+	}
+	/*
+	 * Only calling e820_add_reserve does not work and the
+	 * tables are invalid (memory got used) later.
+	 * memblock_x86_reserve_range works as expected and the tables
+	 * won't get modified. But it's not enough because ioremap will
+	 * complain later (used by acpi_os_map_memory) that the pages
+	 * that should get mapped are not marked "reserved".
+	 * Both memblock_x86_reserve_range and e820_add_region works fine.
+	 */
+	memblock_reserve(acpi_tables_addr, acpi_tables_addr + all_tables_size);
+	e820_add_region(acpi_tables_addr, all_tables_size, E820_ACPI);
+	update_e820();
+	p = early_ioremap(acpi_tables_addr, all_tables_size);
+
+	for (no = 0; no < table_nr; no++) {
+		memcpy(p + offset, early_initrd_files[no].data,
+		       early_initrd_files[no].size);
+		offset += early_initrd_files[no].size;
+	}
+	early_iounmap(p, all_tables_size);
+}
+#endif /* CONFIG_ACPI_INITRD_TABLE_OVERRIDE */
+
+static void acpi_table_taint(struct acpi_table_header *table)
+{
+	pr_warn(PREFIX
+		"Override [%4.4s-%8.8s], this is unsafe: tainting kernel\n",
+		table->signature, table->oem_table_id);
+	add_taint(TAINT_OVERRIDDEN_ACPI_TABLE);
+}
+
 acpi_status
 acpi_os_table_override(struct acpi_table_header * existing_table,
 		       struct acpi_table_header ** new_table)
@@ -547,24 +675,74 @@ acpi_os_table_override(struct acpi_table_header * existing_table,
 	if (strncmp(existing_table->signature, "DSDT", 4) == 0)
 		*new_table = (struct acpi_table_header *)AmlCode;
 #endif
-	if (*new_table != NULL) {
-		printk(KERN_WARNING PREFIX "Override [%4.4s-%8.8s], "
-			   "this is unsafe: tainting kernel\n",
-		       existing_table->signature,
-		       existing_table->oem_table_id);
-		add_taint(TAINT_OVERRIDDEN_ACPI_TABLE);
-	}
+	if (*new_table != NULL)
+		acpi_table_taint(existing_table);
 	return AE_OK;
 }
 
 acpi_status
 acpi_os_physical_table_override(struct acpi_table_header *existing_table,
-				acpi_physical_address * new_address,
-				u32 *new_table_length)
+				acpi_physical_address *address,
+				u32 *table_length)
 {
-	return AE_SUPPORT;
-}
+#ifndef CONFIG_ACPI_INITRD_TABLE_OVERRIDE
+	*table_length = 0;
+	*address = 0;
+	return AE_OK;
+#else
+	int table_offset = 0;
+	struct acpi_table_header *table;
+
+	*table_length = 0;
+	*address = 0;
+
+	if (!acpi_tables_addr)
+		return AE_OK;
+
+	do {
+		if (table_offset + ACPI_HEADER_SIZE > all_tables_size) {
+			WARN_ON(1);
+			return AE_OK;
+		}
+
+		table = acpi_os_map_memory(acpi_tables_addr + table_offset,
+					   ACPI_HEADER_SIZE);
 
+		if (table_offset + table->length > all_tables_size) {
+			acpi_os_unmap_memory(table, ACPI_HEADER_SIZE);
+			WARN_ON(1);
+			return AE_OK;
+		}
+
+		table_offset += table->length;
+
+		if (memcmp(existing_table->signature, table->signature, 4)) {
+			acpi_os_unmap_memory(table,
+				     ACPI_HEADER_SIZE);
+			continue;
+		}
+
+		/* Only override tables with matching oem id */
+		if (memcmp(table->oem_table_id, existing_table->oem_table_id,
+			   ACPI_OEM_TABLE_ID_SIZE)) {
+			acpi_os_unmap_memory(table,
+				     ACPI_HEADER_SIZE);
+			continue;
+		}
+
+		table_offset -= table->length;
+		*table_length = table->length;
+		acpi_os_unmap_memory(table, ACPI_HEADER_SIZE);
+		*address = acpi_tables_addr + table_offset;
+		add_taint(TAINT_OVERRIDDEN_ACPI_TABLE);
+		break;
+	} while (table_offset + ACPI_HEADER_SIZE < all_tables_size);
+
+	if (*address != 0)
+		acpi_table_taint(existing_table);
+	return AE_OK;
+#endif
+}
 
 static irqreturn_t acpi_irq(int irq, void *dev_id)
 {
diff --git a/include/linux/acpi.h b/include/linux/acpi.h
index 4f2a762..87e2c9e 100644
--- a/include/linux/acpi.h
+++ b/include/linux/acpi.h
@@ -76,6 +76,10 @@ typedef int (*acpi_table_handler) (struct acpi_table_header *table);
 
 typedef int (*acpi_table_entry_handler) (struct acpi_subtable_header *header, const unsigned long end);
 
+#ifdef CONFIG_ACPI_INITRD_TABLE_OVERRIDE
+void __init acpi_initrd_override(void *data, size_t size);
+#endif
+
 char * __acpi_map_table (unsigned long phys_addr, unsigned long size);
 void __acpi_unmap_table(char *map, unsigned long size);
 int early_acpi_boot_init(void);
-- 
1.7.6.1


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [PATCH 2/2] ACPI: Override arbitrary ACPI tables via initrd for debugging
  2012-08-30  9:29 ` [PATCH 2/2] ACPI: Override arbitrary ACPI tables via initrd for debugging Thomas Renninger
@ 2012-08-30  9:34   ` Thomas Renninger
  0 siblings, 0 replies; 8+ messages in thread
From: Thomas Renninger @ 2012-08-30  9:34 UTC (permalink / raw)
  To: hpa, initramfs
  Cc: linux-kernel, lenb, robert.moore, bigeasy, vojcek, eric.piel,
	linux-acpi, yinghai

On Thursday, August 30, 2012 11:29:17 AM Thomas Renninger wrote:
> Details can be found in:
> Documentation/acpi/initrd_table_override.txt
> 
> Signed-off-by: Thomas Renninger <trenn@suse.de>
> CC: eric.piel@tremplin-utc.net
> CC: vojcek@tlen.pl
> CC: Lin Ming <ming.m.lin@intel.com>
Lin Ming's address is not valid anymore?:
<ming.m.lin@intel.com>: host mga01.intel.com[192.55.52.88] said: 550 #5.1.0
    Address rejected. (in reply to RCPT TO command)

Please remove it from CC list if you reply on one of these.

Would be great if it can get removed from CC list in the patch
without the need of resending (if the patches are fine).

Thanks,

   Thomas

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 1/2] lib: Add early cpio decoder
  2012-08-30  9:29 ` [PATCH 1/2] lib: Add early cpio decoder Thomas Renninger
@ 2012-09-04 17:00   ` H. Peter Anvin
  2012-09-21 12:51     ` Thomas Renninger
  0 siblings, 1 reply; 8+ messages in thread
From: H. Peter Anvin @ 2012-09-04 17:00 UTC (permalink / raw)
  To: Thomas Renninger
  Cc: linux-kernel, lenb, robert.moore, Fenghua Yu, initramfs, bigeasy,
	vojcek, eric.piel, linux-acpi, yinghai, H. Peter Anvin

On 08/30/2012 02:29 AM, Thomas Renninger wrote:
> From: "H. Peter Anvin" <hpa@linux.intel.com>
>
> Add a simple cpio decoder without library dependencies for the purpose
> of extracting components from the initramfs blob for early kernel
> uses.  Intended consumers so far are microcode and ACPI override.
>
> Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
> CC: Thomas Renninger <trenn@suse.de>
> Link: http://lkml.kernel.org/r/201203261651.29640.trenn@suse.de
> Signed-off-by: Thomas Renninger <trenn@suse.de>

I was trying to figure out if there is a way to do what you want 
(support for multiple files) without the problems of the callback 
interface.  I think it is actually fairly straightforward; we need a 
prefix iterator (so you can give it a string like "kernel/acpi/" rather 
than a full filename) and it needs to be able to accept a "last" pointer 
so it can resume scanning at the point it last left off.  That should be 
a pretty trivial change.

The other thing we presumably want to do -- and this is generic -- is to 
be able to handle multiple sources for the initramfs; at the very least 
there is built in vs provided from the boot loader.  I had originally 
intended to just handle that by calling the earlycpio function once per 
block, but the "last left off" bit makes that a little harder.  Need to 
think about that a little bit.

I am guessing that this may not need to be something we need from the 
very beginning, or am I wrong?

	-hpa

-- 
H. Peter Anvin, Intel Open Source Technology Center
I work for Intel.  I don't speak on their behalf.


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 1/2] lib: Add early cpio decoder
  2012-09-04 17:00   ` H. Peter Anvin
@ 2012-09-21 12:51     ` Thomas Renninger
  2012-09-25  3:47       ` H. Peter Anvin
  0 siblings, 1 reply; 8+ messages in thread
From: Thomas Renninger @ 2012-09-21 12:51 UTC (permalink / raw)
  To: H. Peter Anvin
  Cc: linux-kernel, lenb, robert.moore, Fenghua Yu, initramfs, bigeasy,
	vojcek, eric.piel, linux-acpi, yinghai, H. Peter Anvin

On Tuesday, September 04, 2012 07:00:09 PM H. Peter Anvin wrote:
> On 08/30/2012 02:29 AM, Thomas Renninger wrote:
> > From: "H. Peter Anvin" <hpa@linux.intel.com>
> >
> > Add a simple cpio decoder without library dependencies for the purpose
> > of extracting components from the initramfs blob for early kernel
> > uses.  Intended consumers so far are microcode and ACPI override.
> >
> > Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
> > CC: Thomas Renninger <trenn@suse.de>
> > Link: http://lkml.kernel.org/r/201203261651.29640.trenn@suse.de
> > Signed-off-by: Thomas Renninger <trenn@suse.de>
> 
> I was trying to figure out if there is a way to do what you want 
> (support for multiple files) without the problems of the callback 
> interface.  I think it is actually fairly straightforward; we need a 
> prefix iterator (so you can give it a string like "kernel/acpi/" rather 
> than a full filename) and it needs to be able to accept a "last" pointer 
> so it can resume scanning at the point it last left off.  That should be 
> a pretty trivial change.
Yep, this is a good idea.
 
> The other thing we presumably want to do -- and this is generic -- is to 
> be able to handle multiple sources for the initramfs; at the very least 
> there is built in vs provided from the boot loader.  I had originally 
> intended to just handle that by calling the earlycpio function once per 
> block, but the "last left off" bit makes that a little harder.  Need to 
> think about that a little bit.
I guess I understand the first part, not sure about the "last left off" 
bit.
"Multiple sources" means bootloader already points to multiple sources,
right?
This is somewhat out of scope as this would need both, bootloader and
kernel adjustings.
This is about "built in" which means multiple cpios concatenated together
and passed via bootloader as one "file" (initrd).
At least I understand it that way.
The only disadvantage I run into is that once the cpios are concatenated,
I couldn't figure out an easy way how to slice them into separate
cpio/zip archives again.

> I am guessing that this may not need to be something we need from the 
> very beginning, or am I wrong?

I have re-worked the patches:
  - added an offset argument to be able to iterate over files inside
    a directory as suggested
  - removed the strlen and memcmp function duplication in earlycpio.c
    This is not needed for this patchset and would be confusing.
    This can easily be added in the context of the patches which need it

I'll re-submit.

Thanks,

   Thomas

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 1/2] lib: Add early cpio decoder
  2012-09-21 12:51     ` Thomas Renninger
@ 2012-09-25  3:47       ` H. Peter Anvin
  0 siblings, 0 replies; 8+ messages in thread
From: H. Peter Anvin @ 2012-09-25  3:47 UTC (permalink / raw)
  To: Thomas Renninger
  Cc: H. Peter Anvin, linux-kernel, lenb, robert.moore, Fenghua Yu,
	initramfs, bigeasy, vojcek, eric.piel, linux-acpi, yinghai

On 09/21/2012 05:51 AM, Thomas Renninger wrote:
> I guess I understand the first part, not sure about the "last left off" 
> bit.
> "Multiple sources" means bootloader already points to multiple sources,
> right?
> This is somewhat out of scope as this would need both, bootloader and
> kernel adjustings.
> This is about "built in" which means multiple cpios concatenated together
> and passed via bootloader as one "file" (initrd).
> At least I understand it that way.
> The only disadvantage I run into is that once the cpios are concatenated,
> I couldn't figure out an easy way how to slice them into separate
> cpio/zip archives again.

No, that's not the issue.

The issue is that there isn't just one initramfs blob, there are
(currently) *TWO*... one loaded by the bootloader and one compiled into
the kernel.

	-hpa

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH 1/2] lib: Add early cpio decoder
  2012-09-21 13:28 Early cpio decoder and ACPI table override via initrd making use of it Thomas Renninger
@ 2012-09-21 13:28 ` Thomas Renninger
  0 siblings, 0 replies; 8+ messages in thread
From: Thomas Renninger @ 2012-09-21 13:28 UTC (permalink / raw)
  To: hpa
  Cc: trenn, initramfs, robert.moore, lenb, linux-kernel, linux-acpi,
	yinghai, eric.piel, H. Peter Anvin

From: "H. Peter Anvin" <hpa@linux.intel.com>

Add a simple cpio decoder without library dependencies for the purpose
of extracting components from the initramfs blob for early kernel
uses.  Intended consumers so far are microcode and ACPI override.

Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
CC: Thomas Renninger <trenn@suse.de>
Link: http://lkml.kernel.org/r/201203261651.29640.trenn@suse.de
Link: https://lkml.org/lkml/2012/9/21/190
Signed-off-by: Thomas Renninger <trenn@suse.de>
---
 include/linux/earlycpio.h |   17 +++++
 lib/Makefile              |    2 +-
 lib/earlycpio.c           |  142 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 160 insertions(+), 1 deletions(-)
 create mode 100644 include/linux/earlycpio.h
 create mode 100644 lib/earlycpio.c

diff --git a/include/linux/earlycpio.h b/include/linux/earlycpio.h
new file mode 100644
index 0000000..111f46d
--- /dev/null
+++ b/include/linux/earlycpio.h
@@ -0,0 +1,17 @@
+#ifndef _LINUX_EARLYCPIO_H
+#define _LINUX_EARLYCPIO_H
+
+#include <linux/types.h>
+
+#define MAX_CPIO_FILE_NAME 18
+
+struct cpio_data {
+	void *data;
+	size_t size;
+	char name[MAX_CPIO_FILE_NAME];
+};
+
+struct cpio_data find_cpio_data(const char *path, void *data, size_t len,
+				long *offset);
+
+#endif /* _LINUX_EARLYCPIO_H */
diff --git a/lib/Makefile b/lib/Makefile
index 42d283e..0924041 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -12,7 +12,7 @@ lib-y := ctype.o string.o vsprintf.o cmdline.o \
 	 idr.o int_sqrt.o extable.o prio_tree.o \
 	 sha1.o md5.o irq_regs.o reciprocal_div.o argv_split.o \
 	 proportions.o flex_proportions.o prio_heap.o ratelimit.o show_mem.o \
-	 is_single_threaded.o plist.o decompress.o
+	 is_single_threaded.o plist.o decompress.o earlycpio.o
 
 lib-$(CONFIG_MMU) += ioremap.o
 lib-$(CONFIG_SMP) += cpumask.o
diff --git a/lib/earlycpio.c b/lib/earlycpio.c
new file mode 100644
index 0000000..c6f1a20
--- /dev/null
+++ b/lib/earlycpio.c
@@ -0,0 +1,142 @@
+/* ----------------------------------------------------------------------- *
+ *
+ *   Copyright 2012 Intel Corporation; author H. Peter Anvin
+ *
+ *   This file is part of the Linux kernel, and is made available
+ *   under the terms of the GNU General Public License version 2, as
+ *   published by the Free Software Foundation.
+ *
+ *   This program is distributed in the hope it will be useful, but
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *   General Public License for more details.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * earlycpio.c
+ *
+ * Find a specific cpio member; must precede any compressed content.
+ * This is used to locate data items in the initramfs used by the
+ * kernel itself during early boot (before the main initramfs is
+ * decompressed.)  It is the responsibility of the initramfs creator
+ * to ensure that these items are uncompressed at the head of the
+ * blob.  Depending on the boot loader or package tool that may be a
+ * separate file or part of the same file.
+ */
+
+#include <linux/earlycpio.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+
+enum cpio_fields {
+	C_MAGIC,
+	C_INO,
+	C_MODE,
+	C_UID,
+	C_GID,
+	C_NLINK,
+	C_MTIME,
+	C_FILESIZE,
+	C_MAJ,
+	C_MIN,
+	C_RMAJ,
+	C_RMIN,
+	C_NAMESIZE,
+	C_CHKSUM,
+	C_NFIELDS
+};
+
+/**
+ * cpio_data find_cpio_data - Search for files in an uncompressed cpio
+ * @path:   The directory to search for, including a slash at the end
+ * @data:   Pointer to the the cpio archive or a header inside
+ * @len:    Remaining length of the cpio based on data pointer
+ * @offset: When a matching file is found, this is the offset to the
+ *          beginning of the cpio. It can be used to iterate through
+ *          the cpio to find all files inside of a directory path
+ *
+ * @return: struct cpio_data containing the address, length and
+ *          filename (with the directory path cut off) of the found file.
+ */
+
+struct cpio_data __cpuinit find_cpio_data(const char *path, void *data,
+					  size_t len,  long *offset)
+{
+	const size_t cpio_header_len = 8*C_NFIELDS - 2;
+	struct cpio_data cd = { NULL, 0, "" };
+	const char *p, *dptr, *nptr;
+	unsigned int ch[C_NFIELDS], *chp, v;
+	unsigned char c, x;
+	size_t mypathsize = strlen(path);
+	int i, j;
+
+	p = data;
+
+	while (len > cpio_header_len) {
+		if (!*p) {
+			/* All cpio headers need to be 4-byte aligned */
+			p += 4;
+			len -= 4;
+			continue;
+		}
+
+		j = 6;		/* The magic field is only 6 characters */
+		chp = ch;
+		for (i = C_NFIELDS; i; i--) {
+			v = 0;
+			while (j--) {
+				v <<= 4;
+				c = *p++;
+
+				x = c - '0';
+				if (x < 10) {
+					v += x;
+					continue;
+				}
+
+				x = (c | 0x20) - 'a';
+				if (x < 6) {
+					v += x + 10;
+					continue;
+				}
+
+				goto quit; /* Invalid hexadecimal */
+			}
+			*chp++ = v;
+			j = 8;	/* All other fields are 8 characters */
+		}
+
+		if ((ch[C_MAGIC] - 0x070701) > 1)
+			goto quit; /* Invalid magic */
+
+		len -= cpio_header_len;
+
+		dptr = PTR_ALIGN(p + ch[C_NAMESIZE], 4);
+		nptr = PTR_ALIGN(dptr + ch[C_FILESIZE], 4);
+
+		if (nptr > p + len || dptr < p || nptr < dptr)
+			goto quit; /* Buffer overrun */
+
+		if ((ch[C_MODE] & 0170000) == 0100000 &&
+		    ch[C_NAMESIZE] >= mypathsize &&
+		    !memcmp(p, path, mypathsize)) {
+			*offset = (long)nptr - (long)data;
+			if (ch[C_NAMESIZE] - mypathsize >= MAX_CPIO_FILE_NAME) {
+				pr_warn(
+				"File %s exceeding MAX_CPIO_FILE_NAME [%d]\n",
+				p, MAX_CPIO_FILE_NAME);
+			}
+			strlcpy(cd.name, p + mypathsize, MAX_CPIO_FILE_NAME);
+
+			cd.data = (void *)dptr;
+			cd.size = ch[C_FILESIZE];
+			return cd; /* Found it! */
+		}
+		len -= (nptr - p);
+		p = nptr;
+	}
+
+quit:
+	return cd;
+}
-- 
1.7.6.1


^ permalink raw reply related	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2012-09-25  3:47 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-08-30  9:29 Early cpio decoder and ACPI table override via initrd making use of it Thomas Renninger
2012-08-30  9:29 ` [PATCH 1/2] lib: Add early cpio decoder Thomas Renninger
2012-09-04 17:00   ` H. Peter Anvin
2012-09-21 12:51     ` Thomas Renninger
2012-09-25  3:47       ` H. Peter Anvin
2012-08-30  9:29 ` [PATCH 2/2] ACPI: Override arbitrary ACPI tables via initrd for debugging Thomas Renninger
2012-08-30  9:34   ` Thomas Renninger
2012-09-21 13:28 Early cpio decoder and ACPI table override via initrd making use of it Thomas Renninger
2012-09-21 13:28 ` [PATCH 1/2] lib: Add early cpio decoder Thomas Renninger

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).